Anonymous avatar Anonymous committed c4959da

Improved widget preparation from field definition to make sure textareas always have class, data-mode and data-markuptype attributes.

Comments (0)

Files changed (8)

markupmirror/exceptions.py

     """General execution error for markups."""
 
 
-class MarkupAlreadyRegistered(Exception):
-    """Raised when trying to register an already registered markup."""
-
-
 class InvalidMarkup(Exception):
     """Raised when a markup is not subclassing BaseMarkup."""
-
-
-class MarkupNotFound(Exception):
-    """Raised when trying to look up a markup that was not registered."""

markupmirror/feincms/models.py

 
     content = MarkupMirrorField(
         verbose_name=_(u"Markup content"),
-        markup_type=settings.FEINCMS_MARKUP_TYPE,
+        markup_type=settings.DEFAULT_MARKUP_TYPE,
         blank=True)
 
     class Meta:

markupmirror/fields.py

         self.markup_type_editable = markup_type is None
         self.escape_html = escape_html
 
-        # TODO: this was a list of tuples originally
-        markup_choices = markup_pool.get_all_markups()
-        self.markup_choices_list = sorted(markup_choices.keys())
-        self.markup_choices_dict = markup_choices
-
         if (self.default_markup_type and
-            self.default_markup_type not in self.markup_choices_list):
+            self.default_markup_type not in markup_pool):
             raise ImproperlyConfigured(
                 "Invalid default_markup_type for field '%r', "
                 "available types: %s" % (
                     name or verbose_name,
-                    ', '.join(self.markup_choices_list)))
+                    ', '.join(sorted(markup_pool.markups.keys()))))
 
         # for South FakeORM compatibility: the frozen version of a
         # MarkupMirrorField can't try to add a _rendered field, because the
         """
         if not cls._meta.abstract:
             # markup_type
-            choices = zip(self.markup_choices_list, self.markup_choices_list)
+            choices = [(markup_type, markup.title) for markup_type, markup in
+                sorted(markup_pool.markups.iteritems(),
+                    key=lambda markup: markup[1].title.lower())]
             markup_type_field = models.CharField(
                 choices=choices, max_length=30,
                 default=self.default_markup_type, blank=self.blank,
         value = super(MarkupMirrorField, self).pre_save(model_instance, add)
 
         # check for valid markup type
-        if value.markup_type not in self.markup_choices_list:
+        if value.markup_type not in markup_pool:
             raise ValueError(
                 'Invalid markup type (%s), available types: %s' % (
                     value.markup_type,
-                    ', '.join(self.markup_choices_list)))
+                    ', '.join(sorted(markup_pool.markups.keys()))))
 
         # escape HTML
         if self.escape_html:
         else:
             raw = value.raw
 
-        rendered = self.markup_choices_dict[value.markup_type](raw)
+        rendered = markup_pool[value.markup_type](raw)
         setattr(model_instance, _rendered_field_name(self.attname), rendered)
         return value.raw
 
         return value.raw
 
     def formfield(self, **kwargs):
-        defaults = {'widget': widgets.MarkupMirrorTextarea}
+        """Adds attributes necessary for CodeMirror initialization to the
+        field's widget.
+
+        The class "item-markupmirror" is used to identify textareas that should
+        be enhanced with the editor.
+
+        The ``data-mode`` and ``data-markuptype`` attributes depend on a
+        selected ``default_markup_type``. If a field does not have a default
+        markup type selected, the attributes will be added in the widgets'
+        ``render`` method by accessing the ``markup_type`` property of the
+        markup content wrapper ``markupmirror.fields.Markup``.
+
+        """
+        widget_attrs = {
+            'class': 'item-markupmirror',
+        }
+        if (self.default_markup_type and
+            self.default_markup_type in markup_pool):
+            widget_attrs['data-mode'] = markup_pool[
+                self.default_markup_type].codemirror_mode
+            widget_attrs['data-markuptype'] = self.default_markup_type
+
+        defaults = {
+            'widget': widgets.MarkupMirrorTextarea(attrs=widget_attrs),
+        }
+
         defaults.update(kwargs)
         return super(MarkupMirrorField, self).formfield(**defaults)
 
-    # TODO: this does not work for model fields, only for form fields.
-    #
-    # def widget_attrs(self, widget):
-    #     """Adds attributes necessary for CodeMirror initialization to the
-    #     field's widget.
-
-    #     The class "item-markupmirror" is used to identify textareas that should
-    #     be enhanced with the editor.
-
-    #     The ``data-mode`` and ``data-markuptype`` attributes depend on a
-    #     selected ``default_markup_type``. If a field does not have a default
-    #     markup type selected, the attributes will be added in the widgets'
-    #     ``render`` method by accessing the ``markup_type`` property of the
-    #     markup content wrapper ``markupmirror.fields.Markup``.
-
-    #     """
-    #     attrs = super(MarkupMirrorField, self).widget_attrs(widget)
-    #     attrs = {
-    #         'class': 'item-markupmirror',
-    #     }
-    #     if (self.default_markup_type and
-    #         self.default_markup_type in self.markup_choices_dict):
-    #         attrs['data-mode'] = self.markup_choices_dict[
-    #             self.default_markup_type].codemirror_mode
-    #         attrs['data-markuptype'] = self.default_markup_type
-    #     return attrs
-
 
 __all__ = ('Markup', 'MarkupMirrorFieldDescriptor', 'MarkupMirrorField')
 

