Ejemplo n.º 1
0
class FramedIconPlugin(IconPluginMixin, CascadePluginBase):
    name = _("Icon")
    parent_classes = None
    require_parent = False
    allow_children = False
    render_template = 'cascade/plugins/icon.html'
    model_mixins = (IconModelMixin, )
    ring_plugin = 'FramedIconPlugin'
    SIZE_CHOICES = [('{}em'.format(c), "{} em".format(c))
                    for c in range(1, 13)]
    RADIUS_CHOICES = [(None, _("Square"))] + \
        [('{}px'.format(r), "{} px".format(r)) for r in (1, 2, 3, 5, 7, 10, 15, 20)] + \
        [('50%', _("Circle"))]

    icon_font = GlossaryField(
        widgets.Select(),
        label=_("Font"),
    )

    symbol = GlossaryField(
        widgets.HiddenInput(),
        label=_("Select Symbol"),
    )

    font_size = GlossaryField(
        CascadingSizeWidget(allowed_units=['px', 'em']),
        label=_("Icon size"),
        initial='1em',
    )

    color = GlossaryField(
        widgets.TextInput(attrs={
            'style': 'width: 5em;',
            'type': 'color'
        }),
        label=_("Icon color"),
    )

    background_color = GlossaryField(
        ColorPickerWidget(),
        label=_("Background color"),
    )

    text_align = GlossaryField(
        widgets.RadioSelect(choices=(('', _("Do not align")),
                                     ('text-left', _("Left")), ('text-center',
                                                                _("Center")),
                                     ('text-right', _("Right")))),
        label=_("Text alignment"),
        initial='',
        help_text=_("Align the icon inside the parent column."))

    border = GlossaryField(
        SetBorderWidget(),
        label=_("Set border"),
    )

    border_radius = GlossaryField(
        widgets.Select(choices=RADIUS_CHOICES),
        label=_("Border radius"),
    )

    glossary_field_order = ('icon_font', 'symbol', 'text_align', 'font_size',
                            'color', 'background_color', 'border',
                            'border_radius')

    class Media:
        js = ['cascade/js/admin/framediconplugin.js']

    @classmethod
    def get_tag_type(self, instance):
        if instance.glossary.get('text_align'):
            return 'div'

    @classmethod
    def get_css_classes(cls, instance):
        css_classes = super(FramedIconPlugin, cls).get_css_classes(instance)
        text_align = instance.glossary.get('text_align')
        if text_align:
            css_classes.append(text_align)
        return css_classes

    @classmethod
    def get_inline_styles(cls, instance):
        inline_styles = super(FramedIconPlugin,
                              cls).get_inline_styles(instance)
        inline_styles['font-size'] = instance.glossary.get('font_size', '1em')
        return inline_styles
