Adding custom Dexterity Type via code, possible without addon?

Hi, new to the Plone community so I apologize if there is an obvious answer to this question.

I am attempting to create a new Dexterity content type through xml/python, following the training information mastering plone 5, export_code (can't paste a link or I would)

It seems like it should be pretty straight forward, I've added the types.xml file in (from the project root)
collective.example/src/collective/example/profiles/default/types.xml

<?xml version="1.0"?>
<object name="portal_types" meta_type="Plone Types Tool">
 <property name="title">Controls the available contenttypes in your portal</property>
 <object name="talk" meta_type="Dexterity FTI"/>
 <!-- -*- more types can be added here -*- -->
</object>

and then added the talk.xml file in
collective.example/src/collective/example/profiles/default/types/talk.xml

<?xml version="1.0"?>
  <object name="talk" meta_type="Dexterity FTI" i18n:domain="plone"
     xmlns:i18n="http://xml.zope.org/namespaces/i18n">
   <property name="title" i18n:translate="">Talk</property>
   <property name="description" i18n:translate="">None</property>
   <property name="icon_expr">string:${portal_url}/document_icon.png</property>
   <property name="factory">talk</property>
   <property name="add_view_expr">string:${folder_url}/++add++talk</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">view</property>
   <property name="view_methods">
    <element value="view"/>
   </property>
   <property name="default_view_fallback">False</property>
   <property name="add_permission">cmf.AddPortalContent</property>
   <property name="klass">plone.dexterity.content.Container</property>
   <property name="behaviors">
    <element value="plone.dublincore"/>
    <element value="plone.namefromtitle"/>
   </property>
   <property name="schema"></property>
   <property
      name="model_source">&lt;?xml version='1.0' encoding='utf8'?&gt;
&lt;model xmlns:lingua="http://namespaces.plone.org/supermodel/lingua" xmlns:users="http://namespaces.plone.org/supermodel/users" xmlns:form="http://namespaces.plone.org/supermodel/form" xmlns:i18n="http://xml.zope.org/namespaces/i18n" xmlns:security="http://namespaces.plone.org/supermodel/security" xmlns:marshal="http://namespaces.plone.org/supermodel/marshal" xmlns="http://namespaces.plone.org/supermodel/schema"&gt;
      &lt;schema&gt;
        &lt;field name="type_of_talk" type="zope.schema.Choice"&gt;
          &lt;description/&gt;
          &lt;title&gt;Type of talk&lt;/title&gt;
          &lt;values&gt;
            &lt;element&gt;Talk&lt;/element&gt;
            &lt;element&gt;Training&lt;/element&gt;
            &lt;element&gt;Keynote&lt;/element&gt;
          &lt;/values&gt;
        &lt;/field&gt;
        &lt;field name="details" type="plone.app.textfield.RichText"&gt;
          &lt;description&gt;Add a short description of the talk (max. 2000 characters)&lt;/description&gt;
          &lt;max_length&gt;2000&lt;/max_length&gt;
          &lt;title&gt;Details&lt;/title&gt;
        &lt;/field&gt;
        &lt;field name="audience" type="zope.schema.Set"&gt;
          &lt;description/&gt;
          &lt;title&gt;Audience&lt;/title&gt;
          &lt;value_type type="zope.schema.Choice"&gt;
            &lt;values&gt;
              &lt;element&gt;Beginner&lt;/element&gt;
              &lt;element&gt;Advanced&lt;/element&gt;
              &lt;element&gt;Professionals&lt;/element&gt;
            &lt;/values&gt;
          &lt;/value_type&gt;
        &lt;/field&gt;
        &lt;field name="speaker" type="zope.schema.TextLine"&gt;
          &lt;description&gt;Name (or names) of the speaker&lt;/description&gt;
          &lt;title&gt;Speaker&lt;/title&gt;
        &lt;/field&gt;
        &lt;field name="email" type="plone.schema.email.Email"&gt;
          &lt;description&gt;Adress of the speaker&lt;/description&gt;
          &lt;title&gt;Email&lt;/title&gt;
        &lt;/field&gt;
        &lt;field name="image" type="plone.namedfile.field.NamedBlobImage"&gt;
          &lt;description/&gt;
          &lt;required&gt;False&lt;/required&gt;
          &lt;title&gt;Image&lt;/title&gt;
        &lt;/field&gt;
        &lt;field name="speaker_biography" type="plone.app.textfield.RichText"&gt;
          &lt;description/&gt;
          &lt;max_length&gt;1000&lt;/max_length&gt;
          &lt;required&gt;False&lt;/required&gt;
          &lt;title&gt;Speaker Biography&lt;/title&gt;
        &lt;/field&gt;
      &lt;/schema&gt;
    &lt;/model&gt;</property>
   <property name="model_file"></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>

But unfortunately after restarting plone I am still unable to see that new content type, could someone point me in the direction of what I'm missing on this?

Thanks so much!

Bsically, you make an add-on and install it from the control panel.

I would recommend using GitHub - plone/plonecli: Plone Command Line Client - for creating and working with custom add-ons and themes since it can provide you with all the boilderplate, then you just need to add the fields to the xml file (and maybe a view).

That said: It is actually possible to make a new content type TTW in the dexterity control panel.

Thanks!

I've been looking into making an addon, I'm struggling getting Plone to recognize the addon e.g. I don't see it available to install through the Addons page.

I ran plonecli create addon src/collective.talk

This is my buildout.cfg

[buildout]

# use this extend one of the buildout configuration:
extends =
# -*- mrbob: extra extends -*-
#    test_plone43.cfg
#    test_plone50.cfg
#    test_plone51.cfg
    test_plone52.cfg

eggs =
    plone.restapi
    pas.plugins.ldap
    collective.talk

[sources]
collective.talk = fs collective.talk path=src

I've ran bin/buildout and then started plone back up but it is not available to install, folder structure looks like this -

Am I missing some step to get this to work? I was following Step 16 in Mastering Plone 5 Development.

Thanks!

The sources part is used by mr.developer (a buildout extension).

  • You first need to add mr.deceloper
  • run bin/buildout to activate it so buildout generates the script bin/develop
  • then you activate you addon with bin/develop activate collective.talk. (This changes .mr.developer.cfg in your project dir)
  • run bin/buildout again so that the activated addon is included on the customised PYTHON_PATH in bin/instance bin/zeoclient

Now the addon is available to Zope/Plone

mr.developer is cool to manage multiple add’ons you’re working on or pull in add’on from github repo’s. But you need to know its mechanics.

For just a single add’on you can skip mr.developer and just add

develop = src/collective.talk

Under the [buildout] section and then run bin/buildout . This also add the add’on on pytho s module parh

Basically mr.developer is a handy tool to manage the develop attribute.

Gotcha, thanks that is good information to have.

For now just skipping mr.developer should be sufficient for my use-case (only need to add one addon)

so here is my buildout.cfg now

[buildout]
develop = src/collective.talk

# use this extend one of the buildout configuration:
extends =
# -*- mrbob: extra extends -*-
#    test_plone43.cfg
#    test_plone50.cfg
#    test_plone51.cfg
    test_plone52.cfg

eggs =
    collective.talk

[sources]
collective.talk = fs collective.talk path=src

Unfortunately when trying to run bin/buildout now I get an error message -

Looks like it is trying to find a distribution for collective.example, I'm assuming from github? Not sure why it would be attempting to do that, collective.example is the root application

To do it from scratch, this should work.

  1. Make your add on by using plone cli ( plonecli create addon collective.talk ) and the 'subcommand' plonecli add content_type ( Content Type sub-template — Plone Documentation v5.2 ) . Edit the (xml) files (or you can do this after you know all the other steps below works)
  2. Install Plone with the universal installer
  3. place your add-on in the folder /zinstance/src (if you install with standalone (zeoserver if not)
  4. add your add on to develop section
    src= src/collective.talk
  5. add your add on to eggs section
    eggs=collective.talk
  6. run bin/buildout
  7. start server (every time you run bin/buildout you need to stop and start server)

Thanks espenmn!

I reinstalled plone so I could give these steps a try with a fresh instance, so plone is installed at
~/Plone with the zinstance folder being ~/Plone/zinstance

I ran plonecli create addon collective.talk

plonecli create addon collective.talk

RUN: mrbob bobtemplates.plone:addon -O collective.talk

Welcome to mr.bob interactive mode. Before we generate directory structure, some questions need to be answered.

Answer with a question mark to display help.
Values in square brackets at the end of the questions show the default value if there is no answer.


--> Author's name [FakeGitUserOrEmail]:

--> Author's email [FakeGitUserOrEmail]:

--> Author's GitHub username:

--> Package description [An add-on for Plone]:

--> Do you want me to initialze a GIT repository in your new package? (y/n) [y]: n

--> Plone version [5.2.4]:

--> Python version for virtualenv [python3]: python3.8

--> Do you want me to activate VS Code support? (y/n) [y]: y


Error on isort-apply: isort-apply create: /home/user/Plone/zinstance/src/collective.talk/.tox/isort-apply
ERROR: invocation failed (exit code 1), logfile: /home/user/Plone/zinstance/src/collective.talk/.tox/isort-apply/log/isort-apply-0.log
============================================= log start ==============================================
ModuleNotFoundError: No module named 'virtualenv.seed.via_app_data'

============================================== log end ===============================================
ERROR: InvocationError for command /usr/bin/python3 -m virtualenv --no-download --python /usr/bin/python3 isort-apply (exited with code 1)
______________________________________________ summary _______________________________________________
ERROR:   isort-apply: InvocationError for command /usr/bin/python3 -m virtualenv --no-download --python /usr/bin/python3 isort-apply (exited with code 1)

git init is disabled!
Generated file structure at /home/user/Plone/zinstance/src/collective.talk/collective.talk

then plonecli add content_type

plonecli add content_type

RUN: mrbob bobtemplates.plone:content_type

Welcome to mr.bob interactive mode. Before we generate directory structure, some questions need to be answered.

Answer with a question mark to display help.
Values in square brackets at the end of the questions show the default value if there is no answer.



RUN: git status --porcelain --ignore-submodules
fatal: not a git repository (or any of the parent directories): .git
b''
--> Please commit your changes, before using a sub-template! Continue anyway? [n/y] [n]: y

--> Content type name (Allowed: _ a-z A-Z and whitespace) [Todo Task]:

--> Content type description:

--> Use XML Model [n]:

--> Dexterity base class (Container/Item) [Container]:

--> Should the content type globally addable? [y]:

--> Should we filter content types to be added to this container? [n]:

--> Create a content type class [y]:

--> Activate default behaviors? [y]:

>>> reading Plone version from bobtemplate.cfg

Error on isort-apply: isort-apply create: /home/user/Plone/zinstance/src/collective.talk/.tox/isort-apply
ERROR: invocation failed (exit code 1), logfile: /home/user/Plone/zinstance/src/collective.talk/.tox/isort-apply/log/isort-apply-0.log
============================================= log start ==============================================
ModuleNotFoundError: No module named 'virtualenv.seed.via_app_data'

============================================== log end ===============================================
ERROR: InvocationError for command /usr/bin/python3 -m virtualenv --no-download --python /usr/bin/python3 isort-apply (exited with code 1)
______________________________________________ summary _______________________________________________
ERROR:   isort-apply: InvocationError for command /usr/bin/python3 -m virtualenv --no-download --python /usr/bin/python3 isort-apply (exited with code 1)

Should we run?:
git add .
git commit -m "Add content_type: Todo Task"
in: /home/user/Plone/zinstance/src/collective.talk
[y]/n: n
Skip git commit!
Generated file structure at /home/user/Plone/zinstance/src/collective.talk/src/collective/talk

Then I've added the data onto my buildout.cfg located ~/Plone/zinstance/buildout.cfg
it looks like this now

[buildout]
develop = src/collective.talk
# use this extend one of the buildout configuration:
extends =
# -*- mrbob: extra extends -*-
#    test_plone43.cfg
#    test_plone50.cfg
#    test_plone51.cfg
    test_plone52.cfg

eggs = collective.talk

I then stop the plone server, ran sudo bin/buildout and then started the server back up with

sudo bin/plonectl fg

These are all my portal types, not seeing the TODO Task content type I would expect -

Any idea what I've missed when setting it up?

Thanks so much!

Did you install the and on in plone s control panel?

I did not, Just checked, and I don't see the add-on as available to install in the control panel -

& no active add-ons

please add this to your buildout

[instance]
eggs += collective.talk

run buidlout
install addon via controlpanel

Thanks so much! It looks like that must have done the trick

This is the buildout.cfg I ended up with -

[buildout]
eggs =
    Plone

develop =
     src/collective.talk

[instance]
<= instance_base
recipe = plone.recipe.zope2instance
http-address = 8080
eggs += collective.talk

and I am now able to see collective.talk in the addons panel!

Thanks!

Please add the "+" to eggs-defintion, it append your dev-egg to the list

[buildout]
develop = src/collective.talk
# use this extend one of the buildout configuration:
extends =
# -*- mrbob: extra extends -*-
#    test_plone43.cfg
#    test_plone50.cfg
#    test_plone51.cfg
    test_plone52.cfg

eggs += collective.talk

@Flagreon,
hmm... that works, though I can see issues in the future

Use the develop.cfg file

There is usually a develop.cfg file. That file is structured for this kind of work. It comes with useful add-ons and settings for "development goodness :100:" (testing infrastructure, auto reloading etc...)

What you're doing will work, but it might be working against the framework in the long run.

The pattern I recommend involves working with the develop.cfg

  1. edit zinstance/develop.cfg
  2. run bin/buildout -c develop.cfg

Here's what a develop.cfg for your scenario would look like (with most comments removed):

[sources]
collective.talk = fs collective.talk

[buildout]
test-packages = collective.talk
deprecation-warnings = on
verbose-security = off
extends =
    buildout.cfg
extensions +=
    mr.developer
eggs +=
    Products.DocFinderTab
    plone.reload
    collective.talk
parts +=
    test
    diazotools
    checkdocs
    mrbob
    releaser
    i18ndude

# mr.developer settings:
always-checkout = force
sources = sources
auto-checkout = *


[test]
recipe = collective.xmltestreport
defaults = ['--auto-color', '--auto-progress', '--ignore_dir=.git', '--ignore_dir=bower_components', '--ignore_dir=node_modules']
eggs =
    ${buildout:eggs}
    ${buildout:test-packages}


# Add diazo compile/run tools to bin; useful for debugging understanding
# diazo. See http://docs.diazo.org/en/latest/compiler.html
[diazotools]
recipe = zc.recipe.egg
eggs = diazo


[checkdocs]
# installs collective.checkdocs from pypi [https://github.com/collective/collective.checkdocs]
recipe = zc.recipe.egg
eggs =
    collective.checkdocs


[mrbob]
recipe = zc.recipe.egg
eggs =
    mr.bob
    bobtemplates.plone


[releaser]
# installs zest.releaser, po compiler
recipe = zc.recipe.egg
eggs =
    zest.releaser
    zest.pocompile

[i18ndude]
recipe = zc.recipe.egg
eggs = i18ndude

[plonecli]
recipe = zc.recipe.egg
eggs = plonecli

So all the packages you're developing ideally go in the develop.cfg. Your buildout.cfg then remains untouched and once you've packaged and deployed your work as an egg it can be explicitly added to the buildout.cfg.