Exemple #1
0
 def __init__(self, model=None, admin_site=None):
     glossary_fields = (
         PartialFormField('grid',
                          widgets.Select(choices=self.GRID_CHOICES),
                          label=_('Column Grid'),
                          initial='grid_4',
                          help_text=_("Grid in column units.")),
         PartialFormField(
             'prefix',
             widgets.Select(choices=self.PREFIX_CHOICES),
             label=_('Prefix'),
         ),
         PartialFormField(
             'suffix',
             widgets.Select(choices=self.SUFFIX_CHOICES),
             label=_('Suffix'),
         ),
         PartialFormField(
             'options',
             widgets.CheckboxSelectMultiple(choices=self.OPTION_CHOICES),
             label=_('Options'),
         ),
         PartialFormField('inline_styles',
                          MultipleCascadingSizeWidget(
                              ['min-height', 'margin-top',
                               'margin-bottom']),
                          label=_('Inline Styles'),
                          help_text=_('Minimum height for this column.')),
     )
     super(Grid960BasePlugin,
           self).__init__(model,
                          admin_site,
                          glossary_fields=glossary_fields)
Exemple #2
0
class BootstrapButtonPlugin(LinkPluginBase):
    module = 'Bootstrap'
    name = _("Button")
    form = TextLinkForm
    model_mixins = (LinkElementMixin,)
    parent_classes = ['BootstrapColumnPlugin']
    render_template = 'cascade/bootstrap3/button.html'
    allow_children = False
    text_enabled = True
    tag_type = None
    default_css_class = 'btn'
    default_css_attributes = ('button-type', 'button-size', 'button-options', 'quick-float',)
    fields = ('link_content', ('link_type', 'cms_page', 'ext_url', 'mail_to'), 'glossary',)
    glossary_fields = (
        PartialFormField('button-type',
            widgets.RadioSelect(choices=((k, v) for k, v in ButtonTypeRenderer.BUTTON_TYPES.items()),
                                renderer=ButtonTypeRenderer),
            label=_('Button Type'),
            initial='btn-default',
            help_text=_("Display Link using this Button Style")
        ),
        PartialFormField('button-size',
            widgets.RadioSelect(choices=((k, v) for k, v in ButtonSizeRenderer.BUTTON_SIZES.items()),
                                renderer=ButtonSizeRenderer),
            label=_('Button Size'),
            initial='',
            help_text=_("Display Link using this Button Size")
        ),
        PartialFormField('button-options',
            widgets.CheckboxSelectMultiple(choices=(('btn-block', _('Block level')), ('disabled', _('Disabled')),)),
            label=_('Button Options'),
        ),
        PartialFormField('quick-float',
            widgets.RadioSelect(choices=(('', _("Do not float")), ('pull-left', _("Pull left")), ('pull-right', _("Pull right")),)),
            label=_('Quick Float'),
            initial='',
            help_text=_("Float the button to the left or right.")
        ),
    ) + LinkPluginBase.glossary_fields + (
        PartialFormField('inline_styles',
            MultipleCascadingSizeWidget(['margin-top', 'margin-right', 'margin-bottom', 'margin-left'],
                allowed_units=['px', 'em'], required=False),
            label=_('Margins'),
            help_text=_('Margins for this button wrapper.')
        ),
    )

    class Media:
        css = {'all': ('cascade/css/admin/bootstrap.min.css', 'cascade/css/admin/bootstrap-theme.min.css',)}

    @classmethod
    def get_identifier(cls, obj):
        identifier = super(BootstrapButtonPlugin, cls).get_identifier(obj)
        content = obj.glossary.get('link_content')
        if not content:
            try:
                content = force_text(ButtonTypeRenderer.BUTTON_TYPES[obj.glossary['button-type']])
            except KeyError:
                content = _("Empty")
        return format_html('{0}{1}', identifier, content)
