[Solved] Behavior IPublication.expires - How to change the default value of "required" Attribute

Hi,
I have a custom Contenttype. The Schema includes the plone.app.dexterity.behaviors.metadata.IPublication Behavior.
I would like to change the the Standardvalue of the expires Datetime to required=True.

expires = schema.Datetime(
        title=_(u'label_expiration_date', u'Expiration Date'),
        description=_(
            u'help_expiration_date',
            default=u'When this date is reached, the content will no '
                    u'longer be visible in listings and searches.'),
        required=True
    )

What is the best way to do that? A customized AddForm and EditForm or exists a better and simpler way?

I'd create a new behavior that extends IPublication and sets it's own expires attribute.
Then use that new behavior instead

Something like

class IMyPublication(IPublication):
    expires = schema.Datetime(
        title=_(u'label_expiration_date', u'Expiration Date'),
        description=_(
            u'help_expiration_date',
            default=u'When this date is reached, the content will no '
                    u'longer be visible in listings and searches.'),
        required=False
    )

<!-- configure.zcml-->
<plone:behavior
  title="RequiredPublication"
  description="Set Expiration Date Field to required"
  provides=".metadata.ICTTypePublication"
  marker=".metadata.ICTTypePublication"
  factory="plone.app.dexterity.behaviors.metadata.Publication"
  for="plone.dexterity.interfaces.IDexterityContent" />
<!--typeinfo-->
<?xml version="1.0"?>
<object
  xmlns:i18n="http://xml.zope.org/namespaces/i18n"
  name="Study"
  meta_type="Dexterity FTI" >
  <property name="behaviors" purge="False">
    <element value="my.project.behaviors.metadata.ICTTypePublication"/>
    <element value="plone.app.dexterity.behaviors.metadata.IPublication" remove="True"/>        
  </property>
</object>

# metadata.py
from zope import schema
from zope.interface import provider
from plone.autoform.interfaces import IFormFieldProvider
from plone.app.dexterity.behaviors.metadata import IPublication

@provider(IFormFieldProvider)
class ICTTypePublication(IPublication):
    expires = schema.Datetime(
        title=_(u'label_expiration_date', u'Expiration Date'),
        description=_(
            u'help_expiration_date',
            default=u'When this date is reached, the content will no '
                    u'longer be visible in listings and searches.'),
        required=True
    )

With my Custom Behavior i get the following Traceback.

Traceback (most recent call last):
  File "/Development/Plone/Plone-5/buildout-cache/eggs/plone.app.viewletmanager-2.0.12-py2.7.egg/plone/app/viewletmanager/manager.py", line 106, in render
    html.append(viewlet.render())
  File "/Development/Plone/Plone-5/buildout-cache/eggs/plone.app.layout-2.8.2-py2.7.egg/plone/app/layout/viewlets/common.py", line 59, in render
    return self.index()
  File "/Development/Plone/Plone-5/buildout-cache/eggs/Zope2-2.13.27-py2.7.egg/Products/Five/browser/pagetemplatefile.py", line 125, in __call__
    return self.im_func(im_self, *args, **kw)
  File "/Development/Plone/Plone-5/buildout-cache/eggs/Zope2-2.13.27-py2.7.egg/Products/Five/browser/pagetemplatefile.py", line 59, in __call__
    sourceAnnotations=getattr(debug_flags, 'sourceAnnotations', 0),
  File "/Development/Plone/Plone-5/buildout-cache/eggs/zope.pagetemplate-4.2.1-py2.7.egg/zope/pagetemplate/pagetemplate.py", line 137, in pt_render
    strictinsert=0, sourceAnnotations=sourceAnnotations
  File "/Development/Plone/Plone-5/buildout-cache/eggs/five.pt-2.2.5-py2.7.egg/five/pt/engine.py", line 98, in __call__
    return self.template.render(**kwargs)
  File "/Development/Plone/Plone-5/buildout-cache/eggs/z3c.pt-3.0.0a1-py2.7.egg/z3c/pt/pagetemplate.py", line 163, in render
    return base_renderer(**context)
  File "/Development/Plone/Plone-5/buildout-cache/eggs/Chameleon-2.25-py2.7.egg/chameleon/zpt/template.py", line 261, in render
    return super(PageTemplate, self).render(**vars)
  File "/Development/Plone/Plone-5/buildout-cache/eggs/Chameleon-2.25-py2.7.egg/chameleon/template.py", line 191, in render
    raise_with_traceback(exc, tb)
  File "/Development/Plone/Plone-5/buildout-cache/eggs/Chameleon-2.25-py2.7.egg/chameleon/template.py", line 171, in render
    self._render(stream, econtext, rcontext)
  File "347f05e3d56b8fcdf9174f73207fe5e6.py", line 488, in render
  File "/Development/Plone/Plone-5/buildout-cache/eggs/five.pt-2.2.5-py2.7.egg/five/pt/expressions.py", line 161, in __call__
    return base()
  File "/Development/Plone/Plone-5/buildout-cache/eggs/plone.app.layout-2.8.2-py2.7.egg/plone/app/layout/viewlets/content.py", line 90, in isExpired
    return self.context.expires().isPast()
