Skip to content
New issue

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

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CascadeUtilitiesMixin accept:css_classes or htmt_data_attribute and nested fields django-entangled #363

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 50 additions & 12 deletions cmsplugin_cascade/bootstrap4/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,50 @@ class BootstrapUtilities(type):
}
```

The class ``BootstrapUtilities`` offers a bunch of property methods which return a list of
input fields and/or select boxes. They then can be added to the plugin's editor. This is
specially useful to add CSS classes from the utilities section of Bootstrap-4, such as
specially useful to add CSS classes or HTML data attributes from the utilities section of Bootstrap-4, such as
margins, borders, colors, etc.

The 'property_name' attritbute in property methods is needed because python property methods don't have name
attributes without using inspect module or others things.
The 'attrs_type' attritbute in property methods can have two possiblity values 'css_classes' or 'html_data_attrs'.
The 'anchors_fields' in the property_fields attributes can add choices id elements of the current page, theses
choices are realy set when the request is available.
"""

def __new__(cls, *args):
form_fields = {}
form_fields_by_property_name = {}
form_fields_by_attr_type = {}
fields_choices_anchors = []

for arg in args:
if isinstance(arg, property):
form_fields.update(arg.fget(cls))

property_fields=arg.fget(cls)
form_subfields = property_fields['form_fields']
attrs_type = property_fields['attrs_type']
property_name = property_fields['property_name']

form_fields_by_attr_type.setdefault(attrs_type, [])
form_fields_by_property_name.setdefault(property_name, [])

form_fields.update(form_subfields)
form_fields_by_property_name[property_name].extend(property_fields['form_fields'].keys())
form_fields_by_attr_type[attrs_type ].extend(property_fields['form_fields'].keys())
if 'anchors_fields' in property_fields:
fields_choices_anchors.extend(property_fields['anchors_fields'])
class Meta:
entangled_fields = {'glossary': list(form_fields.keys())}
entangled_fields = {'glossary': [values for values in form_fields_by_property_name.values()] }

utility_form_mixin = type('UtilitiesFormMixin', (EntangledModelFormMixin,), dict(form_fields, Meta=Meta))
return type('BootstrapUtilitiesMixin', (CascadeUtilitiesMixin,), {'utility_form_mixin': utility_form_mixin})
utility_form_mixin = type('UtilitiesFormMixin', (EntangledModelFormMixin,), dict(form_fields, Meta=Meta) )
return type('HtmlAttrsUtilitiesMixin', (CascadeUtilitiesMixin,), {'utility_form_mixin': utility_form_mixin,
'attr_type': form_fields_by_attr_type , 'fields_with_choices_anchors': fields_choices_anchors })

@property
def background_and_color(cls):
attrs_type = 'css_classes'
property_name = 'background_and_color'
choices = [
('', _("Default")),
('bg-primary text-white', _("Primary with white text")),
Expand All @@ -57,15 +81,19 @@ def background_and_color(cls):
('bg-transparent text-dark', _("Transparent with dark text")),
('bg-transparent text-white', _("Transparent with white text")),
]
return {'background_and_color': ChoiceField(
form_fields = {'background_and_color': ChoiceField(
label=_("Background and color"),
choices=choices,
required=False,
initial='',
)}
property_fields = { 'form_fields':form_fields, 'attrs_type': attrs_type, 'property_name':property_name }
return property_fields

@property
def margins(cls):
attrs_type = 'css_classes'
property_name = 'margins'
form_fields = {}
choices_format = [
('m-{}{}', _("4 sided margins ({})")),
Expand All @@ -91,10 +119,13 @@ def margins(cls):
required=False,
initial='',
)
return form_fields
property_fields = { 'form_fields':form_fields, 'attrs_type': attrs_type, 'property_name':property_name }
return property_fields

@property
def vertical_margins(cls):
attrs_type = 'css_classes'
property_name = 'vertical_margins'
form_fields = {}
choices_format = [
('my-{}{}', _("Vertical margins ({})")),
Expand All @@ -116,10 +147,13 @@ def vertical_margins(cls):
required=False,
initial='',
)
return form_fields
property_fields = { 'form_fields':form_fields, 'attrs_type': attrs_type, 'property_name':property_name }
return property_fields

@property
def paddings(cls):
attrs_type = 'css_classes'
property_name = 'paddings'
form_fields = {}
choices_format = [
('p-{}{}', _("4 sided padding ({})")),
Expand All @@ -145,11 +179,14 @@ def paddings(cls):
required=False,
initial='',
)
return form_fields
property_fields = { 'form_fields':form_fields, 'attrs_type': attrs_type, 'property_name':property_name }
return property_fields

@property
def floats(cls):
form_fields = {}
attrs_type = 'css_classes'
property_name = 'floats'
choices_format = [
('float-{}none', _("Do not float")),
('float-{}left', _("Float left")),
Expand All @@ -169,4 +206,5 @@ def floats(cls):
required=False,
initial='',
)
return form_fields
property_fields = { 'form_fields':form_fields, 'attrs_type': attrs_type, 'property_name':property_name }
return property_fields
5 changes: 3 additions & 2 deletions cmsplugin_cascade/plugin_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.utils.module_loading import import_string
from django.utils.text import format_lazy
from django.utils.safestring import SafeText, mark_safe
from entangled.forms import EntangledModelFormMixin
from entangled.forms import EntangledModelFormMixin, gen_separate_fieldsets
from cms.plugin_base import CMSPluginBaseMetaclass, CMSPluginBase
from cms.utils.compat.dj import is_installed
from cmsplugin_cascade import app_settings
Expand Down Expand Up @@ -314,6 +314,8 @@ def extend_children(self, parent, wanted_children, child_class, child_glossary=N
def get_form(self, request, obj=None, **kwargs):
form = kwargs.get('form', self.form)
assert issubclass(form, EntangledModelFormMixin), "Form must inherit from EntangledModelFormMixin"
if not self.fieldsets:
self.fieldsets = tuple(gen_separate_fieldsets(form))
if issubclass(form, ModelForm):
kwargs['form'] = form
else:
Expand Down Expand Up @@ -386,7 +388,6 @@ def render_change_form(self, request, context, add=False, change=False, form_url
context.update(
ring_plugin=self.ring_plugin,
)
context['empty_form'] = not context['adminform'].form._meta.entangled_fields
return super().render_change_form(request, context, add, change, form_url, obj)

def in_edit_mode(self, request, placeholder):
Expand Down
30 changes: 24 additions & 6 deletions cmsplugin_cascade/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,41 @@ class CascadeUtilitiesMixin(metaclass=MediaDefiningClass):
"""
If a Cascade plugin is listed in ``settings.CMSPLUGIN_CASCADE['plugins_with_extra_mixins']``,
then this ``BootstrapUtilsMixin`` class is added automatically to its plugin class in order to
enrich it with utility classes, such as :class:`cmsplugin_cascade.bootstrap4.mixins.BootstrapUtilities`.
enrich it with utility classes or html_attrs, such as :class:`cmsplugin_cascade.bootstrap4.mixins.BootstrapUtilities`.
If anchor_fields is specified in the property_fields attributes, these attribute choices are set when the request
is available whit id elements of the current page.

"""

def __str__(self):
return self.plugin_class.get_identifier(self)

def get_form(self, request, obj=None, **kwargs):
form = kwargs.get('form', self.form)
for anchors_field in self.fields_with_choices_anchors:
currentpage_element_ids = obj.page.cascadepage.glossary.get('element_ids', {})
self.utility_form_mixin.base_fields[anchors_field].choices=[[items,value] for items, value in currentpage_element_ids.items()]
assert issubclass(form, EntangledModelFormMixin), "Form must inherit from EntangledModelFormMixin"
kwargs['form'] = type(form.__name__, (self.utility_form_mixin, form), {})
return super().get_form(request, obj, **kwargs)


@classmethod
def get_css_classes(cls, obj):
"""Enrich list of CSS classes with customized ones"""
css_classes = super().get_css_classes(obj)
for utility_field_name in cls.utility_form_mixin.base_fields.keys():
css_class = obj.glossary.get(utility_field_name)
if css_class:
css_classes.append(css_class)
if 'css_classes' in cls.attr_type:
for utility_field_name in cls.attr_type['css_classes']:
css_class = obj.glossary.get(utility_field_name)
if css_class:
css_classes.append(css_class)
return css_classes

@classmethod
def get_html_tag_attributes(cls, obj):
attributes = super().get_html_tag_attributes(obj)
if 'html_data_attrs' in cls.attr_type:
for utility_field_name in cls.attr_type['html_data_attrs']:
attribute = obj.glossary.get(utility_field_name)
if attribute:
attributes.update({utility_field_name:attribute})
return attributes