class CarouselPlugin(BootstrapPluginBase):
    name = _("Carousel")
    form = CarouselSlidesForm
    default_css_class = 'carousel'
    default_css_attributes = ('options', )
    parent_classes = ['BootstrapColumnPlugin', 'SimpleWrapperPlugin']
    render_template = 'cascade/bootstrap3/{}/carousel.html'
    default_inline_styles = {'overflow': 'hidden'}
    fields = (
        'num_children',
        'glossary',
    )
    DEFAULT_CAROUSEL_ATTRIBUTES = {'data-ride': 'carousel'}
    OPTION_CHOICES = (
        ('slide', _("Animate")),
        ('pause', _("Pause")),
        ('wrap', _("Wrap")),
    )

    interval = GlossaryField(
        NumberInputWidget(attrs={
            'size': '2',
            'style': 'width: 4em;',
            'min': '1'
        }),
        label=_("Interval"),
        initial=5,
        help_text=_("Change slide after this number of seconds."),
    )

    options = GlossaryField(
        widgets.CheckboxSelectMultiple(choices=OPTION_CHOICES),
        label=_('Options'),
        initial=['slide', 'wrap', 'pause'],
        help_text=_("Adjust interval for the carousel."),
    )

    container_max_heights = GlossaryField(
        MultipleCascadingSizeWidget(list(
            tp[0]
            for tp in settings.CMSPLUGIN_CASCADE['bootstrap3']['breakpoints']),
                                    allowed_units=['px']),
        label=_("Carousel heights"),
        initial=dict(
            (bp[0], '{}px'.format(100 + 50 * i)) for i, bp in enumerate(
                settings.CMSPLUGIN_CASCADE['bootstrap3']['breakpoints'])),
        help_text=
        _("Heights of Carousel in pixels for distinct Bootstrap's breakpoints."
          ),
    )

    resize_options = GlossaryField(
        widgets.CheckboxSelectMultiple(
            choices=BootstrapPicturePlugin.RESIZE_OPTIONS),
        label=_("Resize Options"),
        help_text=_("Options to use when resizing the image."),
        initial=['upscale', 'crop', 'subject_location', 'high_resolution'])

    def get_form(self, request, obj=None, **kwargs):
        utils.reduce_breakpoints(self, 'container_max_heights', request)
        return super(CarouselPlugin, self).get_form(request, obj, **kwargs)

    @classmethod
    def get_identifier(cls, obj):
        identifier = super(CarouselPlugin, cls).get_identifier(obj)
        num_cols = obj.get_children().count()
        content = ungettext_lazy('with {0} slide', 'with {0} slides',
                                 num_cols).format(num_cols)
        return format_html('{0}{1}', identifier, content)

    @classmethod
    def get_css_classes(cls, obj):
        css_classes = super(CarouselPlugin, cls).get_css_classes(obj)
        if 'slide' in obj.glossary.get('options', []):
            css_classes.append('slide')
        return css_classes

    @classmethod
    def get_html_tag_attributes(cls, obj):
        attributes = super(CarouselPlugin, 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-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'))
        super(CarouselPlugin, self).save_model(request, obj, form, change)
        self.extend_children(obj, wanted_children, CarouselSlidePlugin)

    @classmethod
    def sanitize_model(cls, obj):
        sanitized = super(CarouselPlugin, cls).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())
        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
        return sanitized