markupmirror/settings.py

 from django.conf import settings
 
 
+# Default markup type
+# Used for fields which have no markup_type or default_markup_type assigned
+# before a markup_type has been selected by the user (selectbox).
+DEFAULT_MARKUP_TYPE = getattr(settings,
+    'MARKUPMIRROR_DEFAULT_MARKUP_TYPE', 'plaintext')
+
+
 # CodeMirror settings
 
 # Minified JS and CSS files
         'CODEMIRROR_WIDTH': '50%',
         'CODEMIRROR_HEIGHT': '300px',
     })
-
-FEINCMS_MARKUP_TYPE = getattr(settings,
-    'MARKUPMIRROR_FEINCMS_MARKUP_TYPE', 'plaintext')

markupmirror/templates/admin/markupmirror/feincms/init_codemirror.html

             }
 
             var markdown_init_fn = function(){
-                $('{% block selectors %}.order-machine textarea[class=item-markupmirror], #frontend_editor textarea[class=item-markupmirror]{% endblock %}').each(function(){
+                $('{% block selectors %}.order-machine textarea.item-markupmirror, #frontend_editor textarea.item-markupmirror{% endblock %}').each(function(){
                     feincms_markdown_add_codemirror(this);
                 });
             }
             {% block enable %}
                 contentblock_init_handlers.push(markdown_init_fn);
                 contentblock_move_handlers.poorify.push(function(item) {
-                    item.find('textarea[class=item-markupmirror]').each(
+                    item.find('textarea.item-markupmirror').each(
                         feincms_markdown_remove_codemirror);
                 });
                 contentblock_move_handlers.richify.push(function(item) {
-                    item.find('textarea[class=item-markupmirror]').each(
+                    item.find('textarea.item-markupmirror').each(
                         feincms_markdown_add_codemirror);
                 });
 

markupmirror/tests/tests/markup_base.py

 from django.test import TestCase
 
 from markupmirror.exceptions import InvalidMarkup
-from markupmirror.exceptions import MarkupAlreadyRegistered
-from markupmirror.exceptions import MarkupNotFound
 from markupmirror.markup.base import BaseMarkup
 from markupmirror.markup.base import markup_pool
 from markupmirror.markup.base import register_markup
         self.assertRaises(InvalidMarkup, register_markup, InvalidMarkupClass)
 
     def test_re_register(self):
-        """Registering a markup converter twice raises a
-        ``MarkupAlreadyRegistered`` exception.
+        """Registering a markup converter would overwrite it.
 
         """
         register_markup(DummyMarkup)
-        self.assertRaises(MarkupAlreadyRegistered,
-                          register_markup, DummyMarkup)
+        dummy_markup = markup_pool['dummy']
+        register_markup(DummyMarkup)
+        self.assertTrue(isinstance(markup_pool['dummy'], DummyMarkup))
+        self.assertFalse(dummy_markup is markup_pool['dummy'])
 
     def test_register(self):
         """Registering a markup converter makes it available through its name.
 
         """
-        # before the markup is registered, we'll get ``MarkupNotFound`` if
+        # before the markup is registered, we'll get ``KeyError`` if
         # we try to retrieve it.
-        self.assertRaises(MarkupNotFound, markup_pool.get_markup, 'dummy')
+        self.assertRaises(KeyError, markup_pool.get_markup, 'dummy')
 
         # it is available after registering it
         register_markup(DummyMarkup)
-        self.assertTrue(
-            isinstance(markup_pool.get_markup('dummy'), DummyMarkup))
+        self.assertTrue(isinstance(markup_pool['dummy'], DummyMarkup))
 
     def test_unregister(self):
         """Unregistering a markup converter removes it from the pool."""
         register_markup(DummyMarkup)
-        self.assertTrue(
-            isinstance(markup_pool.get_markup('dummy'), DummyMarkup))
-        markup_pool.unregister_markup('dummy')
-        self.assertRaises(MarkupNotFound, markup_pool.get_markup, 'dummy')
+        self.assertTrue(isinstance(markup_pool['dummy'], DummyMarkup))
+        del markup_pool['dummy']
+        self.assertRaises(KeyError, markup_pool.get_markup, 'dummy')
 
     def test_get_all(self):
         """``markup_pool.get_all_markups`` returns all markup converters."""
-        all_markups = markup_pool.get_all_markups()
+        all_markups = markup_pool.markups
         self.assertEqual(
             ['html', 'markdown', 'plaintext', 'restructuredtext', 'textile'],
             sorted(all_markups.keys()))

markupmirror/tests/tests/markup_markdown.py

     #     """
     #     # first, remove already registered markdown converter
     #     markup_pool.unregister_markup('markdown')
-    #     self.assertRaises(MarkupNotFound, markup_pool.get_markup, 'markdown')
+    #     self.assertRaises(KeyError, markup_pool.get_markup, 'markdown')
 
     #     # trying to import markdown fails
     #     with self.assertRaises(ImportError):
     #     from markupmirror.markup import markdown_
 
     #     # markdown should not be found
-    #     self.assertRaises(MarkupNotFound, markup_pool.get_markup, 'markdown')
+    #     self.assertRaises(KeyError, markup_pool.get_markup, 'markdown')
 
     #     # re-register markdown to restore default state
     #     register_markup(MarkdownMarkup)

markupmirror/widgets.py

 
 class MarkupMirrorTextarea(forms.Textarea):
 
-    def render(self, name, value, attrs=None):
-        if value is not None and not isinstance(value, unicode):
-            value = value.raw
-        return super(MarkupMirrorTextarea, self).render(name, value, attrs)
+    css_class = 'item-markupmirror'
 
-    class Media:
-        css = {
-            'all': settings.MARKUPMIRROR_CSS
+    def __init__(self, attrs=None):
+        """Adds the ``item-markupmirror`` class to the textarea to make sure
+        it can be identified through JS.
+
+        """
+        css_class = self.css_class
+        if attrs and 'class' in attrs:
+            css_class += ' %s' % attrs.pop('class')
+
+        default_attrs = {
+            'class': css_class,
         }
-        js = settings.MARKUPMIRROR_JS
 
-
-class AdminMarkupMirrorTextareaWidget(
-    MarkupMirrorTextarea, AdminTextareaWidget):
+        if attrs:
+            default_attrs.update(attrs)
+        super(MarkupMirrorTextarea, self).__init__(attrs=default_attrs)
 
     def render(self, name, value, attrs=None):
-        """Adds attributes necessary to load CodeMirror for each textarea
-        in the admin.
-
-        """
-        if value and hasattr(value, 'markup_type'):
+        default_attrs = {}
+        if value is not None and not isinstance(value, unicode):
             # get markup converter by type.
             # ``value`` is ``markupmirror.fields.Markup``.
             markup_type = value.markup_type
                 'data-mode': markup.codemirror_mode,
                 'data-markuptype': markup_type,
             }
+
+            # get real value
+            value = value.raw
+        else:
+            default = settings.DEFAULT_MARKUP_TYPE
+            default_attrs = {
+                'data-mode': markup_pool[default].codemirror_mode,
+                'data-markuptype': default,
+            }
+
+        if attrs:
             default_attrs.update(attrs)
 
-        return super(AdminMarkupMirrorTextareaWidget, self).render(
-            name, value, attrs)
+        return super(MarkupMirrorTextarea, self).render(name, value,
+                                                        default_attrs)
+
+    class Media:
+        css = {
+            'all': settings.MARKUPMIRROR_CSS
+        }
+        js = settings.MARKUPMIRROR_JS
+
+
+class AdminMarkupMirrorTextareaWidget(
+    MarkupMirrorTextarea, AdminTextareaWidget):
+
+    css_class = 'vLargeTextField item-markupmirror'
 
 
 __all__ = ('MarkupMirrorTextarea', 'AdminMarkupMirrorTextareaWidget')
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.