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

[WIP] Cascade entangled nested #377

Open
wants to merge 39 commits into
base: releases/1.3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cceca90
test dev branch etangled
haricot Feb 27, 2020
d84b9ef
test django-entangled branch develop
haricot Feb 27, 2020
af5aa4b
Update with django-entangled nested form
haricot Feb 28, 2020
18cf1db
Update dev requirements django-entangled develop
haricot Feb 28, 2020
255ab2c
accordion.py use entangled nested branch dev
haricot Mar 1, 2020
aa21503
fix plugin form with entangled nested branch dev
haricot Mar 1, 2020
76ad096
modif key related entangled nested branch dev
haricot Mar 1, 2020
1bfa26c
clean code unneeded
haricot Mar 1, 2020
9f7bd4e
test_accordion.py use entangled nested branch dev
haricot Mar 1, 2020
cd57611
test_accordion.py use entangled nested
haricot Mar 1, 2020
5a6f3fb
fix test test_accordion.py
haricot Mar 1, 2020
ca89af5
add `Class ManageNestedFormMixin` (initial data)
haricot Mar 3, 2020
fa5d3f1
fix initial data
haricot Mar 3, 2020
521a970
wip cascade entangled nested
haricot Mar 3, 2020
7999408
wip cascade entangled nested
haricot Mar 3, 2020
309e1fd
clean unneeded code
haricot Mar 3, 2020
c963805
clean unneeded code
haricot Mar 3, 2020
3e5b012
clean code
haricot Mar 3, 2020
d4b2fdc
add def gen_fieldsets_by_entangled_form
haricot Mar 3, 2020
e699aae
clean code
haricot Mar 3, 2020
8e48d7a
clean code
haricot Mar 3, 2020
924e999
try fix accordion test
haricot Mar 3, 2020
ad77ac0
clean code
haricot Mar 3, 2020
40cbeb0
fix test accordion
haricot Mar 3, 2020
792d4af
clean code
haricot Mar 3, 2020
c5d26eb
add buttons cascade entangled nested
haricot Mar 3, 2020
85324d0
add carousel,container entangled_nested
haricot Mar 4, 2020
e4f6c52
fix test container
haricot Mar 4, 2020
5cbe34f
fix test container
haricot Mar 4, 2020
708b9eb
fix test row
haricot Mar 4, 2020
d44d942
fix test row
haricot Mar 4, 2020
0be10ca
retry fix test container row
haricot Mar 4, 2020
05a2e91
fix syntax test container row
haricot Mar 4, 2020
dcb96ba
fix syntax test container row
haricot Mar 4, 2020
c9cc524
fix test container
haricot Mar 4, 2020
f15cbc3
fix retry test container
haricot Mar 4, 2020
ad0e197
add framed icon&fix test container
haricot Mar 4, 2020
7295f10
fix test framed icon&retry fix container
haricot Mar 4, 2020
064d512
fix test stride & add image_nested form
haricot Mar 4, 2020
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
2 changes: 1 addition & 1 deletion cmsplugin_cascade/app_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def CMSPLUGIN_CASCADE(self):
if 'cmsplugin_cascade.sharable' in INSTALLED_APPS:
config['plugins_with_sharables'].setdefault(
'FramedIconPlugin',
['font_size', 'color', 'background_color', 'text_align', 'border', 'border_radius'])
['icon_nested.font_size', 'icon_nested.color', 'icon_nested.background_color', 'icon_nested.text_align', 'icon_nested.border', 'icon_nested.border_radius'])

config['exclude_hiding_plugin'] = list(config.get('exclude_hiding_plugin', []))
config['exclude_hiding_plugin'].append('SegmentPlugin')
Expand Down
42 changes: 25 additions & 17 deletions cmsplugin_cascade/bootstrap4/accordion.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
from django.utils.safestring import mark_safe
from django.utils.text import Truncator
from django.utils.html import escape
from entangled.forms import EntangledModelFormMixin
from entangled.forms import EntangledModelFormMixin, EntangledFormField, EntangledForm
from cms.plugin_pool import plugin_pool
from cmsplugin_cascade.forms import ManageChildrenFormMixin
from cmsplugin_cascade.forms import ManageChildrenFormMixin, ManageNestedFormMixin
from cmsplugin_cascade.plugin_base import TransparentWrapper, TransparentContainer
from cmsplugin_cascade.widgets import NumberInputWidget
from .plugin_base import BootstrapPluginBase


