diff --git a/cmsplugin_cascade/bootstrap4/buttons.py b/cmsplugin_cascade/bootstrap4/buttons.py
index 0e8e1e4c1..53dd5f4ac 100644
--- a/cmsplugin_cascade/bootstrap4/buttons.py
+++ b/cmsplugin_cascade/bootstrap4/buttons.py
@@ -110,7 +110,7 @@ class Meta:
class BootstrapButtonMixin(IconPluginMixin):
require_parent = True
- parent_classes = ['BootstrapColumnPlugin', 'SimpleWrapperPlugin']
+ parent_classes = ['BootstrapColumnPlugin', 'SimpleWrapperPlugin','BootstrapNavItemsPlugin', 'BootstrapListsPlugin']
render_template = 'cascade/bootstrap4/button.html'
allow_children = False
default_css_class = 'btn'
@@ -167,6 +167,9 @@ def get_css_classes(cls, obj):
css_classes = cls.super(BootstrapButtonPlugin, cls).get_css_classes(obj)
if obj.glossary.get('stretched_link'):
css_classes.append('stretched_link')
+ if hasattr(obj, 'parent') and hasattr(obj.parent,'parent') and hasattr(obj.parent.parent,'plugin_type'):
+ if obj.parent.parent.plugin_type == 'BootstrapNavBrandPlugin':
+ css_classes.insert(0,'nav-link text-left')
return css_classes
@classmethod
diff --git a/cmsplugin_cascade/bootstrap4/lists.py b/cmsplugin_cascade/bootstrap4/lists.py
new file mode 100644
index 000000000..b755f232a
--- /dev/null
+++ b/cmsplugin_cascade/bootstrap4/lists.py
@@ -0,0 +1,70 @@
+from django.utils.html import format_html
+from django.utils.translation import ugettext_lazy as _
+from django.forms.fields import ChoiceField
+from .plugin_base import BootstrapPluginBase
+from cms.plugin_pool import plugin_pool
+from entangled.forms import EntangledModelFormMixin
+
+import logging
+logger = logging.getLogger('cascade')
+
+class BootstrapListsMixin( EntangledModelFormMixin):
+ list_options = ChoiceField(
+ label=_("List Options"),
+ choices=[
+ ('inherit', _("inherit")),
+ ('inline-item', _('ul(class="inline-item").li(class="list-inline-item")')),
+ ('navbar-nav', _('ul(class="navbar-nav").li(class="nav-item")')),
+ ],
+ required=False,
+ )
+ class Meta:
+ entangled_fields = {'glossary': ['list_options']}
+
+
+@plugin_pool.register_plugin
+class BootstrapListsPlugin(BootstrapPluginBase):
+ name = _("Lists")
+ alien_plugins = True
+ form = BootstrapListsMixin
+ alien_child_classes = True
+ render_template = 'cascade/bootstrap4/navbar_list.html'
+ default_css_class = ''
+ require_parent = False
+
+ @classmethod
+ def get_css_classes(cls, obj):
+ css_classes = cls.super(BootstrapListsPlugin, cls).get_css_classes(obj)
+ list_options = [obj.glossary.get('list_options')] if obj.glossary.get('list_options') else ''
+ if list_options:
+ for opts in list_options:
+ css_classes.append(opts)
+ return css_classes
+
+
+ @classmethod
+ def get_identifier(cls, obj):
+ identifier = super(BootstrapListsPlugin, cls).get_identifier(obj)
+ if hasattr(cls,'default_css_class'):
+ css_classes_without_default = obj.css_classes.replace( cls.default_css_class , '' , 1)
+ else:
+ css_classes_without_default = obj.css_classes
+ return format_html('
{0}{1}
',
+ identifier, css_classes_without_default )
+
+ @classmethod
+ def sanitize_model(cls, obj):
+ list_child_css_classes = obj.glossary['child_css_classes'].split(' ') if 'child_css_classes' in obj.glossary else []
+ list_options = 'list-inline-item' if obj.glossary.get('list_options') else ''
+ list_options = 'nav-item' if obj.glossary.get('list_options') == 'navbar-nav' else list_options
+ if list_options:
+ list_child_css_classes.append(list_options)
+ obj.glossary['child_css_classes'] = ' '.join(list_child_css_classes)
+ super().sanitize_model(obj)
+
+@plugin_pool.register_plugin
+class BootstrapListsSeparatorPlugin(BootstrapPluginBase):
+ name = _("Li Separator")
+ require_parent = True
+ parent_classes = ['BootstrapListsPlugin']
+ render_template = 'cascade/bootstrap4/navbar_list_separator.html'
diff --git a/cmsplugin_cascade/bootstrap4/mixins.py b/cmsplugin_cascade/bootstrap4/mixins.py
index 35202923f..41f58933b 100644
--- a/cmsplugin_cascade/bootstrap4/mixins.py
+++ b/cmsplugin_cascade/bootstrap4/mixins.py
@@ -170,3 +170,104 @@ def floats(cls):
initial='',
)
return form_fields
+
+ @property
+ def flex_directions(cls):
+ form_fields = {}
+ choices_format = [
+ ('flex-{}row', _("horizontal")),
+ ('flex-{}row-reverse', _("horizontal reverse")),
+ ('flex-{}column', _("Vertical")),
+ ('flex-{}column-reverse', _("Vertical reverse")),
+ ]
+ for bp in Breakpoint.range(Breakpoint.xs, Breakpoint.xl):
+ if bp == Breakpoint.xs:
+ choices = [ (c.format(''), l ) for c, l in choices_format]
+ choices.insert(0, ('', _("No Flex Directions")))
+ else:
+ choices = [(c.format(bp.name + '-'), l) for c, l in choices_format]
+ choices.insert(0, ('', _("Inherit from above")))
+ form_fields['Flex_{}'.format(bp.name)] = ChoiceField(
+ label=format_lazy(_("Flex Directions for {breakpoint}"), breakpoint=bp.label),
+ choices=choices,
+ required=False,
+ initial=''
+ )
+ return form_fields
+
+ @property
+ def display_propertys(cls):
+ form_fields = {}
+ choices_format = [
+ ('d-{}{}', _("horizontal")),
+ ]
+ notation = ['none', 'inline', 'inline-block', 'block', 'table', 'table-cell', 'table-row', 'flex', 'inline-flex']
+ for bp in Breakpoint.range(Breakpoint.xs, Breakpoint.xl):
+ if bp == Breakpoint.xs:
+ choices = [(c.format('', n), c.format('', n)) for c, l in choices_format for n in notation]
+ choices.insert(0, ('', _("No Display Propertys")))
+ else:
+ choices = [(c.format(bp.name + '-', n), c.format(bp.name + '-', n)) for c, l in choices_format for n in notation]
+ choices.insert(0, ('', _("Inherit from above")))
+ form_fields['Flex_{}'.format(bp.name)] = ChoiceField(
+ label=format_lazy(_("Flex Directions for {breakpoint}"), breakpoint=bp.label),
+ choices=choices,
+ required=False,
+ initial=''
+ )
+ return form_fields
+
+ @property
+ def justify_content(cls):
+ form_fields = {}
+ choices_format = [
+ ('justify-content-{}{}', _("Justify Content")),
+ ]
+ notation = [ 'start', 'end', 'center', 'between', 'around']
+ for bp in Breakpoint.range(Breakpoint.xs, Breakpoint.xl):
+ if bp == Breakpoint.xs:
+ choices = [(c.format('', n), c.format('', n)) for c, l in choices_format for n in notation]
+ choices.insert(0, ('', _("No Justify content")))
+ else:
+ choices = [(c.format(bp.name + '-', n),
+ c.format(bp.name + '-', n)) for c, l in choices_format for n in notation]
+ choices.insert(0, ('', _("Inherit from above")))
+ form_fields['Justify_content_{}'.format(bp.name)] = ChoiceField(
+ label=format_lazy(_("Justify Content for {breakpoint}"), breakpoint=bp.label),
+ choices=choices,
+ required=False,
+ initial=''
+ )
+ return form_fields
+
+ @property
+ def positions(cls):
+ form_fields = {}
+ choices_format = [
+ ('{}', _("Position")),
+ ]
+ notation = ['inherit', 'fixed-top' , 'fixed-bottom' , 'sticky-top']
+ choices = [ (str(n), str(n)) for c, l in choices_format for n in notation]
+ form_fields['Position'] = ChoiceField(
+ label=format_lazy(_("Position")),
+ choices=choices,
+ required=False,
+ initial=''
+ )
+ return form_fields
+
+ @property
+ def list_inline(cls):
+ form_fields = {}
+ choices_format = [
+ ('{}', _("List inline")),
+ ]
+ notation = ['inherit', 'fixed-top' , 'fixed-bottom' , 'sticky-top']
+ choices = [ (str(n), str(n)) for c, l in choices_format for n in notation]
+ form_fields['Position'] = ChoiceField(
+ label=format_lazy(_("Position")),
+ choices=choices,
+ required=False,
+ initial=''
+ )
+ return form_fields
diff --git a/cmsplugin_cascade/bootstrap4/navbar.py b/cmsplugin_cascade/bootstrap4/navbar.py
new file mode 100644
index 000000000..4f329fd4b
--- /dev/null
+++ b/cmsplugin_cascade/bootstrap4/navbar.py
@@ -0,0 +1,292 @@
+from django.utils.html import format_html
+from django.utils.translation import ugettext_lazy as _
+from django.conf import settings
+from django.forms import widgets
+from django.forms.fields import BooleanField, CharField, ChoiceField
+from cmsplugin_cascade.fields import SizeField
+from .plugin_base import BootstrapPluginBase
+from cmsplugin_cascade.bootstrap4.jumbotron import ImageBackgroundMixin
+from cmsplugin_cascade.bootstrap4.container import ContainerFormMixin, ContainerGridMixin
+from cmsplugin_cascade.image import ImageFormMixin, ImagePropertyMixin
+from cmsplugin_cascade.bootstrap4.container import get_widget_choices, ContainerBreakpointsWidget
+from cmsplugin_cascade.bootstrap4.image import get_image_tags
+from .grid import Breakpoint
+from cmsplugin_cascade.link.config import LinkPluginBase
+from cms.plugin_pool import plugin_pool
+from entangled.forms import EntangledModelFormMixin
+from django.core.exceptions import ValidationError
+
+import logging
+logger = logging.getLogger('cascade')
+
+
+class BootstrapNavbarFormMixin(EntangledModelFormMixin):
+ OPTION_NAV_COLLAPSE = [(c, c) for c in [ "inherit", "navbar-expand","navbar-expand-sm", "navbar-expand-md","navbar-expand-lg", "navbar-expand-xl"] ]
+ OPTION_NAV_COLOR = [(c, c) for c in [ "inherit", "navbar-light", "navbar-dark"]]
+ OPTION_NAV_BG_COLOR = [ "inherit", "bg-primary", "bg-secondary","bg-success", "bg-danger", "bg-warning", "bg-info" ,"bg-light", "bg-dark" , "bg-white", "bg-transparent"]
+ OPTION_NAV_BG_GRADIENT = [ "bg-gradient-primary", "bg-gradient-secondary", "bg-gradient-success", "bg-gradient-danger", "bg-gradient-warning", "bg-gradient-info", "bg-gradient-light", "bg-gradient-dark"]
+ OPTION_NAV_BG_MIX = OPTION_NAV_BG_COLOR + OPTION_NAV_BG_GRADIENT
+ OPTION_NAV_PLACEMENTS=["inherit", "fixed-top" , "fixed-bottom" , "sticky-top"]
+
+ navbar_collapse = ChoiceField(
+ label=_('Navbar collapse'),
+ choices=OPTION_NAV_COLLAPSE,
+ help_text=_("Adjust interval for the navbar_collapse.")
+ )
+
+ navbar_color = ChoiceField(
+ label=_('Navbar text color'),
+ choices=OPTION_NAV_COLOR,
+ help_text=_("Adjust the navbar color.")
+ )
+
+ navbar_bg_color = ChoiceField(
+ label=_('Navbar bg color'),
+ choices=[(c, c) for c in OPTION_NAV_BG_MIX ],
+ help_text=_("Adjust interval for the navbar background color."),
+ )
+
+ navbar_placement = ChoiceField(
+ label=_('navbar-place'),
+ choices=[(c, c) for c in OPTION_NAV_PLACEMENTS],
+ help_text=_("Adjust position ('fixed-top or fixed-button need to be set in Jumbotron if it is a plugin parent.')")
+ )
+
+ class Meta:
+ entangled_fields = {'glossary':['navbar_collapse', 'navbar_color', 'navbar_bg_color', 'navbar_placement']}
+
+ def validate_optional_field(self, name):
+ field = self.fields[name]
+ value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
+ if value in field.empty_values:
+ self.add_error(name, ValidationError(field.error_messages['required'], code='required'))
+ else:
+ return value
+
+ def clean(self):
+ cleaned_data = super().clean()
+ return cleaned_data
+
+
+@plugin_pool.register_plugin
+class BootstrapNavbarPlugin(BootstrapPluginBase):
+ name = _("Navbar")
+ parent_classes = None
+ require_parent = False
+ model_mixins = (ContainerGridMixin,)
+ default_css_class = 'navbar'
+ default_css_attributes = ('options')
+ alien_child_classes = True
+ render_template = 'cascade/bootstrap4/navbar.html'
+ ring_plugin = 'BootstrapNavbarPlugin'
+ fixed_top_and_toolbar = None
+
+ class Media:
+ js = ['cascade/js/admin/navbarplugin.js']
+
+ def get_form(self, request, obj=None, **kwargs):
+ kwargs['form'] = BootstrapNavbarFormMixin
+ if hasattr(obj, 'glossary'):
+ if obj.glossary.get('navbar_placement') == 'fixed-top':
+ obj.fixed_top_and_toolbar=True
+ return super().get_form(request, obj, **kwargs)
+
+ @classmethod
+ def sanitize_model(cls, obj):
+ sanitized = False
+ if 'Position' in obj.get_parent_glossary():
+ obj.glossary['position_jumbotron'] = obj.get_parent_glossary()['Position']
+ if hasattr(obj,'fixed_top_and_toolbar'):
+ navbar_placement = obj.glossary.get('navbar_placement')
+ del obj.fixed_top_and_toolbar
+ super().sanitize_model(obj)
+ return sanitized
+
+
+ @classmethod
+ def get_css_classes(cls, obj, ):
+ css_classes = cls.super(BootstrapNavbarPlugin, cls).get_css_classes(obj)
+ navbar_collapse = obj.glossary.get('navbar_collapse', '')
+ navbar_color = obj.glossary.get('navbar_color', '')
+ navbar_bg_color = obj.glossary.get('navbar_bg_color', '')
+ navbar_placement = obj.glossary.get('navbar_placement', '')
+ if navbar_collapse != 'inherit':
+ css_classes.append(navbar_collapse)
+ if navbar_color != 'inherit':
+ css_classes.append(navbar_color)
+ if navbar_bg_color != 'inherit':
+ css_classes.append(navbar_bg_color)
+ if navbar_placement != 'inherit':
+ css_classes.append(navbar_placement)
+ return css_classes
+
+ @classmethod
+ def get_identifier(cls, obj):
+ identifier = super().get_identifier(obj)
+ css_classes_without_default = obj.css_classes.replace( cls.default_css_class ,'',1)
+ return format_html('{0}{1}
',
+ identifier, css_classes_without_default)
+
+
+
+@plugin_pool.register_plugin
+class BootstrapNavBrandPlugin(LinkPluginBase):
+ name = _("Nav brand")
+ parent_classes = ['BootstrapNavbarPlugin']
+ render_template = 'cascade/bootstrap4/navbar_brand.html'
+ raw_id_fields = LinkPluginBase.raw_id_fields + ['image_file']
+ default_css_class = ''
+ require_parent = False
+ allow_children = True
+ alien_child_classes = True
+
+ @classmethod
+ def get_child_css_classes(cls, obj):
+ child_css_classes = cls.super(BootstrapNavBrandPlugin, cls).get_child_css_classes(obj)
+ child_css_classes = obj.glossary.get('child_css_class')
+ if child_css_classes:
+ child_css_classes.append(css_class)
+ return css_classes
+
+ @classmethod
+ def get_identifier(cls, obj):
+ identifier = super(BootstrapNavBrandPlugin, cls).get_identifier(obj)
+ css_classes_without_default = obj.css_classes.replace( cls.default_css_class,'',1)
+ return format_html('{0}{1}
',
+ identifier, css_classes_without_default)
+
+
+class BootstrapNavBrandThumbImageFormMixin(EntangledModelFormMixin):
+ image_width_fixed = SizeField(
+ label=_("Fixed Image Width"),
+ allowed_units=['px'],
+ required = False,
+ help_text=_("Set a fixed image width in pixels.")
+ )
+
+ class Meta:
+ entangled_fields = {'glossary': ['image_width_fixed']}
+
+class BootstrapNavBrandImageFormMixin(ImageFormMixin, BootstrapNavBrandThumbImageFormMixin):
+ pass
+
+
+@plugin_pool.register_plugin
+class BootstrapNavBrandImagePlugin(BootstrapPluginBase):
+ name = _("Nav brand Image")
+ model_mixins = (ImagePropertyMixin,)
+ parent_classes = ['BootstrapNavBrandPlugin', 'BootstrapListsPlugin']
+ allow_children = True
+ alien_child_classes = True
+ html_tag_attributes = {'image_title': 'title', 'alt_tag': 'tag'}
+ raw_id_fields = ['image_file']
+ SIZE_CHOICES = ('auto', 'width/height', 'cover', 'contain')
+ form = BootstrapNavBrandImageFormMixin
+ raw_id_fields = ['image_file']
+ render_template = 'cascade/bootstrap4/navbar_brand_image.html'
+ default_css_class = 'nav-brand-logo-ts'
+
+ def render(self, context, instance, placeholder, tags=None):
+ try:
+ tags = get_image_tags(instance)
+ except Exception as exc:
+ logger.warning("Unable generate image tags. Reason: {}".format(exc))
+ tags = tags if tags else {}
+ if 'extra_styles' in tags:
+ extra_styles = tags.pop('extra_styles')
+ inline_styles = instance.glossary.get('inline_styles', {})
+ inline_styles.update(extra_styles)
+ instance.glossary['inline_styles'] = inline_styles
+ context.update(dict(instance=instance, placeholder=placeholder, **tags))
+ return context
+
+ @classmethod
+ def get_css_classes(cls, obj):
+ css_classes = cls.super(BootstrapNavBrandImagePlugin, cls).get_css_classes(obj)
+ css_class = obj.glossary.get('css_class')
+ if css_class:
+ css_classes.append(css_class)
+ return css_classes
+
+ @classmethod
+ def get_child_css_classes(cls, obj):
+ child_css_classes = cls.super(BootstrapNavBrandImagePlugin, cls).get_child_css_classes(obj)
+ child_css_classes = obj.glossary.get('child_css_class')
+ if child_css_classes:
+ child_css_classes.append(css_class)
+ return css_classes
+
+
+@plugin_pool.register_plugin
+class BootstrapNavCollapsePlugin(BootstrapPluginBase):
+ name = _("Nav Collapse")
+ parent_classes = ['BootstrapNavbarPlugin']
+ if not settings.CMSPLUGIN_CASCADE['bootstrap4'].get('template_basedir'):
+ render_template = 'cascade/bootstrap4/navbar_collapse.html'
+ else:
+ render_template = 'cascade/bootstrap4/ng_navbar_collapse.html'
+ default_css_class = 'collapse navbar-collapse'
+
+ @classmethod
+ def get_css_classes(cls, obj):
+ css_classes = cls.super( BootstrapNavCollapsePlugin, cls).get_css_classes(obj)
+ return css_classes
+
+ @classmethod
+ def get_identifier(cls, obj):
+ identifier = super(BootstrapNavCollapsePlugin, cls).get_identifier(obj)
+ css_classes_without_default = obj.css_classes.replace( cls.default_css_class,'',1)
+ return format_html('{0}{1}
',
+ identifier, css_classes_without_default)
+
+ @classmethod
+ def sanitize_model(cls, obj):
+ sanitized = super().sanitize_model(obj)
+ return sanitized
+
+@plugin_pool.register_plugin
+class BootstrapNavItemsMainMenuPlugin(BootstrapPluginBase):
+ name = _("NavItems MainMenu ")
+ parent_classes = ['BootstrapListsPlugin']
+ require_parent = False
+ allow_children = False
+ alien_child_classes = True
+ render_template = 'cascade/bootstrap4/navbar_nav_items_li_menu_main_links.html'
+
+ @classmethod
+ def get_css_classes(cls, obj):
+ css_classes = cls.super(BootstrapNavItemsMainMenuPlugin, cls).get_css_classes(obj)
+ return css_classes
+
+ @classmethod
+ def get_identifier(cls, obj):
+ identifier = super(BootstrapNavItemsMainMenuPlugin, cls).get_identifier(obj)
+ if hasattr(cls,'default_css_class'):
+ css_classes_without_default = obj.css_classes.replace( cls.default_css_class,'',1)
+ else:
+ css_classes_without_default = obj.css_classes
+ return format_html('{0}{1}
',
+ identifier, css_classes_without_default)
+
+
+@plugin_pool.register_plugin
+class BootstrapNavbarToogler(BootstrapPluginBase):
+ name = _("Nav toogler")
+ default_css_class = 'navbar-toggler'
+ parent_classes = ['BootstrapNavbarPlugin',]
+ if not settings.CMSPLUGIN_CASCADE['bootstrap4'].get('template_basedir'):
+ render_template = 'cascade/bootstrap4/navbar_toogler.html'
+ else:
+ render_template = 'cascade/bootstrap4/ng_navbar_toogler.html'
+
+
+
+@plugin_pool.register_plugin
+class BootstrapNavbarLanguageChooser(BootstrapPluginBase):
+ name = _("Nav Lang Chooser")
+ default_css_class = 'nav-item dropdown'
+ parent_classes = ['BootstrapListsPlugin']
+ if not settings.CMSPLUGIN_CASCADE['bootstrap4'].get('template_basedir'):
+ render_template = 'bootstrap4/includes/language-chooser.html'
+ else:
+ render_template = 'bootstrap4/includes/ng-language-chooser.html'
diff --git a/cmsplugin_cascade/bootstrap4/picture.py b/cmsplugin_cascade/bootstrap4/picture.py
index 533df3e8a..3a0045c60 100644
--- a/cmsplugin_cascade/bootstrap4/picture.py
+++ b/cmsplugin_cascade/bootstrap4/picture.py
@@ -127,4 +127,4 @@ def sanitize_model(cls, obj):
return
return sanitized
-plugin_pool.register_plugin(BootstrapPicturePlugin)
+plugin_pool.register_plugin(BootstrapPicturePlugin)
\ No newline at end of file
diff --git a/cmsplugin_cascade/bootstrap4/settings.py b/cmsplugin_cascade/bootstrap4/settings.py
index e52f64331..15f9f6354 100644
--- a/cmsplugin_cascade/bootstrap4/settings.py
+++ b/cmsplugin_cascade/bootstrap4/settings.py
@@ -7,7 +7,7 @@
CASCADE_PLUGINS = ['accordion', 'buttons', 'card', 'carousel', 'container', 'embeds', 'icon', 'image', 'jumbotron',
- 'picture', 'tabs']
+ 'picture', 'tabs', 'navbar', 'lists']
if 'cms_bootstrap' in settings.INSTALLED_APPS:
CASCADE_PLUGINS.append('secondary_menu')
@@ -53,7 +53,9 @@ def set_defaults(config):
config['plugins_with_extra_mixins'].setdefault('HorizontalRulePlugin', BootstrapUtilities(
BootstrapUtilities.margins,
))
-
+ config['plugins_with_extra_mixins'].setdefault('BootstrapJumbotronPlugin', BootstrapUtilities(
+ BootstrapUtilities.positions,
+ ))
config['plugins_with_extra_fields'].setdefault('BootstrapTabSetPlugin', PluginExtraFieldsConfig(
css_classes={
'multiple': True,
@@ -65,3 +67,25 @@ def set_defaults(config):
('cascade/bootstrap4/secmenu-list-group.html', _("List Group")),
('cascade/bootstrap4/secmenu-unstyled-list.html', _("Unstyled List"))
])
+ config['plugins_with_extra_fields'].setdefault('BootstrapNavbarPlugin', PluginExtraFieldsConfig(
+ inline_styles={
+ 'extra_fields:Colors':['color','background-color'],
+ }
+ ))
+ config['plugins_with_extra_mixins'].setdefault('BootstrapNavBrandPlugin', BootstrapUtilities(
+ BootstrapUtilities.background_and_color
+ ))
+ config['plugins_with_extra_mixins'].setdefault('BootstrapListsPlugin', BootstrapUtilities(
+ BootstrapUtilities.flex_directions, BootstrapUtilities.margins, BootstrapUtilities.paddings, BootstrapUtilities.display_propertys, BootstrapUtilities.background_and_color
+ ))
+ config['plugins_with_extra_fields'].setdefault('BootstrapListsPlugin', PluginExtraFieldsConfig(
+ inline_styles={
+ 'extra_fields:Colors':['color','background-color'],
+ },
+ ))
+ config['plugins_with_extra_mixins'].setdefault('BootstrapNavbarToogler', BootstrapUtilities(
+ BootstrapUtilities.background_and_color
+ ))
+ config['plugins_with_extra_mixins'].setdefault('BootstrapNavCollapsePlugin', BootstrapUtilities(
+ BootstrapUtilities.justify_content
+ ))
diff --git a/cmsplugin_cascade/icon/simpleicon.py b/cmsplugin_cascade/icon/simpleicon.py
index e11c63446..cc800ad76 100644
--- a/cmsplugin_cascade/icon/simpleicon.py
+++ b/cmsplugin_cascade/icon/simpleicon.py
@@ -20,5 +20,13 @@ class SimpleIconPlugin(IconPluginMixin, LinkPluginBase):
class Media:
js = ['admin/js/jquery.init.js', 'cascade/js/admin/iconplugin.js']
+ @classmethod
+ def get_css_classes(cls, obj):
+ css_classes = cls.super(SimpleIconPlugin, cls).get_css_classes(obj)
+ if hasattr(obj,'parent') and hasattr(obj.parent,'parent') and hasattr(obj.parent.parent,'plugin_type'):
+ if obj.parent.parent.plugin_type == 'BootstrapNavCollapsePlugin':
+ css_classes.insert(0,'nav-link navbar-text')
+ return css_classes
+
plugin_pool.register_plugin(SimpleIconPlugin)
diff --git a/cmsplugin_cascade/link/cms_plugins.py b/cmsplugin_cascade/link/cms_plugins.py
index 739783dfc..1e4fbcc3a 100644
--- a/cmsplugin_cascade/link/cms_plugins.py
+++ b/cmsplugin_cascade/link/cms_plugins.py
@@ -18,6 +18,18 @@ class TextLinkPlugin(LinkPluginBase):
class Media:
js = ['admin/js/jquery.init.js', 'cascade/js/admin/textlinkplugin.js']
+
+ @classmethod
+ def get_css_classes(cls, obj):
+ css_classes = cls.super(TextLinkPlugin, cls).get_css_classes(obj)
+ if hasattr(obj.parent.parent, 'plugin_type' ) and obj.parent.parent.plugin_type == 'BootstrapListsPlugin':
+ css_classes.insert(0,'nav-link navbar-text')
+ # strides pass
+ if hasattr(obj.parent.parent, 'plugin'):
+ css_classes.insert(0,'nav-link navbar-text')
+ return css_classes
+
+
@classmethod
def get_identifier(cls, obj):
return mark_safe(obj.glossary.get('link_content', ''))
diff --git a/cmsplugin_cascade/static/cascade/js/admin/placeholder_position.js b/cmsplugin_cascade/static/cascade/js/admin/placeholder_position.js
new file mode 100644
index 000000000..51a47c30e
--- /dev/null
+++ b/cmsplugin_cascade/static/cascade/js/admin/placeholder_position.js
@@ -0,0 +1,17 @@
+function set_placeholder_to_first_child_position(title, cms_structure_content){
+ //Quick way to move placeholder selected by string name as first child position in cms structure mode
+ var p_header_title = `.cms-dragbar-title[title="${title}"]`
+ var p_header_cms_dragbar = document.querySelectorAll(p_header_title)[0];
+ var p_header_cms_dragarea = p_header_cms_dragbar.parentNode.parentNode;
+ var isfirstchild = (p_header_cms_dragarea == p_header_cms_dragarea.parentNode.firstChild);
+ if (cms_structure_content.classList.contains('empty') || p_header_cms_dragbar && !isfirstchild ){
+ cms_structure_content.prepend(p_header_cms_dragarea);
+ }
+};
+
+function placeholder_position_top(title_placeholder){
+ var cms_structure_content = document.querySelectorAll('.cms-structure-content')[0];
+ var observer = new MutationObserver(function(e) { set_placeholder_to_first_child_position(title_placeholder,cms_structure_content);});
+ observer.observe(cms_structure_content, { childList: true});
+};
+
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html b/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html
index fdffc3a0d..81ae3c745 100644
--- a/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html
@@ -28,6 +28,17 @@
}
}
{% endfor %}
+
+{% else %}
+ #cascadeelement_id-{{ instance.pk }} {
+ {# fallback instance #}
+ }
+{% endif %}
+{% if request.toolbar and request.toolbar.edit_mode_active %}
+{% if 'fixed-top' in instance.glossary.values and instance.get_first_child.plugin_type == 'BootstrapNavbarPlugin' %}
+ body { padding-top:2.8rem }
+ .jumbotron-fluid { margin-top: 2.8rem !important ;}
+{% endif %}
{% endif %}
{% endlocalize %}{% endaddtoblock %}
@@ -37,5 +48,5 @@
{% for plugin in instance.child_plugin_instances %}
{% render_plugin plugin %}
{% endfor %}
-
+
{% endspaceless %}{% endlocalize %}
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar.html
new file mode 100644
index 000000000..fb75c1a3c
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar.html
@@ -0,0 +1,25 @@
+{% load l10n cascade_tags thumbnail sekizai_tags %}
+{% block extra-styles %}{% endblock %}
+{% block extra-scripts %}{% endblock %}
+
+{# The Navbar plug-in compensates for the height of the CMS toolbar #}
+{% if request.toolbar and request.toolbar.edit_mode_active %}
+ {% if instance.plugin_type == 'BootstrapNavbarPlugin' and 'fixed-top' in instance.css_classes %}
+
+{% endif %}
+{% endif %}
+
+{% with instance_css_classes=instance.css_classes instance_inline_styles=instance.inline_styles %}
+
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_brand.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_brand.html
new file mode 100644
index 000000000..43aa0aa49
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_brand.html
@@ -0,0 +1,13 @@
+{% extends "cascade/link/link-base.html" %}
+{% load l10n cascade_tags sekizai_tags %}
+{% block link_link %}{% with instance_link=instance|default:"#" %}
+ {% block navbar-brand %}
+ {% with instance_css_classes=instance.css_classes instance_inline_styles=instance.inline_styles %}
+ {% for child in instance.child_plugin_instances %}
+ {{ block.super }}
+ {% render_plugin child %}
+ {% endfor %}
+{% endwith %}
+ {% endblock %}
+{% endwith %}
+{% endblock %}
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_brand_image.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_brand_image.html
new file mode 100644
index 000000000..3a818c2ed
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_brand_image.html
@@ -0,0 +1,14 @@
+{% load l10n thumbnail %}
+{% localize off %}{% spaceless %}
+{% with css_classes=instance.css_classes inline_styles=instance.inline_styles %}
+{% if instance.image %}
{% if not instance.image %}{% endif %}
+ {% endwith %}
+{% endspaceless %}{% endlocalize %}
+
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_collapse.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_collapse.html
new file mode 100644
index 000000000..e24b26e51
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_collapse.html
@@ -0,0 +1,7 @@
+{% load l10n cascade_tags sekizai_tags %}
+{% with instance_css_classes=instance.css_classes instance_inline_styles=instance.inline_styles %}
+
+{% block navbar-collapse %}{% for child in instance.child_plugin_instances %}{% render_plugin child %}{% endfor %}
+{% endblock %}
+
+{% endwith %}
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_links.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_links.html
new file mode 100644
index 000000000..dd5416f12
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_links.html
@@ -0,0 +1,4 @@
+{% load bootstrap_tags cms_tags %}
+
+{% block navbar-nav %}
{% main_menu "bootstrap4/menu/navbar.html" %}
{% endblock %}
+
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_list.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_list.html
new file mode 100644
index 000000000..e04506b2c
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_list.html
@@ -0,0 +1,18 @@
+{% load l10n cascade_tags sekizai_tags i18n menu_tags %}
+{% with instance_css_classes=instance.css_classes instance_inline_styles=instance.inline_styles child_css_classes=instance.glossary.child_css_classes %}
+{% block navbar-nav-list %}
+{% for child in instance.child_plugin_instances %}
+-
+{% render_plugin child %}
+
+{% endfor %}
+
+{% endblock %}
+{% endwith %}
+
+
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_list_separator.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_list_separator.html
new file mode 100644
index 000000000..5fba5df37
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_list_separator.html
@@ -0,0 +1 @@
+
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_nav_items_li_menu_main_links.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_nav_items_li_menu_main_links.html
new file mode 100644
index 000000000..d624b2899
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_nav_items_li_menu_main_links.html
@@ -0,0 +1,9 @@
+{% load bootstrap_tags l10n cascade_tags sekizai_tags i18n menu_tags %}
+{% block navbar-nav %}
+
+{% if DJANGO_CLIENT_FRAMEWORK == 'angular-ui/' %}
+{% main_menu "bootstrap4/menu/navbar.html" %}
+{% else %}
+{% main_menu "bootstrap4/menu/ng-navbar.html" %}
+{% endif %}
+{% endblock %}
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_toogler.html b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_toogler.html
new file mode 100644
index 000000000..00cf42f2d
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/navbar_toogler.html
@@ -0,0 +1,6 @@
+{% load bootstrap_tags cascade_tags i18n %}
+{% with instance_css_classes=instance.css_classes instance_inline_styles=instance.inline_styles child_css_classes=instance.glossary.child_css_classes %}
+
+{% endwith %}
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_collapse.html b/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_collapse.html
new file mode 100644
index 000000000..dfbeaa3ca
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_collapse.html
@@ -0,0 +1,7 @@
+{% load l10n cascade_tags sekizai_tags i18n %}
+{% with instance_css_classes=instance.css_classes instance_inline_styles=instance.inline_styles %}
+
+{% block navbar-collapse %}{% for child in instance.child_plugin_instances %}{% render_plugin child %}{% endfor %}
+{% endblock %}
+
+{% endwith %}
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_list.html b/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_list.html
new file mode 100644
index 000000000..18b3f63b2
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_list.html
@@ -0,0 +1,18 @@
+{% load l10n cascade_tags sekizai_tags %}
+{% with instance_css_classes=instance.css_classes instance_inline_styles=instance.inline_styles child_css_classes=instance.glossary.child_css_classes %}
+{% block navbar-nav-list %}
+{% for child in instance.child_plugin_instances %}
+-
+{% render_plugin child %}
+
+{% endfor %}
+
+{% endblock %}
+{% endwith %}
+
+
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_toogler.html b/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_toogler.html
new file mode 100644
index 000000000..331348f03
--- /dev/null
+++ b/cmsplugin_cascade/templates/cascade/bootstrap4/ng_navbar_toogler.html
@@ -0,0 +1,6 @@
+{% load bootstrap_tags cascade_tags i18n %}
+{% with instance_css_classes=instance.css_classes instance_inline_styles=instance.inline_styles child_css_classes=instance.glossary.child_css_classes %}
+
+{% endwith %}
diff --git a/cmsplugin_cascade/templates/cascade/plugins/simpleicon.html b/cmsplugin_cascade/templates/cascade/plugins/simpleicon.html
index 5c1f88396..b61615b78 100644
--- a/cmsplugin_cascade/templates/cascade/plugins/simpleicon.html
+++ b/cmsplugin_cascade/templates/cascade/plugins/simpleicon.html
@@ -1,4 +1,4 @@
-{% load sekizai_tags %}
+{% load sekizai_tags cascade_tags %}
{% if stylesheet_url %}{% addtoblock "css" %}{% endaddtoblock %}{% endif %}
{% spaceless %}
{% with instance_link=instance.link tag_type=instance.tag_type css_classes=instance.css_classes inline_styles=instance.inline_styles %}
@@ -7,7 +7,7 @@
{% elif css_classes or inline_styles %}
{% endif %}
-
+ {{ instance.glossary.content }}
{% if instance_link %}{% elif css_classes or inline_styles %}{% endif %}
{% endwith %}
-{% endspaceless %}
\ No newline at end of file
+{% endspaceless %}
diff --git a/examples/bs4demo/settings.py b/examples/bs4demo/settings.py
index 30532ead0..ff2c8c8bf 100644
--- a/examples/bs4demo/settings.py
+++ b/examples/bs4demo/settings.py
@@ -213,7 +213,7 @@
)
CMSPLUGIN_CASCADE = {
- 'alien_plugins': ('TextPlugin', 'TextLinkPlugin',),
+ 'alien_plugins': ('TextPlugin', 'TextLinkPlugin','BootstrapListPlugin', 'BootstrapNavMainMemuPlugin'),
'plugins_with_sharables': {
'BootstrapImagePlugin': ('image_shapes', 'image_width_responsive', 'image_width_fixed',
'image_height', 'resize_options',),
@@ -225,17 +225,26 @@
'cache_strides': True,
}
+CASCADE_CLIPS_LIBRARY = True
+
CMS_PLACEHOLDER_CONF = {
+
+ 'Header Content': {
+ 'plugins': ['BootstrapContainerPlugin', 'BootstrapJumbotronPlugin','BootstrapNavbarPlugin', 'BootstrapListPlugin', 'BootstrapNavItemsMainMemuPlugin'],
+ 'parent_classes': {'BootstrapContainerPlugin': None, 'BootstrapJumbotronPlugin': None, 'BootstrapNavItemsMainMemuPlugin':None},
+
+ },
+
# this placeholder is used in templates/main.html, it shows how to
# scaffold a djangoCMS page starting with an empty placeholder
'Main Content': {
- 'plugins': ['BootstrapContainerPlugin', 'BootstrapJumbotronPlugin'],
- 'parent_classes': {'BootstrapContainerPlugin': None, 'BootstrapJumbotronPlugin': None},
+ 'plugins': ['BootstrapContainerPlugin', 'BootstrapJumbotronPlugin' , 'BootstrapListPlugin'],
+ 'parent_classes': {'BootstrapContainerPlugin': None, 'BootstrapJumbotronPlugin': None , },
},
# this placeholder is used in templates/wrapped.html, it shows how to
# add content to an existing Bootstrap column
'Bootstrap Column': {
- 'plugins': ['BootstrapRowPlugin', 'TextPlugin', ],
+ 'plugins': ['BootstrapRowPlugin', 'TextPlugin','BootstrapListPlugin' ],
'parent_classes': {'BootstrapRowPlugin': None},
'require_parent': False,
},
diff --git a/examples/bs4demo/templates/bs4demo/base.html b/examples/bs4demo/templates/bs4demo/base.html
index 002ad408b..8b22dc44b 100644
--- a/examples/bs4demo/templates/bs4demo/base.html
+++ b/examples/bs4demo/templates/bs4demo/base.html
@@ -11,7 +11,7 @@
{% render_block "css" %}
-
+
{% cms_toolbar %}