Exemple #1
0
class RevealImagePlugin(CascadePluginBase):
    name = _("Image")
    module = 'Reveal'
    model_mixins = (ImagePropertyMixin,)
    render_template = 'reveal/image.html'
    require_parent = True
    parent_classes = ('RevealSectionPlugin', 'SimpleWrapperPlugin',)
    allow_children = False
    raw_id_fields = ('image_file',)
    text_enabled = True
    admin_preview = False
    form = ImageForm
    glossary_fields = (
        PartialFormField('image-width',
            CascadingSizeWidget(allowed_units=['px', '%'], required=False),
            label=_("Image Width"),
            help_text=_("Set a fixed image width in pixels."),
        ),
        PartialFormField('image-height',
            CascadingSizeWidget(allowed_units=['px', '%'], required=False),
            label=_("Image Height"),
            help_text=_("Set a fixed height in pixels, or percent relative to the image width."),
        ),
    )

    def render(self, context, instance, placeholder):
        glossary = dict(instance.get_complete_glossary())
        #    extra_styles = tags.pop('extra_styles')
        inline_styles = instance.glossary.get('inline_styles', {})
        # inline_styles.update(extra_styles)
        # instance.glossary['inline_styles'] = inline_styles
        size = (960, 600)
        src = {'size': size, 'crop': False}
        context.update(dict(instance=instance, src=src, placeholder=placeholder))
        return context
