Commits

mitar committed 4948e5b

Support for scripts and stylesheets provided by plugins. Support for additional URLs/views by plugins.

Comments (0)

Files changed (12)

cmsplugin_markup/cms_plugins.py

     render_template = 'cmsplugin_markup/markup.html'
     change_form_template = 'cmsplugin_markup/markup_plugin_change_form.html'
 
+    class PluginMedia:
+        js = ('%scmsplugin_markup/markup.js' % (getattr(settings, 'STATIC_URL', settings.MEDIA_URL),),)
+
     def render(self, context, instance, placeholder):
         context.update({
             'object': instance,
             url(r'^preview/$', admin.site.admin_view(self.preview), name='cmsplugin_markup_preview'),
         )
 
-        return preview_urls + urls
+        plugin_urls = []
+        for c in utils.get_list_of_markup_classes().values():
+            plugin_urls.extend(c().get_plugin_urls())
+
+        return preview_urls + plugin_urls + urls
 
     def preview(self, request):
         if request.method != 'POST':
         if not request.POST.get('markup'):
             return http.HttpResponse('')
 
-        return http.HttpResponse(markup.markup_parser(request.POST.get('text'), request.POST.get('markup'), template.RequestContext(request, {
+        (content, parser) = markup.markup_parser(request.POST.get('text'), request.POST.get('markup'), template.RequestContext(request, {
                 'object': plugin,
                 'placeholder': placeholder,
-            }), placeholder))
+            }), placeholder)
+
+        content += utils.content_scripts(parser.get_scripts()) + utils.content_stylesheets(parser.get_stylesheets())
+
+        return http.HttpResponse(content)
 
 plugin_pool.register_plugin(MarkupPlugin)

cmsplugin_markup/forms.py

 class MarkupForm(ModelForm):
     class Meta:
         model = MarkupField
-        exclude = ('body_html',)
+        exclude = ('body_html', 'body_scripts', 'body_stylesheets')
         widgets = {
             'body': widgets.Textarea(attrs={'class': 'django-resizable'}),
         }

cmsplugin_markup/migrations/0002_auto__add_field_markupfield_body_scripts__add_field_markupfield_body_s.py

+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'MarkupField.body_scripts'
+        db.add_column('cmsplugin_markupfield', 'body_scripts', self.gf('django.db.models.fields.TextField')(default='', blank=True), keep_default=False)
+
+        # Adding field 'MarkupField.body_stylesheets'
+        db.add_column('cmsplugin_markupfield', 'body_stylesheets', self.gf('django.db.models.fields.TextField')(default='', blank=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'MarkupField.body_scripts'
+        db.delete_column('cmsplugin_markupfield', 'body_scripts')
+
+        # Deleting field 'MarkupField.body_stylesheets'
+        db.delete_column('cmsplugin_markupfield', 'body_stylesheets')
+
+
+    models = {
+        'cms.cmsplugin': {
+            'Meta': {'object_name': 'CMSPlugin'},
+            'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
+            'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
+            'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+            'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+        },
+        'cms.placeholder': {
+            'Meta': {'object_name': 'Placeholder'},
+            'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
+        },
+        'cmsplugin_markup.markupfield': {
+            'Meta': {'object_name': 'MarkupField', 'db_table': "'cmsplugin_markupfield'", '_ormbases': ['cms.CMSPlugin']},
+            'body': ('django.db.models.fields.TextField', [], {}),
+            'body_html': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'body_scripts': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'body_stylesheets': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+            'dynamic': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'markup': ('django.db.models.fields.CharField', [], {'default': "'tracwiki'", 'max_length': '20'})
+        }
+    }
+
+    complete_apps = ['cmsplugin_markup']

cmsplugin_markup/models.py

 class MarkupField(CMSPlugin):
     body = models.TextField(_('Body'))
     body_html = models.TextField(blank=True)
+    body_scripts = models.TextField(blank=True)
+    body_stylesheets = models.TextField(blank=True)
     markup = models.CharField(
             _('Markup'),
             max_length=20,
 
     def save(self, *args, **kwargs):
         # We store it in any case to also check the parser for possible exceptions and to use it for __unicode__
-        self.body_html = utils.markup_parser(self.body, self.markup)
+        (content, parser) = utils.markup_parser(self.body, self.markup)
+        self.body_html = content
+        self.body_scripts = utils.content_scripts(parser.get_scripts())
+        self.body_stylesheets = utils.content_stylesheets(parser.get_stylesheets())
         if not utils.get_markup_object(self.markup).is_dynamic:
             self.dynamic = False
         return super(MarkupField, self).save(*args, **kwargs)
 
     def render(self, context):
         if self.dynamic:
-            return mark_safe(utils.markup_parser(self.body, self.markup, context, context.get('placeholder')))
+            (content, parser) = utils.markup_parser(self.body, self.markup, context, context.get('placeholder'))
+            context['markup_scripts'] = context.get('markup_scripts', []) + [utils.content_scripts(parser.get_scripts())]
+            context['markup_stylesheets'] = context.get('markup_stylesheets', []) + [utils.content_stylesheets(parser.get_stylesheets())]
+            return mark_safe(content)
         else:
+            context['markup_scripts'] = context.get('markup_scripts', []) + [self.body_scripts]
+            context['markup_stylesheets'] = context.get('markup_stylesheets', []) + [self.body_stylesheets]
             return mark_safe(self.body_html)
 
     def clean_plugins(self):

cmsplugin_markup/plugins/base.py

     text_enabled_plugins = False
     is_dynamic = False
 
+    def parse(self, value, context=None, placeholder=None):
+        """
+        This is the main method of the parser. It returns parsed output from a given markup input.
+
+        While parsing it can store internally additional scripts and stylesheets to be retrieved later from the parser object.
+        """
+        raise NotImplementedError
+
     def plugin_id_list(self, text):
         """
         Returns the list of plugins inserted and currently used in the markup text.
         """
         return None
 
+    def get_scripts(self):
+        """
+        Returns a list of {href, type} dictionaries for all scripts which should be additionaly loaded.
+
+        They will be inserted at the end of the page.
+        """
+        return []
+
+    def get_stylesheets(self):
+        """
+        Returns a list of {href, type} dictionaries for all stylesheets which should be additionally loaded.
+
+        JavaScript code will be added at the end of the page to inject them into the head.
+        """
+        return []
+
+    def get_plugin_urls(self):
+        """
+        Returns URL patterns for views used by this plugin.
+        """
+        return []
+
 class MarkupPluginException(Exception):
     pass

cmsplugin_markup/static/cmsplugin_markup/markup.js

+// Based on Trac version, http://trac.edgewall.org/
+
+(function($) {
+  $.loadStyleSheet = function(href, type) {
+    type = type || "text/css";
+    $(document).ready(function() {
+      if ($('head link[href="' + href + '"]').length == 0) {
+        if (document.createStyleSheet) { // MSIE
+          document.createStyleSheet(href);
+        } else {
+          $("<link rel='stylesheet' type='" + type + "' href='" + href + "' />")
+            .appendTo("head");
+        }
+      }
+    });
+  }
+})(jQuery);

cmsplugin_markup/templates/cmsplugin_markup/markup.html

-{% load markuptags %}{% rendermarkup %}
+{% load markuptags %}{% rendermarkup %}{% renderstylesheets %}{% renderscripts %}

cmsplugin_markup/templates/cmsplugin_markup/markup_plugin_change_form.html

 /* ]]> */
 </script>
 <script type="text/javascript" src="{{ STATIC_URL|default:MEDIA_URL }}cmsplugin_markup/markup_admin.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL|default:MEDIA_URL }}cmsplugin_markup/markup.js"></script>
 <script type="text/javascript" src="{{ CMS_MEDIA_URL }}js/lib/ui.core.js"></script>
 <script type="text/javascript" src="{{ CMS_MEDIA_URL }}js/placeholder_editor_registry.js"></script>
 <script type="text/javascript">

cmsplugin_markup/templatetags/markuptags.py

                 raise
             else:
                 return u''
+
+@register.tag
+def renderstylesheets(parser, token):
+    return RenderMarkupStylesheetsNode()
+
+class RenderMarkupStylesheetsNode(template.Node):
+    def render(self, context):
+        if 'markup_stylesheets' in context:
+            stylesheets = "".join(context['markup_stylesheets'])
+            del context['markup_stylesheets']
+            return stylesheets
+        else:
+            return u''
+
+@register.tag
+def renderscripts(parser, token):
+    return RenderMarkupScriptsNode()
+
+class RenderMarkupScriptsNode(template.Node):
+    def render(self, context):
+        if 'markup_scripts' in context:
+            scripts = "".join(context['markup_scripts'])
+            del context['markup_scripts']
+            return scripts
+        else:
+            return u''

cmsplugin_markup/utils/__init__.py

 from cmsplugin_markup.utils.markup import *
 from cmsplugin_markup.utils.plugins import *
+from cmsplugin_markup.utils.media import *

cmsplugin_markup/utils/markup.py

 
         try:
             # Check for required attributes
-            for attribute in ['name', 'identifier', 'parse']:
+            for attribute in ['name', 'identifier']:
                 if not hasattr(module.Markup, attribute):
                     raise MarkupPluginException("Markup plugin '%s' is missing '%s' attribute" % (markup, attribute))
             if not issubclass(module.Markup, MarkupBase):
 
 def markup_parser(value, parser_identifier, context=None, placeholder=None):
     """
-    Takes a string and a parser identifier and returns a string parsed
-    by that parser. If anything goes wrong it returns the original string
+    Takes a string and a parser identifier and returns a tuple with string parsed
+    by that parser and parser object itself.
     """
 
-    return get_markup_object(parser_identifier).parse(value, context, placeholder)
+    parser = get_markup_object(parser_identifier)
+    return (parser.parse(value, context, placeholder), parser)

cmsplugin_markup/utils/media.py

+def content_scripts(scripts):
+    output = ""
+    for script in scripts:
+        output += '<script type="%s" src="%s"></script>' % (script.get('type', "text/javascript"), script['href'])
+    return output
+
+def content_stylesheets(stylesheets):
+    output = ""
+    for stylesheet in stylesheets:
+        output += 'jQuery.loadStyleSheet("%s", "%s");\n' % (stylesheet['href'], stylesheet.get('type', "text/css"))
+    if output:
+        output = '<script type="text/javascript">\n' + output + '</script>\n'
+    return output
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.