TypeError: 'datetime.datetime' object is not callable

 - Expression: "view/isExpired"
 - Filename:   ... 2-py2.7.egg/plone/app/layout/viewlets/document_byline.pt
 - Location:   (line 49: col 30)
 - Source:     ... al:expired tal:condition="view/isExpired">
                                             ^^^^^^^^^^^^^^
 - Arguments:  repeat: {...} (0)
               template: <ViewPageTemplateFile - at 0x7f21fd973f90>
               views: <ViewMapper - at 0x7f21fd922e50>
               modules: <instance - at 0x7f220b8d6c20>
               args: <tuple - at 0x7f221464c050>
               here: <ImplicitAcquisitionWrapper blub3 at 0x7f22007c0aa0>
               user: <ImplicitAcquisitionWrapper - at 0x7f22007bc960>
               nothing: <NoneType - at 0x55f0151fe560>
               container: <ImplicitAcquisitionWrapper blub3 at 0x7f22007c0aa0>
               request: <instance - at 0x7f2200d27200>
               wrapped_repeat: <SafeMapping - at 0x7f21f7ef4310>
               traverse_subpath: <list - at 0x7f21fd3ea6c8>
               default: <object - at 0x7f221456f560>
               loop: {...} (0)
               context: <ImplicitAcquisitionWrapper blub3 at 0x7f22007c0aa0>
               view: <DocumentBylineViewlet plone.belowcontenttitle.documentbyline at 0x7f21fd922610>
               translate: <function translate at 0x7f21f7e797d0>
               root: <ImplicitAcquisitionWrapper Zope at 0x7f2206ed17d0>
               options: {...} (0)
               target_language: de

I think it's a Problem between ZopeDateTime and python datetime.

Just guessing, but the behavior factory you are using is accessing the IPublication schema directly:

class Publication(MetadataBase):
    effective = DCFieldProperty(
        IPublication['effective'],
        get_name='effective_date'
    )
    expires = DCFieldProperty(
        IPublication['expires'],
        get_name='expiration_date'
    )

The same Error with a custom Factory.
So that's not it.

# metadata.py
from zope import schema
from zope.interface import provider
from plone.autoform.interfaces import IFormFieldProvider
from plone.app.dexterity.behaviors.metadata import Publication
from plone.app.dexterity.behaviors.metadata import DCFieldProperty
from plone.app.dexterity.behaviors.metadata import IPublication

@provider(IFormFieldProvider)
class ICTTypePublication(IPublication):
    expires = schema.Datetime(
        title=_(u'label_expiration_date', u'Expiration Date'),
        description=_(
            u'help_expiration_date',
            default=u'When this date is reached, the content will no '
                    u'longer be visible in listings and searches.'),
        required=True
    )

class CTTypePublication(Publication):
    expires = DCFieldProperty(
        IStudyPublication['expires'],
        get_name='expiration_date'
    )
<!-- configure.zcml-->
<plone:behavior
  title="RequiredPublication"
  description="Set Expiration Date Field to required"
  provides=".metadata.ICTTypePublication"
  marker=".metadata.ICTTypePublication"
  factory=".metadata.CTTypePublication"
  for="plone.dexterity.interfaces.IDexterityContent" />

No custom Behavior is needed. The solution with a custom Add and Edit Form does it. I think it's to much for so a seemingly little Problem, but i have no other solution.

<!-- configure.zcml -->

<!-- Register AddForm for Study -->
<adapter
  for="Products.CMFCore.interfaces.IFolderish
    zope.publisher.interfaces.browser.IDefaultBrowserLayer
    plone.dexterity.interfaces.IDexterityFTI"
  name="Study"
  provides="zope.publisher.interfaces.browser.IBrowserPage"
  factory="my.project.browser.forms.StudyAddFormView"/>
    
<!-- Override EditForm for Study -->
<browser:page
  for="my.project.interfaces.IStudy"
  name="edit"
  class="my.project.browser.forms.StudyEditFormView"
  layer="my.project.interfaces.IStudyLayer"
  permission="my.project.AddStudyPermission" />
# forms.py
import copy
from plone.dexterity.browser.add import DefaultAddForm
from plone.dexterity.browser.add import DefaultAddView
from plone.dexterity.browser.edit import DefaultEditForm
from plone.z3cform.layout import FormWrapper

def make_field_required(form = None, fieldname=None):
    if not fieldname:
        return
    if not form:
        return
    
    _field = None
    
    if 'IPublication.expires' in form.fields.keys():
        _field = form.fields.select('IPublication.expires')
    else:
        for group in form.groups:
            if 'IPublication.expires' in group.fields.keys():
                _field = group.fields['IPublication.expires']
    if _field:
        """ create a copy of the field, because all other contenttypes
             use the Publication Behavior, and should not changed
        """
        _schema_field = copy.deepcopy(_field.field) # shallow copy of an instance
        _schema_field.required = True
        _field.field = _schema_field    

""" Study Add Form"""
class StudyAddForm(DefaultAddForm):    
    
    def updateFields(self):
        super(StudyAddForm, self).updateFields()        
        make_field_required(form=self, field='IPublication.expires')

""" Study Add Form View"""
class StudyAddFormView(DefaultAddView):
    form = StudyAddForm

""" Study Edit Form"""
class StudyEditForm(DefaultEditForm):
    
    def updateFields(self):
        super(StudyEditForm, self).updateFields()        
        make_field_required(form=self, fieldname='IPublication.expires')

""" Study Edit Form View"""
class StudyEditFormView(FormWrapper):
    form = StudyEditForm