xiaq avatar xiaq committed f6afb9a

Replace CONTENTTYPE_GROUPS with information collected in content_registry.

Comments (0)

Files changed (6)

MoinMoin/apps/frontend/views.py

 from MoinMoin.apps.frontend import frontend
 from MoinMoin.forms import OptionalText, RequiredText, URL, YourOpenID, YourEmail, RequiredPassword, Checkbox, InlineCheckbox, Select, Tags, Natural, Submit, Hidden, MultiSelect
 from MoinMoin.items import BaseChangeForm, Item, NonExistent
+from MoinMoin.items.content import content_registry
 from MoinMoin import config, user, util
-from MoinMoin.config import CONTENTTYPE_GROUPS
 from MoinMoin.constants.keys import *
 from MoinMoin.constants.itemtypes import ITEMTYPES
 from MoinMoin.util import crypto
         abort(403)
 
 
-contenttype_groups = []
+contenttype_groups = content_registry.group_names[:]
 contenttype_group_descriptions = {}
-for gname, contenttypes in CONTENTTYPE_GROUPS:
-    contenttype_groups.append(gname)
-    contenttype_group_descriptions[gname] = ', '.join([ctlabel for ctname, ctlabel in contenttypes])
+for g in contenttype_groups:
+    contenttype_group_descriptions[g] = ', '.join([e.display_name for e in content_registry.groups[g]])
 contenttype_groups.append('unknown items')
 
 ContenttypeGroup = MultiSelect.of(Enum.using(valid_values=contenttype_groups).with_properties(descriptions=contenttype_group_descriptions)).using(optional=True)

MoinMoin/constants/contenttypes.py

 CONTENTTYPE_USER = u'application/x.moin.userprofile'
 CONTENTTYPE_DEFAULT = u'application/octet-stream'
 
-# structure for contenttype groups
-CONTENTTYPE_GROUPS = [
-    ('markup text items', [
-        ('text/x.moin.wiki;charset=utf-8', 'Wiki (MoinMoin)'),
-        ('text/x.moin.creole;charset=utf-8', 'Wiki (Creole)'),
-        ('text/x-mediawiki;charset=utf-8', 'Wiki (MediaWiki)'),
-        ('text/x-markdown;charset=utf-8', 'Markdown'),
-        ('text/x-rst;charset=utf-8', 'ReST'),
-        ('application/docbook+xml;charset=utf-8', 'DocBook'),
-        ('text/html;charset=utf-8', 'HTML'),
-    ]),
-    ('other text items', [
-        ('text/plain;charset=utf-8', 'plain text'),
-        ('text/x-diff;charset=utf-8', 'diff/patch'),
-        ('text/x-python;charset=utf-8', 'python code'),
-        ('text/csv;charset=utf-8', 'csv'),
-        ('text/x-irclog;charset=utf-8', 'IRC log'),
-    ]),
-    ('image items', [
-        ('image/jpeg', 'JPEG'),
-        ('image/png', 'PNG'),
-        ('image/svg+xml', 'SVG'),
-    ]),
-    ('audio items', [
-        ('audio/wave', 'WAV'),
-        ('audio/ogg', 'OGG'),
-        ('audio/mpeg', 'MP3'),
-        ('audio/webm', 'WebM'),
-    ]),
-    ('video items', [
-        ('video/ogg', 'OGG'),
-        ('video/webm', 'WebM'),
-        ('video/mp4', 'MP4'),
-    ]),
-    ('drawing items', [
-        ('application/x-twikidraw', 'TDRAW'),
-        ('application/x-anywikidraw', 'ADRAW'),
-        ('application/x-svgdraw', 'SVGDRAW'),
-    ]),
-    ('other items', [
-        ('application/pdf', 'PDF'),
-        ('application/zip', 'ZIP'),
-        ('application/x-tar', 'TAR'),
-        ('application/x-gtar', 'TGZ'),
-        ('application/octet-stream', 'binary file'),
-    ]),
-]
+
+GROUP_MARKUP_TEXT = 'markup text items'
+GROUP_OTHER_TEXT = 'other text items'
+GROUP_IMAGE = 'image items'
+GROUP_AUDIO = 'audio items'
+GROUP_VIDEO = 'video items'
+GROUP_DRAWING = 'drawing items'
+GROUP_OTHER = 'other items'

MoinMoin/items/__init__.py

 from MoinMoin.storage.error import NoSuchItemError, NoSuchRevisionError, StorageError
 from MoinMoin.i18n import L_
 from MoinMoin.themes import render_template
+from MoinMoin.util.mime import Type
 from MoinMoin.util.interwiki import url_for_item
 from MoinMoin.util.registry import RegistryBase
 from MoinMoin.util.clock import timed
     CONTENTTYPE, SIZE, ACTION, ADDRESS, HOSTNAME, USERID, COMMENT,
     HASH_ALGORITHM, ITEMID, REVID, DATAID, CURRENT, PARENTID
     )