class AccordionFormMixin(ManageChildrenFormMixin, EntangledModelFormMixin):
class AccordionForm(EntangledForm):
num_children = IntegerField(
min_value=1,
initial=1,
Expand All @@ -35,9 +35,12 @@ class AccordionFormMixin(ManageChildrenFormMixin, EntangledModelFormMixin):
help_text=_("Start with the first card open.")
)

class AccordionFormMixin(EntangledModelFormMixin, ManageNestedFormMixin):
accordion_nested = EntangledFormField(AccordionForm)

class Meta:
untangled_fields = ['num_children']
entangled_fields = {'glossary': ['close_others', 'first_is_open']}
untangled_fields = ['accordion_nested.num_children']
entangled_fields = {'glossary': ['accordion_nested']}


class BootstrapAccordionPlugin(TransparentWrapper, BootstrapPluginBase):
Expand All @@ -59,20 +62,20 @@ def get_identifier(cls, obj):
def render(self, context, instance, placeholder):
context = self.super(BootstrapAccordionPlugin, self).render(context, instance, placeholder)
context.update({
'close_others': instance.glossary.get('close_others', True),
'first_is_open': instance.glossary.get('first_is_open', True),
'close_others': instance.glossary.get('accordion_nested', {}).get('close_others', True),
'first_is_open': instance.glossary.get('accordion_nested', {}).get('first_is_open', True),
})
return context

def save_model(self, request, obj, form, change):
wanted_children = int(form.cleaned_data.get('num_children'))
def save_model(self, request, obj, form, change):
wanted_children = int(form.cleaned_data.get('accordion_nested.num_children'))
super().save_model(request, obj, form, change)
self.extend_children(obj, wanted_children, BootstrapAccordionGroupPlugin)

plugin_pool.register_plugin(BootstrapAccordionPlugin)


class AccordionGroupFormMixin(EntangledModelFormMixin):
class AccordionGroupForm(EntangledForm):
heading = CharField(
label=_("Heading"),
widget=widgets.TextInput(attrs={'size': 80}),
Expand All @@ -85,11 +88,15 @@ class AccordionGroupFormMixin(EntangledModelFormMixin):
help_text=_("Add standard padding to card body."),
)

class Meta:
entangled_fields = {'glossary': ['heading', 'body_padding']}

def clean_heading(self):
return escape(self.cleaned_data['heading'])
return escape(self.cleaned_data.get('heading'))


class AccordionGroupFormMixin( ManageNestedFormMixin, EntangledModelFormMixin):
accordion_nested = EntangledFormField(AccordionGroupForm)

class Meta:
entangled_fields = {'glossary': ['accordion_nested']}


class BootstrapAccordionGroupPlugin(TransparentContainer, BootstrapPluginBase):
Expand All @@ -102,15 +109,16 @@ class BootstrapAccordionGroupPlugin(TransparentContainer, BootstrapPluginBase):

@classmethod
def get_identifier(cls, instance):
heading = instance.glossary.get('heading', '')
heading = instance.glossary.get('accordion_nested', {}).get('heading' , '')
return Truncator(heading).words(3, truncate=' ...')

def render(self, context, instance, placeholder):
context = self.super(BootstrapAccordionGroupPlugin, self).render(context, instance, placeholder)
context.update({
'heading': mark_safe(instance.glossary.get('heading', '')),
'no_body_padding': not instance.glossary.get('body_padding', True),
'heading': mark_safe(instance.glossary.get('accordion_nested', {}).get('heading' , '')),
'no_body_padding': not instance.glossary.get('accordion_nested', {}).get('no_body_padding' , True),
})
return context

plugin_pool.register_plugin(BootstrapAccordionGroupPlugin)

17 changes: 9 additions & 8 deletions cmsplugin_cascade/bootstrap4/buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
from django.forms.fields import BooleanField, CharField, ChoiceField, MultipleChoiceField
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _
from entangled.forms import EntangledModelFormMixin
from entangled.forms import EntangledModelFormMixin, EntangledFormField, EntangledForm
from cms.plugin_pool import plugin_pool
from cmsplugin_cascade.icon.plugin_base import IconPluginMixin
from cmsplugin_cascade.icon.forms import IconFormMixin
from cmsplugin_cascade.link.config import LinkPluginBase, LinkFormMixin
from cmsplugin_cascade.link.plugin_base import LinkElementMixin

from cmsplugin_cascade.forms import ManageChildrenFormMixin, ManageNestedFormMixin

class ButtonTypeWidget(widgets.RadioSelect):
"""
Expand All @@ -24,8 +24,7 @@ class ButtonSizeWidget(widgets.RadioSelect):
"""
template_name = 'cascade/admin/legacy_widgets/button_sizes.html' if DJANGO_VERSION < (2, 0) else 'cascade/admin/widgets/button_sizes.html'


