Added pictogram to milestones
authorThierry Florac <tflorac@ulthar.net>
Wed, 25 Sep 2019 15:11:13 +0200
changeset 1353 5940373ec65c
parent 1352 8242968d86b1
child 1354 1816388f2887
Added pictogram to milestones
src/pyams_content/component/paragraph/interfaces/milestone.py
src/pyams_content/component/paragraph/milestone.py
src/pyams_content/component/paragraph/zmi/milestone.py
--- a/src/pyams_content/component/paragraph/interfaces/milestone.py	Wed Sep 25 09:52:05 2019 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/milestone.py	Wed Sep 25 15:11:13 2019 +0200
@@ -10,8 +10,6 @@
 # FOR A PARTICULAR PURPOSE.
 #
 
-__docformat__ = 'restructuredtext'
-
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.container.constraints import containers, contains
 from zope.interface import Attribute, Interface
@@ -19,8 +17,12 @@
 
 from pyams_content.component.paragraph import CONTENT_PARAGRAPHS_VOCABULARY, IBaseParagraph
 from pyams_content.interfaces.container import IOrderedContainer
+from pyams_content.reference.pictograms.interfaces import SELECTED_PICTOGRAM_VOCABULARY
 from pyams_i18n.schema import I18nTextField, I18nTextLineField
 
+
+__docformat__ = 'restructuredtext'
+
 from pyams_content import _
 
 
@@ -42,9 +44,17 @@
                               required=True)
 
     label = I18nTextField(title=_("Associated label"),
-                          description=_("The way this label will be rendered depends on presentation template"),
+                          description=_("The way this label will be rendered depends on "
+                                        "presentation template"),
                           required=False)
 
