88from plone .api .validation import required_parameters
99from plone .app .linkintegrity .exceptions import LinkIntegrityNotificationException
1010from plone .app .uuid .utils import uuidToObject
11+ from plone .dexterity .fti import DexterityFTI
12+ from plone .dexterity .utils import addContentToContainer
13+ from plone .dexterity .utils import createContent
1114from plone .uuid .interfaces import IUUID
1215from Products .CMFCore .WorkflowCore import WorkflowException
16+ from Products .CMFPlone .interfaces import IConstrainTypes
17+ from zExceptions import BadRequest
1318from zope .component import getMultiAdapter
1419from zope .component import getSiteManager
1520from zope .container .interfaces import INameChooser
1823from zope .interface import providedBy
1924
2025import random
21- import transaction
2226
2327
2428_marker = []
2731@required_parameters ("container" , "type" )
2832@at_least_one_of ("id" , "title" )
2933def create (
30- container = None ,
31- type = None ,
32- id = None ,
33- title = None ,
34- safe_id = False ,
35- ** kwargs # NOQA: C816, S101
36- ):
34+ container = None , type = None , id = None , title = None , safe_id = False , ** kwargs
35+ ): # NOQA: S101
3736 """Create a new content item.
3837
3938 :param container: [required] Container object in which to create the new
@@ -61,14 +60,39 @@ def create(
6160 :class:`~plone.api.exc.InvalidParameterError`
6261 :Example: :ref:`content-create-example`
6362 """
64- # Create a temporary id if the id is not given
65- content_id = not safe_id and id or str (random .randint (0 , 99999999 ))
63+ fti = portal .get_tool ("portal_types" ).get (type )
6664
6765 if title :
6866 kwargs ["title" ] = title
6967
7068 try :
71- container .invokeFactory (type , content_id , ** kwargs )
69+ if isinstance (fti , DexterityFTI ):
70+ # For dexterity objects we want to not use the invokeFactory
71+ # method because we want to have the id generated by the name chooser
72+ constraints = IConstrainTypes (container , None )
73+ if constraints and fti not in constraints .allowedContentTypes ():
74+ raise ValueError (
75+ f"Subobject type disallowed by IConstrainTypes adapter: { type } "
76+ )
77+ if id :
78+ kwargs ["id" ] = id
79+ content = createContent (type , ** kwargs )
80+
81+ if not content .id :
82+ content .id = INameChooser (container ).chooseName (title , content )
83+ if not safe_id :
84+ content .id = INameChooser (container ).chooseName (
85+ content .id or title , content
86+ )
87+ if id and id != content .id :
88+ raise BadRequest (
89+ "The id you provided conflicts with an existing object or it is reserved"
90+ )
91+ addContentToContainer (container , content )
92+ content_id = content .id
93+ else :
94+ content_id = not safe_id and id or str (random .randint (0 , 99999999 ))
95+ container .invokeFactory (type , content_id , ** kwargs )
7296 except UnicodeDecodeError :
7397 # UnicodeDecodeError is a subclass of ValueError,
7498 # so will be swallowed below unless we re-raise it here
@@ -88,20 +112,6 @@ def create(
88112 )
89113
90114 content = container [content_id ]
91- if not id or (safe_id and id ):
92- # Create a new id from title
93- chooser = INameChooser (container )
94- derived_id = id or title
95- new_id = chooser .chooseName (derived_id , content )
96- # kacee: we must do a partial commit, else the renaming fails because
97- # the object isn't in the zodb.
98- # Thus if it is not in zodb, there's nothing to move. We should
99- # choose a correct id when
100- # the object is created.
101- # maurits: tests run fine without this though.
102- transaction .savepoint (optimistic = True )
103- content .aq_parent .manage_renameObject (content_id , new_id )
104-
105115 return content
106116
107117
0 commit comments