================= Extending Cascade ================= All Cascade plugins are derived from the same base class :class:`cmsplugin_cascade.plugin_base.CascadeModelBase`, which stores all its model fields inside a dictionary, serialized as JSON string in the database. This makes it much easier to extend the Cascade eco-system, since no database migration [#migration]_ is required when adding a new, or extending plugins from this project. The database model ``CascadeModelBase`` stores all the plugin settings in a single JSON field named ``glossary``. This in practice behaves like a Django context, but in order to avoid confusion with the latter, it has been named "glossary". .. note:: Custom Cascade plugins should set the ``app_label`` attribute (see below). This is important so migrations for the proxy models generated by Cascade are created in the correct app. If this attribute is not set, Cascade will default to the left-most part of the plugin's module path. So if your plugin lives in ``myapp.cascadeplugins``, Cascade will use ``myapp`` as the app label. We recommend that you always set ``app_label`` explicitly. Simple Example ============== This plugin is very simple and just renders static content which has been declared in the template. .. code-block:: python from cms.plugin_pool import plugin_pool from cmsplugin_cascade.plugin_base import CascadePluginBase class StylishPlugin(CascadePluginBase): name = 'Stylish Element' render_template = 'myapp/cascade/stylish-element.html' plugin_pool.register_plugin(StylishPlugin) If the editor form pops up for this plugin, a dumb message appears: "There are no further settings for this plugin". This is because no editable fields have been added to that plugin yet. Customize the Plugin Editor =========================== In order to make the plugin remember its settings and other optional data, we must specify a Django form to be used by the plugin. Since its payload data is stored in a JSON field, we use django-entangled_, to map the form fields. Each of those form fields handles a special field value, or in some cases, a list of field values. They all require one or more Django form fields, which are rendered by the plugins popup editor. Let's add a simple selector to choose between a red and a green color. Do this by adding ``form`` to the plugin class. .. code-block:: python from django.forms import ChoiceField from entangled.forms import EntangledModelFormMixin from cmsplugin_cascade.plugin_base import CascadePluginBase class StylishFormMixin(EntangledModelFormMixin): color = ChoiceField( choices=[('red', 'Red'), ('green', 'Green')], label="Element's Color", initial='red', help_text="Specify the color of the DOM element." ) class Meta: entangled_fields = {'glossary': ['color']} class StylishPlugin(CascadePluginBase): … form = StylishFormMixin In the plugin's editor, the form now pops up with a single select box, where the user can choose between a *red* and a *green* element. The form ``StylishFormMixin`` inherits from ``EntangledModelFormMixin`` available through the separate Django app django-entangled_. This app allows to edit JSON-Model fields using a standard Django form. Since **djangocms-cascade** may extend this form with additional fields, here we use a special mixin class, rather than a Django ``ModelForm``. Remember to add class ``Meta`` to this form, in order to specify the mapping of form fields inside the JSON field named ``glossary``. .. _django-entangled: https://pypi.org/project/django-entangled/ Special Form Field for Plugin Editors ===================================== For single text fields or select boxes, Django's built-in fields, such as ``CharField`` or ``ChoiceField`` can be used. Sometimes these simple fields are not enough, therefore **djangocms-cascade** additionally provides special form fields, which makes it easier to create editors specialized for styling CSS. These special fields are all part of the module ``cmsplugin_cascade.fields``. :SizeField: When entering measurements, such as margins, paddings, widths, heights, etc, one may choose between different units, such as ``px``, ``em``, ``rem`` or ``%``. This fields validates its input by checking if a unit is specified. :MultiSizeField: Use this field to group a list of size input fields together. This for instance is used, to encapsulate all margins into one list inside the JSON object. :ColorField: Use this field when the user shall enter a color value. Since the color picker widget built into the browser often is inconvenient, the special picker a-color-picker_ can be used instead. This external library even supports alpha channels. Simply install it into the directory of the Django project and add ``node_modules`` to the list of ``STATICFILES_DIRS``. Since we can not leave the color field empty, this field adds a checkbox to inform the plugin editor, if no color is desired. The latter means, that the color is inherited by an upper DOM element. :BorderChoiceField: Use this field to style borders. It adds three input fields, one to set the border width, one for the border style and one for the border color. The latter uses the special picker a-color-picker_, if installed. Otherwise it falls back to the built-in color widget. .. _a-color-picker: https://www.npmjs.com/package/a-color-picker Overriding the Model ==================== Since all **djangocms-cascade** plugins store their data in a JSON-serializable field, there rarely is a need to add another database field to the common models ``CascadeElement`` and/or ``SharableCascadeElement`` and thus no need for database migrations. However, quite often there is a need to add or override the methods for these models. Therefore each Cascade plugin creates its own `proxy model`_ on the fly. These models inherit from ``CascadeElement`` and/or ``SharableCascadeElement`` and named like the plugin class, with the suffix ``Model``. By default, their behavior is the same as for their parent model classes. To extend this behavior, the author of a plugin may declare a tuple of mixin classes, which are injected during the creation of the proxy model. Example: .. code-block:: python class MySpecialPropertyMixin(object): def processed_value(self): value = self.glossary.get('field_name') # process value return value class MySpecialPlugin(LinkPluginBase): module = 'My Module' name = 'My special Plugin' model_mixins = (MySpecialPropertyMixin,) render_template = 'my_module/my_special_plugin.html' ... The proxy model created for this plugin class, now contains the extra method ``processed_value()``, which for instance may be accessed during template rendering. ``templates/my_module/my_special_plugin.html``: .. code-block:: html