+    pictogram_name = Choice(title=_("Pictogram"),
+                            description=_("Name of the pictogram associated with this milestone"),
+                            required=False,
+                            vocabulary=SELECTED_PICTOGRAM_VOCABULARY)
+
+    pictogram = Attribute("Selected pictogram object")
+
     anchor = Choice(title=_("Anchor"),
                     description=_("Paragraph to which this milestone should lead"),
                     vocabulary=CONTENT_PARAGRAPHS_VOCABULARY,
--- a/src/pyams_content/component/paragraph/milestone.py	Wed Sep 25 09:52:05 2019 +0200
+++ b/src/pyams_content/component/paragraph/milestone.py	Wed Sep 25 15:11:13 2019 +0200
@@ -10,38 +10,44 @@
 # FOR A PARTICULAR PURPOSE.
 #
 
-__docformat__ = 'restructuredtext'
-
 from persistent import Persistent
 from pyramid.events import subscriber
 from zope.container.contained import Contained
 from zope.container.ordered import OrderedContainer
 from zope.interface import implementer
 from zope.lifecycleevent import ObjectModifiedEvent
-from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent, IObjectRemovedEvent
+from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent, \
+    IObjectRemovedEvent
 from zope.location import locate
 from zope.location.interfaces import ISublocations
 from zope.schema.fieldproperty import FieldProperty
 from zope.traversing.interfaces import ITraversable
 
 from pyams_catalog.utils import index_object
-from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
-from pyams_content.component.paragraph.interfaces import IParagraphContainer, IParagraphContainerTarget, \
-    IParagraphFactory
-from pyams_content.component.paragraph.interfaces.milestone import IMilestone, IMilestoneContainer, \
-    IMilestoneContainerTarget, IMilestoneParagraph, MILESTONE_CONTAINER_KEY, MILESTONE_PARAGRAPH_NAME, \
-    MILESTONE_PARAGRAPH_RENDERERS, MILESTONE_PARAGRAPH_TYPE
+from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, \
+    BaseParagraphFactory
+from pyams_content.component.paragraph.interfaces import IParagraphContainer, \
+    IParagraphContainerTarget, IParagraphFactory
+from pyams_content.component.paragraph.interfaces.milestone import IMilestone, \
+    IMilestoneContainer, IMilestoneContainerTarget, IMilestoneParagraph, MILESTONE_CONTAINER_KEY, \
+    MILESTONE_PARAGRAPH_NAME, MILESTONE_PARAGRAPH_RENDERERS, MILESTONE_PARAGRAPH_TYPE
 from pyams_content.features.checker import BaseContentChecker
-from pyams_content.features.checker.interfaces import ERROR_VALUE, IContentChecker, MISSING_LANG_VALUE, MISSING_VALUE
+from pyams_content.features.checker.interfaces import ERROR_VALUE, IContentChecker, \
+    MISSING_LANG_VALUE, MISSING_VALUE
 from pyams_content.features.renderer import RenderersVocabulary
+from pyams_content.reference.pictograms import IPictogramTable
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
 from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
 from pyams_utils.factory import factory_config
-from pyams_utils.registry import get_current_registry, get_utility, utility_config
+from pyams_utils.registry import get_current_registry, get_utility, utility_config, query_utility
 from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
+from pyams_utils.zodb import volatile_property
+
+
+__docformat__ = 'restructuredtext'
 
 from pyams_content import _
 
@@ -57,9 +63,26 @@
     visible = FieldProperty(IMilestone['visible'])
     title = FieldProperty(IMilestone['title'])
     label = FieldProperty(IMilestone['label'])
+    _pictogram_name = FieldProperty(IMilestone['pictogram_name'])
     anchor = FieldProperty(IMilestone['anchor'])
 
     @property
+    def pictogram_name(self):
+        return self._pictogram_name
+
+    @pictogram_name.setter
+    def pictogram_name(self, value):
+        if value != self._pictogram_name:
+            self._pictogram_name = value
+            del self.pictogram
+
+    @volatile_property
+    def pictogram(self):
+        table = query_utility(IPictogramTable)
+        if table is not None:
+            return table.get(self._pictogram_name)
+
+    @property
     def target(self):
         container = get_parent(self, IParagraphContainer)
         if container is not None:
--- a/src/pyams_content/component/paragraph/zmi/milestone.py	Wed Sep 25 09:52:05 2019 +0200
+++ b/src/pyams_content/component/paragraph/zmi/milestone.py	Wed Sep 25 15:11:13 2019 +0200
@@ -10,8 +10,6 @@
 # FOR A PARTICULAR PURPOSE.
 #
 
-__docformat__ = 'restructuredtext'
-
 import json
 
 from pyramid.decorator import reify
@@ -23,15 +21,18 @@
 from zope.interface import Interface, implementer
 from zope.schema.vocabulary import getVocabularyRegistry
 
-from pyams_content.component.paragraph.interfaces import CONTENT_PARAGRAPHS_VOCABULARY, IParagraphContainerTarget
+from pyams_content.component.paragraph.interfaces import CONTENT_PARAGRAPHS_VOCABULARY, \
+    IParagraphContainerTarget
 from pyams_content.component.paragraph.interfaces.milestone import IMilestone, IMilestoneContainer, \
     IMilestoneContainerTarget, IMilestoneParagraph, MILESTONE_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.milestone import Milestone, MilestoneParagraph
-from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
-    BaseParagraphAddForm, BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons
+from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, \
+    BaseParagraphAJAXEditForm, BaseParagraphAddForm, BaseParagraphAddMenu, \
+    BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons
 from pyams_content.component.paragraph.zmi import IParagraphContainerView
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
+from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget
 from pyams_content.shared.common import IWfSharedContent
 from pyams_form.form import AJAXAddForm, ajax_config
 from pyams_form.interfaces.form import IInnerForm, IInnerSubForm
@@ -40,11 +41,12 @@
 from pyams_i18n.interfaces import II18n
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.container import switch_element_visibility
-from pyams_skin.event import get_json_switched_table_refresh_event, get_json_table_row_refresh_event, \
-    get_json_widget_refresh_event
+from pyams_skin.event import get_json_switched_table_refresh_event, \
+    get_json_table_row_refresh_event, get_json_widget_refresh_event
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu, IWidgetTitleViewletManager
 from pyams_skin.layer import IPyAMSLayer
-from pyams_skin.table import BaseTable, I18nColumn, SorterColumn, TrashColumn, VisibilitySwitcherColumn
+from pyams_skin.table import BaseTable, I18nColumn, SorterColumn, TrashColumn, \
+    VisibilitySwitcherColumn
 from pyams_skin.viewlet.toolbar import ToolbarAction
 from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config
 from pyams_utils.traversing import get_parent
@@ -53,6 +55,9 @@
 from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
 from pyams_zmi.zmi.table import InnerTableView
 
+
+__docformat__ = 'restructuredtext'
+
 from pyams_content import _
 
 
@@ -64,8 +69,9 @@
     """Milestones parent form marker interface"""
 
 
-@viewlet_config(name='add-milestone-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
-                layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600)
+@viewlet_config(name='add-milestone-paragraph.menu', context=IParagraphContainerTarget,
+                view=IParagraphContainerView, layer=IPyAMSLayer, manager=IToolbarAddingMenu,
+                weight=600)
 class MilestoneParagraphAddMenu(BaseParagraphAddMenu):
     """Milestone paragraph add menu"""
 
@@ -75,9 +81,11 @@
     paragraph_type = MILESTONE_PARAGRAPH_TYPE
 
 
-@pagelet_config(name='add-milestone-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+@pagelet_config(name='add-milestone-paragraph.html', context=IParagraphContainerTarget,
+                layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
-@ajax_config(name='add-milestone-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+@ajax_config(name='add-milestone-paragraph.json', context=IParagraphContainerTarget,
+             layer=IPyAMSLayer,
              base=BaseParagraphAJAXAddForm)
 class MilestoneParagraphAddForm(BaseParagraphAddForm):
     """Milestone paragraph add form"""
@@ -117,7 +125,8 @@
         return output
 
 
-@adapter_config(context=(IMilestoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+@adapter_config(context=(IMilestoneParagraph, IPyAMSLayer),
+                provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IMilestoneParagraph, layer=IPyAMSLayer,
              base=BaseParagraphAJAXEditForm)
 @implementer(IInnerForm, IMilestonesParentForm)
@@ -178,7 +187,8 @@
         return list(super(MilestonesTable, self).values)
 
 
-@adapter_config(context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable), provides=IValues)
+@adapter_config(context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable),
+                provides=IValues)
 class MilestonesTableValuesAdapter(ContextRequestViewAdapter):
     """Milestones table values adapter"""
 
@@ -187,13 +197,16 @@
         return IMilestoneContainer(self.context).values()
 
 
-@adapter_config(name='sorter', context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable), provides=IColumn)
+@adapter_config(name='sorter',
+                context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable),
+                provides=IColumn)
 class MilestonesTableSorterColumn(ProtectedFormObjectMixin, SorterColumn):
     """Milestones table sorter column"""
 
 
