diff --git a/core/src/zeit/content/article/edit/browser/configure.zcml b/core/src/zeit/content/article/edit/browser/configure.zcml
index f3125cf720..2b3c7c163c 100644
--- a/core/src/zeit/content/article/edit/browser/configure.zcml
+++ b/core/src/zeit/content/article/edit/browser/configure.zcml
@@ -827,4 +827,47 @@
permission="zope.View"
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/zeit/content/article/edit/browser/edit.py b/core/src/zeit/content/article/edit/browser/edit.py
index c98746df03..62ec5dfd08 100644
--- a/core/src/zeit/content/article/edit/browser/edit.py
+++ b/core/src/zeit/content/article/edit/browser/edit.py
@@ -575,3 +575,45 @@ class EditAnimation(zeit.cms.browser.manual.FormMixin, zeit.edit.browser.form.In
@property
def prefix(self):
return 'animation.{0}'.format(self.context.__name__)
+
+
+class EditImageRow(zeit.edit.browser.form.InlineForm):
+ legend = ''
+ form_fields = zope.formlib.form.FormFields(zeit.content.article.edit.interfaces.IImageRow).omit(
+ *list(zeit.edit.interfaces.IBlock)
+ )
+
+ @property
+ def prefix(self):
+ return 'image_row.{0}'.format(self.context.__name__)
+
+
+class EditImageGrid(zeit.edit.browser.form.InlineForm):
+ legend = ''
+ omitted_fields_prefix = ['images_', 'display_mode_', 'variant_name_']
+ form_fields = zope.formlib.form.FormFields(
+ zeit.content.article.edit.interfaces.IImageGrid
+ ).omit(*list(zeit.edit.interfaces.IBlock))
+ _old_form_fields = form_fields
+
+ @property
+ def prefix(self):
+ return 'image_grid.{0}'.format(self.context.__name__)
+
+ def setUpWidgets(self, *args, **kw):
+ super().setUpWidgets(*args, **kw)
+ visible_count = 1
+ for i in range(2, 4): # Start from 2 since images_1 is always visible
+ previous_image_field = f'images_{i - 1}'
+
+ if getattr(self.context, previous_image_field, None):
+ visible_count = i
+
+ omitted_fields = [
+ f'{prefix}{j}'
+ for prefix in self.omitted_fields_prefix
+ for j in range(visible_count + 1, 4)
+ ]
+ print('Visible: ' + str(visible_count))
+
+ self.form_fields = self._old_form_fields.omit(*omitted_fields)
diff --git a/core/src/zeit/content/article/edit/configure.zcml b/core/src/zeit/content/article/edit/configure.zcml
index 53afc662a7..3d159ab4a0 100644
--- a/core/src/zeit/content/article/edit/configure.zcml
+++ b/core/src/zeit/content/article/edit/configure.zcml
@@ -462,6 +462,28 @@
/>
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/zeit/content/article/edit/image_grid.py b/core/src/zeit/content/article/edit/image_grid.py
new file mode 100644
index 0000000000..780dc345a0
--- /dev/null
+++ b/core/src/zeit/content/article/edit/image_grid.py
@@ -0,0 +1,48 @@
+import grokcore.component as grok
+import lxml
+
+from zeit.cms.content.property import ObjectPathAttributeProperty
+from zeit.cms.i18n import MessageFactory as _
+import zeit.cms.content.property
+import zeit.cms.content.reference
+import zeit.content.article.edit.block
+import zeit.content.article.edit.interfaces
+
+
+@grok.implementer(zeit.content.article.edit.interfaces.IImageGrid)
+class ImageGrid(zeit.content.article.edit.block.Block):
+ type = 'image_grid'
+
+ def __init__(self, context, xml):
+ # call base constructor
+ super(ImageGrid, self).__init__(context, xml)
+ for i in range(3):
+ if not self.xml.xpath('//group%s' % (i + 1)):
+ self.xml.append(lxml.etree.Element('group%s' % (i + 1)))
+
+ show_caption = ObjectPathAttributeProperty(
+ '.', 'show_caption', zeit.content.article.edit.interfaces.IImageRow['show_caption']
+ )
+ show_source = ObjectPathAttributeProperty(
+ '.', 'show_source', zeit.content.article.edit.interfaces.IImageRow['show_source']
+ )
+
+ # Images and display properties for first group
+ images_1 = zeit.cms.content.reference.MultiResource('.group1.image', 'image')
+ display_mode_1 = ObjectPathAttributeProperty('.group1', 'display_mode_1')
+ variant_name_1 = ObjectPathAttributeProperty('.group1', 'variant_name_1')
+
+ # Images and display properties for second group
+ images_2 = zeit.cms.content.reference.MultiResource('.group2.image', 'image')
+ display_mode_2 = ObjectPathAttributeProperty('.group2', 'display_mode_2')
+ variant_name_2 = ObjectPathAttributeProperty('.group2', 'variant_name_2')
+
+ # Images and display properties for third group
+ images_3 = zeit.cms.content.reference.MultiResource('.group3.image', 'image')
+ display_mode_3 = ObjectPathAttributeProperty('.group3', 'display_mode_3')
+ variant_name_3 = ObjectPathAttributeProperty('.group3', 'variant_name_3')
+
+
+class Factory(zeit.content.article.edit.block.BlockFactory):
+ produces = ImageGrid
+ title = _('Image Grid')
diff --git a/core/src/zeit/content/article/edit/image_row.py b/core/src/zeit/content/article/edit/image_row.py
new file mode 100644
index 0000000000..fd5e1b1174
--- /dev/null
+++ b/core/src/zeit/content/article/edit/image_row.py
@@ -0,0 +1,74 @@
+import grokcore.component as grok
+import lxml
+
+from zeit.cms.content.property import ObjectPathAttributeProperty
+from zeit.cms.i18n import MessageFactory as _
+from zeit.content.article.source import LEGACY_DISPLAY_MODE_SOURCE, LEGACY_VARIANT_NAME_SOURCE
+import zeit.cms.content.property
+import zeit.cms.content.reference
+import zeit.content.article.edit.block
+import zeit.content.article.edit.interfaces
+
+
+@grok.implementer(zeit.content.article.edit.interfaces.IImageRow)
+class ImageRow(zeit.content.article.edit.block.Block):
+ def __init__(self, context, xml):
+ # call base constructor
+ super(ImageRow, self).__init__(context, xml)
+ # set xml object to default template, if it has no body
+ if not self.xml.xpath('//body'):
+ self.xml.append(lxml.etree.Element('body'))
+
+ type = 'image_row'
+
+ show_caption = ObjectPathAttributeProperty(
+ '.', 'show_caption', zeit.content.article.edit.interfaces.IImageRow['show_caption']
+ )
+ show_source = ObjectPathAttributeProperty(
+ '.', 'show_source', zeit.content.article.edit.interfaces.IImageRow['show_source']
+ )
+ _display_mode = zeit.cms.content.property.ObjectPathAttributeProperty('.', 'display_mode')
+ _variant_name = zeit.cms.content.property.ObjectPathAttributeProperty('.', 'variant_name')
+ images = zeit.cms.content.reference.MultiResource('.body.image', 'image')
+
+ @property
+ def display_mode(self):
+ if self._display_mode is not None:
+ return self._display_mode
+ # backward compatibility by mapping old layout to display_mode
+ layout = self.xml.get('layout', None)
+ mapping = dict(list(LEGACY_DISPLAY_MODE_SOURCE(self)))
+ return mapping.get(
+ layout, zeit.content.article.edit.interfaces.IImage['display_mode'].default
+ )
+
+ @display_mode.setter
+ def display_mode(self, value):
+ self._display_mode = value
+
+ @property
+ def variant_name(self):
+ if self._variant_name is not None:
+ return self._variant_name
+ # backward compatibility by mapping old layout to display_mode
+ layout = self.xml.get('layout', None)
+ mapping = dict(list(LEGACY_VARIANT_NAME_SOURCE(self)))
+ return mapping.get(
+ layout, zeit.content.article.edit.interfaces.IImage['variant_name'].default
+ )
+
+ @variant_name.setter
+ def variant_name(self, value):
+ self._variant_name = value
+
+ # first_image = zeit.cms.content.reference.SingleResource('.image[1]', 'image')
+ # second_image = zeit.cms.content.reference.SingleResource('.image[2]', 'image')
+ # third_image = zeit.cms.content.reference.SingleResource('.image[3]', 'image')
+ # first_image = zeit.cms.content.reference.SingleResource('.image-1', 'image')
+ # second_image = zeit.cms.content.reference.SingleResource('.image-2', 'image')
+ # third_image = zeit.cms.content.reference.SingleResource('.image-3', 'image')
+
+
+class Factory(zeit.content.article.edit.block.BlockFactory):
+ produces = ImageRow
+ title = _('Image Row')
diff --git a/core/src/zeit/content/article/edit/interfaces.py b/core/src/zeit/content/article/edit/interfaces.py
index 51685733cb..bfc751f0bb 100644
--- a/core/src/zeit/content/article/edit/interfaces.py
+++ b/core/src/zeit/content/article/edit/interfaces.py
@@ -653,3 +653,93 @@ class IIngredientDice(IBlock):
"""
pass
+
+
+class IImageRow(IBlock):
+ show_caption = zope.schema.Bool(title=_('Show caption'), required=False, default=True)
+ show_source = zope.schema.Bool(title=_('Show source'), required=False, default=True)
+ images = zope.schema.Tuple(
+ title=_('Images'),
+ default=(),
+ max_length=3,
+ required=False,
+ value_type=zope.schema.Choice(source=zeit.content.image.interfaces.ImageGroupSource()),
+ )
+ display_mode = zope.schema.Choice(
+ title=_('Display Mode'),
+ source=zeit.content.article.source.IMAGE_DISPLAY_MODE_SOURCE,
+ default='column-width',
+ required=False,
+ )
+ # Currently need default for bw compat.
+ variant_name = zope.schema.Choice(
+ title=_('Variant Name'),
+ source=zeit.content.article.source.IMAGE_VARIANT_NAME_SOURCE,
+ default='wide',
+ required=False,
+ )
+
+
+class IImageGrid(IBlock):
+ show_caption = zope.schema.Bool(title=_('Show caption'), required=False, default=True)
+ show_source = zope.schema.Bool(title=_('Show source'), required=False, default=True)
+ images_1 = zope.schema.Tuple(
+ title=_('Images'),
+ default=(),
+ max_length=3,
+ required=False,
+ value_type=zope.schema.Choice(source=zeit.content.image.interfaces.ImageGroupSource()),
+ )
+ display_mode_1 = zope.schema.Choice(
+ title=_('Display Mode'),
+ source=zeit.content.article.source.IMAGE_DISPLAY_MODE_SOURCE,
+ default='column-width',
+ required=False,
+ )
+ # Currently need default for bw compat.
+ variant_name_1 = zope.schema.Choice(
+ title=_('Variant Name'),
+ source=zeit.content.article.source.IMAGE_VARIANT_NAME_SOURCE,
+ default='wide',
+ required=False,
+ )
+ images_2 = zope.schema.Tuple(
+ title=_('Images'),
+ default=(),
+ max_length=3,
+ required=False,
+ value_type=zope.schema.Choice(source=zeit.content.image.interfaces.ImageGroupSource()),
+ )
+ display_mode_2 = zope.schema.Choice(
+ title=_('Display Mode'),
+ source=zeit.content.article.source.IMAGE_DISPLAY_MODE_SOURCE,
+ default='column-width',
+ required=False,
+ )
+ # Currently need default for bw compat.
+ variant_name_2 = zope.schema.Choice(
+ title=_('Variant Name'),
+ source=zeit.content.article.source.IMAGE_VARIANT_NAME_SOURCE,
+ default='wide',
+ required=False,
+ )
+ images_3 = zope.schema.Tuple(
+ title=_('Images'),
+ default=(),
+ max_length=3,
+ required=False,
+ value_type=zope.schema.Choice(source=zeit.content.image.interfaces.ImageGroupSource()),
+ )
+ display_mode_3 = zope.schema.Choice(
+ title=_('Display Mode'),
+ source=zeit.content.article.source.IMAGE_DISPLAY_MODE_SOURCE,
+ default='column-width',
+ required=False,
+ )
+ # Currently need default for bw compat.
+ variant_name_3 = zope.schema.Choice(
+ title=_('Variant Name'),
+ source=zeit.content.article.source.IMAGE_VARIANT_NAME_SOURCE,
+ default='wide',
+ required=False,
+ )
diff --git a/core/src/zeit/content/article/edit/tests/modules.xml b/core/src/zeit/content/article/edit/tests/modules.xml
index de62ac7594..c3dd0c206d 100644
--- a/core/src/zeit/content/article/edit/tests/modules.xml
+++ b/core/src/zeit/content/article/edit/tests/modules.xml
@@ -29,4 +29,6 @@
Video
Ausgabe
ARD Video
+ Bilderreihe
+ Bilder-Grid