-from MoinMoin.constants.contenttypes import charset, CONTENTTYPE_GROUPS
+from MoinMoin.constants.contenttypes import charset
 from MoinMoin.constants.itemtypes import ITEMTYPES
 
-from .content import Content, NonExistentContent, Draw
+from .content import content_registry, Content, NonExistentContent, Draw
 
 
 COLS = 80
                      if e.relname.startswith((startswith, startswith.swapcase()))]
 
         def build_contenttypes(groups):
-            ctypes = [[ctype for ctype, clabel in contenttypes]
-                      for gname, contenttypes in CONTENTTYPE_GROUPS
-                      if gname in groups]
-            ctypes_chain = itertools.chain(*ctypes)
-            contenttypes = list(ctypes_chain)
-            contenttypes_without_encoding = [contenttype[:contenttype.index(u';')]
-                                             for contenttype in contenttypes
-                                             if u';' in contenttype]
-            contenttypes.extend(contenttypes_without_encoding) # adding more mime-types without the encoding term
+            contenttypes = []
+            for g in groups:
+                entries = content_registry.groups.get(g, []) # .get is a temporary workaround for "unknown items" group
+                contenttypes.extend([e.content_type for e in entries])
             return contenttypes
 
+        def contenttype_match(tested, cts):
+            for ct in cts:
+                if ct.issupertype(tested):
+                    return True
+            return False
+
         if selected_groups is not None:
-            all_groups = [gname for gname, contenttypes in CONTENTTYPE_GROUPS]
             selected_contenttypes = build_contenttypes(selected_groups)
-            filtered_index = [e for e in index
-                              if e.meta[CONTENTTYPE] in selected_contenttypes]
+            filtered_index = [e for e in index if contenttype_match(Type(e.meta[CONTENTTYPE]), selected_contenttypes)]
 
             unknown_item_group = "unknown items"
             if unknown_item_group in selected_groups:
-                all_contenttypes = build_contenttypes(all_groups)
+                all_contenttypes = build_contenttypes(content_registry.group_names)
                 filtered_index.extend([e for e in index
-                                       if e.meta[CONTENTTYPE] not in all_contenttypes])
+                                       if not contenttype_match(Type(e.meta[CONTENTTYPE]), all_contenttypes)])
 
             index = filtered_index
         return index
                 return render_template('modify_select_contenttype.html',
                                        item_name=self.name,
                                        itemtype=self.itemtype,
-                                       contenttype_groups=CONTENTTYPE_GROUPS,
+                                       group_names=content_registry.group_names,
+                                       groups=content_registry.groups,
                                       )
             item = self
             if isinstance(self.rev, DummyRev):

MoinMoin/items/content.py

 from StringIO import StringIO
 from array import array
 from collections import namedtuple
+from operator import attrgetter
 
 from flask import current_app as app
 from flask import g as flaskg
 from MoinMoin.util.crypto import cache_key
 from MoinMoin.util.clock import timed
 from MoinMoin.forms import File
+from MoinMoin.constants.contenttypes import (
+    GROUP_MARKUP_TEXT, GROUP_OTHER_TEXT, GROUP_IMAGE, GROUP_AUDIO, GROUP_VIDEO,
+    GROUP_DRAWING, GROUP_OTHER,
+    )
 from MoinMoin.constants.keys import (
     NAME, NAME_EXACT, WIKINAME, CONTENTTYPE, SIZE, TAGS, HASH_ALGORITHM
     )
 
 
 class RegistryContent(RegistryBase):
-    class Entry(namedtuple('Entry', 'factory content_type priority')):
+    class Entry(namedtuple('Entry', 'factory content_type default_contenttype_params display_name ingroup_order priority')):
         def __call__(self, content_type, *args, **kw):
             if self.content_type.issupertype(Type(content_type)):
                 return self.factory(content_type, *args, **kw)
                 return False
             return NotImplemented
 
-    def register(self, factory, content_type, priority=RegistryBase.PRIORITY_MIDDLE):
+    def __init__(self, group_names):
+        super(RegistryContent, self).__init__()
+        self.group_names = group_names
+        self.groups = dict([(g, []) for g in group_names])
+
+    def register(self, factory, contenttype, default_contenttype_params, display_name, group, ingroup_order, priority=RegistryBase.PRIORITY_MIDDLE):
         """
         Register a factory
 
         :param factory: Factory to register. Callable, must return an object.
         """