-@view_config(name='set-milestones-order.json', context=IMilestoneContainer, request_type=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+@view_config(name='set-milestones-order.json', context=IMilestoneContainer,
+             request_type=IPyAMSLayer, permission=MANAGE_CONTENT_PERMISSION,
+             renderer='json', xhr=True)
 def set_milestones_order(request):
     """Update milestones order"""
     order = list(map(str, json.loads(request.params.get('names'))))
@@ -201,20 +214,24 @@
     return {'status': 'success'}
 
 
-@adapter_config(name='show-hide', context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable),
+@adapter_config(name='show-hide',
+                context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable),
                 provides=IColumn)
 class MilestonesTableShowHideColumn(ProtectedFormObjectMixin, VisibilitySwitcherColumn):
     """Milestones container visibility switcher column"""
 
 
-@view_config(name='switch-milestone-visibility.json', context=IMilestoneContainer, request_type=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+@view_config(name='switch-milestone-visibility.json', context=IMilestoneContainer,
+             request_type=IPyAMSLayer, permission=MANAGE_CONTENT_PERMISSION,
+             renderer='json', xhr=True)
 def switch_milestone_visibility(request):
     """Set milestone visibility"""
     return switch_element_visibility(request, IMilestoneContainer)
 
 
-@adapter_config(name='name', context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable), provides=IColumn)
+@adapter_config(name='name',
+                context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable),
+                provides=IColumn)
 class MilestonesTableNameColumn(I18nColumn, I18nAttrColumn):
     """Milestones table name column"""
 
