How to create a clone of "News Item" type

I know I did this before, but I lost the docs on it. Could someone give me the steps to create a clone of a "News Item" type and name it something else? I know how to do it over the web, but not programmatically.

This may sound counter intuitive, but to create a clone of a news item "programmatically", starting through the web (TTW) is still the easiest way. Jump to step 4 where we export our TTW work.

Step 1 - Go to Dexterity Content Types

Site Setup > Content > Dexterity Content Types

Step 2 - Clone the News Item

Select News Item and click Clone

Step 3 - Name your new content type

We'll name our new content type "Article"

An "Article" will be a clone of "News Item", it should be possible to create new "Articles" which behave just like News Items.

Step 4 - Export the generic setup type profile

Select "Article" (or whatever you named your clone) and click Export Type Profiles.

Step 5 - Place the profile in your profiles/default directory of your custom add-on

The resulting profile will include a types folder and a types.xml file.

Both of these should be moved to the profiles/default directory of your add-on.
If you already have a types.xml file you'll want to move the key directives from the exported types.xml into your existing types.xml.

Here's what the types.xml file looks like

<?xml version="1.0"?>
<object name="portal_types" meta_type="Plone Types Tool">
 <property name="title">Controls the available content types in your portal</property>
 <object name="article" meta_type="Dexterity FTI"/>
 <!-- -*- extra stuff goes here -*- -->
</object>

The important line, if you're cutting and pasting into an existing types.xml file would be <object name="article" meta_type="Dexterity FTI"/>

You also want to take a look inside the types folder. It will contain an article.xml file.
This is where the "cloning" really happens:

<?xml version="1.0"?>
<object name="article" meta_type="Dexterity FTI" i18n:domain="plone"
   xmlns:i18n="http://xml.zope.org/namespaces/i18n">
 <property name="title" i18n:translate="">Article</property>
 <property name="description" i18n:translate=""></property>
 <property name="icon_expr"></property>
 <property name="factory">article</property>
 <property name="add_view_expr">string:${folder_url}/++add++article</property>
 <property name="link_target"></property>
 <property name="immediate_view">view</property>
 <property name="global_allow">True</property>
 <property name="filter_content_types">True</property>
 <property name="allowed_content_types"/>
 <property name="allow_discussion">False</property>
 <property name="default_view">newsitem_view</property>
 <property name="view_methods">
  <element value="newsitem_view"/>
 </property>
 <property name="default_view_fallback">False</property>
 <property name="add_permission">plone.app.contenttypes.addNewsItem</property>
 <property name="klass">plone.app.contenttypes.content.NewsItem</property>
 <property name="behaviors">
  <element value="plone.app.dexterity.behaviors.metadata.IDublinCore"/>
  <element value="plone.app.contenttypes.behaviors.richtext.IRichText"/>
  <element value="plone.app.content.interfaces.INameFromTitle"/>
  <element value="plone.app.dexterity.behaviors.discussion.IAllowDiscussion"/>
  <element value="plone.app.dexterity.behaviors.id.IShortName"/>
  <element
     value="plone.app.dexterity.behaviors.exclfromnav.IExcludeFromNavigation"/>
  <element value="plone.app.relationfield.behavior.IRelatedItems"/>
  <element value="plone.app.contenttypes.behaviors.leadimage.ILeadImage"/>
  <element value="plone.app.versioningbehavior.behaviors.IVersionable"/>
  <element value="plone.app.lockingbehavior.behaviors.ILocking"/>
 </property>
 <property name="schema"></property>
 <property name="model_source"></property>
 <property
    name="model_file">plone.app.contenttypes.schema:news_item.xml</property>
 <property name="schema_policy">dexterity</property>
 <alias from="(Default)" to="(dynamic view)"/>
 <alias from="edit" to="@@edit"/>
 <alias from="sharing" to="@@sharing"/>
 <alias from="view" to="(selected layout)"/>
 <action title="View" action_id="view" category="object" condition_expr=""
    description="" icon_expr="" link_target="" url_expr="string:${object_url}"
    visible="True">
  <permission value="View"/>
 </action>
 <action title="Edit" action_id="edit" category="object" condition_expr=""
    description="" icon_expr="" link_target=""
    url_expr="string:${object_url}/edit" visible="True">
  <permission value="Modify portal content"/>
 </action>