Exemple #2
0
class BootstrapGalleryPlugin(CascadePluginBase):
    name = _("Gallery")
    module = 'Bootstrap'
    parent_classes = ['BootstrapColumnPlugin']
    require_parent = True
    allow_children = False
    raw_id_fields = ('image_file', )
    text_enabled = True
    admin_preview = False
    render_template = 'cascade/bootstrap3/gallery.html'
    default_css_attributes = ('image_shapes', )
    html_tag_attributes = {'image_title': 'title', 'alt_tag': 'tag'}
    inlines = (GalleryPluginInline, )
    SHAPE_CHOICES = (('img-responsive', _("Responsive")), )
    RESIZE_OPTIONS = (
        ('upscale', _("Upscale image")),
        ('crop', _("Crop image")),
        ('subject_location', _("With subject location")),
        ('high_resolution', _("Optimized for Retina")),
    )

    image_shapes = GlossaryField(
        widgets.CheckboxSelectMultiple(choices=SHAPE_CHOICES),
        label=_("Image Responsiveness"),
        initial=['img-responsive'],
    )

    image_width_responsive = GlossaryField(
        CascadingSizeWidget(allowed_units=['%'], required=False),
        label=_("Responsive Image Width"),
        initial='100%',
        help_text=_(
            "Set the image width in percent relative to containing element."),
    )

    image_width_fixed = GlossaryField(
        CascadingSizeWidget(allowed_units=['px'], required=False),
        label=_("Fixed Image Width"),
        help_text=_("Set a fixed image width in pixels."),
    )

    image_height = GlossaryField(
        CascadingSizeWidget(allowed_units=['px', '%'], required=False),
        label=_("Adapt Image Height"),
        help_text=
        _("Set a fixed height in pixels, or percent relative to the image width."
          ),
    )

    thumbnail_width = GlossaryField(
        CascadingSizeWidget(allowed_units=['px']),
        label=_("Thumbnail Width"),
        help_text=_("Set a fixed thumbnail width in pixels."),
    )

    thumbnail_height = GlossaryField(
        CascadingSizeWidget(allowed_units=['px', '%']),
        label=_("Thumbnail Height"),
        help_text=
        _("Set a fixed height in pixels, or percent relative to the thumbnail width."
          ),
    )

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

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

    def get_form(self, request, obj=None, **kwargs):
        utils.reduce_breakpoints(self, 'responsive_heights')
        form = super(BootstrapGalleryPlugin,
                     self).get_form(request, obj, **kwargs)
        return form

    def render(self, context, instance, placeholder):
        gallery_instances = []
        options = dict(instance.get_complete_glossary())
        for inline_element in instance.sortinline_elements.all():
            # since inline_element requires the property `image`, add ImagePropertyMixin
            # to its class during runtime
            try:
                ProxyModel = create_proxy_model('GalleryImage',
                                                (ImagePropertyMixin, ),
                                                SortableInlineCascadeElement,
                                                module=__name__)
                inline_element.__class__ = ProxyModel
                options.update(
                    inline_element.glossary, **{
                        'image_width_fixed': options['thumbnail_width'],
                        'image_height': options['thumbnail_height'],
                        'is_responsive': False,
                    })
                thumbnail_tags = utils.get_image_tags(context, inline_element,
                                                      options)
                for key, val in thumbnail_tags.items():
                    setattr(inline_element, key, val)
                gallery_instances.append(inline_element)
            except (KeyError, AttributeError):
                pass
        inline_styles = instance.glossary.get('inline_styles', {})
        inline_styles.update(width=options['thumbnail_width'])
        instance.glossary['inline_styles'] = inline_styles
        context.update(
            dict(instance=instance,
                 placeholder=placeholder,
                 gallery_instances=gallery_instances))
        return context

    @classmethod
    def get_css_classes(cls, obj):
        css_classes = super(BootstrapGalleryPlugin, 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(BootstrapGalleryPlugin, cls).get_identifier(obj)
        num_elems = obj.sortinline_elements.count()
        content = ungettext_lazy("with {0} image", "with {0} images",
                                 num_elems).format(num_elems)
        return format_html('{0}{1}', identifier, content)
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
Exemple #4
0
class BootstrapImagePlugin(ImageAnnotationMixin, LinkPluginBase):
    name = _("Image")
    model_mixins = (ImagePropertyMixin, LinkElementMixin,)
    module = 'Bootstrap'
    parent_classes = ['BootstrapColumnPlugin']
    require_parent = True
    allow_children = False
    raw_id_fields = LinkPluginBase.raw_id_fields + ['image_file']
    admin_preview = False
    ring_plugin = 'ImagePlugin'
    render_template = 'cascade/bootstrap4/linked-image.html'
    default_css_attributes = ['image_shapes', 'image_alignment']
    html_tag_attributes = {'image_title': 'title', 'alt_tag': 'tag'}
    html_tag_attributes.update(LinkPluginBase.html_tag_attributes)
    fields = ['image_file'] + list(LinkPluginBase.fields)
    SHAPE_CHOICES = [
        ('img-fluid', _("Responsive")),
        ('rounded', _('Rounded')),
        ('rounded-circle', _('Circle')),
        ('img-thumbnail', _('Thumbnail')),
    ]
    RESIZE_OPTIONS = [
        ('upscale', _("Upscale image")),
        ('crop', _("Crop image")),
        ('subject_location', _("With subject location")),
        ('high_resolution', _("Optimized for Retina")),
    ]
    ALIGNMENT_OPTIONS = [
        ('float-left', _("Left")),
        ('float-right', _("Right")),
        ('mx-auto', _("Center")),
    ]

    image_shapes = GlossaryField(
        widgets.CheckboxSelectMultiple(choices=SHAPE_CHOICES),
        label=_("Image Shapes"),
        initial=['img-fluid']
    )

    image_width_responsive = GlossaryField(
        CascadingSizeWidget(allowed_units=['%'], required=False),
        label=_("Responsive Image Width"),
        initial='100%',
        help_text=_("Set the image width in percent relative to containing element."),
    )

    image_width_fixed = GlossaryField(
        CascadingSizeWidget(allowed_units=['px'], required=False),
        label=_("Fixed Image Width"),
        help_text=_("Set a fixed image width in pixels."),
    )

    image_height = GlossaryField(
        CascadingSizeWidget(allowed_units=['px', '%'], required=False),
        label=_("Adapt Image Height"),
        help_text=_("Set a fixed height in pixels, or percent relative to the image width."),
    )

    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'],
    )

    image_alignment = GlossaryField(
        widgets.RadioSelect(choices=ALIGNMENT_OPTIONS),
        label=_("Image Alignment"),
        help_text=_("How to align a non-responsive image."),
    )

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

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

    def render(self, context, instance, placeholder):
        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(BootstrapImagePlugin, 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(BootstrapImagePlugin, cls).get_identifier(obj)
        try:
            content = force_text(obj.image)
        except AttributeError:
            content = _("No Image")
        return format_html('{0}{1}', identifier, content)

    @classmethod
    def sanitize_model(cls, obj):
        sanitized = False
        parent = obj.parent
        try:
            while parent.plugin_type != 'BootstrapColumnPlugin':
                parent = parent.parent
            grid_column = parent.get_bound_plugin().get_grid_instance()
            min_max_bounds = grid_column.get_min_max_bounds()
            if obj.glossary.get('column_bounds') != min_max_bounds:
                obj.glossary['column_bounds'] = min_max_bounds
                sanitized = True
            obj.glossary.setdefault('media_queries', {})
            for bp in Breakpoint:
                media_query = '{} {:.2f}px'.format(bp.media_query, grid_column.get_bound(bp).max)
                if obj.glossary['media_queries'].get(bp.name) != media_query:
                    obj.glossary['media_queries'][bp.name] = media_query
                    sanitized = True
        except AttributeError:
            logger.warning("ImagePlugin(pk={}) has no ColumnPlugin as ancestor.".format(obj.pk))
            return
        return sanitized
class TextImagePlugin(ImageAnnotationMixin, LinkPluginBase):
    name = _("Image in text")
    text_enabled = True
    ring_plugin = 'TextImagePlugin'
    render_template = 'cascade/plugins/textimage.html'
    parent_classes = ('TextPlugin', )
    model_mixins = (ImagePropertyMixin, LinkElementMixin)
    allow_children = False
    require_parent = False
    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"))]

    image_width = GlossaryField(
        CascadingSizeWidget(allowed_units=['px'], required=True),
        label=_("Image Width"),
        help_text=_("Set the image width in pixels."),
    )

    image_height = GlossaryField(
        CascadingSizeWidget(allowed_units=['px'], required=False),
        label=_("Image Height"),
        help_text=_("Set the image height in pixels."),
    )

    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'])

    alignement = GlossaryField(
        widgets.RadioSelect(
            choices=[('', _("Not aligned")), ('left',
                                              _("Left")), ('right',
                                                           _("Right"))]),
        initial='',
        label=_("Alignement"),
    )

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

    def get_form(self, request, obj=None, **kwargs):
        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(TextImagePlugin, self).get_form(request, obj, **kwargs)

    @classmethod
    def requires_parent_plugin(cls, slot, page):
        """
        Workaround for `PluginPool.get_all_plugins()`, otherwise TextImagePlugin is not allowed
        as a child of a `TextPlugin`.
        """
        return False

    @classmethod
    def get_inline_styles(cls, instance):
        inline_styles = cls.super(TextImagePlugin,
                                  cls).get_inline_styles(instance)
        alignement = instance.glossary.get('alignement')
        if alignement:
            inline_styles['float'] = alignement
        return inline_styles

    def render(self, context, instance, placeholder):
        try:
            aspect_ratio = compute_aspect_ratio(instance.image)
        except Exception:
            # if accessing the image file fails, abort here
            return context
        resize_options = instance.glossary.get('resize_options', {})
        crop = 'crop' in resize_options
        upscale = 'upscale' in resize_options
        subject_location = instance.image.subject_location if 'subject_location' in resize_options else False
        high_resolution = 'high_resolution' in resize_options
        image_width = instance.glossary.get('image_width', '')
        if not image_width.endswith('px'):
            return context
        image_width = int(image_width.rstrip('px'))
        image_height = instance.glossary.get('image_height', '')
        if image_height.endswith('px'):
            image_height = int(image_height.rstrip('px'))
        else:
            image_height = int(round(image_width * aspect_ratio))
        src = {
            'size': (image_width, image_height),
            'size2x': (image_width * 2, image_height * 2),
            'crop': crop,
            'upscale': upscale,
            'subject_location': subject_location,
            'high_resolution': high_resolution,
        }
        context.update(
            dict(instance=instance, placeholder=placeholder, src=src))
        return context
class BootstrapImagePlugin(LinkPluginBase):
    name = _("Image")
    model_mixins = (
        ImagePropertyMixin,
        LinkElementMixin,
    )
    module = 'Bootstrap'
    parent_classes = ['BootstrapColumnPlugin']
    require_parent = True
    allow_children = False
    raw_id_fields = ('image_file', )
    text_enabled = True
    admin_preview = False
    render_template = 'cascade/bootstrap3/linked-image.html'
    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')
    SHAPE_CHOICES = (
        ('img-responsive', _("Responsive")),
        ('img-rounded', _('Rounded')),
        ('img-circle', _('Circle')),
        ('img-thumbnail', _('Thumbnail')),
    )
    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('image-shapes',
                         widgets.CheckboxSelectMultiple(choices=SHAPE_CHOICES),
                         label=_("Image Shapes"),
                         initial=['img-responsive']),
        PartialFormField(
            'image-width-responsive',
            CascadingSizeWidget(allowed_units=['%'], required=False),
            label=_("Responsive Image Width"),
            initial='100%',
            help_text=_(
                "Set the image width in percent relative to containing element."
            ),
        ),
        PartialFormField(
            'image-width-fixed',
            CascadingSizeWidget(allowed_units=['px'], required=False),
            label=_("Fixed Image Width"),
            help_text=_("Set a fixed image width in pixels."),
        ),
        PartialFormField(
            'image-height',
            CascadingSizeWidget(allowed_units=['px', '%'], required=False),
            label=_("Adapt Image Height"),
            help_text=
            _("Set a fixed height in pixels, or percent relative to the image width."
              ),
        ),
        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/imageplugin.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(BootstrapImagePlugin,
                     self).get_form(request, obj, **kwargs)

    def render(self, context, instance, placeholder):
        is_responsive = 'img-responsive' in instance.glossary.get(
            'image-shapes', [])
        options = dict(instance.get_complete_glossary(),
                       is_responsive=is_responsive)
        tags = utils.get_image_tags(context, instance, options)
        if 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 = super(BootstrapImagePlugin, 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(BootstrapImagePlugin, cls).get_identifier(obj)
        try:
            content = force_text(obj.image)
        except AttributeError:
            content = _("No Image")
        return format_html('{0}{1}', identifier, content)
Exemple #7
0
class FramedIconPlugin(IconPluginMixin, CascadePluginBase):
    name = _("Icon with frame")
    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 = 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
Exemple #8
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)})
Exemple #9
0
class LeafletPlugin(CascadePluginBase):
    name = _("Map")
    parent_classes = None
    require_parent = False
    allow_children = False
    change_form_template = 'cascade/admin/leaflet_plugin_change_form.html'
    ring_plugin = 'LeafletPlugin'
    admin_preview = False
    render_template = 'cascade/plugins/leaflet.html'
    inlines = (MarkerInline, )
    glossary_field_order = ('map_width', 'map_height')
    model_mixins = (LeafletModelMixin, )
    form = LeafletForm
    settings = mark_safe(json.dumps(app_settings.CMSPLUGIN_CASCADE['leaflet']))

    map_width = GlossaryField(
        CascadingSizeWidget(allowed_units=['px', '%'], required=True),
        label=_("Map Width"),
        initial='100%',
        help_text=_(
            "Set the map width in percent relative to containing element."),
    )

    map_height = GlossaryField(
        CascadingSizeWidget(allowed_units=['px', '%'], required=True),
        label=_("Adapt Map Height"),
        initial='400px',
        help_text=
        _("Set a fixed height in pixels, or percent relative to the map width."
          ),
    )

    class Media:
        css = {
            'all': [
                'node_modules/leaflet/dist/leaflet.css',
                'node_modules/leaflet-easybutton/src/easy-button.css',
                'cascade/css/admin/leafletplugin.css',
            ]
        }
        js = [
            'node_modules/leaflet/dist/leaflet.js',
            'node_modules/leaflet-easybutton/src/easy-button.js',
            'cascade/js/admin/leafletplugin.js',
        ]

    def add_view(self, request, form_url='', extra_context=None):
        extra_context = dict(extra_context or {}, settings=self.settings)
        return super(LeafletPlugin, self).add_view(request, form_url,
                                                   extra_context)

    def change_view(self, request, object_id, form_url='', extra_context=None):
        extra_context = dict(extra_context or {}, settings=self.settings)
        return super(LeafletPlugin, self).change_view(request, object_id,
                                                      form_url, extra_context)

    def render(self, context, instance, placeholder):
        marker_instances = []
        for inline_element in instance.inline_elements.all():
            try:
                ProxyModel = create_proxy_model(
                    'LeafletMarker', (ImagePropertyMixin, MarkerModelMixin),
                    InlineCascadeElement,
                    module=__name__)
                marker = ProxyModel(id=inline_element.id,
                                    glossary=inline_element.glossary)
                try:
                    aspect_ratio = compute_aspect_ratio(marker.image)
                    width = parse_responsive_length(
                        marker.glossary.get('marker_width') or '25px')
                    marker.size = list(
                        get_image_size(width[0], (None, None), aspect_ratio))
                    marker.size2x = 2 * marker.size[0], 2 * marker.size[1]
                except Exception:
                    # if accessing the image file fails, skip size computations
                    pass
                else:
                    try:
                        marker_anchor = marker.glossary['marker_anchor']
                        top = parse_responsive_length(marker_anchor['top'])
                        left = parse_responsive_length(marker_anchor['left'])
                        if top[0] is None or left[0] is None:
                            left = width[0] * left[1]
                            top = width[0] * aspect_ratio * top[1]
                        else:
                            left, top = left[0], top[0]
                        marker.anchor = [left, top]
                    except Exception:
                        pass
                marker_instances.append(marker)
            except (KeyError, AttributeError):
                pass

        context.update(
            dict(instance=instance,
                 placeholder=placeholder,
                 settings=self.settings,
                 config=app_settings.CMSPLUGIN_CASCADE['leaflet'],
                 markers=marker_instances))
        return context

    @classmethod
    def get_css_classes(cls, obj):
        css_classes = cls.super(LeafletPlugin, 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(LeafletPlugin, cls).get_identifier(obj)
        num_elems = obj.inline_elements.count()
        content = ungettext_lazy("with {0} marker", "with {0} markers",
                                 num_elems).format(num_elems)
        return format_html('{0}{1}', identifier, content)

    @classmethod
    def get_data_representation(cls, instance):
        data = super(LeafletPlugin, cls).get_data_representation(instance)
        data.update(
            inlines=[ie.glossary for ie in instance.inline_elements.all()])
        return data
Exemple #10
0
class BootstrapImagePlugin(ImageAnnotationMixin, LinkPluginBase):
    name = _("Image")
    model_mixins = (
        ImagePropertyMixin,
        LinkElementMixin,
    )
    module = 'Bootstrap'
    parent_classes = ('BootstrapColumnPlugin', )
    require_parent = True
    allow_children = False
    raw_id_fields = LinkPluginBase.raw_id_fields + ['image_file']
    admin_preview = False
    ring_plugin = 'ImagePlugin'
    render_template = 'cascade/bootstrap3/linked-image.html'
    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)
    SHAPE_CHOICES = [
        ('img-responsive', _("Responsive")),
        ('img-rounded', _('Rounded')),
        ('img-circle', _('Circle')),
        ('img-thumbnail', _('Thumbnail')),
    ]
    RESIZE_OPTIONS = [
        ('upscale', _("Upscale image")),
        ('crop', _("Crop image")),
        ('subject_location', _("With subject location")),
        ('high_resolution', _("Optimized for Retina")),
    ]

    image_shapes = GlossaryField(
        widgets.CheckboxSelectMultiple(choices=SHAPE_CHOICES),
        label=_("Image Shapes"),
        initial=['img-responsive'])

    image_width_responsive = GlossaryField(
        CascadingSizeWidget(allowed_units=['%'], required=False),
        label=_("Responsive Image Width"),
        initial='100%',
        help_text=_(
            "Set the image width in percent relative to containing element."),
    )

    image_width_fixed = GlossaryField(
        CascadingSizeWidget(allowed_units=['px'], required=False),
        label=_("Fixed Image Width"),
        help_text=_("Set a fixed image width in pixels."),
    )

    image_height = GlossaryField(
        CascadingSizeWidget(allowed_units=['px', '%'], required=False),
        label=_("Adapt Image Height"),
        help_text=
        _("Set a fixed height in pixels, or percent relative to the image width."
          ),
    )

    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/imageplugin.js']

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

    def render(self, context, instance, placeholder):
        is_responsive = 'img-responsive' in instance.glossary.get(
            'image_shapes', [])
        options = dict(instance.get_complete_glossary(),
                       is_responsive=is_responsive)
        tags = utils.get_image_tags(context, instance, options)
        if 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(BootstrapImagePlugin, 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(BootstrapImagePlugin, cls).get_identifier(obj)
        try:
            content = force_text(obj.image)
        except AttributeError:
            content = _("No Image")
        return format_html('{0}{1}', identifier, content)