@@ -226,7 +243,9 @@
         return super(MilestonesTableNameColumn, self).getValue(obj) or '--'
 
 
-@adapter_config(name='info', context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable), provides=IColumn)
+@adapter_config(name='info',
+                context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable),
+                provides=IColumn)
 class MilestonesTableInfoColumn(I18nColumn, I18nAttrColumn):
     """Milestones table information column"""
 
@@ -238,7 +257,8 @@
         return super(MilestonesTableInfoColumn, self).getValue(obj) or '--'
 
 
-@adapter_config(name='anchor', context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable), provides=IColumn)
+@adapter_config(name='anchor', context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable),
+                provides=IColumn)
 class MilestonesTableAnchorColumn(I18nColumn, GetAttrColumn):
     """Milestones table anchor column"""
 
@@ -260,12 +280,14 @@
             return self.request.localizer.translate(_("(missing paragraph)")).format(obj.anchor)
 
 
-@adapter_config(name='trash', context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable), provides=IColumn)
+@adapter_config(name='trash', context=(IMilestoneContainerTarget, IPyAMSLayer, MilestonesTable),
+                provides=IColumn)
 class MilestonesTableTrashColumn(ProtectedFormObjectMixin, TrashColumn):
     """Milestones table trash column"""
 
 
-@adapter_config(name='milestones', context=(IMilestoneContainerTarget, IPyAMSLayer, IMilestonesParentForm),
+@adapter_config(name='milestones',
+                context=(IMilestoneContainerTarget, IPyAMSLayer, IMilestonesParentForm),
                 provides=IInnerSubForm)
 @implementer(IMilestonesView)
 class MilestonesView(InnerTableView):
@@ -281,8 +303,9 @@
 # Milestones forms
 #
 
-@viewlet_config(name='add-milestone.action', context=IMilestoneContainerTarget, layer=IPyAMSLayer, view=IMilestonesView,
-                manager=IWidgetTitleViewletManager, permission=MANAGE_CONTENT_PERMISSION, weight=1)
+@viewlet_config(name='add-milestone.action', context=IMilestoneContainerTarget, layer=IPyAMSLayer,
+                view=IMilestonesView, manager=IWidgetTitleViewletManager,
+                permission=MANAGE_CONTENT_PERMISSION, weight=1)
 class MilestoneAddAction(ToolbarAction):
     """Milestone add action"""
 
@@ -303,6 +326,8 @@
     icon_css_class = 'fa fa-fw fa-arrow-h'
 
     fields = field.Fields(IMilestone).omit('__parent__', '__name__', 'visible')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def updateWidgets(self, prefix=None):
@@ -320,11 +345,13 @@
         return {
             'status': 'success',
             'message': self.request.localizer.translate(_("Milestone was correctly added")),
-            'events': [get_json_switched_table_refresh_event(self.context, self.request, MilestonesTable)]
+            'events': [
+                get_json_switched_table_refresh_event(self.context, self.request, MilestonesTable)]
         }
 
 
-@pagelet_config(name='properties.html', context=IMilestone, layer=IPyAMSLayer, permission=MANAGE_CONTENT_PERMISSION)
+@pagelet_config(name='properties.html', context=IMilestone, layer=IPyAMSLayer,
+                permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='properties.json', context=IMilestone, layer=IPyAMSLayer)
 class MilestonePropertiesEditForm(AdminDialogEditForm):
     """Milestone properties edit form"""
@@ -335,6 +362,8 @@
     icon_css_class = 'fa fa-fw fa-arrows-h'
 
     fields = field.Fields(IMilestone).omit('__parent__', '__name__', 'visible')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def updateWidgets(self, prefix=None):
@@ -346,6 +375,7 @@
         output = super(self.__class__, self).get_ajax_output(changes)
         if changes:
             target = get_parent(self.context, IMilestoneContainerTarget)
-            output.setdefault('events', []).append(get_json_table_row_refresh_event(target, self.request,
-                                                                                    MilestonesTable, self.context))
+            output.setdefault('events', []).append(
+                get_json_table_row_refresh_event(target, self.request,
+                                                 MilestonesTable, self.context))
         return output