diff -r 000000000000 -r b2be9a32f3fc src/source/howto-paragraph.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/source/howto-paragraph.rst Thu Dec 06 08:24:10 2018 +0100 @@ -0,0 +1,345 @@ +.. _paragraphhowto: + + +How to create a Paragraph type? +=============================== + +Paragraphs or Blocks are components that contain elements/fields and provide one or many renderer methods to compose +the front office website + + +Create a Paragraph +"""""""""""""""""" + +In this example we will create a contact paragraph to display at the user, who to contact. + +1) Interface +------------ + +At first we create a new paragraph interface. + +.. code-block:: python + + CONTACT_PHONE_PARAGRAPH_TYPE = 'PhoneContact' + CONTACT_PHONE_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.contact.phone.renderers' + + + class IContactPhoneParagraph(IBaseParagraph): + """Contact with phone number paragraph interface""" + + name = TextLine(title=_("Contact identity"), + description=_("Name of the contact"), + required=True) + + photo = ImageField(title=_("Photo"), + description=_("Use 'browse' button to select contact picture"), + required=False) + + phone = TextLine(title=_("Phone Number"), + description=_("Name of the contact", required=False)) + + renderer = Choice(title=_("Contact template"), + description=_("Presentation template used for this contact"), + vocabulary=CONTACT_PHONE_PARAGRAPH_RENDERERS, + default='default') + + + + +2) Implement the interface +-------------------------- + +An implementation of the interface + +.. code-block:: python + + @implementer(IContactPhoneParagraph) + @factory_config(provided=IContactPhoneParagraph) + class ContactPhoneParagraph(BaseParagraph): + """Contact paragraph""" + + + icon_class = 'fa-phone' + icon_hint = _("Phone number card") + + name = FieldProperty(IContactPhoneParagraph['name']) + _photo = FileProperty(IContactPhoneParagraph['photo']) + + renderer = FieldProperty(IContactParagraph['renderer']) + + @property + def photo(self): + return self._photo + + @photo.setter + def photo(self, value): + self._photo = value + if IImage.providedBy(self._photo): + alsoProvides(self._photo, IResponsiveImage) + + +3) Renderers Vocabulary +----------------------- + +The list of rendered available for a paragraph is build automatically and is based on adapters that provide this interface + +.. code-block:: python + + @vocabulary_config(name=CONTACT_PARAGRAPH_RENDERERS) + class ContactParagraphRendererVocabulary(RenderersVocabulary): + """Contact Phone paragraph renderers vocabulary""" + + content_interface = IContactPhoneParagraph + + +.. seealso:: + + :ref:`rendererhowto` + + +Paragraph in the ZMI +"""""""""""""""""""" + + +To display and manage the new paragraph in the ZMI, you should create this associated forms + +1) Paragraph factory +-------------------- + +To create a new element instance inside the zodb, we need a container to this object. PyAMS provide +`IParagraphContainerTarget`, it's the default container for all paragraphs. We could use this container interface +as context to create a new paragraph. + + +Declaration of the **factory** of `ContactPhoneParagraph` + +.. code-block:: python + + @utility_config(name=CONTACT_PHONE_PARAGRAPH_TYPE, provides=IParagraphFactory) + class ContactPhoneParagraphFactory(BaseParagraphFactory): + """Contact paragraph factory""" + + name = _("Contact Phone card") + content_type = ContactPhoneParagraph + secondary_menu = True + + +Definition of a form to create a new ContactPhone instance + +.. code-block:: python + + from pyams_form.form import ajax_config + + + @pagelet_config(name='add-contact-phone-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer, + permission=MANAGE_CONTENT_PERMISSION) + @ajax_config(name='add-contact-phone-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer, + base=BaseParagraphAJAXAddForm) + class ContactPhoneParagraphAddForm(AdminDialogAddForm): + """Contact phone paragraph add form""" + + legend = _("Add new contact phone card") + dialog_class = 'modal-large' + icon_css_class = 'fa fa-fw fa-phone' + + fields = field.Fields(IContactPhoneParagraph).omit('__parent__', '__name__', 'visible') + edit_permission = MANAGE_CONTENT_PERMISSION + + def create(self, data): + return ContactPhoneParagraph() + + def add(self, object): + IParagraphContainer(self.context).append(object) + + +2) Create the Paragraph addform button in the menu +-------------------------------------------------- + +We have created a new form and we want add a quick access button to create a new paragraph + +.. code-block:: python + + @viewlet_config(name='add-contact-phone-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView, + layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600) + class ContactPhoneParagraphAddMenu(BaseParagraphAddMenu): + """Contact paragraph add menu""" + + label = _("Contact card...") + label_css_class = 'fa fa-fw fa-id-card-o' + url = 'add-contact-paragraph.html' + paragraph_type = CONTACT_PARAGRAPH_TYPE + + + +3) Create Edit inner form +------------------------- + +.. code-block:: python + + @adapter_config(context=(IContactPhoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor) + permission=MANAGE_CONTENT_PERMISSION) + @ajax_config(name='inner-properties.json', context=IContactPhoneParagraph, layer=IPyAMSLayer, + base=BaseParagraphAJAXEditForm) + @implementer(IInnerForm) + class ContactPhoneParagraphInnerEditForm(ContactPhoneParagraphPropertiesEditForm): + """Contact paragraph inner edit form""" + + legend = None + + @property + def buttons(self): + if self.mode == INPUT_MODE: + return button.Buttons(IParagraphEditFormButtons) + else: + return button.Buttons() + + def get_ajax_output(self, changes): + output = super(ContactParagraphInnerAJAXEditForm, self).get_ajax_output(changes) + updated = changes.get(IBaseParagraph, ()) + if 'title' in updated: + output.setdefault('events', []).append(get_json_paragraph_refresh_event(self.context, self.request)) + updated = changes.get(IContactParagraph, ()) + if ('photo' in updated) or ('renderer' in updated): + # we have to commit transaction to be able to handle blobs... + if 'photo' in updated: + ITransactionManager(self.context).get().commit() + output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request, + ContactParagraphInnerEditForm)) + return output + + +4) Create an Edit modal form +----------------------------- + +This form is used inside modals popup + + +.. code-block:: python + + @ajax_config(name='properties.json', context=IContactPhoneParagraph, request_type=IPyAMSLayer, + permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True) + @pagelet_config(name='properties.html', context=IContactParagraph, layer=IPyAMSLayer, + permission=MANAGE_CONTENT_PERMISSION) + class ContactPhoneParagraphPropertiesEditForm(BaseParagraphPropertiesEditForm): + """Contact phone paragraph properties edit form""" + + prefix = 'contact_properties.' + + legend = _("Edit contact card properties") + icon_css_class = 'fa fa-fw fa-id-card-o' + + fields = field.Fields(IContactParagraph).omit('__parent__', '__name__', 'visible') + fields['renderer'].widgetFactory = RendererFieldWidget + + edit_permission = MANAGE_CONTENT_PERMISSION + + +.. note:: + + Select the new content block types in ZMI to make it available in tools + + .. image:: _static/select_paragraph.png + + +How to associate links or Illustrations to a Paragraph ? +======================================================== + +Adding the following marker interface, we add new behavior to the Paragraph `ContactPhoneParagraph` . + + +Paragraph advanced +"""""""""""""""""" + +You can attach Associated files, links or illustration and manage them directly through the rubric `Links and Attachments`. + +.. image:: _static/select_links_n_attachment.png + + +1) Paragraph with Links and Attachements +---------------------------------------- + +To "activate" this features the paragrath object must to implement specific interface + + +.. code-block:: python + + @implementer(IContactPhoneParagraph, IIllustrationTarget,ILinkContainerTarget,IExtFileContainerTarget)) + @factory_config(provided=IContactPhoneParagraph) + class ContactPhoneParagraph(BaseParagraph): + """Contact paragraph""" + ... + + +These interfaces will allow to link other data to the paragraph. + +**Marker interfaces:** + + +--------------------------------+ + |:py:class:`IIllustrationTarget` | + +===================+============+ + | | | + +-------------------+------------+ + + +---------------------------------+ + |:py:class:`ILinkContainerTarget` | + +==============+==================+ + | | Add internal link| + | +------------------+ + | | Add external link| + | +------------------+ + | | Add mailto link | + +--------------+------------------+ + + +------------------------------------+ + |:py:class:`IExtFileContainerTarget` | + +================+===================+ + | | Add external file | + | +-------------------+ + | | Add image | + | +-------------------+ + | | Add video | + | +-------------------+ + | | Add audio file | + +----------------+-------------------+ + + +**ZMI overview:** + +.. image:: _static/select_add_links.png + + + +2) Add link and association form +-------------------------------- + +You can add form to manage links and attachments directly in paragraph form to do that your form must implement +``IPropertiesEditForm`` and/or ``IAssociationParentForm`` + + +.. code-block:: python + + @adapter_config(context=(IContactPhoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor) + @implementer(IInnerForm, IPropertiesEditForm, IAssociationParentForm) + class ContactPhoneParagraphInnerEditForm(ContactPhoneParagraphPropertiesEditForm): + """Contact paragraph inner edit form""" + + legend = None + ajax_handler = 'inner-properties.json' + + +**Marker interfaces:** + ++-----------------------------------+ +|:py:class:`IPropertiesEditForm` | ++=========+=========================+ +| | Add Illustration form | ++---------+-------------------------+ + ++-----------------------------------+ +|:py:class:`IAssociationParentForm` | ++===========+=======================+ +| | Add Association form | ++-----------+-----------------------+ + +.. image:: _static/associations_form.png +