-        return self._register(self.Entry(factory, content_type, priority))
+        e = self.Entry(factory, contenttype, default_contenttype_params, display_name, ingroup_order, priority)
+        # If group is specified and contenttype is not a wildcard one
+        if group and contenttype.type and contenttype.subtype:
+            if group not in self.groups:
+                raise ValueError('Unknown group name: {0}'.format(group))
+            self.groups[group].append(e)
+            self.groups[group].sort(key=attrgetter('ingroup_order'))
+        return self._register(e)
 
 
-content_registry = RegistryContent()
+content_registry = RegistryContent([
+    GROUP_MARKUP_TEXT,
+    GROUP_OTHER_TEXT,
+    GROUP_IMAGE,
+    GROUP_AUDIO,
+    GROUP_VIDEO,
+    GROUP_DRAWING,
+    GROUP_OTHER
+])
 
 def register(cls):
-    content_registry.register(cls._factory, Type(cls.contenttype))
+    content_registry.register(cls._factory, Type(cls.contenttype), cls.default_contenttype_params, cls.display_name, cls.group, cls.ingroup_order)
     return cls
 
 
     Base for content classes defining some helpers, agnostic about content
     data.
     """
+    # placeholder values for registry entry properties
+    contenttype = None
+    default_contenttype_params = {}
+    display_name = None
+    group = GROUP_OTHER
+    ingroup_order = 0
+
     @classmethod
     def _factory(cls, *args, **kw):
         return cls(*args, **kw)
 class NonExistentContent(Content):
     """Dummy Content to use with NonExistent."""
     contenttype = 'application/x-nonexistent'
+    group = None
 
     def do_get(self, force_attachment=False, mimetype=None):
         abort(404)
                          add_etags=True, etag=hash, conditional=True)
 
 
+@register
+class OctetStream(Binary):
+    """
+    Fallback Content for uploaded file of unknown contenttype.
+    """
+    contenttype = 'application/octet-stream'
+    display_name = 'binary file'
+
+
 class RenderableBinary(Binary):
     """ Base class for some binary stuff that renders with a object tag. """
 
     Tar items
     """
     contenttype = 'application/x-tar'
+    display_name = 'TAR'
 
 
 @register
     Compressed tar items
     """
     contenttype = 'application/x-gtar'
+    display_name = 'TGZ'
 
 
 class ZipMixin(object):
     Zip items
     """
     contenttype = 'application/zip'
+    display_name = 'ZIP'
 
 
 @register
 class PDF(Application):
     """ PDF """
     contenttype = 'application/pdf'
+    display_name = 'PDF'
 
 
 @register
 class Video(Binary):
     """ Base class for video/* """
     contenttype = 'video/*'
+    group = GROUP_VIDEO
+
+
+@register
+class OGGVideo(Video):
+    contenttype = 'video/ogg'
+    display_name = 'OGG'
+
+
+@register
+class WebMVideo(Video):
+    contenttype = 'video/webm'
+    display_name = 'WebM'
+
+
+@register
+class MP4(Video):
+    contenttype = 'video/mp4'
+    display_name = 'MP4'
 
 
 @register
 class Audio(Binary):
     """ Base class for audio/* """
     contenttype = 'audio/*'
+    group = GROUP_AUDIO
+
+
+@register
+class WAV(Audio):
+    contenttype = 'audio/wave'
+    display_name = 'WAV'
+
+
+@register
+class OGGAudio(Audio):
+    contenttype = 'audio/ogg'
+    display_name = 'OGG'
+
+
+@register
+class MP3(Audio):
+    contenttype = 'audio/mpeg'
+    display_name = 'MP3'
+
+
+@register
+class WebMAudio(Audio):
+    contenttype = 'audio/webm'
+    display_name = 'WebM'
 
 
 @register
 
 class RenderableImage(RenderableBinary):
     """ Base class for renderable Image mimetypes """
+    group = GROUP_IMAGE
 
 
 @register
 class SvgImage(RenderableImage):
     """ SVG images use <object> tag mechanism from RenderableBinary base class """
     contenttype = 'image/svg+xml'
+    display_name = 'SVG'
 
 
 class RenderableBitmapImage(RenderableImage):
 class PNG(TransformableBitmapImage):
     """ PNG image. """
     contenttype = 'image/png'
+    display_name = 'PNG'
 
 
 @register
 class JPEG(TransformableBitmapImage):
     """ JPEG image. """
     contenttype = 'image/jpeg'
+    display_name = 'JPEG'
 
 
 @register
 class GIF(TransformableBitmapImage):
     """ GIF image. """
     contenttype = 'image/gif'
+    display_name = 'GIF'
 
 
 @register
 class Text(Binary):
     """ Base class for text/* """
     contenttype = 'text/*'
