def set(self, instance, value, **kwargs): """ Set inner fields of this field. self is an instance of BaseInnerContentField instance is the instance of the article we are working on. value can be: InnerContentContainer A list of InnerContentProxy objects: A list of dictionaries: Each item of the dictionary will be handled kwargs are optionnals arguments changing the way this field is updated : "update": If True, update inner content proxies present in the value's dict list. If False, update inner content proxies present in the value's dict list, but delete all inner content proxies that are missing from the list but have been created before. "_initializing_": creates a new inner container This method calls the mutator of each inner field of this field, by sending to it only what has been returned by the associated widget. Example of the value when the image already existed and hasn't changed: ({'description': ('Existing image', {}), 'id': ('imageinnercontentproxy.2007-01-30.0017453092', {}), 'title': ('Existing image', {})},) Example of value when there is a new image: ({'description': ('New image', {}), 'id': ('imageinnercontentproxy.2007-02-04.7516632708', {}), 'attachedImage': (<BaseUnit at fss.JPG>, {}), 'title': ('New image', {})}) ({'description': ('Referenced image', {}), 'referencedContent': ('817b168ec61fbfcb080de02629431631', {}), 'id': ('imageinnercontentproxy.2007-02-04.1880613482', {}), 'title': ('Referenced image', {})}) """ if kwargs.get('_initializing_', False): return if isinstance(value, InnerContentContainer): return self.set(instance, value.objectValues(), **kwargs) # value must be a list of dictionnary # Each dictionnary defines values of inner content fields # Values can be wrapped into a tuple if for example you want to # define some extra args on the field mutator if type(value) not in (ListType, TupleType,): raise ValueError, \ "Value must be a list or a tuple, get: %s" % type(value) # Value can be a list of dictionnary or a list of InnerContent proxy # objects. In this case, wrap object into a dictionnary for index in range(0, len(value)): item = value[index] if IBaseInnerContentProxy.providedBy(item): schema = item.Schema() data = {} # Loop on fields and extract data using accessor for field in schema.fields(): if 'w' not in field.mode: continue field_id = field.getName() accessor = field.getAccessor(item) if accessor is None: continue data[field_id] = accessor() value[index] = data # Get inner container container_name = self.getContainerName() if shasattr(instance, container_name): container = getattr(instance, container_name, None) else: container = None # Do nothing if value is empty and container doesn't already exist if not isinstance(container, InnerContentContainer) and not value: return # Create a new container if it doesn't already exist if not isinstance(container, InnerContentContainer): container = InnerContentContainer(container_name) instance._setObject(container_name, container) container = instance._getOb(container_name) container.initializeArchetype() #instance._p_changed = 1 # 'update' argument is a switch for innercontent deletion: # - if True, ids that are not present in the request WON'T be destroyed # - if False or doesn't exist, all ids that are not in request WILL be deleted. update = kwargs.get('update', False) # Format all field values to be a tuple. If no extra args defined, # replace by an empty dictionnary for item in value: for item_key in item.keys(): item_value = item[item_key] if type(item_value) not in (TupleType, ListType,): item[item_key] = (item_value, {}) # Build a list containing all inner content ids to be replaced or updated inner_content_ids = [x['id'][0] for x in value] # If update not in kwargs, delete objects in container if not update and container is not None: inner_content_ids_to_remove = [x for x in container.objectIds() if x not in inner_content_ids] container.manage_delObjects(ids=inner_content_ids_to_remove) # Check for new inner contents for item, item_index in izip(value, count()): inner_content_field_values = item.copy() inner_content_id = inner_content_field_values['id'][0] # no need to reindex until a new inner content is really added to_reindex = False # Create a new inner content if it doesn't exist if not shasattr(container, inner_content_id): container.invokeFactory(type_name=self.inner_portal_type, id=inner_content_id) to_reindex = True # reorder content only if update not in kwargs if not update: content_position = container.getObjectPosition(inner_content_id) if content_position != item_index: container.moveObjectToPosition(inner_content_id, item_index) inner_content = getattr(container, inner_content_id) # removing id from field_values since id shouldn't be updated # (it's the key) del inner_content_field_values['id'] # Update inner content fields (new and already existing) for field_name, field_args in inner_content_field_values.items(): field = inner_content.getField(field_name) mutator = field.getMutator(inner_content) mutator(field_args[0], **field_args[1]) to_reindex = True # Then reindex object if to_reindex: inner_content.reindexObject()