Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api.content.create replaces archetype-content title with site title during Plone site creation #99

Closed
datakurre opened this issue Mar 26, 2013 · 11 comments

Comments

@datakurre
Copy link
Member

How to reproduce:

  1. Use plone.api.content.create to create content in "setuphandlers.py" (or custom GS profile configuration step).
  2. Create a new Plone site and select the profile in question to be installed for the new site.

Then

Each new Archetype-based content object created with plone.api.content.create will have site title as its title.

Why

  1. plone.api.content.create calls Archetypes' processForm-method
  2. processForm-method tries to re-fill content fields from request
  3. title-widget finds a title field from the request (because the default form for creating a new Plone site has a title field)

Our local fix is to pop "title" key from the request.form before running code with plone.api.content.create.

But note that this is not limited to the site creation from but any values in request.form may cause similar surprises with api.content.create.

If it's not a feature to read field values for plone.api.content.create from request, then maybe request.form should somehow be cleared before creating Archetypes-content and restored afterwise.

@zupo
Copy link
Member

zupo commented Apr 3, 2013

Hey @datakurre I'm trying to reproduce this without success. Can you provide more info please:

  • snippet of code in your setuphandlers.py
  • plone and plone.api versions
  • possibly a test case exhibiting this bug?

@hvelarde
Copy link
Member

I can confirm the same issue:

  • Plone 4.3.2
  • plone.api 1.1.0

here's the code I'm using:

SITE_STRUCTURE = (
    dict(type='Folder', title=u'One'),
    dict(type='Folder', title=u'Two'),
    dict(type='Folder', title=u'Three'),
    dict(type='Folder', title=u'Four'),
    dict(type='Folder', title=u'Five'),
)

def create_new_portal_structure(site):
    """Create new site structure as defined.
    """
    logger.info('Creating contet.')
    for item in SITE_STRUCTURE:
        title = item['title']
        id = idnormalizer.normalize(title, 'en')
        if not hasattr(site, id):
            # by default, the whole structure is excluded from navigation
            item['excludeFromNav'] = True
            api.content.create(site, **item)
            logger.info(u'{0} created'.format(title))
        else:
            logger.warn(u'skipping {0}; content already exist'.format(title))

I wrote this test to demonstrate the issue:

    def test_new_portal_structure_titles(self):
        """XXX: plone.api bug: https://github.com/plone/plone.api/issues/99
        """
        for item in SITE_STRUCTURE:
            title = item['title']
            id = idnormalizer.normalize(title, 'en')
            self.assertEqual(self.portal[id].title, title)

but then it gets worst: test pass but running the code in the instance fails: the title of all items is inherited from the portal: 'Site'.

as a workaround I'm using the following:

            obj = api.content.create(site, **item)
            # XXX: following two lines are a workaround for issue in plone.api
            #      see: https://github.com/plone/plone.api/issues/99
            obj.setTitle(title)
            obj.reindexObject('Title')

@tdesvenain
Copy link
Member

I get the same problem, but using invokeFactory

On Fri, Oct 18, 2013 at 4:34 PM, Héctor Velarde notifications@github.comwrote:

I can confirm the same issue:

  • Plone 4.3.2
  • plone.api 1.1.0

here's the code I'm using:

SITE_STRUCTURE = (
dict(type='Folder', title=u'One'),
dict(type='Folder', title=u'Two'),
dict(type='Folder', title=u'Three'),
dict(type='Folder', title=u'Four'),
dict(type='Folder', title=u'Five'),)
def create_new_portal_structure(site):
"""Create new site structure as defined. """
logger.info('Creating contet.')
for item in SITE_STRUCTURE:
title = item['title']
id = idnormalizer.normalize(title, 'en')
if not hasattr(site, id):
# by default, the whole structure is excluded from navigation
item['excludeFromNav'] = True
api.content.create(site, **item)
logger.info(u'{0} created'.format(title))
else:
logger.warn(u'skipping {0}; content already exist'.format(title))

I wrote this test to demonstrate the issue:

def test_new_portal_structure_titles(self):
    """XXX: plone.api bug: https://github.com/plone/plone.api/issues/99        """
    for item in SITE_STRUCTURE:
        title = item['title']
        id = idnormalizer.normalize(title, 'en')
        self.assertEqual(self.portal[id].title, title)