+    default_contenttype_params = dict(charset='utf-8')
+    group = GROUP_OTHER_TEXT
 
     class ModifyForm(Binary.ModifyForm):
         template = 'modify_text.html'
     some kind of item with markup
     (internal links and transcluded items)
     """
+    group = GROUP_MARKUP_TEXT
 
 
 @register
 class MoinWiki(MarkupItem):
     """ MoinMoin wiki markup """
     contenttype = 'text/x.moin.wiki'
+    display_name = 'Wiki (MoinMoin)'
 
 
 @register
 class CreoleWiki(MarkupItem):
     """ Creole wiki markup """
     contenttype = 'text/x.moin.creole'
+    display_name = 'Wiki (Creole)'
 
 
 @register
 class MediaWiki(MarkupItem):
     """ MediaWiki markup """
     contenttype = 'text/x-mediawiki'
+    display_name = 'Wiki (MediaWiki)'
 
 
 @register
 class ReST(MarkupItem):
     """ ReStructured Text markup """
     contenttype = 'text/x-rst'
+    display_name = 'ReST'
 
 
 @register
 class Markdown(MarkupItem):
     """ Markdown markup """
     contenttype = 'text/x-markdown'
+    display_name = 'Markdown'
 
 
 @register
-class HTML(Text):
+class HTML(MarkupItem):
     """
     HTML markup
 
     Note: If raw revision data is accessed, unsafe stuff might be present!
     """
     contenttype = 'text/html'
+    display_name = 'HTML'
 
     class ModifyForm(Text.ModifyForm):
         template = "modify_text_html.html"
 class DocBook(MarkupItem):
     """ DocBook Document """
     contenttype = 'application/docbook+xml'
+    display_name = 'DocBook'
 
     def _convert(self, doc):
         from emeraldtree import ElementTree as ET
                          add_etags=False, etag=None, conditional=True)
 
 
+@register
+class PlainText(Text):
+    contenttype = 'text/plain'
+    display_name = 'plain text'
+
+
+@register
+class Diff(Text):
+    contenttype = 'text/x-diff'
+    display_name = 'diff/patch'
+
+
+@register
+class PythonCode(Text):
+    contenttype = 'text/x-python'
+    display_name = 'python code'
+
+
+@register
+class CSV(Text):
+    contenttype = 'text/csv'
+    display_name = 'csv'
+
+
+@register
+class IRCLog(Text):
+    contenttype = 'text/x-irclog'
+    display_name = 'IRC log'
+
+
 class Draw(TarMixin, Image):
     """
     Base class for *Draw that use special Java/Javascript applets to modify and store data in a tar file.
     """
+    group = GROUP_DRAWING
+
     class ModifyForm(Binary.ModifyForm):
         # Set the workaround flag respected in modify.html
         is_draw = True
     drawings by TWikiDraw applet. It creates three files which are stored as tar file.
     """
     contenttype = 'application/x-twikidraw'
+    display_name = 'TDRAW'
 
     class ModifyForm(Draw.ModifyForm):
         template = "modify_twikidraw.html"
     drawings by AnyWikiDraw applet. It creates three files which are stored as tar file.
     """
     contenttype = 'application/x-anywikidraw'
+    display_name = 'ADRAW'
 
     class ModifyForm(Draw.ModifyForm):
         template = "modify_anywikidraw.html"
 class SvgDraw(Draw):
     """ drawings by svg-edit. It creates two files (svg, png) which are stored as tar file. """
     contenttype = 'application/x-svgdraw'
+    display_name = 'SVGDRAW'
 
     class ModifyForm(Draw.ModifyForm):
         template = "modify_svg-edit.html"

MoinMoin/templates/modify_select_contenttype.html

 {{ _("Please select the contenttype of the new %(itemtype)s item.", itemtype=itemtype) }}
 </p>
 <table id="moin-create-table" class="zebra">
-    {% for gname, contenttypes in contenttype_groups %}
+    {% for group in group_names %}
     <tr>
-        <th>{{ gname }}</th>
+        <th>{{ group }}</th>
     </tr>
     <tr>
         <td> |&nbsp
-        {% for ctname, ctlabel in contenttypes %}
-            <a href="{{ url_for('frontend.modify_item', item_name=item_name, itemtype=itemtype, contenttype=ctname) }}">{{ ctlabel }}</a> &nbsp|&nbsp
+        {% for e in groups[group] %}
+            <a href="{{ url_for('frontend.modify_item', item_name=item_name, itemtype=itemtype, contenttype=Type(e.content_type, parameters=e.default_contenttype_params)|string) }}">{{ e.display_name }}</a> &nbsp|&nbsp
         {% endfor %}
         </td>
     </tr>

MoinMoin/themes/__init__.py

 from MoinMoin.util.crypto import cache_key
 from MoinMoin.util.forms import make_generator
 from MoinMoin.util.clock import timed
+from MoinMoin.util.mime import Type
 
 
 def get_current_theme():
                             # _, gettext, ngettext
                             'isinstance': isinstance,
                             'list': list,
+                            'Type': Type,
                             # please note that flask-themes installs:
                             # theme, theme_static
                             'theme_supp': ThemeSupport(app.cfg),
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.