Creating new Paragraph (or blocks)¶
Paragraphs or Blocks are components that contain elements/fields and provide one or many renderer methods to compose the front office website
How to create a Paragraph type?¶
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.
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
@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
@vocabulary_config(name=CONTACT_PARAGRAPH_RENDERERS)
class ContactParagraphRendererVocabulary(RenderersVocabulary):
"""Contact Phone paragraph renderers vocabulary"""
content_interface = IContactPhoneParagraph
See also
rendererhowto
Paragraph in the ZMI¶
1) Container Paragraph¶
For example IParagraphContainerTarget
, it’s a marker interface for paragraph containers.
To create a new element instance inside the zodb, we need a container to this object. PyAMS provide IParagraphContainerTarget, it’s the default marker interface container for all paragraphs. We could use this interface as context to declare a new pagelet.
Definition of a Contact Phone form to create a new ContactPhone object
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)
To display and manage the new paragraph in the ZMI, you should create this associated forms
3) Create Edit inner form¶
@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
@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
Add the new paragraph in “content block types” to make it available in tools
5) Paragraph Factory¶
If you want to create automatically a paragraph object for a shared content you must define a factory
Declaration of the factory of ContactPhoneParagraph
@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
When the contributors will create new shared content with predefined paragraphs, it’s the container factory class that will be used to create the paragraph object.
How to associate links or Illustrations to a Paragraph ?¶
Adding the following marker interface, we add new behavior to the Paragraph ContactPhoneParagraph . You can attach Associated files, links or illustration and manage them directly through the rubric Links and Attachments.
1) Paragraph with Links and Attachements¶
To “activate” this features the paragrath object must to implement specific interface
@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:
IIllustrationTarget
ILinkContainerTarget
Add internal link Add external link Add mailto link
IExtFileContainerTarget
Add external file Add image Add video Add audio file
ZMI overview:
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
@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:
IPropertiesEditForm |
|
---|---|
Add Illustration form |
IAssociationParentForm |
|
---|---|
Add Association form |