class BootstrapPicturePlugin(ImageAnnotationMixin, LinkPluginBase):
    name = _("Picture")
    model_mixins = (
        ImagePropertyMixin,
        LinkElementMixin,
    )
    module = 'Bootstrap'
    parent_classes = ['Bootstrap4ColumnPlugin', 'SimpleWrapperPlugin']
    require_parent = True
    allow_children = False
    raw_id_fields = ('image_file', )
    admin_preview = False
    ring_plugin = 'PicturePlugin'
    render_template = 'cascade/bootstrap4/linked-picture.html'
    default_css_class = 'img-fluid'
    default_css_attributes = ('image_shapes', )
    html_tag_attributes = {'image_title': 'title', 'alt_tag': 'tag'}
    html_tag_attributes.update(LinkPluginBase.html_tag_attributes)
    fields = ('image_file', ) + LinkPluginBase.fields
    RESIZE_OPTIONS = (
        ('upscale', _("Upscale image")),
        ('crop', _("Crop image")),
        ('subject_location', _("With subject location")),
        ('high_resolution', _("Optimized for Retina")),
    )

    responsive_heights = GlossaryField(
        MultipleCascadingSizeWidget(BS4_BREAKPOINT_KEYS,
                                    allowed_units=['px', '%'],
                                    required=False),
        label=_("Adapt Picture 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."
          ),
    )

    responsive_zoom = GlossaryField(
        MultipleCascadingSizeWidget(BS4_BREAKPOINT_KEYS,
                                    allowed_units=['%'],
                                    required=False),
        label=_("Adapt Picture Zoom"),
        initial={
            'xs': '0%',
            'sm': '0%',
            'md': '0%',
            'lg': '0%',
            'xl': '0%'
        },
        help_text=
        _("Magnification of picture in percent for distinct Bootstrap's breakpoints."
          ),
    )

    resize_options = GlossaryField(
        widgets.CheckboxSelectMultiple(choices=RESIZE_OPTIONS),
        label=_("Resize Options"),
        help_text=_("Options to use when resizing the image."),
        initial=['subject_location', 'high_resolution'])

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

    def get_form(self, request, obj=None, **kwargs):
        reduce_breakpoints(self, 'responsive_heights')
        LINK_TYPE_CHOICES = (('none', _("No Link")),) + \
                            tuple(t for t in getattr(LinkForm, 'LINK_TYPE_CHOICES') if t[0] != 'email')
        image_file = ModelChoiceField(queryset=Image.objects.all(),
                                      required=False,
                                      label=_("Image"))
        Form = type(str('ImageForm'), (
            ImageFormMixin,
            getattr(LinkForm, 'get_form_class')(),
        ), {
            'LINK_TYPE_CHOICES': LINK_TYPE_CHOICES,
            'image_file': image_file
        })
        kwargs.update(form=Form)
        return super(BootstrapPicturePlugin,
                     self).get_form(request, obj, **kwargs)

    def render(self, context, instance, placeholder):
        # image shall be rendered in a responsive context using the picture element
        elements = get_picture_elements(context, instance)
        fluid = instance.get_complete_glossary().get('fluid') == 'on'
        context.update({
            'is_responsive': True,
            'instance': instance,
            'is_fluid': fluid,
            'placeholder': placeholder,
            'elements': elements,
        })
        return context

    @classmethod
    def get_css_classes(cls, obj):
        css_classes = cls.super(BootstrapPicturePlugin,
                                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_identifier(cls, obj):
        identifier = super(BootstrapPicturePlugin, cls).get_identifier(obj)
        try:
            content = force_text(obj.image)
        except AttributeError:
            content = _("No Picture")
        return format_html('{0}{1}', identifier, content)
Exemple #5
0
class MarkerForm(ModelForm):
    title = CharField(
        label=_("Marker Title"),
        widget=widgets.TextInput(attrs={'size': 60}),
        help_text=_(
            "Please choose a title, then go to the map to set a marker pin"))

    use_icon = BooleanField(
        label=_("Use customized marker icon"),
        initial=False,
        required=False,
    )

    marker_image = ModelChoiceField(
        queryset=Image.objects.all(),
        label=_("Marker Image"),
        required=False,
    )

    marker_width = GlossaryFormField(
        widget=CascadingSizeWidget(allowed_units=['px'], required=False),
        label=_("Marker Width"),
        required=False,
        help_text=_("Width of the marker icon in pixels."),
    )

    marker_anchor = GlossaryFormField(
        widget=MultipleCascadingSizeWidget(['left', 'top'],
                                           allowed_units=['px', '%'],
                                           required=False),
        required=False,
        label=_("Marker Anchor"),
        help_text=
        _("The coordinates of the icon's anchor (relative to its top left corner)."
          ),
    )

    popup_text = HTMLFormField(
        required=False,
        help_text=_("Optional rich text to display in popup."),
    )

    position = Field(widget=widgets.HiddenInput)

    glossary_field_order = [
        'title', 'marker_width', 'marker_anchor', 'popup_text'
    ]

    class Meta:
        exclude = ['glossary']

    def __init__(self, *args, **kwargs):
        try:
            initial = dict(kwargs['instance'].glossary)
            has_original = True
        except (KeyError, AttributeError):
            initial = {}
            has_original = False
        initial.update(kwargs.pop('initial', {}))
        self.base_fields['position'].initial = json.dumps(
            initial.pop('position', {}))
        for key in self.glossary_field_order:
            self.base_fields[key].initial = initial.get(key)
        try:
            self.base_fields['marker_image'].initial = initial['image']['pk']
        except KeyError:
            self.base_fields['marker_image'].initial = None
            self.base_fields['use_icon'].initial = False
        else:
            self.base_fields['use_icon'].initial = True
        self.base_fields['marker_image'].widget = AdminFileWidget(
            ManyToOneRel(FilerImageField, Image, 'file_ptr'), site)
        super(MarkerForm, self).__init__(*args, **kwargs)
        if has_original:
            self.fields['title'].help_text = None

    def clean(self):
        try:
            position = self.cleaned_data['position']
            if isinstance(position, six.string_types):
                position = json.loads(position)
            elif not isinstance(position, dict):
                raise ValueError
        except (ValueError, KeyError):
            raise ValidationError(
                "Invalid internal position data. Check your Javascript imports."
            )
        else:
            if 'lat' not in position or 'lng' not in position:
                # place the marker in the center of the current map
                position = {
                    k: v
                    for k, v in self.instance.cascade_element.
                    glossary['map_position'].items() if k in ['lat', 'lng']
                }
            self.instance.glossary.update(position=position)

        marker_image = self.cleaned_data.pop('marker_image', None)
        if marker_image:
            image_data = {'pk': marker_image.pk, 'model': 'filer.Image'}
            self.instance.glossary.update(image=image_data)
        else:
            self.instance.glossary.pop('image', None)

        popup_text = self.cleaned_data.pop('popup_text', None)
        if strip_tags(popup_text):
            popup_text = strip_spaces_between_tags(popup_text)
            self.cleaned_data.update(popup_text=popup_text)

        for key in self.glossary_field_order:
            self.instance.glossary.update({key: self.cleaned_data.get(key)})
class BootstrapPicturePlugin(LinkPluginBase):
    name = _("Picture")
    model_mixins = (
        ImagePropertyMixin,
        LinkElementMixin,
    )
    module = 'Bootstrap'
    parent_classes = ['BootstrapColumnPlugin']
    require_parent = True
    allow_children = False
    raw_id_fields = ('image_file', )
    text_enabled = False
    admin_preview = False
    render_template = 'cascade/bootstrap3/linked-picture.html'
    default_css_class = 'img-responsive'
    default_css_attributes = ('image-shapes', )
    html_tag_attributes = {'image-title': 'title', 'alt-tag': 'tag'}
    fields = (
        'image_file',
        getattr(LinkPluginBase, 'glossary_field_map')['link'],
        'glossary',
    )
    LINK_TYPE_CHOICES = (('none', _("No Link")),) + \
        tuple(t for t in getattr(LinkForm, 'LINK_TYPE_CHOICES') if t[0] != 'email')
    RESIZE_OPTIONS = (
        ('upscale', _("Upscale image")),
        ('crop', _("Crop image")),
        ('subject_location', _("With subject location")),
        ('high_resolution', _("Optimized for Retina")),
    )
    glossary_fields = (
        PartialFormField(
            'image-title',
            widgets.TextInput(),
            label=_('Image Title'),
            help_text=
            _("Caption text added to the 'title' attribute of the <img> element."
              ),
        ),
        PartialFormField(
            'alt-tag',
            widgets.TextInput(),
            label=_('Alternative Description'),
            help_text=
            _("Textual description of the image added to the 'alt' tag of the <img> element."
              ),
        ),
    ) + getattr(LinkPluginBase, 'glossary_fields', ()) + (
        PartialFormField(
            'responsive-heights',
            MultipleCascadingSizeWidget(BS3_BREAKPOINT_KEYS,
                                        allowed_units=['px', '%'],
                                        required=False),
            label=_("Adapt Picture Heights"),
            initial={
                'xs': '100%',
                'sm': '100%',
                'md': '100%',
                'lg': '100%'
            },
            help_text=
            _("Heights of picture in percent or pixels for distinct Bootstrap's breakpoints."
              ),
        ),
        PartialFormField(
            'responsive-zoom',
            MultipleCascadingSizeWidget(
                BS3_BREAKPOINT_KEYS, allowed_units=['%'], required=False),
            label=_("Adapt Picture Zoom"),
            initial={
                'xs': '0%',
                'sm': '0%',
                'md': '0%',
                'lg': '0%'
            },
            help_text=
            _("Magnification of picture in percent for distinct Bootstrap's breakpoints."
              ),
        ),
        PartialFormField(
            'resize-options',
            widgets.CheckboxSelectMultiple(choices=RESIZE_OPTIONS),
            label=_("Resize Options"),
            help_text=_("Options to use when resizing the image."),
            initial=['subject_location', 'high_resolution']),
    )

    class Media:
        js = resolve_dependencies('cascade/js/admin/pictureplugin.js')

    def get_form(self, request, obj=None, **kwargs):
        utils.reduce_breakpoints(self, 'responsive-heights')
        image_file = ModelChoiceField(queryset=Image.objects.all(),
                                      required=False,
                                      label=_("Image"))
        Form = type(str('ImageForm'), (
            ImageFormMixin,
            getattr(LinkForm, 'get_form_class')(),
        ), {
            'LINK_TYPE_CHOICES': self.LINK_TYPE_CHOICES,
            'image_file': image_file
        })
        kwargs.update(form=Form)
        return super(BootstrapPicturePlugin,
                     self).get_form(request, obj, **kwargs)

    def render(self, context, instance, placeholder):
        # image shall be rendered in a responsive context using the picture element
        elements = utils.get_picture_elements(context, instance)
        context.update({
            'is_responsive': True,
            'instance': instance,
            'placeholder': placeholder,
            'elements': elements,
        })
        return context

    @classmethod
    def get_css_classes(cls, obj):
        css_classes = super(BootstrapPicturePlugin, 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_identifier(cls, obj):
        identifier = super(BootstrapPicturePlugin, cls).get_identifier(obj)
        try:
            content = force_text(obj.image)
        except AttributeError:
            content = _("No Picture")
        return format_html('{0}{1}', identifier, content)
Exemple #7
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)
Exemple #8
0
class BootstrapJumbotronPlugin(BootstrapPluginBase):
    name = _("Jumbotron")
    model_mixins = (ImagePropertyMixin, ImageBackgroundMixin)
    form = JumbotronPluginForm
    default_css_class = 'jumbotron'
    parent_classes = ['BootstrapColumnPlugin']
    require_parent = False
    allow_children = True
    alien_child_classes = True
    raw_id_fields = ('image_file', )
    fields = (
        'glossary',
        'image_file',
    )
    render_template = 'cascade/bootstrap3/jumbotron.html'
    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 = (
        PartialFormField(
            'breakpoints',
            widgets.CheckboxSelectMultiple(
                choices=get_widget_choices(),
                renderer=ContainerBreakpointsRenderer),
            label=_("Available Breakpoints"),
            initial=list(BS3_BREAKPOINT_KEYS)[::-1],
            help_text=_(
                "Supported display widths for Bootstrap's grid system.")),
        PartialFormField(
            'container_max_heights',
            MultipleCascadingSizeWidget(BS3_BREAKPOINT_KEYS,
                                        allowed_units=['px', '%'],
                                        required=False),
            label=_("Adapt Picture Heights"),
            initial={
                'xs': '100%',
                'sm': '100%',
                'md': '100%',
                'lg': '100%'
            },
            help_text=
            _("Heights of picture in percent or pixels for distinct Bootstrap's breakpoints."
              ),
        ),
        PartialFormField(
            'resize-options',
            widgets.CheckboxSelectMultiple(
                choices=BootstrapPicturePlugin.RESIZE_OPTIONS),
            label=_("Resize Options"),
            help_text=_("Options to use when resizing the image."),
            initial=['crop', 'subject_location', 'high_resolution']),
    )
    glossary_fields = (
        PartialFormField(
            'background-color',
            ColorPickerWidget(),
            label=_("Background color"),
        ),
        PartialFormField(
            'background-repeat',
            widgets.RadioSelect(choices=[(c, c) for c in REPEAT_CHOICES]),
            initial='no-repeat',
            label=_("This property specifies how an image repeates."),
        ),
        PartialFormField(
            'background-attachment',
            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."
              ),
        ),
        PartialFormField(
            'background-vertical-position',
            widgets.Select(choices=[(c, c)
                                    for c in VERTICAL_POSITION_CHOICES]),
            initial='center',
            label=
            _("This property moves a background image vertically within its container."
              ),
        ),
        PartialFormField(
            'background-horizontal-position',
            widgets.Select(choices=[(c, c)
                                    for c in HORIZONTAL_POSITION_CHOICES]),
            initial='center',
            label=
            _("This property moves a background image horizontally within its container."
              ),
        ),
        PartialFormField(
            'background-size',
            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."),
        ),
        PartialFormField(
            'background-width-height',
            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:
        css = {'all': (settings.CMSPLUGIN_CASCADE['fontawesome_css_url'], )}
        js = resolve_dependencies('cascade/js/admin/jumbotronplugin.js')

    def get_form(self, request, obj=None, **kwargs):
        if self.get_parent_instance(request) 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({
            'instance':
            instance,
            'placeholder':
            placeholder,
            'elements':
            [e for e in elements if 'media' in e] if elements else [],
        })
        return 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)
Exemple #9
0
class BootstrapPicturePlugin(ImageAnnotationMixin, LinkPluginBase):
    name = _("Picture")
    model_mixins = (
        ImagePropertyMixin,
        LinkElementMixin,
    )
    module = 'Bootstrap'
    parent_classes = ['BootstrapColumnPlugin', 'SimpleWrapperPlugin']
    require_parent = True
    allow_children = False
    raw_id_fields = ('image_file', )
    admin_preview = False
    ring_plugin = 'PicturePlugin'
    render_template = 'cascade/bootstrap4/linked-picture.html'
    default_css_class = 'img-fluid'
    default_css_attributes = ('image_shapes', )
    html_tag_attributes = {'image_title': 'title', 'alt_tag': 'tag'}
    html_tag_attributes.update(LinkPluginBase.html_tag_attributes)
    fields = ['image_file'] + list(LinkPluginBase.fields)
    RESIZE_OPTIONS = [('upscale', _("Upscale image")),
                      ('crop', _("Crop image")),
                      ('subject_location', _("With subject location")),
                      ('high_resolution', _("Optimized for Retina"))]

    responsive_heights = GlossaryField(
        MultipleCascadingSizeWidget([bp.name for bp in Breakpoint],
                                    allowed_units=['px', '%'],
                                    required=False),
        label=_("Adapt Picture 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."
          ),
    )

    responsive_zoom = GlossaryField(
        MultipleCascadingSizeWidget([bp.name for bp in Breakpoint],
                                    allowed_units=['%'],
                                    required=False),
        label=_("Adapt Picture Zoom"),
        initial={
            'xs': '0%',
            'sm': '0%',
            'md': '0%',
            'lg': '0%',
            'xl': '0%'
        },
        help_text=
        _("Magnification of picture in percent for distinct Bootstrap's breakpoints."
          ),
    )

    resize_options = GlossaryField(
        widgets.CheckboxSelectMultiple(choices=RESIZE_OPTIONS),
        label=_("Resize Options"),
        help_text=_("Options to use when resizing the image."),
        initial=['subject_location', 'high_resolution'])

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

    def get_form(self, request, obj=None, **kwargs):
        LINK_TYPE_CHOICES = [('none', _("No Link"))]
        LINK_TYPE_CHOICES.extend(
            t for t in getattr(LinkForm, 'LINK_TYPE_CHOICES')
            if t[0] != 'email')
        image_file = ModelChoiceField(queryset=Image.objects.all(),
                                      required=False,
                                      label=_("Image"))
        Form = type(str('ImageForm'), (
            ImageFormMixin,
            getattr(LinkForm, 'get_form_class')(),
        ), {
            'LINK_TYPE_CHOICES': LINK_TYPE_CHOICES,
            'image_file': image_file
        })
        kwargs.update(form=Form)
        return super(BootstrapPicturePlugin,
                     self).get_form(request, obj, **kwargs)

    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:
            context.update({
                'instance': instance,
                'is_fluid': True,
                'placeholder': placeholder,
                'elements': elements,
            })
        return context

    @classmethod
    def get_css_classes(cls, obj):
        css_classes = cls.super(BootstrapPicturePlugin,
                                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_identifier(cls, obj):
        identifier = super(BootstrapPicturePlugin, cls).get_identifier(obj)
        try:
            content = force_text(obj.image)
        except AttributeError:
            content = _("No Picture")
        return format_html('{0}{1}', identifier, content)

    @classmethod
    def sanitize_model(cls, obj):
        sanitized = False
        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
        return sanitized