forked from django-cms/django-cms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplugin_pool.py
239 lines (203 loc) · 9.1 KB
/
plugin_pool.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# -*- coding: utf-8 -*-
from operator import attrgetter
from django.core.exceptions import ImproperlyConfigured
from django.conf.urls import url, include
from django.db.models import signals
from django.template.defaultfilters import slugify
from django.utils import six
from django.utils.encoding import force_text
from django.utils.translation import get_language, deactivate_all, activate
from django.template import TemplateDoesNotExist, TemplateSyntaxError
from cms.exceptions import PluginAlreadyRegistered, PluginNotRegistered
from cms.plugin_base import CMSPluginBase
from cms.models import CMSPlugin
from cms.utils.django_load import load
from cms.utils.helpers import reversion_register
from cms.utils.compat.dj import is_installed
from cms.utils.helpers import normalize_name
class PluginPool(object):
def __init__(self):
self.plugins = {}
self.discovered = False
self.patched = False
def discover_plugins(self):
if self.discovered:
return
from cms.cache import invalidate_cms_page_cache
invalidate_cms_page_cache()
load('cms_plugins')
self.discovered = True
def clear(self):
self.discovered = False
self.plugins = {}
self.patched = False
def validate_templates(self, plugin=None):
"""
Plugins templates are validated at this stage
"""
if plugin:
plugins = [plugin]
else:
plugins = self.plugins.values()
for plugin in plugins:
if (plugin.render_plugin and not type(plugin.render_plugin) == property
or hasattr(plugin.model, 'render_template')
or hasattr(plugin, 'get_render_template')):
if (plugin.render_template is None and
not hasattr(plugin, 'get_render_template')):
raise ImproperlyConfigured(
"CMS Plugins must define a render template, "
"a get_render_template method or "
"set render_plugin=False: %s" % plugin
)
# If plugin class defines get_render_template we cannot
# statically check for valid template file as it depends
# on plugin configuration and context.
# We cannot prevent developer to shoot in the users' feet
elif not hasattr(plugin, 'get_render_template'):
from django.template import loader
template = plugin.render_template
if isinstance(template, six.string_types) and template:
try:
loader.get_template(template)
except TemplateDoesNotExist as e:
# Note that the template loader will throw
# TemplateDoesNotExist if the plugin's render_template
# does in fact exist, but it includes a template that
# doesn't.
if six.text_type(e) == template:
raise ImproperlyConfigured(
"CMS Plugins must define a render template (%s) that exists: %s"
% (plugin, template)
)
else:
pass
except TemplateSyntaxError:
pass
else:
if plugin.allow_children:
raise ImproperlyConfigured(
"CMS Plugins can not define render_plugin=False and allow_children=True: %s"
% plugin
)
def register_plugin(self, plugin):
"""
Registers the given plugin(s).
Static sanity checks is also performed.
If a plugin is already registered, this will raise PluginAlreadyRegistered.
"""
if not issubclass(plugin, CMSPluginBase):
raise ImproperlyConfigured(
"CMS Plugins must be subclasses of CMSPluginBase, %r is not."
% plugin
)
plugin_name = plugin.__name__
if plugin_name in self.plugins:
raise PluginAlreadyRegistered(
"Cannot register %r, a plugin with this name (%r) is already "
"registered." % (plugin, plugin_name)
)
plugin.value = plugin_name
self.plugins[plugin_name] = plugin
from cms.signals import pre_save_plugins, post_delete_plugins, pre_delete_plugins
signals.pre_save.connect(pre_save_plugins, sender=plugin.model,
dispatch_uid='cms_pre_save_plugin_%s' % plugin_name)
signals.post_delete.connect(post_delete_plugins, sender=CMSPlugin,
dispatch_uid='cms_post_delete_plugin_%s' % plugin_name)
signals.pre_delete.connect(pre_delete_plugins, sender=CMSPlugin,
dispatch_uid='cms_pre_delete_plugin_%s' % plugin_name)
if is_installed('reversion'):
from cms.utils.reversion_hacks import RegistrationError
try:
reversion_register(plugin.model)
except RegistrationError:
pass
return plugin
def unregister_plugin(self, plugin):
"""
Unregisters the given plugin(s).
If a plugin isn't already registered, this will raise PluginNotRegistered.
"""
plugin_name = plugin.__name__
if plugin_name not in self.plugins:
raise PluginNotRegistered(
'The plugin %r is not registered' % plugin
)
del self.plugins[plugin_name]
def set_plugin_meta(self):
"""
Patches a plugin model by forcing a specifc db_table whether the
'new style' table name exists or not. The same goes for all the
ManyToMany attributes.
This method must be run whenever a plugin model is accessed
directly.
The model is modified in place; a 'patched' attribute is added
to the model to check whether it's already been modified.
"""
if self.patched:
return
self.patched = True
def get_all_plugins(self, placeholder=None, page=None, setting_key="plugins", include_page_only=True):
from cms.utils.placeholder import get_placeholder_conf
self.discover_plugins()
self.set_plugin_meta()
plugins = sorted(self.plugins.values(), key=attrgetter('name'))
template = page and page.get_template() or None
allowed_plugins = get_placeholder_conf(
setting_key,
placeholder,
template,
) or ()
excluded_plugins = get_placeholder_conf(
'excluded_plugins',
placeholder,
template,
) or ()
if not include_page_only:
# Filters out any plugin marked as page only because
# the include_page_only flag has been set to False
plugins = (plugin for plugin in plugins if not plugin.page_only)
if allowed_plugins:
# Check that plugins are in the list of the allowed ones
plugins = (plugin for plugin in plugins if plugin.__name__ in allowed_plugins)
if excluded_plugins:
# Check that plugins are not in the list of the excluded ones
plugins = (plugin for plugin in plugins if plugin.__name__ not in excluded_plugins)
if placeholder:
# Filters out any plugin that requires a parent or has set parent classes
plugins = (plugin for plugin in plugins if not plugin.requires_parent_plugin(placeholder, page))
return sorted(plugins, key=attrgetter('module'))
def get_text_enabled_plugins(self, placeholder, page):
plugins = self.get_all_plugins(placeholder, page)
plugins += self.get_all_plugins(placeholder, page, 'text_only_plugins')
return sorted((p for p in set(plugins) if p.text_enabled),
key=attrgetter('module', 'name'))
def get_plugin(self, name):
"""
Retrieve a plugin from the cache.
"""
self.discover_plugins()
self.set_plugin_meta()
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_text(normalize_name(p.__class__.__name__)))
url_patterns += [
url(r'^plugin/%s/' % (slug,), include(p.plugin_urls)),
]
finally:
# Reactivate translation
activate(lang)
return url_patterns
def get_system_plugins(self):
self.discover_plugins()
self.set_plugin_meta()
return [plugin.__name__ for plugin in self.plugins.values() if plugin.system]
plugin_pool = PluginPool()