</object>

I think the important thing here is reference to the news_item.xml model file <property name="model_file">plone.app.contenttypes.schema:news_item.xml</property>

4 Likes

Thank you so much for the in depth explanation. I see that I can do this through the web by importing the dexterity type. However, I want to add this to an installable product and the document I found (https://docs.plone.org/external/plone.app.dexterity/docs/model-driven-types.html) says that I need a dexterity_type.py file basically to load the xml. I have one quesiton... where would I put this file in relation to the actual xml it is referencing? and how would my product know to execute it?

We get to do a lot of "cheating" because we're cloning a content type, the example that I gave does not require such a .py file, instead we make use of an existing one at plone.app.contenttypes > content.py.
The documentation on model driven types is a complete "From scratch" approach which does not involve any cheating/cloning. That's really all you need to know to proceed.

Key concepts for cloning of default Plone types

Everything that I mention here is done for you by using the technique mentioned earlier. This will help you to understand what is going on in the background but is not needed to get the task done.

  • Reuse the cloned item's .py file (located under plone.app.contenttypes content.py)
  • Many fields are provided by dexterity behaviours

Reuse the cloned item's .py file (located under plone.app.contenttypes content.py)

In this case, because it is a clone of the News Item you don't need to create an {article}.py file. Instead you reuse the content.py class provided by plone.app.contenttypes.

This is the line where we call plone.app.contenttypess, get the content.py file and access the NewsItem class:

<property name="klass">plone.app.contenttypes.content.NewsItem</property>

Many fields are provided by dexterity behaviours

In the case of a News Item, All the fields of a News Item are provided by dexterity behaviours. Here are the important lines.

<property name="behaviors">
  <element value="plone.app.dexterity.behaviors.metadata.IDublinCore"/>
  <element value="plone.app.contenttypes.behaviors.richtext.IRichText"/>
  <element value="plone.app.content.interfaces.INameFromTitle"/>
  <element value="plone.app.dexterity.behaviors.discussion.IAllowDiscussion"/>
  <element value="plone.app.dexterity.behaviors.id.IShortName"/>
  <element
     value="plone.app.dexterity.behaviors.exclfromnav.IExcludeFromNavigation"/>
  <element value="plone.app.relationfield.behavior.IRelatedItems"/>
  <element value="plone.app.contenttypes.behaviors.leadimage.ILeadImage"/>
  <element value="plone.app.versioningbehavior.behaviors.IVersionable"/>
  <element value="plone.app.lockingbehavior.behaviors.ILocking"/>
 </property>

To satisfy your curiousity, here's the News Item model file with an empty schema. You don't need a schema because, as we mentioned above, the fields are implemented using behaviours.

After that everything is wired together using GenericSetup's xml profiles.

Further reading

The default Plone content types in Plone 5 are dexterity based. I recommend that you dig your way through the code of plone.app.contenttypes. It should help you to better understand what is happening.

If I were to start you off on a guided tour of the machinery involved in the default News Item I'd start you off with the NewsItem class from plone.app.contenttypes:

Then I'd have you look at the News_Item.xml file (looks alot like the article.xml file doesn't it?) :).
Things to pay attention to...

  • Notice that all the capabilities of the News Item come from the defined behaviours.
  • Also notice that the News_item.xml is part of the GenericSetup profile so the path to it is profiles/default/types/News_Item.xml (your cloned content type information would similarly be placed in your profiles/default folder).
  • (The News_Item.xml references an empty model file, If you choose to read the entire file you'll see. I'll leave that as an exercise for the reader).

If you're still curious the rest is up to you.