Ejemplo n.º 2
0
class BootstrapJumbotronPlugin(BootstrapPluginBase):
    name = _("Jumbotron")
    model_mixins = (ImagePropertyMixin, ImageBackgroundMixin)
    form = JumbotronPluginForm
    default_css_class = 'jumbotron'
    require_parent = False
    parent_classes = ('BootstrapColumnPlugin',)
    allow_children = True
    alien_child_classes = True
    raw_id_fields = ['image_file']
    fields = ('glossary', 'image_file',)
    render_template = 'cascade/bootstrap3/jumbotron.html'
    ring_plugin = 'JumbotronPlugin'
    ATTACHMENT_CHOICES = ('scroll', 'fixed', 'local')
    VERTICAL_POSITION_CHOICES = ('top', '10%', '20%', '30%', '40%', 'center', '60%', '70%', '80%', '90%', 'bottom')
    HORIZONTAL_POSITION_CHOICES = ('left', '10%', '20%', '30%', '40%', 'center', '60%', '70%', '80%', '90%', 'right')
    REPEAT_CHOICES = ('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
    SIZE_CHOICES = ('auto', 'width/height', 'cover', 'contain')
    container_glossary_fields = (
        GlossaryField(
            ContainerBreakpointsWidget(choices=get_widget_choices()),
            label=_("Available Breakpoints"),
            name='breakpoints',
            initial=list(BS3_BREAKPOINT_KEYS)[::-1],
            help_text=_("Supported display widths for Bootstrap's grid system.")
        ),
        GlossaryField(
            MultipleCascadingSizeWidget(BS3_BREAKPOINT_KEYS,
                                        allowed_units=['px', '%'], required=False),
            label=_("Adapt Picture Heights"),
            name='container_max_heights',
            initial={'xs': '100%', 'sm': '100%', 'md': '100%', 'lg': '100%'},
            help_text=_("Heights of picture in percent or pixels for distinct Bootstrap's breakpoints.")
        ),
        GlossaryField(
            widgets.CheckboxSelectMultiple(choices=BootstrapPicturePlugin.RESIZE_OPTIONS),
            label=_("Resize Options"),
            name='resize_options',
            initial=['crop', 'subject_location', 'high_resolution'],
            help_text=_("Options to use when resizing the image.")
        ),
    )

    background_color = GlossaryField(
        ColorPickerWidget(),
        label=_("Background color"),
    )

    background_repeat = GlossaryField(
        widgets.RadioSelect(choices=[(c, c) for c in REPEAT_CHOICES]),
        initial='no-repeat',
        label=_("This property specifies how an image repeates."),
    )

    background_attachment = GlossaryField(
        widgets.RadioSelect(choices=[(c, c) for c in ATTACHMENT_CHOICES]),
        initial='local',
        label=_("This property specifies how to move the background relative to the viewport."),
    )

    background_vertical_position = GlossaryField(
        widgets.Select(choices=[(c, c) for c in VERTICAL_POSITION_CHOICES]),
        initial='center',
        label=_("This property moves a background image vertically within its container."),
    )

    background_horizontal_position = GlossaryField(
        widgets.Select(choices=[(c, c) for c in HORIZONTAL_POSITION_CHOICES]),
        initial='center',
        label=_("This property moves a background image horizontally within its container."),
    )

    background_size = GlossaryField(
        widgets.RadioSelect(choices=[(c, c) for c in SIZE_CHOICES]),
        initial='auto',
        label=_("Background size"),
        help_text=_("This property specifies how an image is sized."),
    )

    background_width_height = GlossaryField(
        MultipleCascadingSizeWidget(['width', 'height'], allowed_units=['px', '%'],
                                    required=False),
        label=_("Background width and height"),
        help_text=_("This property specifies the width and height of a background image."),
    )
    footnote_html = """
<p>For more information about the Jumbotron please read </p>
    """

    class Media:
        js = ['cascade/js/admin/jumbotronplugin.js']

    def get_form(self, request, obj=None, **kwargs):
        if self.get_parent_instance(request, obj) is None:
            # we only ask for breakpoints, if the jumbotron is the root of the placeholder
            kwargs.update(glossary_fields=list(self.container_glossary_fields))
            kwargs['glossary_fields'].extend(self.glossary_fields)
        form = super(BootstrapJumbotronPlugin, self).get_form(request, obj, **kwargs)
        return form

    def render(self, context, instance, placeholder):
        # image shall be rendered in a responsive context using the ``<picture>`` element
        elements = get_picture_elements(context, instance)
        context.update({
            'elements': [e for e in elements if 'media' in e] if elements else [],
            'CSS_PREFIXES': app_settings.CSS_PREFIXES,
        })
        return self.super(BootstrapJumbotronPlugin, self).render(context, instance, placeholder)

    @classmethod
    def sanitize_model(cls, obj):
        # if the jumbotron is the root of the placeholder, we consider it as "fluid"
        obj.glossary['fluid'] = obj.parent is None
        sanitized = super(BootstrapJumbotronPlugin, cls).sanitize_model(obj)
        compute_media_queries(obj)
        return sanitized

    @classmethod
    def get_identifier(cls, obj):
        identifier = super(BootstrapJumbotronPlugin, cls).get_identifier(obj)
        try:
            content = obj.image.name or obj.image.original_filename
        except AttributeError:
            content = _("Without background image")
        return format_html('{0}{1}', identifier, content)
Ejemplo n.º 3
0
class FramedIconPlugin(IconPluginMixin, LinkPluginBase):
    name = _("Icon with frame")
    parent_classes = None
    require_parent = False
    allow_children = False
    render_template = 'cascade/plugins/framedicon.html'
    model_mixins = (LinkElementMixin,)
    ring_plugin = 'FramedIconPlugin'
    fields = list(LinkPluginBase.fields)
    SIZE_CHOICES = [('{}em'.format(c), "{} em".format(c)) for c in range(1, 13)]
    RADIUS_CHOICES = [(None, _("Square"))] + \
        [('{}px'.format(r), "{} px".format(r)) for r in (1, 2, 3, 5, 7, 10, 15, 20)] + \
        [('50%', _("Circle"))]

    icon_font = GlossaryField(
        widgets.Select(),
        label=_("Font"),
        initial=get_default_icon_font,
    )

    symbol = GlossaryField(
        widgets.HiddenInput(),
        label=_("Select Symbol"),
    )

    font_size = GlossaryField(
        CascadingSizeWidget(allowed_units=['px', 'em']),
        label=_("Icon size"),
        initial='1em',
    )

    color = GlossaryField(
        ColorPickerWidget(),
        label=_("Icon color"),
    )

    background_color = GlossaryField(
        ColorPickerWidget(),
        label=_("Background color"),
    )

    text_align = GlossaryField(
        widgets.RadioSelect(choices=[
            ('', _("Do not align")),
            ('text-left', _("Left")),
            ('text-center', _("Center")),
            ('text-right', _("Right"))
        ]),
        label=_("Text alignment"),
        initial='',
        help_text=_("Align the icon inside the parent column.")
    )

    border = GlossaryField(
        SetBorderWidget(),
        label=_("Set border"),
    )

    border_radius = GlossaryField(
        widgets.Select(choices=RADIUS_CHOICES),
        label=_("Border radius"),
    )

    glossary_field_order = ['icon_font', 'symbol', 'text_align', 'font_size', 'color', 'background_color',
                            'border', 'border_radius']

    class Media:
        js = ['cascade/js/admin/framediconplugin.js']

    @classmethod
    def get_tag_type(self, instance):
        if instance.glossary.get('text_align') or instance.glossary.get('font_size'):
            return 'div'

    @classmethod
    def get_css_classes(cls, instance):
        css_classes = cls.super(FramedIconPlugin, cls).get_css_classes(instance)
        text_align = instance.glossary.get('text_align')
        if text_align:
            css_classes.append(text_align)
        return css_classes

    @classmethod
    def get_inline_styles(cls, instance):
        inline_styles = cls.super(FramedIconPlugin, cls).get_inline_styles(instance)
        inline_styles['font-size'] = instance.glossary.get('font_size', '1em')
        return inline_styles

    def get_form(self, request, obj=None, **kwargs):
        kwargs.update(form=VoluntaryLinkForm.get_form_class())
        return super(FramedIconPlugin, self).get_form(request, obj, **kwargs)

    def render(self, context, instance, placeholder):
        context = self.super(FramedIconPlugin, self).render(context, instance, placeholder)
        icon_font = self.get_icon_font(instance)
        symbol = instance.glossary.get('symbol')
        attrs = []
        if icon_font and symbol:
            attrs.append(mark_safe('class="{}{}"'.format(icon_font.config_data.get('css_prefix_text', 'icon-'), symbol)))
        styles = {'display': 'inline-block'}
        disabled, color = instance.glossary.get('color', (True, '#000000'))
        if not disabled:
            styles['color'] = color
        disabled, background_color = instance.glossary.get('background_color', (True, '#000000'))
        if not disabled:
            styles['background-color'] = background_color
        border = instance.glossary.get('border')
        if isinstance(border, list) and border[0] and border[1] != 'none':
            styles.update(border='{0} {1} {2}'.format(*border))
            radius = instance.glossary.get('border_radius')
            if radius:
                styles['border-radius'] = radius
        attrs.append(format_html('style="{}"',
                                 format_html_join('', '{0}:{1};',
                                                  [(k, v) for k, v in styles.items()])))
        context['icon_font_attrs'] = mark_safe(' '.join(attrs))
        return context
Ejemplo n.º 4
0
class TextIconPlugin(IconPluginMixin, LinkPluginBase):
    name = _("Icon in text")
    text_enabled = True
    render_template = 'cascade/plugins/texticon.html'
    ring_plugin = 'IconPlugin'
    parent_classes = ('TextPlugin',)
    model_mixins = (TextIconModelMixin, LinkElementMixin,)
    allow_children = False
    require_parent = False

    symbol = GlossaryField(
        widgets.HiddenInput(),
        label=_("Select Symbol"),
    )

    color = GlossaryField(
        ColorPickerWidget(),
        label=_("Icon color"),
    )

    glossary_field_order = ['symbol', 'color']

    class Media:
        js = ['cascade/js/admin/iconplugin.js']

    @classmethod
    def requires_parent_plugin(cls, slot, page):
        return False

    def get_form(self, request, obj=None, **kwargs):
        LINK_TYPE_CHOICES = [('none', _("No Link"))]
        LINK_TYPE_CHOICES.extend(getattr(LinkForm, 'LINK_TYPE_CHOICES'))
        Form = type(str('TextIconForm'), (getattr(LinkForm, 'get_form_class')(),),
                    {'LINK_TYPE_CHOICES': LINK_TYPE_CHOICES})
        kwargs.update(form=Form)
        return super(TextIconPlugin, self).get_form(request, obj, **kwargs)

    def get_plugin_urls(self):
        urls = [
            url(r'^wysiwig-config\.js$', self.render_wysiwig_config,
                name='cascade_texticon_wysiwig_config'),
        ]
        return urls

    def render_wysiwig_config(self, request):
        """Find the icon font associated to the CMS page, from which this subrequest is originating."""
        context = {}
        # Since this request is originating from CKEditor, we have no other choice rather than using
        # the referer, to determine the current CMS page.
        referer = urlparse(request.META['HTTP_REFERER'])
        matches = re.match(r'.+/edit-plugin/(\d+)/$', referer.path)
        if matches:
            cms_plugin = CMSPlugin.objects.get(id=matches.group(1))
            try:
                context['icon_font'] = cms_plugin.page.cascadepage.icon_font
            except CascadePage.DoesNotExist:
                pass
        javascript = render_to_string('cascade/admin/ckeditor.wysiwyg.txt', context)
        return HttpResponse(javascript, content_type='application/javascript')

    @classmethod
    def get_inline_styles(cls, instance):
        inline_styles = cls.super(TextIconPlugin, cls).get_inline_styles(instance)
        color = instance.glossary.get('color')
        if isinstance(color, list) and len(color) == 2 and not color[0]:
            inline_styles['color'] = color[1]
        return inline_styles
Ejemplo n.º 5
0
class BootstrapJumbotronPlugin(BootstrapPluginBase):
    name = _("Jumbotron")
    model_mixins = (ContainerGridMixin, ImagePropertyMixin, ImageBackgroundMixin)
    form = JumbotronPluginForm
    require_parent = False
    parent_classes = ('BootstrapColumnPlugin',)
    allow_children = True
    alien_child_classes = True
    raw_id_fields = ['image_file']
    fields = ['glossary', 'image_file']
    render_template = 'cascade/bootstrap4/jumbotron.html'
    ring_plugin = 'JumbotronPlugin'
    ATTACHMENT_CHOICES = ['scroll', 'fixed', 'local']
    VERTICAL_POSITION_CHOICES = ['top', '10%', '20%', '30%', '40%', 'center', '60%', '70%', '80%', '90%', 'bottom']
    HORIZONTAL_POSITION_CHOICES = ['left', '10%', '20%', '30%', '40%', 'center', '60%', '70%', '80%', '90%', 'right']
    REPEAT_CHOICES = ['repeat', 'repeat-x', 'repeat-y', 'no-repeat']
    SIZE_CHOICES = ['auto', 'width/height', 'cover', 'contain']
    container_glossary_fields = (
        GlossaryField(
            ContainerBreakpointsWidget(choices=get_widget_choices()),
            label=_("Available Breakpoints"),
            name='breakpoints',
            initial=app_settings.CMSPLUGIN_CASCADE['bootstrap4']['default_bounds'].keys(),
            help_text=_("Supported display widths for Bootstrap's grid system.")
        ),
        GlossaryField(
            MultipleCascadingSizeWidget([bp.name for bp in Breakpoint], allowed_units=['px', '%'], required=False),
            label=_("Adapt Picture Heights"),
            name='container_max_heights',
            initial={'xs': '100%', 'sm': '100%', 'md': '100%', 'lg': '100%', 'xl': '100%'},
            help_text=_("Heights of picture in percent or pixels for distinct Bootstrap's breakpoints.")
        ),
        GlossaryField(
            widgets.CheckboxSelectMultiple(choices=BootstrapPicturePlugin.RESIZE_OPTIONS),
            label=_("Resize Options"),
            name='resize_options',
            initial=['crop', 'subject_location', 'high_resolution'],
            help_text=_("Options to use when resizing the image.")
        ),
    )

    background_color = GlossaryField(
        ColorPickerWidget(),
        label=_("Background color"),
    )

    background_repeat = GlossaryField(
        widgets.RadioSelect(choices=[(c, c) for c in REPEAT_CHOICES]),
        initial='no-repeat',
        label=_("This property specifies how an image repeates."),
    )

    background_attachment = GlossaryField(
        widgets.RadioSelect(choices=[(c, c) for c in ATTACHMENT_CHOICES]),
        initial='local',
        label=_("This property specifies how to move the background relative to the viewport."),
    )

    background_vertical_position = GlossaryField(
        widgets.Select(choices=[(c, c) for c in VERTICAL_POSITION_CHOICES]),
        initial='center',
        label=_("This property moves a background image vertically within its container."),
    )

    background_horizontal_position = GlossaryField(
        widgets.Select(choices=[(c, c) for c in HORIZONTAL_POSITION_CHOICES]),
        initial='center',
        label=_("This property moves a background image horizontally within its container."),
    )

    background_size = GlossaryField(
        widgets.RadioSelect(choices=[(c, c) for c in SIZE_CHOICES]),
        initial='auto',
        label=_("Background size"),
        help_text=_("This property specifies how an image is sized."),
    )

    background_width_height = GlossaryField(
        MultipleCascadingSizeWidget(['width', 'height'], allowed_units=['px', '%'],
                                    required=False),
        label=_("Background width and height"),
        help_text=_("This property specifies the width and height of a background image."),
    )
    footnote_html = """
<p>For more information about the Jumbotron please read </p>
    """

    class Media:
        js = ['cascade/js/admin/jumbotronplugin.js']

    def get_form(self, request, obj=None, **kwargs):
        if self.get_parent_instance(request, obj) is None:
            # we only ask for breakpoints, if the jumbotron is the root of the placeholder
            kwargs.update(glossary_fields=list(self.container_glossary_fields))
            kwargs['glossary_fields'].extend(self.glossary_fields)
        form = super(BootstrapJumbotronPlugin, self).get_form(request, obj, **kwargs)
        return form

    def render(self, context, instance, placeholder):
        # image shall be rendered in a responsive context using the ``<picture>`` element
        try:
            elements = get_picture_elements(instance)
        except Exception as exc:
            logger.warning("Unable generate picture elements. Reason: {}".format(exc))
        else:
            if instance.child_plugin_instances and instance.child_plugin_instances[0].plugin_type == 'BootstrapRowPlugin':
                padding='padding: {0}px {0}px;'.format(int( app_settings.CMSPLUGIN_CASCADE['bootstrap4']['gutter']/2))
                context.update({'add_gutter_if_child_is_BootstrapRowPlugin': padding,})
            context.update({
                'elements': [e for e in elements if 'media' in e] if elements else [],
                'CSS_PREFIXES': app_settings.CSS_PREFIXES,
            })
        return self.super(BootstrapJumbotronPlugin, self).render(context, instance, placeholder)

    @classmethod
    def sanitize_model(cls, obj):
        sanitized = False
        # if the jumbotron is the root of the placeholder, we consider it as "fluid"
        obj.glossary['fluid'] = obj.parent is None
        super(BootstrapJumbotronPlugin, cls).sanitize_model(obj)
        grid_container = obj.get_bound_plugin().get_grid_instance()
        obj.glossary.setdefault('media_queries', {})
        for bp, bound in grid_container.bounds.items():
            obj.glossary['media_queries'].setdefault(bp.name, {})
            width = round(bound.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
    def get_css_classes(cls, obj):
        css_classes = cls.super(BootstrapJumbotronPlugin, cls).get_css_classes(obj)
        if obj.glossary.get('fluid'):
            css_classes.append('jumbotron-fluid')
        else:
            css_classes.append('jumbotron')
        return css_classes

    @classmethod
    def get_identifier(cls, obj):
        identifier = super(BootstrapJumbotronPlugin, cls).get_identifier(obj)
        try:
            content = obj.image.name or obj.image.original_filename
        except AttributeError:
            content = _("Without background image")
        return format_html('{0}{1}', identifier, content)