class ButtonFormMixin(EntangledModelFormMixin):
class ButtonForm(EntangledForm):
BUTTON_TYPES = [
('btn-primary', _("Primary")),
('btn-secondary', _("Secondary")),
Expand Down Expand Up @@ -93,6 +92,9 @@ class ButtonFormMixin(EntangledModelFormMixin):
"relative parent, perfect for entirely clickable cards!")
)

class ButtonFormMixin(EntangledModelFormMixin, ManageNestedFormMixin):
button_opts_nested = EntangledFormField(ButtonForm)

icon_align = ChoiceField(
label=_("Icon alignment"),
choices=[
Expand All @@ -105,8 +107,7 @@ class ButtonFormMixin(EntangledModelFormMixin):
)

class Meta:
entangled_fields = {'glossary': ['link_content', 'button_type', 'button_size', 'button_options', 'icon_align',
'stretched_link']}
entangled_fields = {'glossary': ['button_opts_nested', 'icon_align',]}


class BootstrapButtonMixin(IconPluginMixin):
Expand Down Expand Up @@ -154,7 +155,7 @@ class Media:

@classmethod
def get_identifier(cls, instance):
content = instance.glossary.get('link_content')
content = instance.glossary.get('button_opts_nested', {}).get('link_content')
if not content:
try:
button_types = dict(ButtonFormMixin.BUTTON_TYPES)
Expand All @@ -166,7 +167,7 @@ def get_identifier(cls, instance):
@classmethod
def get_css_classes(cls, obj):
css_classes = cls.super(BootstrapButtonPlugin, cls).get_css_classes(obj)
if obj.glossary.get('stretched_link'):
if obj.glossary.get('button_opts_nested', {}).get('stretched_link'):
css_classes.append('stretched_link')
return css_classes

Expand Down
66 changes: 36 additions & 30 deletions cmsplugin_cascade/bootstrap4/carousel.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
from django.utils.safestring import mark_safe
from django.utils.translation import ungettext_lazy, ugettext_lazy as _

from entangled.forms import EntangledModelFormMixin
from entangled.forms import EntangledModelFormMixin, EntangledFormField, EntangledForm
from cms.plugin_pool import plugin_pool
from cmsplugin_cascade.bootstrap4.fields import BootstrapMultiSizeField
from cmsplugin_cascade.bootstrap4.grid import Breakpoint
from cmsplugin_cascade.bootstrap4.picture import get_picture_elements
from cmsplugin_cascade.bootstrap4.plugin_base import BootstrapPluginBase
from cmsplugin_cascade.bootstrap4.utils import IMAGE_RESIZE_OPTIONS
from cmsplugin_cascade.forms import ManageChildrenFormMixin
from cmsplugin_cascade.forms import ManageChildrenFormMixin, ManageNestedFormMixin
from cmsplugin_cascade.image import ImagePropertyMixin, ImageFormMixin

logger = logging.getLogger('cascade')


class CarouselSlidesFormMixin(ManageChildrenFormMixin, EntangledModelFormMixin):
class CarouselSlidesForm(EntangledForm):
OPTION_CHOICES = [('slide', _("Animate")), ('pause', _("Pause")), ('wrap', _("Wrap"))]

num_children = IntegerField(min_value=1, initial=1,
Expand Down Expand Up @@ -55,9 +55,13 @@ class CarouselSlidesFormMixin(ManageChildrenFormMixin, EntangledModelFormMixin):
initial=['upscale', 'crop', 'subject_location', 'high_resolution'],
)

class CarouselSlidesFormMixin(EntangledModelFormMixin, ManageNestedFormMixin):
carousel_nested = EntangledFormField(CarouselSlidesForm)

class Meta:
untangled_fields = ['num_children']
entangled_fields = {'glossary': ['interval', 'options', 'container_max_heights', 'resize_options']}
#untangled_fields = ['num_children']
# untangled_fields = ['accordion_nested.num_children']
entangled_fields = {'glossary': ['carousel_nested']}


class BootstrapCarouselPlugin(BootstrapPluginBase):
Expand All @@ -79,22 +83,22 @@ def get_identifier(cls, obj):
@classmethod
def get_css_classes(cls, obj):
css_classes = cls.super(BootstrapCarouselPlugin, cls).get_css_classes(obj)
if 'slide' in obj.glossary.get('options', []):
if 'slide' in obj.glossary.get('carousel_nested',{}).get('options', []):
css_classes.append('slide')
return css_classes

@classmethod
def get_html_tag_attributes(cls, obj):
attributes = cls.super(BootstrapCarouselPlugin, cls).get_html_tag_attributes(obj)
attributes.update(cls.DEFAULT_CAROUSEL_ATTRIBUTES)
attributes['data-interval'] = 1000 * int(obj.glossary.get('interval', 5))
options = obj.glossary.get('options', [])
attributes['data-interval'] = 1000 * int(obj.glossary.get('carousel_nested',{}).get('interval', 5))
options = obj.glossary.get('carousel_nested',{}).get('options', [])
attributes['data-pause'] = 'pause' in options and 'hover' or 'false'
attributes['data-wrap'] = 'wrap' in options and 'true' or 'false'
return attributes

def save_model(self, request, obj, form, change):
wanted_children = int(form.cleaned_data.get('num_children'))
wanted_children = int(form.cleaned_data.get('carousel_nested',{}).get('num_children', 1))
super().save_model(request, obj, form, change)
self.extend_children(obj, wanted_children, BootstrapCarouselSlidePlugin)
obj.sanitize_children()
Expand All @@ -104,11 +108,11 @@ def sanitize_model(cls, obj):
sanitized = super().sanitize_model(obj)
complete_glossary = obj.get_complete_glossary()
# fill all invalid heights for this container to a meaningful value
max_height = max(obj.glossary['container_max_heights'].values())
max_height = max(obj.glossary.get('carousel_nested',{})['container_max_heights'].values())
pattern = re.compile(r'^(\d+)px$')
for bp in complete_glossary.get('breakpoints', ()):
if not pattern.match(obj.glossary['container_max_heights'].get(bp, '')):
obj.glossary['container_max_heights'][bp] = max_height
if not pattern.match(obj.glossary.get('carousel_nested',{})['container_max_heights'].get(bp, '')):
obj.glossary.get('carousel_nested',{})['container_max_heights'][bp] = max_height
return sanitized

plugin_pool.register_plugin(BootstrapCarouselPlugin)
Expand All @@ -130,7 +134,7 @@ def render(self, context, instance, placeholder):
# slide image shall be rendered in a responsive context using the ``<picture>`` element
try:
parent_glossary = instance.parent.get_bound_plugin().glossary
instance.glossary.update(responsive_heights=parent_glossary['container_max_heights'])
instance.glossary.update(responsive_heights=parent_glossary.get('carousel_nested',{})['container_max_heights'])
elements = get_picture_elements(instance)
except Exception as exc:
logger.warning("Unable generate picture elements. Reason: {}".format(exc))
Expand All @@ -144,27 +148,29 @@ def render(self, context, instance, placeholder):
@classmethod
def sanitize_model(cls, obj):
sanitized = super().sanitize_model(obj)
resize_options = obj.get_parent_glossary().get('resize_options', [])
resize_options = obj.get_parent_glossary().get('carousel_nested',{}).get('resize_options', [])
if obj.glossary.get('resize_options') != resize_options:
obj.glossary.update(resize_options=resize_options)
sanitized = True

parent = obj.parent
while parent.plugin_type != 'BootstrapColumnPlugin':
parent = parent.parent
if parent is None:
logger.warning("PicturePlugin(pk={}) has no ColumnPlugin as ancestor.".format(obj.pk))
return
grid_column = parent.get_bound_plugin().get_grid_instance()
obj.glossary.setdefault('media_queries', {})
for bp in Breakpoint:
obj.glossary['media_queries'].setdefault(bp.name, {})
width = round(grid_column.get_bound(bp).max)
if obj.glossary['media_queries'][bp.name].get('width') != width:
obj.glossary['media_queries'][bp.name]['width'] = width
sanitized = True
if obj.glossary['media_queries'][bp.name].get('media') != bp.media_query:
obj.glossary['media_queries'][bp.name]['media'] = bp.media_query
sanitized = True
if parent: # prevent copy slide error
while parent.plugin_type != 'BootstrapColumnPlugin':
parent = parent.parent
if parent is None:
logger.warning("PicturePlugin(pk={}) has no ColumnPlugin as ancestor.".format(obj.pk))
return
grid_column = parent.get_bound_plugin().get_grid_instance()
obj.glossary.setdefault('media_queries', {})
for bp in Breakpoint:
obj.glossary['media_queries'].setdefault(bp.name, {})
width = round(grid_column.get_bound(bp).max)
if obj.glossary['media_queries'][bp.name].get('width') != width:
obj.glossary['media_queries'][bp.name]['width'] = width
sanitized = True
if obj.glossary['media_queries'][bp.name].get('media') != bp.media_query:
obj.glossary['media_queries'][bp.name]['media'] = bp.media_query
sanitized = True
return sanitized

@classmethod
Expand Down
Loading