but then it gets worst: test pass but running the code in the instance
fails: the title of all items is inherited from the portal: 'Site'.


Reply to this email directly or view it on GitHubhttps://github.com//issues/99#issuecomment-26600331
.

Thomas Desvenain

Téléphone : 09 51 37 35 18

@hvelarde
Copy link
Member

seems to me this is an Archetypes bug; any reference?

@hvelarde
Copy link
Member

hvelarde commented Apr 3, 2014

same issue happens with Dexterity-based content types.

I just discovered a new thing: this behavior does not occurs on tests, only when running the instance.

laulaz added a commit to IMIO/cpskin.policy that referenced this issue Jun 2, 2014
@mattss
Copy link
Member

mattss commented Oct 31, 2014

It seems like the processForm call from api.content.create is an easy but dangerous way to finalise the archetype content, is it will pull any matching field values from the request and override those passed into api.content.create. Can we take what we need from processForm instead (rename after creation and relevant events)?

@jaroel
Copy link
Member

jaroel commented Jun 9, 2015

What needs to be done, and who is going to do it?

@pgrunewald
Copy link

I am also interested in getting this issue solved.

Maybe we could use the values parameter of processForm and populate it with dictionary containing title (if present) and all other passed data from kwargs. That way the request object is not consulted, when retrieving potential form data.

gotcha added a commit to gotcha/plone.api that referenced this issue Aug 31, 2016
suggested by pgrunewald
gotcha added a commit to gotcha/plone.api that referenced this issue Feb 22, 2017
suggested by pgrunewald
gforcada added a commit that referenced this issue Feb 23, 2017
fix #99 for Archetypes content
@david-batranu
Copy link
Member

david-batranu commented Feb 9, 2018

The changes introduced in f73bddc breaks content creation when other fields are involved. Consider this usecase:

from datetime import datetime

from Products.Archetypes import atapi
from Products.ATContentTypes.content import schemata
from Products.ATContentTypes.content import base

from plone import api


class IMyContent(Interface):
    """ """

MyContentSchema = schemata.ATContentTypeSchema.copy() + atapi.Schema((

    atapi.DateTimeField(
        'date',
        schemata='default',
        required=True,
        languageIndependent=True,
    ),
)


class MyContent(base.ATCTContent):
    implements(IMyContent)

    meta_type = 'MyContent'
    schema = MyContentSchema


atapi.registerType(MyContent, 'my.package')

def do_manual_creation(context):
    fields = {'title': 'A title', 'date': datetime.now()}
    content = api.content.create(context, 'MyContent', id='mycontent', **fields)

    # pre 1.6.1:
    # >>> type(content.date)
    # <class 'DateTime.DateTime.DateTime'>

    # post 1.6.1
    # >>> type(content.date)
    # <type 'NoneType'>

This is because CalendarWidget.process_form is called with fields as form. That method does some insane things, completely ignoring the passed value and finally returning '' which gets saved by DateTimeField as None.

Pre 1.6.1, form was request.form, set by BaseObject._processForm, so in the abve usecase, date would be completely missing from the form, resulting in empty_marker to be returned by the widget, and the field to not be mutated; the value set by invokeFactory preserved.

I believe a better solution for plone.api.content.create would be to replace processForm(values=kwargs) with processForm(values={}). From what I can see, widgets mostly do the right thing and return empty_marker if field.getName() is missing from the request. Perfect when programatically adding content through invokeFactory/plone.api.content.create as the set values don't get modified.

I will make a pull request, with the change.

EDIT: it's actually {None: None} as {} is falsy.

@david-batranu david-batranu reopened this Feb 9, 2018
@gotcha
Copy link
Member

gotcha commented Mar 8, 2018

@david-batranu Did you check that the bug with title mentioned above by @datakurre is still fixed ?

@david-batranu
Copy link
Member

@gotcha you're right! I fixed the pull request, it now uses a {None: None} dict, so it will pass the check in BaseObject._processForm.

david-batranu added a commit to david-batranu/plone.api that referenced this issue Mar 12, 2018
@gotcha gotcha closed this as completed in d953e0f Mar 13, 2018
gotcha added a commit that referenced this issue Mar 13, 2018
fix #99 Call processForm with empty values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants