cms.models.pluginmodel: 226 total statements, 0.0% covered

Generated: Wed 2013-03-13 10:33 CET

Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/cms/models/pluginmodel.py

Stats: 0 executed, 207 missed, 19 excluded, 142 ignored

  1. # -*- coding: utf-8 -*-
  2. import os
  3. import warnings
  4. from datetime import date
  5. from django.conf import settings
  6. from django.core.exceptions import ValidationError, ObjectDoesNotExist
  7. from django.db import models
  8. from django.db.models.base import (model_unpickle, simple_class_factory)
  9. from django.db.models.query_utils import DeferredAttribute
  10. from django.utils.translation import ugettext_lazy as _
  11. from cms.exceptions import DontUsePageAttributeWarning
  12. from cms.models.placeholdermodel import Placeholder
  13. from cms.plugin_rendering import PluginContext, render_plugin
  14. from cms.utils.helpers import reversion_register
  15. from cms.utils import timezone
  16. from mptt.models import MPTTModel, MPTTModelBase
  17. class BoundRenderMeta(object):
  18. def __init__(self, meta):
  19. self.index = 0
  20. self.total = 1
  21. self.text_enabled = getattr(meta, 'text_enabled', False)
  22. class PluginModelBase(MPTTModelBase):
  23. """
  24. Metaclass for all CMSPlugin subclasses. This class should not be used for
  25. any other type of models.
  26. """
  27. def __new__(cls, name, bases, attrs):
  28. # remove RenderMeta from the plugin class
  29. attr_meta = attrs.pop('RenderMeta', None)
  30. # create a new class (using the super-metaclass)
  31. new_class = super(PluginModelBase, cls).__new__(cls, name, bases, attrs)
  32. # if there is a RenderMeta in attrs, use this one
  33. if attr_meta:
  34. meta = attr_meta
  35. else:
  36. # else try to use the one from the superclass (if present)
  37. meta = getattr(new_class, '_render_meta', None)
  38. # set a new BoundRenderMeta to prevent leaking of state
  39. new_class._render_meta = BoundRenderMeta(meta)
  40. # turn 'myapp_mymodel' into 'cmsplugin_mymodel' by removing the
  41. # 'myapp_' bit from the db_table name.
  42. if [base for base in bases if isinstance(base, PluginModelBase)]:
  43. splitter = '%s_' % new_class._meta.app_label
  44. if splitter in new_class._meta.db_table:
  45. splitted = new_class._meta.db_table.split(splitter, 1)
  46. table_name = 'cmsplugin_%s' % splitted[1]
  47. else:
  48. table_name = new_class._meta.db_table
  49. new_class._meta.db_table = table_name
  50. return new_class
  51. class CMSPlugin(MPTTModel):
  52. '''
  53. The base class for a CMS plugin model. When defining a new custom plugin, you should
  54. store plugin-instance specific information on a subclass of this class.
  55. An example for this would be to store the number of pictures to display in a galery.
  56. Two restrictions apply when subclassing this to use in your own models:
  57. 1. Subclasses of CMSPlugin *cannot be further subclassed*
  58. 2. Subclasses of CMSPlugin cannot define a "text" field.
  59. '''
  60. __metaclass__ = PluginModelBase
  61. placeholder = models.ForeignKey(Placeholder, editable=False, null=True)
  62. parent = models.ForeignKey('self', blank=True, null=True, editable=False)
  63. position = models.PositiveSmallIntegerField(_("position"), blank=True, null=True, editable=False)
  64. language = models.CharField(_("language"), max_length=15, blank=False, db_index=True, editable=False)
  65. plugin_type = models.CharField(_("plugin_name"), max_length=50, db_index=True, editable=False)
  66. creation_date = models.DateTimeField(_("creation date"), editable=False, default=timezone.now)
  67. changed_date = models.DateTimeField(auto_now=True)
  68. level = models.PositiveIntegerField(db_index=True, editable=False)
  69. lft = models.PositiveIntegerField(db_index=True, editable=False)
  70. rght = models.PositiveIntegerField(db_index=True, editable=False)
  71. tree_id = models.PositiveIntegerField(db_index=True, editable=False)
  72. class Meta:
  73. app_label = 'cms'
  74. class RenderMeta:
  75. index = 0
  76. total = 1
  77. text_enabled = False
  78. def __reduce__(self):
  79. """
  80. Provide pickling support. Normally, this just dispatches to Python's
  81. standard handling. However, for models with deferred field loading, we
  82. need to do things manually, as they're dynamically created classes and
  83. only module-level classes can be pickled by the default path.
  84. """
  85. data = self.__dict__
  86. model = self.__class__
  87. # The obvious thing to do here is to invoke super().__reduce__()
  88. # for the non-deferred case. Don't do that.
  89. # On Python 2.4, there is something wierd with __reduce__,
  90. # and as a result, the super call will cause an infinite recursion.
  91. # See #10547 and #12121.
  92. defers = []
  93. pk_val = None
  94. if self._deferred:
  95. factory = deferred_class_factory
  96. for field in self._meta.fields:
  97. if isinstance(self.__class__.__dict__.get(field.attname),
  98. DeferredAttribute):
  99. defers.append(field.attname)
  100. if pk_val is None:
  101. # The pk_val and model values are the same for all
  102. # DeferredAttribute classes, so we only need to do this
  103. # once.
  104. obj = self.__class__.__dict__[field.attname]
  105. model = obj.model_ref()
  106. else:
  107. factory = simple_class_factory
  108. return (model_unpickle, (model, defers, factory), data)
  109. def __unicode__(self):
  110. return unicode(self.id)
  111. def get_plugin_name(self):
  112. from cms.plugin_pool import plugin_pool
  113. return plugin_pool.get_plugin(self.plugin_type).name
  114. def get_short_description(self):
  115. instance = self.get_plugin_instance()[0]
  116. if instance is not None:
  117. return unicode(instance)
  118. return _("<Empty>")
  119. def get_plugin_class(self):
  120. from cms.plugin_pool import plugin_pool
  121. return plugin_pool.get_plugin(self.plugin_type)
  122. def get_plugin_instance(self, admin=None):
  123. plugin_class = self.get_plugin_class()
  124. plugin = plugin_class(plugin_class.model, admin) # needed so we have the same signature as the original ModelAdmin
  125. if plugin.model != self.__class__: # and self.__class__ == CMSPlugin:
  126. # (if self is actually a subclass, getattr below would break)
  127. try:
  128. instance = getattr(self, plugin.model.__name__.lower())
  129. # could alternatively be achieved with:
  130. # instance = plugin_class.model.objects.get(cmsplugin_ptr=self)
  131. instance._render_meta = self._render_meta
  132. except (AttributeError, ObjectDoesNotExist):
  133. instance = None
  134. else:
  135. instance = self
  136. return instance, plugin
  137. def render_plugin(self, context=None, placeholder=None, admin=False, processors=None):
  138. instance, plugin = self.get_plugin_instance()
  139. if instance and not (admin and not plugin.admin_preview):
  140. if not isinstance(placeholder, Placeholder):
  141. placeholder = instance.placeholder
  142. placeholder_slot = placeholder.slot
  143. context = PluginContext(context, instance, placeholder)
  144. context = plugin.render(context, instance, placeholder_slot)
  145. if plugin.render_plugin:
  146. template = hasattr(instance, 'render_template') and instance.render_template or plugin.render_template
  147. if not template:
  148. raise ValidationError("plugin has no render_template: %s" % plugin.__class__)
  149. else:
  150. template = None
  151. return render_plugin(context, instance, placeholder, template, processors)
  152. return ""
  153. def get_media_path(self, filename):
  154. pages = self.placeholder.page_set.all()
  155. if pages.count():
  156. return pages[0].get_media_path(filename)
  157. else: # django 1.0.2 compatibility
  158. today = date.today()
  159. return os.path.join(settings.CMS_PAGE_MEDIA_PATH,
  160. str(today.year), str(today.month), str(today.day), filename)
  161. @property
  162. def page(self):
  163. warnings.warn(
  164. "Don't use the page attribute on CMSPlugins! CMSPlugins are not "
  165. "guaranteed to have a page associated with them!",
  166. DontUsePageAttributeWarning)
  167. return self.placeholder.page if self.placeholder_id else None
  168. def get_instance_icon_src(self):
  169. """
  170. Get src URL for instance's icon
  171. """
  172. instance, plugin = self.get_plugin_instance()
  173. if instance:
  174. return plugin.icon_src(instance)
  175. else:
  176. return u''
  177. def get_instance_icon_alt(self):
  178. """
  179. Get alt text for instance's icon
  180. """
  181. instance, plugin = self.get_plugin_instance()
  182. if instance:
  183. return unicode(plugin.icon_alt(instance))
  184. else:
  185. return u''
  186. def save(self, no_signals=False, *args, **kwargs):
  187. if no_signals: # ugly hack because of mptt
  188. super(CMSPlugin, self).save_base(cls=self.__class__)
  189. else:
  190. super(CMSPlugin, self).save()
  191. def set_base_attr(self, plugin):
  192. for attr in ['parent_id', 'placeholder', 'language', 'plugin_type', 'creation_date', 'level', 'lft', 'rght', 'position', 'tree_id']:
  193. setattr(plugin, attr, getattr(self, attr))
  194. def copy_plugin(self, target_placeholder, target_language, plugin_tree):
  195. """
  196. Copy this plugin and return the new plugin.
  197. """
  198. try:
  199. plugin_instance, cls = self.get_plugin_instance()
  200. except KeyError: # plugin type not found anymore
  201. return
  202. new_plugin = CMSPlugin()
  203. new_plugin.placeholder = target_placeholder
  204. new_plugin.tree_id = None
  205. new_plugin.lft = None
  206. new_plugin.rght = None
  207. new_plugin.level = None
  208. if self.parent:
  209. pdif = self.level - plugin_tree[-1].level
  210. if pdif < 0:
  211. plugin_tree[:] = plugin_tree[:pdif - 1]
  212. new_plugin.parent = plugin_tree[-1]
  213. if pdif != 0:
  214. plugin_tree.append(new_plugin)
  215. else:
  216. plugin_tree[:] = [new_plugin]
  217. new_plugin.level = None
  218. new_plugin.language = target_language
  219. new_plugin.plugin_type = self.plugin_type
  220. new_plugin.position = self.position
  221. new_plugin.save()
  222. if plugin_instance:
  223. plugin_instance.pk = new_plugin.pk
  224. plugin_instance.id = new_plugin.pk
  225. plugin_instance.placeholder = target_placeholder
  226. plugin_instance.tree_id = new_plugin.tree_id
  227. plugin_instance.lft = new_plugin.lft
  228. plugin_instance.rght = new_plugin.rght
  229. plugin_instance.level = new_plugin.level
  230. plugin_instance.cmsplugin_ptr = new_plugin
  231. plugin_instance.language = target_language
  232. plugin_instance.parent = new_plugin.parent
  233. plugin_instance.position = new_plugin.position # added to retain the position when creating a public copy of a plugin
  234. plugin_instance.save()
  235. old_instance = plugin_instance.__class__.objects.get(pk=self.pk)
  236. plugin_instance.copy_relations(old_instance)
  237. return new_plugin
  238. def post_copy(self, old_instance, new_old_ziplist):
  239. """
  240. Handle more advanced cases (eg Text Plugins) after the original is
  241. copied
  242. """
  243. pass
  244. def copy_relations(self, old_instance):
  245. """
  246. Handle copying of any relations attached to this plugin. Custom plugins
  247. have to do this themselves!
  248. """
  249. pass
  250. def delete_with_public(self):
  251. """
  252. Delete the public copy of this plugin if it exists,
  253. then delete the draft
  254. """
  255. position = self.position
  256. slot = self.placeholder.slot
  257. page = self.placeholder.page
  258. if page and getattr(page, 'publisher_public'):
  259. try:
  260. placeholder = Placeholder.objects.get(page=page.publisher_public, slot=slot)
  261. except Placeholder.DoesNotExist:
  262. pass
  263. else:
  264. public_plugin = CMSPlugin.objects.filter(placeholder=placeholder, position=position)
  265. public_plugin.delete()
  266. self.placeholder = None
  267. self.delete()
  268. def has_change_permission(self, request):
  269. page = self.placeholder.page if self.placeholder else None
  270. if page:
  271. return page.has_change_permission(request)
  272. elif self.placeholder:
  273. return self.placeholder.has_change_permission(request)
  274. elif self.parent:
  275. return self.parent.has_change_permission(request)
  276. return False
  277. def is_first_in_placeholder(self):
  278. return self.position == 0
  279. def is_last_in_placeholder(self):
  280. """
  281. WARNING: this is a rather expensive call compared to is_first_in_placeholder!
  282. """
  283. return self.placeholder.cmsplugin_set.all().order_by('-position')[0].pk == self.pk
  284. def get_position_in_placeholder(self):
  285. """
  286. 1 based position!
  287. """
  288. return self.position + 1
  289. reversion_register(CMSPlugin)
  290. def deferred_class_factory(model, attrs):
  291. """
  292. Returns a class object that is a copy of "model" with the specified "attrs"
  293. being replaced with DeferredAttribute objects. The "pk_value" ties the
  294. deferred attributes to a particular instance of the model.
  295. """
  296. class Meta:
  297. pass
  298. setattr(Meta, "proxy", True)
  299. setattr(Meta, "app_label", model._meta.app_label)
  300. class RenderMeta:
  301. pass
  302. setattr(RenderMeta, "index", model._render_meta.index)
  303. setattr(RenderMeta, "total", model._render_meta.total)
  304. setattr(RenderMeta, "text_enabled", model._render_meta.text_enabled)
  305. # The app_cache wants a unique name for each model, otherwise the new class
  306. # won't be created (we get an old one back). Therefore, we generate the
  307. # name using the passed in attrs. It's OK to reuse an old case if the attrs
  308. # are identical.
  309. name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs))))
  310. overrides = dict([(attr, DeferredAttribute(attr, model))
  311. for attr in attrs])
  312. overrides["Meta"] = RenderMeta
  313. overrides["RenderMeta"] = RenderMeta
  314. overrides["__module__"] = model.__module__
  315. overrides["_deferred"] = True
  316. return type(name, (model,), overrides)
  317. # The above function is also used to unpickle model instances with deferred
  318. # fields.
  319. deferred_class_factory.__safe_for_unpickling__ = True