Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Support for plugins to register URL patterns #1237

Merged
merged 9 commits into from Aug 19, 2013
View
@@ -131,6 +131,9 @@ def get_urls(self):
pat(r'^(?P<object_id>\d+)/change_template/$', self.change_template), # copy dialog
)
+ if plugin_pool.get_all_plugins():
+ url_patterns += plugin_pool.get_patterns()
+
url_patterns += super(PageAdmin, self).get_urls()
return url_patterns
View
@@ -255,6 +255,17 @@ def requires_reload(self, action):
reload_required = options.get('requires_reload', False)
return reload_required
+ def get_plugin_urls(self):
+ """
+ Return URL patterns for which the plugin wants to register
+ views for.
+ """
+ return []
+
+ def plugin_urls(self):
+ return self.get_plugin_urls()
+ plugin_urls = property(plugin_urls)
+
def __repr__(self):
return smart_str(self.name)
View
@@ -7,6 +7,10 @@
from cms.utils.compat.dj import force_unicode
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
+from django.conf.urls.defaults import url, patterns, include
+from django.contrib.formtools.wizard.views import normalize_name
+from django.template.defaultfilters import slugify
+from django.utils.translation import get_language, deactivate_all, activate
class PluginPool(object):
def __init__(self):
@@ -112,7 +116,27 @@ def get_plugin(self, name):
"""
self.discover_plugins()
return self.plugins[name]
+
+ def get_patterns(self):
+ self.discover_plugins()
+
+ # We want untranslated name of the plugin for its slug so we deactivate translation
+ lang = get_language()
+ deactivate_all()
+
+ try:
+ url_patterns = []
+ for plugin in self.get_all_plugins():
+ p = plugin()
+ slug = slugify(force_unicode(normalize_name(p.__class__.__name__)))
+ url_patterns += patterns('',
+ url(r'^plugin/%s/' % (slug,), include(p.plugin_urls)),
+ )
+ finally:
+ # Reactivate translation
+ activate(lang)
+ return url_patterns
plugin_pool = PluginPool()
View
@@ -28,10 +28,12 @@
from cms.sitemaps.cms_sitemap import CMSSitemap
from cms.test_utils.util.context_managers import SettingsOverride
from cms.utils.copy_plugins import copy_plugins_to
+from django import http
from django.utils import timezone
from django.conf import settings
from django.contrib import admin
from django.contrib.auth.models import User
+from django.core import urlresolvers
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.management import call_command
@@ -51,6 +53,20 @@ def render(self, context, instance, placeholder):
return context
+class DumbFixturePluginWithUrls(DumbFixturePlugin):
+ name = DumbFixturePlugin.name + " With custom URLs."
+
+ def test_view(self, request):
+ return http.HttpResponse("It works")
+
+ def get_plugin_urls(self):
+ from django.conf.urls.defaults import patterns, url
+ return patterns('',
+ url(r'^testview/$', admin.site.admin_view(self.test_view), name='dumbfixtureplugin'),
+ )
+plugin_pool.register_plugin(DumbFixturePluginWithUrls)
+
+
class PluginsTestBaseCase(CMSTestCase):
def setUp(self):
self.super_user = User(username="test", is_staff=True, is_active=True, is_superuser=True)
@@ -878,6 +894,12 @@ def test_plugin_copy_with_reload(self):
plugin_pool.unregister_plugin(ReloadDrivenPlugin)
plugin_pool.unregister_plugin(NonReloadDrivenPlugin)
+ def test_custom_plugin_urls(self):
+ plugin_url = urlresolvers.reverse('admin:dumbfixtureplugin')
+
+ response = self.client.get(plugin_url)
+ self.assertEquals(response.status_code, 200)
+ self.assertEquals(response.content, b"It works")
class FileSystemPluginTests(PluginsTestBaseCase):
@@ -208,6 +208,13 @@ cms.plugin_base
Custom form class to be used to edit this plugin.
+ .. method:: get_plugin_urls(instance)
+
+ Returns URL patterns for which the plugin wants to register views for.
+ They are included under django CMS PageAdmin in the plugin path
+ (e.g.: ``/admin/cms/page/plugin/<plugin-name>/`` in the default case).
+ Useful if your plugin needs to asynchronously talk to the admin.
+
.. attribute:: model
Is the :class:`CMSPlugin` model we created earlier. If you don't need