danielebarchiesi@4: ' . t('About') . ''; danielebarchiesi@4: $output .= '

' . t('The field collection module provides a field, to which any number of fields can be attached. See the Field module help page for more information about fields.', array('@field-help' => url('admin/help/field'))) . '

'; danielebarchiesi@4: return $output; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_entity_info(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_entity_info() { danielebarchiesi@4: $return['field_collection_item'] = array( danielebarchiesi@4: 'label' => t('Field collection item'), danielebarchiesi@4: 'label callback' => 'entity_class_label', danielebarchiesi@4: 'uri callback' => 'entity_class_uri', danielebarchiesi@4: 'entity class' => 'FieldCollectionItemEntity', danielebarchiesi@4: 'controller class' => 'EntityAPIController', danielebarchiesi@4: 'base table' => 'field_collection_item', danielebarchiesi@4: 'revision table' => 'field_collection_item_revision', danielebarchiesi@4: 'fieldable' => TRUE, danielebarchiesi@4: // For integration with Redirect module. danielebarchiesi@4: // @see http://drupal.org/node/1263884 danielebarchiesi@4: 'redirect' => FALSE, danielebarchiesi@4: 'entity keys' => array( danielebarchiesi@4: 'id' => 'item_id', danielebarchiesi@4: 'revision' => 'revision_id', danielebarchiesi@4: 'bundle' => 'field_name', danielebarchiesi@4: ), danielebarchiesi@4: 'module' => 'field_collection', danielebarchiesi@4: 'view modes' => array( danielebarchiesi@4: 'full' => array( danielebarchiesi@4: 'label' => t('Full content'), danielebarchiesi@4: 'custom settings' => FALSE, danielebarchiesi@4: ), danielebarchiesi@4: ), danielebarchiesi@4: 'access callback' => 'field_collection_item_access', danielebarchiesi@4: 'metadata controller class' => 'FieldCollectionItemMetadataController' danielebarchiesi@4: ); danielebarchiesi@4: danielebarchiesi@4: // Add info about the bundles. We do not use field_info_fields() but directly danielebarchiesi@4: // use field_read_fields() as field_info_fields() requires built entity info danielebarchiesi@4: // to work. danielebarchiesi@4: foreach (field_read_fields(array('type' => 'field_collection')) as $field_name => $field) { danielebarchiesi@4: $return['field_collection_item']['bundles'][$field_name] = array( danielebarchiesi@4: 'label' => t('Field collection @field', array('@field' => $field_name)), danielebarchiesi@4: 'admin' => array( danielebarchiesi@4: 'path' => 'admin/structure/field-collections/%field_collection_field_name', danielebarchiesi@4: 'real path' => 'admin/structure/field-collections/' . strtr($field_name, array('_' => '-')), danielebarchiesi@4: 'bundle argument' => 3, danielebarchiesi@4: 'access arguments' => array('administer field collections'), danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: return $return; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Menu callback for loading the bundle names. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_name_load($arg) { danielebarchiesi@4: $field_name = strtr($arg, array('-' => '_')); danielebarchiesi@4: if (($field = field_info_field($field_name)) && $field['type'] == 'field_collection') { danielebarchiesi@4: return $field_name; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Loads a field collection item. danielebarchiesi@4: * danielebarchiesi@4: * @return field_collection_item danielebarchiesi@4: * The field collection item entity or FALSE. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_item_load($item_id, $reset = FALSE) { danielebarchiesi@4: $result = field_collection_item_load_multiple(array($item_id), array(), $reset); danielebarchiesi@4: return $result ? reset($result) : FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Loads a field collection revision. danielebarchiesi@4: * danielebarchiesi@4: * @param $revision_id danielebarchiesi@4: * The field collection revision ID. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_item_revision_load($revision_id) { danielebarchiesi@4: return entity_revision_load('field_collection_item', $revision_id); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Loads field collection items. danielebarchiesi@4: * danielebarchiesi@4: * @return danielebarchiesi@4: * An array of field collection item entities. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_item_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) { danielebarchiesi@4: return entity_load('field_collection_item', $ids, $conditions, $reset); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Class for field_collection_item entities. danielebarchiesi@4: */ danielebarchiesi@4: class FieldCollectionItemEntity extends Entity { danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Field collection field info. danielebarchiesi@4: * danielebarchiesi@4: * @var array danielebarchiesi@4: */ danielebarchiesi@4: protected $fieldInfo; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * The host entity object. danielebarchiesi@4: * danielebarchiesi@4: * @var object danielebarchiesi@4: */ danielebarchiesi@4: protected $hostEntity; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * The host entity ID. danielebarchiesi@4: * danielebarchiesi@4: * @var integer danielebarchiesi@4: */ danielebarchiesi@4: protected $hostEntityId; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * The host entity revision ID if this is not the default revision. danielebarchiesi@4: * danielebarchiesi@4: * @var integer danielebarchiesi@4: */ danielebarchiesi@4: protected $hostEntityRevisionId; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * The host entity type. danielebarchiesi@4: * danielebarchiesi@4: * @var string danielebarchiesi@4: */ danielebarchiesi@4: protected $hostEntityType; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * The language under which the field collection item is stored. danielebarchiesi@4: * danielebarchiesi@4: * @var string danielebarchiesi@4: */ danielebarchiesi@4: protected $langcode = LANGUAGE_NONE; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Entity ID. danielebarchiesi@4: * danielebarchiesi@4: * @var integer danielebarchiesi@4: */ danielebarchiesi@4: public $item_id; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Field collection revision ID. danielebarchiesi@4: * danielebarchiesi@4: * @var integer danielebarchiesi@4: */ danielebarchiesi@4: public $revision_id; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * The name of the field-collection field this item is associated with. danielebarchiesi@4: * danielebarchiesi@4: * @var string danielebarchiesi@4: */ danielebarchiesi@4: public $field_name; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Whether this revision is the default revision. danielebarchiesi@4: * danielebarchiesi@4: * @var bool danielebarchiesi@4: */ danielebarchiesi@4: public $default_revision = TRUE; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Whether the field collection item is archived, i.e. not in use. danielebarchiesi@4: * danielebarchiesi@4: * @see FieldCollectionItemEntity::isInUse() danielebarchiesi@4: * @var bool danielebarchiesi@4: */ danielebarchiesi@4: public $archived = FALSE; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Constructs the entity object. danielebarchiesi@4: */ danielebarchiesi@4: public function __construct(array $values = array(), $entityType = NULL) { danielebarchiesi@4: parent::__construct($values, 'field_collection_item'); danielebarchiesi@4: // Workaround issues http://drupal.org/node/1084268 and danielebarchiesi@4: // http://drupal.org/node/1264440: danielebarchiesi@4: // Check if the required property is set before checking for the field's danielebarchiesi@4: // type. If the property is not set, we are hitting a PDO or a core's bug. danielebarchiesi@4: // FIXME: Remove when #1264440 is fixed and the required PHP version is danielebarchiesi@4: // properly identified and documented in the module documentation. danielebarchiesi@4: if (isset($this->field_name)) { danielebarchiesi@4: // Ok, we have the field name property, we can proceed and check the field's type danielebarchiesi@4: $field_info = $this->fieldInfo(); danielebarchiesi@4: if (!$field_info || $field_info['type'] != 'field_collection') { danielebarchiesi@4: throw new Exception("Invalid field name given: {$this->field_name} is not a Field Collection field."); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Provides info about the field on the host entity, which embeds this danielebarchiesi@4: * field collection item. danielebarchiesi@4: */ danielebarchiesi@4: public function fieldInfo() { danielebarchiesi@4: return field_info_field($this->field_name); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Provides info of the field instance containing the reference to this danielebarchiesi@4: * field collection item. danielebarchiesi@4: */ danielebarchiesi@4: public function instanceInfo() { danielebarchiesi@4: if ($this->fetchHostDetails()) { danielebarchiesi@4: return field_info_instance($this->hostEntityType(), $this->field_name, $this->hostEntityBundle()); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the field instance label translated to interface language. danielebarchiesi@4: */ danielebarchiesi@4: public function translatedInstanceLabel($langcode = NULL) { danielebarchiesi@4: if ($info = $this->instanceInfo()) { danielebarchiesi@4: if (module_exists('i18n_field')) { danielebarchiesi@4: return i18n_string("field:{$this->field_name}:{$info['bundle']}:label", $info['label'], array('langcode' => $langcode)); danielebarchiesi@4: } danielebarchiesi@4: return $info['label']; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Specifies the default label, which is picked up by label() by default. danielebarchiesi@4: */ danielebarchiesi@4: public function defaultLabel() { danielebarchiesi@4: // @todo make configurable. danielebarchiesi@4: if ($this->fetchHostDetails()) { danielebarchiesi@4: $field = $this->fieldInfo(); danielebarchiesi@4: $label = $this->translatedInstanceLabel(); danielebarchiesi@4: danielebarchiesi@4: if ($field['cardinality'] == 1) { danielebarchiesi@4: return $label; danielebarchiesi@4: } danielebarchiesi@4: elseif ($this->item_id) { danielebarchiesi@4: return t('!instance_label @count', array('!instance_label' => $label, '@count' => $this->delta() + 1)); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: return t('New !instance_label', array('!instance_label' => $label)); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return t('Unconnected field collection item'); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the path used to view the entity. danielebarchiesi@4: */ danielebarchiesi@4: public function path() { danielebarchiesi@4: if ($this->item_id) { danielebarchiesi@4: return field_collection_field_get_path($this->fieldInfo()) . '/' . $this->item_id; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the URI as returned by entity_uri(). danielebarchiesi@4: */ danielebarchiesi@4: public function defaultUri() { danielebarchiesi@4: return array( danielebarchiesi@4: 'path' => $this->path(), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Sets the host entity. Only possible during creation of a item. danielebarchiesi@4: * danielebarchiesi@4: * @param $create_link danielebarchiesi@4: * (optional) Whether a field-item linking the host entity to the field danielebarchiesi@4: * collection item should be created. danielebarchiesi@4: */ danielebarchiesi@4: public function setHostEntity($entity_type, $entity, $langcode = LANGUAGE_NONE, $create_link = TRUE) { danielebarchiesi@4: if (!empty($this->is_new)) { danielebarchiesi@4: $this->hostEntityType = $entity_type; danielebarchiesi@4: $this->hostEntity = $entity; danielebarchiesi@4: $this->langcode = $langcode; danielebarchiesi@4: danielebarchiesi@4: list($this->hostEntityId, $this->hostEntityRevisionId) = entity_extract_ids($this->hostEntityType, $this->hostEntity); danielebarchiesi@4: // If the host entity is not saved yet, set the id to FALSE. So danielebarchiesi@4: // fetchHostDetails() does not try to load the host entity details. danielebarchiesi@4: if (!isset($this->hostEntityId)) { danielebarchiesi@4: $this->hostEntityId = FALSE; danielebarchiesi@4: } danielebarchiesi@4: // We are create a new field collection for a non-default entity, thus danielebarchiesi@4: // set archived to TRUE. danielebarchiesi@4: if (!entity_revision_is_default($entity_type, $entity)) { danielebarchiesi@4: $this->hostEntityId = FALSE; danielebarchiesi@4: $this->archived = TRUE; danielebarchiesi@4: } danielebarchiesi@4: if ($create_link) { danielebarchiesi@4: $entity->{$this->field_name}[$this->langcode][] = array('entity' => $this); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: throw new Exception('The host entity may be set only during creation of a field collection item.'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the host entity, which embeds this field collection item. danielebarchiesi@4: */ danielebarchiesi@4: public function hostEntity() { danielebarchiesi@4: if ($this->fetchHostDetails()) { danielebarchiesi@4: if (!isset($this->hostEntity) && $this->isInUse()) { danielebarchiesi@4: $this->hostEntity = entity_load_single($this->hostEntityType, $this->hostEntityId); danielebarchiesi@4: } danielebarchiesi@4: elseif (!isset($this->hostEntity) && $this->hostEntityRevisionId) { danielebarchiesi@4: $this->hostEntity = entity_revision_load($this->hostEntityType, $this->hostEntityRevisionId); danielebarchiesi@4: } danielebarchiesi@4: return $this->hostEntity; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the entity type of the host entity, which embeds this danielebarchiesi@4: * field collection item. danielebarchiesi@4: */ danielebarchiesi@4: public function hostEntityType() { danielebarchiesi@4: if ($this->fetchHostDetails()) { danielebarchiesi@4: return $this->hostEntityType; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the id of the host entity, which embeds this field collection item. danielebarchiesi@4: */ danielebarchiesi@4: public function hostEntityId() { danielebarchiesi@4: if ($this->fetchHostDetails()) { danielebarchiesi@4: if (!$this->hostEntityId && $this->hostEntityRevisionId) { danielebarchiesi@4: $this->hostEntityId = entity_id($this->hostEntityType, $this->hostEntity()); danielebarchiesi@4: } danielebarchiesi@4: return $this->hostEntityId; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the bundle of the host entity, which embeds this field collection danielebarchiesi@4: * item. danielebarchiesi@4: */ danielebarchiesi@4: public function hostEntityBundle() { danielebarchiesi@4: if ($entity = $this->hostEntity()) { danielebarchiesi@4: list($id, $rev_id, $bundle) = entity_extract_ids($this->hostEntityType, $entity); danielebarchiesi@4: return $bundle; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: protected function fetchHostDetails() { danielebarchiesi@4: if (!isset($this->hostEntityId)) { danielebarchiesi@4: if ($this->item_id) { danielebarchiesi@4: // For saved field collections, query the field data to determine the danielebarchiesi@4: // right host entity. danielebarchiesi@4: $query = new EntityFieldQuery(); danielebarchiesi@4: $query->fieldCondition($this->fieldInfo(), 'revision_id', $this->revision_id); danielebarchiesi@4: if (!$this->isInUse()) { danielebarchiesi@4: $query->age(FIELD_LOAD_REVISION); danielebarchiesi@4: } danielebarchiesi@4: $result = $query->execute(); danielebarchiesi@4: list($this->hostEntityType, $data) = each($result); danielebarchiesi@4: danielebarchiesi@4: if ($this->isInUse()) { danielebarchiesi@4: $this->hostEntityId = $data ? key($data) : FALSE; danielebarchiesi@4: $this->hostEntityRevisionId = FALSE; danielebarchiesi@4: } danielebarchiesi@4: // If we are querying for revisions, we get the revision ID. danielebarchiesi@4: else { danielebarchiesi@4: $this->hostEntityId = FALSE; danielebarchiesi@4: $this->hostEntityRevisionId = $data ? key($data) : FALSE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: // No host entity available yet. danielebarchiesi@4: $this->hostEntityId = FALSE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return !empty($this->hostEntityId) || !empty($this->hostEntity) || !empty($this->hostEntityRevisionId); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Determines the $delta of the reference pointing to this field collection danielebarchiesi@4: * item. danielebarchiesi@4: */ danielebarchiesi@4: public function delta() { danielebarchiesi@4: if (($entity = $this->hostEntity()) && isset($entity->{$this->field_name})) { danielebarchiesi@4: foreach ($entity->{$this->field_name} as $langcode => &$data) { danielebarchiesi@4: foreach ($data as $delta => $item) { danielebarchiesi@4: if (isset($item['value']) && $item['value'] == $this->item_id) { danielebarchiesi@4: $this->langcode = $langcode; danielebarchiesi@4: return $delta; danielebarchiesi@4: } danielebarchiesi@4: elseif (isset($item['entity']) && $item['entity'] === $this) { danielebarchiesi@4: $this->langcode = $langcode; danielebarchiesi@4: return $delta; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Determines the language code under which the item is stored. danielebarchiesi@4: */ danielebarchiesi@4: public function langcode() { danielebarchiesi@4: if ($this->delta() != NULL) { danielebarchiesi@4: return $this->langcode; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Determines whether this field collection item revision is in use. danielebarchiesi@4: * danielebarchiesi@4: * Field collection items may be contained in from non-default host entity danielebarchiesi@4: * revisions. If the field collection item does not appear in the default danielebarchiesi@4: * host entity revision, the item is actually not used by default and so danielebarchiesi@4: * marked as 'archived'. danielebarchiesi@4: * If the field collection item appears in the default revision of the host danielebarchiesi@4: * entity, the default revision of the field collection item is in use there danielebarchiesi@4: * and the collection is not marked as archived. danielebarchiesi@4: */ danielebarchiesi@4: public function isInUse() { danielebarchiesi@4: return $this->default_revision && !$this->archived; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Save the field collection item. danielebarchiesi@4: * danielebarchiesi@4: * By default, always save the host entity, so modules are able to react danielebarchiesi@4: * upon changes to the content of the host and any 'last updated' dates of danielebarchiesi@4: * entities get updated. danielebarchiesi@4: * danielebarchiesi@4: * For creating an item a host entity has to be specified via setHostEntity() danielebarchiesi@4: * before this function is invoked. For the link between the entities to be danielebarchiesi@4: * fully established, the host entity object has to be updated to include a danielebarchiesi@4: * reference on this field collection item during saving. So do not skip danielebarchiesi@4: * saving the host for creating items. danielebarchiesi@4: * danielebarchiesi@4: * @param $skip_host_save danielebarchiesi@4: * (internal) If TRUE is passed, the host entity is not saved automatically danielebarchiesi@4: * and therefore no link is created between the host and the item or danielebarchiesi@4: * revision updates might be skipped. Use with care. danielebarchiesi@4: */ danielebarchiesi@4: public function save($skip_host_save = FALSE) { danielebarchiesi@4: // Make sure we have a host entity during creation. danielebarchiesi@4: if (!empty($this->is_new) && !(isset($this->hostEntityId) || isset($this->hostEntity) || isset($this->hostEntityRevisionId))) { danielebarchiesi@4: throw new Exception("Unable to create a field collection item without a given host entity."); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Only save directly if we are told to skip saving the host entity. Else, danielebarchiesi@4: // we always save via the host as saving the host might trigger saving danielebarchiesi@4: // field collection items anyway (e.g. if a new revision is created). danielebarchiesi@4: if ($skip_host_save) { danielebarchiesi@4: return entity_get_controller($this->entityType)->save($this); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $host_entity = $this->hostEntity(); danielebarchiesi@4: if (!$host_entity) { danielebarchiesi@4: throw new Exception("Unable to save a field collection item without a valid reference to a host entity."); danielebarchiesi@4: } danielebarchiesi@4: // If this is creating a new revision, also do so for the host entity. danielebarchiesi@4: if (!empty($this->revision) || !empty($this->is_new_revision)) { danielebarchiesi@4: $host_entity->revision = TRUE; danielebarchiesi@4: if (!empty($this->default_revision)) { danielebarchiesi@4: entity_revision_set_default($this->hostEntityType, $host_entity); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: // Set the host entity reference, so the item will be saved with the host. danielebarchiesi@4: // @see field_collection_field_presave() danielebarchiesi@4: $delta = $this->delta(); danielebarchiesi@4: if (isset($delta)) { danielebarchiesi@4: $host_entity->{$this->field_name}[$this->langcode][$delta] = array('entity' => $this); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $host_entity->{$this->field_name}[$this->langcode][] = array('entity' => $this); danielebarchiesi@4: } danielebarchiesi@4: return entity_save($this->hostEntityType, $host_entity); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Deletes the field collection item and the reference in the host entity. danielebarchiesi@4: */ danielebarchiesi@4: public function delete() { danielebarchiesi@4: parent::delete(); danielebarchiesi@4: $this->deleteHostEntityReference(); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Deletes the host entity's reference of the field collection item. danielebarchiesi@4: */ danielebarchiesi@4: protected function deleteHostEntityReference() { danielebarchiesi@4: $delta = $this->delta(); danielebarchiesi@4: if ($this->item_id && isset($delta)) { danielebarchiesi@4: unset($this->hostEntity->{$this->field_name}[$this->langcode][$delta]); danielebarchiesi@4: entity_save($this->hostEntityType, $this->hostEntity); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Intelligently delete a field collection item revision. danielebarchiesi@4: * danielebarchiesi@4: * If a host entity is revisioned with its field collection items, deleting danielebarchiesi@4: * a field collection item on the default revision of the host should not danielebarchiesi@4: * delete the collection item from archived revisions too. Instead, we delete danielebarchiesi@4: * the current default revision and archive the field collection. danielebarchiesi@4: * danielebarchiesi@4: * If no revisions are left or the host is not revisionable, the whole item danielebarchiesi@4: * is deleted. danielebarchiesi@4: */ danielebarchiesi@4: public function deleteRevision($skip_host_update = FALSE) { danielebarchiesi@4: if (!$this->revision_id) { danielebarchiesi@4: return; danielebarchiesi@4: } danielebarchiesi@4: $info = entity_get_info($this->hostEntityType()); danielebarchiesi@4: if (empty($info['entity keys']['revision']) || !$this->hostEntity()) { danielebarchiesi@4: return $this->delete(); danielebarchiesi@4: } danielebarchiesi@4: if (!$skip_host_update) { danielebarchiesi@4: // Just remove the item from the host, which cares about deleting the danielebarchiesi@4: // item (depending on whether the update creates a new revision). danielebarchiesi@4: $this->deleteHostEntityReference(); danielebarchiesi@4: } danielebarchiesi@4: elseif (!$this->isDefaultRevision()) { danielebarchiesi@4: entity_revision_delete('field_collection_item', $this->revision_id); danielebarchiesi@4: } danielebarchiesi@4: // If deleting the default revision, take care! danielebarchiesi@4: else { danielebarchiesi@4: $row = db_select('field_collection_item_revision', 'r') danielebarchiesi@4: ->fields('r') danielebarchiesi@4: ->condition('item_id', $this->item_id) danielebarchiesi@4: ->condition('revision_id', $this->revision_id, '<>') danielebarchiesi@4: ->execute() danielebarchiesi@4: ->fetchAssoc(); danielebarchiesi@4: // If no other revision is left, delete. Else archive the item. danielebarchiesi@4: if (!$row) { danielebarchiesi@4: $this->delete(); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: // Make the other revision the default revision and archive the item. danielebarchiesi@4: db_update('field_collection_item') danielebarchiesi@4: ->fields(array('archived' => 1, 'revision_id' => $row['revision_id'])) danielebarchiesi@4: ->condition('item_id', $this->item_id) danielebarchiesi@4: ->execute(); danielebarchiesi@4: entity_get_controller('field_collection_item')->resetCache(array($this->item_id)); danielebarchiesi@4: entity_revision_delete('field_collection_item', $this->revision_id); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Export the field collection item. danielebarchiesi@4: * danielebarchiesi@4: * Since field collection entities are not directly exportable (i.e., do not danielebarchiesi@4: * have 'exportable' set to TRUE in hook_entity_info()) and since Features danielebarchiesi@4: * calls this method when exporting the field collection as a field attached danielebarchiesi@4: * to another entity, we return the export in the format expected by danielebarchiesi@4: * Features, rather than in the normal Entity::export() format. danielebarchiesi@4: */ danielebarchiesi@4: public function export($prefix = '') { danielebarchiesi@4: // Based on code in EntityDefaultFeaturesController::export_render(). danielebarchiesi@4: $export = "entity_import('" . $this->entityType() . "', '"; danielebarchiesi@4: $export .= addcslashes(parent::export(), '\\\''); danielebarchiesi@4: $export .= "')"; danielebarchiesi@4: return $export; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Magic method to only serialize what's necessary. danielebarchiesi@4: */ danielebarchiesi@4: public function __sleep() { danielebarchiesi@4: $vars = get_object_vars($this); danielebarchiesi@4: unset($vars['entityInfo'], $vars['idKey'], $vars['nameKey'], $vars['statusKey']); danielebarchiesi@4: unset($vars['fieldInfo']); danielebarchiesi@4: // Also do not serialize the host entity, but only if it has already an id. danielebarchiesi@4: if ($this->hostEntity && ($this->hostEntityId || $this->hostEntityRevisionId)) { danielebarchiesi@4: unset($vars['hostEntity']); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Also key the returned array with the variable names so the method may danielebarchiesi@4: // be easily overridden and customized. danielebarchiesi@4: return drupal_map_assoc(array_keys($vars)); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Magic method to invoke setUp() on unserialization. danielebarchiesi@4: * danielebarchiesi@4: * @todo: Remove this once it appears in a released entity API module version. danielebarchiesi@4: */ danielebarchiesi@4: public function __wakeup() { danielebarchiesi@4: $this->setUp(); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_menu(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_menu() { danielebarchiesi@4: $items = array(); danielebarchiesi@4: if (module_exists('field_ui')) { danielebarchiesi@4: $items['admin/structure/field-collections'] = array( danielebarchiesi@4: 'title' => 'Field collections', danielebarchiesi@4: 'description' => 'Manage fields on field collections.', danielebarchiesi@4: 'page callback' => 'field_collections_overview', danielebarchiesi@4: 'access arguments' => array('administer field collections'), danielebarchiesi@4: 'type' => MENU_NORMAL_ITEM, danielebarchiesi@4: 'file' => 'field_collection.admin.inc', danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Add menu paths for viewing/editing/deleting field collection items. danielebarchiesi@4: foreach (field_info_fields() as $field) { danielebarchiesi@4: if ($field['type'] == 'field_collection') { danielebarchiesi@4: $path = field_collection_field_get_path($field); danielebarchiesi@4: $count = count(explode('/', $path)); danielebarchiesi@4: danielebarchiesi@4: $items[$path . '/%field_collection_item'] = array( danielebarchiesi@4: 'page callback' => 'field_collection_item_page_view', danielebarchiesi@4: 'page arguments' => array($count), danielebarchiesi@4: 'access callback' => 'field_collection_item_access', danielebarchiesi@4: 'access arguments' => array('view', $count), danielebarchiesi@4: 'file' => 'field_collection.pages.inc', danielebarchiesi@4: ); danielebarchiesi@4: $items[$path . '/%field_collection_item/view'] = array( danielebarchiesi@4: 'title' => 'View', danielebarchiesi@4: 'type' => MENU_DEFAULT_LOCAL_TASK, danielebarchiesi@4: 'weight' => -10, danielebarchiesi@4: ); danielebarchiesi@4: $items[$path . '/%field_collection_item/edit'] = array( danielebarchiesi@4: 'page callback' => 'drupal_get_form', danielebarchiesi@4: 'page arguments' => array('field_collection_item_form', $count), danielebarchiesi@4: 'access callback' => 'field_collection_item_access', danielebarchiesi@4: 'access arguments' => array('update', $count), danielebarchiesi@4: 'title' => 'Edit', danielebarchiesi@4: 'type' => MENU_LOCAL_TASK, danielebarchiesi@4: 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, danielebarchiesi@4: 'file' => 'field_collection.pages.inc', danielebarchiesi@4: ); danielebarchiesi@4: $items[$path . '/%field_collection_item/delete'] = array( danielebarchiesi@4: 'page callback' => 'drupal_get_form', danielebarchiesi@4: 'page arguments' => array('field_collection_item_delete_confirm', $count), danielebarchiesi@4: 'access callback' => 'field_collection_item_access', danielebarchiesi@4: 'access arguments' => array('delete', $count), danielebarchiesi@4: 'title' => 'Delete', danielebarchiesi@4: 'type' => MENU_LOCAL_TASK, danielebarchiesi@4: 'context' => MENU_CONTEXT_INLINE, danielebarchiesi@4: 'file' => 'field_collection.pages.inc', danielebarchiesi@4: ); danielebarchiesi@4: // Add entity type and the entity id as additional arguments. danielebarchiesi@4: $items[$path . '/add/%/%'] = array( danielebarchiesi@4: 'page callback' => 'field_collection_item_add', danielebarchiesi@4: 'page arguments' => array($field['field_name'], $count + 1, $count + 2), danielebarchiesi@4: // The pace callback takes care of checking access itself. danielebarchiesi@4: 'access callback' => TRUE, danielebarchiesi@4: 'file' => 'field_collection.pages.inc', danielebarchiesi@4: ); danielebarchiesi@4: // Add menu items for dealing with revisions. danielebarchiesi@4: $items[$path . '/%field_collection_item/revisions/%field_collection_item_revision'] = array( danielebarchiesi@4: 'page callback' => 'field_collection_item_page_view', danielebarchiesi@4: 'page arguments' => array($count + 2), danielebarchiesi@4: 'access callback' => 'field_collection_item_access', danielebarchiesi@4: 'access arguments' => array('view', $count + 2), danielebarchiesi@4: 'file' => 'field_collection.pages.inc', danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: $items['field_collection/ajax'] = array( danielebarchiesi@4: 'title' => 'Remove item callback', danielebarchiesi@4: 'page callback' => 'field_collection_remove_js', danielebarchiesi@4: 'delivery callback' => 'ajax_deliver', danielebarchiesi@4: 'access callback' => TRUE, danielebarchiesi@4: 'theme callback' => 'ajax_base_page_theme', danielebarchiesi@4: 'type' => MENU_CALLBACK, danielebarchiesi@4: 'file path' => 'includes', danielebarchiesi@4: 'file' => 'form.inc', danielebarchiesi@4: ); danielebarchiesi@4: danielebarchiesi@4: return $items; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_menu_alter() to fix the field collections admin UI tabs. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_menu_alter(&$items) { danielebarchiesi@4: if (module_exists('field_ui') && isset($items['admin/structure/field-collections/%field_collection_field_name/fields'])) { danielebarchiesi@4: // Make the fields task the default local task. danielebarchiesi@4: $items['admin/structure/field-collections/%field_collection_field_name'] = $items['admin/structure/field-collections/%field_collection_field_name/fields']; danielebarchiesi@4: $item = &$items['admin/structure/field-collections/%field_collection_field_name']; danielebarchiesi@4: $item['type'] = MENU_NORMAL_ITEM; danielebarchiesi@4: $item['title'] = 'Manage fields'; danielebarchiesi@4: $item['title callback'] = 'field_collection_admin_page_title'; danielebarchiesi@4: $item['title arguments'] = array(3); danielebarchiesi@4: danielebarchiesi@4: $items['admin/structure/field-collections/%field_collection_field_name/fields'] = array( danielebarchiesi@4: 'title' => 'Manage fields', danielebarchiesi@4: 'type' => MENU_DEFAULT_LOCAL_TASK, danielebarchiesi@4: 'weight' => 1, danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Menu title callback. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_admin_page_title($field_name) { danielebarchiesi@4: return t('Field collection @field_name', array('@field_name' => $field_name)); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_admin_paths(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_admin_paths() { danielebarchiesi@4: if (variable_get('node_admin_theme')) { danielebarchiesi@4: return array( danielebarchiesi@4: 'field-collection/*/*/edit' => TRUE, danielebarchiesi@4: 'field-collection/*/*/delete' => TRUE, danielebarchiesi@4: 'field-collection/*/add/*/*' => TRUE, danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_permission(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_permission() { danielebarchiesi@4: return array( danielebarchiesi@4: 'administer field collections' => array( danielebarchiesi@4: 'title' => t('Administer field collections'), danielebarchiesi@4: 'description' => t('Create and delete fields on field collections.'), danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Determines whether the given user has access to a field collection. danielebarchiesi@4: * danielebarchiesi@4: * @param $op danielebarchiesi@4: * The operation being performed. One of 'view', 'update', 'create', 'delete'. danielebarchiesi@4: * @param $item danielebarchiesi@4: * Optionally a field collection item. If nothing is given, access for all danielebarchiesi@4: * items is determined. danielebarchiesi@4: * @param $account danielebarchiesi@4: * The user to check for. Leave it to NULL to check for the global user. danielebarchiesi@4: * @return boolean danielebarchiesi@4: * Whether access is allowed or not. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_item_access($op, FieldCollectionItemEntity $item = NULL, $account = NULL) { danielebarchiesi@4: // We do not support editing field collection revisions that are not used at danielebarchiesi@4: // the hosts default revision as saving the host might result in a new default danielebarchiesi@4: // revision. danielebarchiesi@4: if (isset($item) && !$item->isInUse() && $op != 'view') { danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: if (user_access('administer field collections', $account)) { danielebarchiesi@4: return TRUE; danielebarchiesi@4: } danielebarchiesi@4: if (!isset($item)) { danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: $op = $op == 'view' ? 'view' : 'edit'; danielebarchiesi@4: // Access is determined by the entity and field containing the reference. danielebarchiesi@4: $field = field_info_field($item->field_name); danielebarchiesi@4: $entity_access = entity_access($op == 'view' ? 'view' : 'update', $item->hostEntityType(), $item->hostEntity(), $account); danielebarchiesi@4: return $entity_access && field_access($op, $field, $item->hostEntityType(), $item->hostEntity(), $account); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_theme(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_theme() { danielebarchiesi@4: return array( danielebarchiesi@4: 'field_collection_item' => array( danielebarchiesi@4: 'render element' => 'elements', danielebarchiesi@4: 'template' => 'field-collection-item', danielebarchiesi@4: ), danielebarchiesi@4: 'field_collection_view' => array( danielebarchiesi@4: 'render element' => 'element', danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_info(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_info() { danielebarchiesi@4: return array( danielebarchiesi@4: 'field_collection' => array( danielebarchiesi@4: 'label' => t('Field collection'), danielebarchiesi@4: 'description' => t('This field stores references to embedded entities, which itself may contain any number of fields.'), danielebarchiesi@4: 'instance_settings' => array(), danielebarchiesi@4: 'default_widget' => 'field_collection_hidden', danielebarchiesi@4: 'default_formatter' => 'field_collection_view', danielebarchiesi@4: // As of now there is no UI for setting the path. danielebarchiesi@4: 'settings' => array( danielebarchiesi@4: 'path' => '', danielebarchiesi@4: 'hide_blank_items' => TRUE, danielebarchiesi@4: ), danielebarchiesi@4: // Add entity property info. danielebarchiesi@4: 'property_type' => 'field_collection_item', danielebarchiesi@4: 'property_callbacks' => array('field_collection_entity_metadata_property_callback'), danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_instance_settings_form(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_instance_settings_form($field, $instance) { danielebarchiesi@4: danielebarchiesi@4: $element['fieldset'] = array( danielebarchiesi@4: '#type' => 'fieldset', danielebarchiesi@4: '#title' => t('Default value'), danielebarchiesi@4: '#collapsible' => FALSE, danielebarchiesi@4: // As field_ui_default_value_widget() does, we change the #parents so that danielebarchiesi@4: // the value below is writing to $instance in the right location. danielebarchiesi@4: '#parents' => array('instance'), danielebarchiesi@4: ); danielebarchiesi@4: // Be sure to set the default value to NULL, e.g. to repair old fields danielebarchiesi@4: // that still have one. danielebarchiesi@4: $element['fieldset']['default_value'] = array( danielebarchiesi@4: '#type' => 'value', danielebarchiesi@4: '#value' => NULL, danielebarchiesi@4: ); danielebarchiesi@4: $element['fieldset']['content'] = array( danielebarchiesi@4: '#pre' => '

', danielebarchiesi@4: '#markup' => t('To specify a default value, configure it via the regular default value setting of each field that is part of the field collection. To do so, go to the Manage fields screen of the field collection.', array('!url' => url('admin/structure/field-collections/' . strtr($field['field_name'], array('_' => '-')) . '/fields'))), danielebarchiesi@4: '#suffix' => '

', danielebarchiesi@4: ); danielebarchiesi@4: return $element; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the base path to use for field collection items. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_get_path($field) { danielebarchiesi@4: if (empty($field['settings']['path'])) { danielebarchiesi@4: return 'field-collection/' . strtr($field['field_name'], array('_' => '-')); danielebarchiesi@4: } danielebarchiesi@4: return $field['settings']['path']; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_settings_form(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_settings_form($field, $instance) { danielebarchiesi@4: danielebarchiesi@4: $form['hide_blank_items'] = array( danielebarchiesi@4: '#type' => 'checkbox', danielebarchiesi@4: '#title' => t('Hide blank items'), danielebarchiesi@4: '#default_value' => $field['settings']['hide_blank_items'], danielebarchiesi@4: '#description' => t("A blank item is always added to any multivalued field's form. If checked, any additional blank items are hidden except of the first item which is always shown."), danielebarchiesi@4: '#weight' => 10, danielebarchiesi@4: '#states' => array( danielebarchiesi@4: // Hide the setting if the cardinality is 1. danielebarchiesi@4: 'invisible' => array( danielebarchiesi@4: ':input[name="field[cardinality]"]' => array('value' => '1'), danielebarchiesi@4: ), danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: return $form; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_presave(). danielebarchiesi@4: * danielebarchiesi@4: * Support saving field collection items in @code $item['entity'] @endcode. This danielebarchiesi@4: * may be used to seamlessly create field collection items during host-entity danielebarchiesi@4: * creation or to save changes to the host entity and its collections at once. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_presave($host_entity_type, $host_entity, $field, $instance, $langcode, &$items) { danielebarchiesi@4: foreach ($items as &$item) { danielebarchiesi@4: // In case the entity has been changed / created, save it and set the id. danielebarchiesi@4: // If the host entity creates a new revision, save new item-revisions as danielebarchiesi@4: // well. danielebarchiesi@4: if (isset($item['entity']) || !empty($host_entity->revision)) { danielebarchiesi@4: danielebarchiesi@4: if ($entity = field_collection_field_get_entity($item)) { danielebarchiesi@4: danielebarchiesi@4: if (!empty($entity->is_new)) { danielebarchiesi@4: $entity->setHostEntity($host_entity_type, $host_entity, LANGUAGE_NONE, FALSE); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // If the host entity is saved as new revision, do the same for the item. danielebarchiesi@4: if (!empty($host_entity->revision)) { danielebarchiesi@4: $entity->revision = TRUE; danielebarchiesi@4: $is_default = entity_revision_is_default($host_entity_type, $host_entity); danielebarchiesi@4: // If an entity type does not support saving non-default entities, danielebarchiesi@4: // assume it will be saved as default. danielebarchiesi@4: if (!isset($is_default) || $is_default) { danielebarchiesi@4: $entity->default_revision = TRUE; danielebarchiesi@4: $entity->archived = FALSE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $entity->save(TRUE); danielebarchiesi@4: danielebarchiesi@4: $item = array( danielebarchiesi@4: 'value' => $entity->item_id, danielebarchiesi@4: 'revision_id' => $entity->revision_id, danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_update(). danielebarchiesi@4: * danielebarchiesi@4: * Care about removed field collection items. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { danielebarchiesi@4: $items_original = !empty($entity->original->{$field['field_name']}[$langcode]) ? $entity->original->{$field['field_name']}[$langcode] : array(); danielebarchiesi@4: $original_by_id = array_flip(field_collection_field_item_to_ids($items_original)); danielebarchiesi@4: danielebarchiesi@4: foreach ($items as $item) { danielebarchiesi@4: unset($original_by_id[$item['value']]); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // If there are removed items, care about deleting the item entities. danielebarchiesi@4: if ($original_by_id) { danielebarchiesi@4: $ids = array_flip($original_by_id); danielebarchiesi@4: danielebarchiesi@4: // If we are creating a new revision, the old-items should be kept but get danielebarchiesi@4: // marked as archived now. danielebarchiesi@4: if (!empty($entity->revision)) { danielebarchiesi@4: db_update('field_collection_item') danielebarchiesi@4: ->fields(array('archived' => 1)) danielebarchiesi@4: ->condition('item_id', $ids, 'IN') danielebarchiesi@4: ->execute(); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: // Delete unused field collection items now. danielebarchiesi@4: foreach (field_collection_item_load_multiple($ids) as $item) { danielebarchiesi@4: $item->deleteRevision(TRUE); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_delete(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) { danielebarchiesi@4: // Also delete all embedded entities. danielebarchiesi@4: if ($ids = field_collection_field_item_to_ids($items)) { danielebarchiesi@4: // We filter out entities that are still being referenced by other danielebarchiesi@4: // host-entities. This should never be the case, but it might happened e.g. danielebarchiesi@4: // when modules cloned a node without knowing about field-collection. danielebarchiesi@4: $entity_info = entity_get_info($entity_type); danielebarchiesi@4: $entity_id_name = $entity_info['entity keys']['id']; danielebarchiesi@4: $field_column = key($field['columns']); danielebarchiesi@4: danielebarchiesi@4: foreach ($ids as $id_key => $id) { danielebarchiesi@4: $query = new EntityFieldQuery(); danielebarchiesi@4: $entities = $query danielebarchiesi@4: ->fieldCondition($field['field_name'], $field_column, $id) danielebarchiesi@4: ->execute(); danielebarchiesi@4: unset($entities[$entity_type][$entity->$entity_id_name]); danielebarchiesi@4: danielebarchiesi@4: if (!empty($entities[$entity_type])) { danielebarchiesi@4: // Filter this $id out. danielebarchiesi@4: unset($ids[$id_key]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: entity_delete_multiple('field_collection_item', $ids); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_delete_revision(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) { danielebarchiesi@4: foreach ($items as $item) { danielebarchiesi@4: if (!empty($item['revision_id'])) { danielebarchiesi@4: if ($entity = field_collection_item_revision_load($item['revision_id'])) { danielebarchiesi@4: $entity->deleteRevision(TRUE); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Get an array of field collection item IDs stored in the given field items. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_item_to_ids($items) { danielebarchiesi@4: $ids = array(); danielebarchiesi@4: foreach ($items as $item) { danielebarchiesi@4: if (!empty($item['value'])) { danielebarchiesi@4: $ids[] = $item['value']; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $ids; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_is_empty(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_is_empty($item, $field) { danielebarchiesi@4: if (!empty($item['value'])) { danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: elseif (isset($item['entity'])) { danielebarchiesi@4: return field_collection_item_is_empty($item['entity']); danielebarchiesi@4: } danielebarchiesi@4: return TRUE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Determines whether a field collection item entity is empty based on the collection-fields. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_item_is_empty(FieldCollectionItemEntity $item) { danielebarchiesi@4: $instances = field_info_instances('field_collection_item', $item->field_name); danielebarchiesi@4: $is_empty = TRUE; danielebarchiesi@4: danielebarchiesi@4: foreach ($instances as $instance) { danielebarchiesi@4: $field_name = $instance['field_name']; danielebarchiesi@4: $field = field_info_field($field_name); danielebarchiesi@4: danielebarchiesi@4: // Determine the list of languages to iterate on. danielebarchiesi@4: $languages = field_available_languages('field_collection_item', $field); danielebarchiesi@4: danielebarchiesi@4: foreach ($languages as $langcode) { danielebarchiesi@4: if (!empty($item->{$field_name}[$langcode])) { danielebarchiesi@4: // If at least one collection-field is not empty; the danielebarchiesi@4: // field collection item is not empty. danielebarchiesi@4: foreach ($item->{$field_name}[$langcode] as $field_item) { danielebarchiesi@4: if (!module_invoke($field['module'], 'field_is_empty', $field_item, $field)) { danielebarchiesi@4: $is_empty = FALSE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Allow other modules a chance to alter the value before returning. danielebarchiesi@4: drupal_alter('field_collection_is_empty', $is_empty, $item); danielebarchiesi@4: return $is_empty; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_formatter_info(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_formatter_info() { danielebarchiesi@4: return array( danielebarchiesi@4: 'field_collection_list' => array( danielebarchiesi@4: 'label' => t('Links to field collection items'), danielebarchiesi@4: 'field types' => array('field_collection'), danielebarchiesi@4: 'settings' => array( danielebarchiesi@4: 'edit' => t('Edit'), danielebarchiesi@4: 'delete' => t('Delete'), danielebarchiesi@4: 'add' => t('Add'), danielebarchiesi@4: 'description' => TRUE, danielebarchiesi@4: ), danielebarchiesi@4: ), danielebarchiesi@4: 'field_collection_view' => array( danielebarchiesi@4: 'label' => t('Field collection items'), danielebarchiesi@4: 'field types' => array('field_collection'), danielebarchiesi@4: 'settings' => array( danielebarchiesi@4: 'edit' => t('Edit'), danielebarchiesi@4: 'delete' => t('Delete'), danielebarchiesi@4: 'add' => t('Add'), danielebarchiesi@4: 'description' => TRUE, danielebarchiesi@4: 'view_mode' => 'full', danielebarchiesi@4: ), danielebarchiesi@4: ), danielebarchiesi@4: 'field_collection_fields' => array( danielebarchiesi@4: 'label' => t('Fields only'), danielebarchiesi@4: 'field types' => array('field_collection'), danielebarchiesi@4: 'settings' => array( danielebarchiesi@4: 'view_mode' => 'full', danielebarchiesi@4: ), danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_formatter_settings_form(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { danielebarchiesi@4: $display = $instance['display'][$view_mode]; danielebarchiesi@4: $settings = $display['settings']; danielebarchiesi@4: $elements = array(); danielebarchiesi@4: danielebarchiesi@4: if ($display['type'] != 'field_collection_fields') { danielebarchiesi@4: $elements['edit'] = array( danielebarchiesi@4: '#type' => 'textfield', danielebarchiesi@4: '#title' => t('Edit link title'), danielebarchiesi@4: '#default_value' => $settings['edit'], danielebarchiesi@4: '#description' => t('Leave the title empty, to hide the link.'), danielebarchiesi@4: ); danielebarchiesi@4: $elements['delete'] = array( danielebarchiesi@4: '#type' => 'textfield', danielebarchiesi@4: '#title' => t('Delete link title'), danielebarchiesi@4: '#default_value' => $settings['delete'], danielebarchiesi@4: '#description' => t('Leave the title empty, to hide the link.'), danielebarchiesi@4: ); danielebarchiesi@4: $elements['add'] = array( danielebarchiesi@4: '#type' => 'textfield', danielebarchiesi@4: '#title' => t('Add link title'), danielebarchiesi@4: '#default_value' => $settings['add'], danielebarchiesi@4: '#description' => t('Leave the title empty, to hide the link.'), danielebarchiesi@4: ); danielebarchiesi@4: $elements['description'] = array( danielebarchiesi@4: '#type' => 'checkbox', danielebarchiesi@4: '#title' => t('Show the field description beside the add link.'), danielebarchiesi@4: '#default_value' => $settings['description'], danielebarchiesi@4: '#description' => t('If enabled and the add link is shown, the field description is shown in front of the add link.'), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Add a select form element for view_mode if viewing the rendered field_collection. danielebarchiesi@4: if ($display['type'] !== 'field_collection_list') { danielebarchiesi@4: danielebarchiesi@4: $entity_type = entity_get_info('field_collection_item'); danielebarchiesi@4: $options = array(); danielebarchiesi@4: foreach ($entity_type['view modes'] as $mode => $info) { danielebarchiesi@4: $options[$mode] = $info['label']; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: $elements['view_mode'] = array( danielebarchiesi@4: '#type' => 'select', danielebarchiesi@4: '#title' => t('View mode'), danielebarchiesi@4: '#options' => $options, danielebarchiesi@4: '#default_value' => $settings['view_mode'], danielebarchiesi@4: '#description' => t('Select the view mode'), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: return $elements; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_formatter_settings_summary(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_formatter_settings_summary($field, $instance, $view_mode) { danielebarchiesi@4: $display = $instance['display'][$view_mode]; danielebarchiesi@4: $settings = $display['settings']; danielebarchiesi@4: $output = array(); danielebarchiesi@4: danielebarchiesi@4: if ($display['type'] !== 'field_collection_fields') { danielebarchiesi@4: $links = array_filter(array_intersect_key($settings, array_flip(array('add', 'edit', 'delete')))); danielebarchiesi@4: if ($links) { danielebarchiesi@4: $output[] = t('Links: @links', array('@links' => check_plain(implode(', ', $links)))); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $output[] = t('Links: none'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: if ($display['type'] !== 'field_collection_list') { danielebarchiesi@4: $entity_type = entity_get_info('field_collection_item'); danielebarchiesi@4: if (!empty($entity_type['view modes'][$settings['view_mode']]['label'])) { danielebarchiesi@4: $output[] = t('View mode: @mode', array('@mode' => $entity_type['view modes'][$settings['view_mode']]['label'])); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: return implode('
', $output); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_formatter_view(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { danielebarchiesi@4: $element = array(); danielebarchiesi@4: $settings = $display['settings']; danielebarchiesi@4: danielebarchiesi@4: switch ($display['type']) { danielebarchiesi@4: case 'field_collection_list': danielebarchiesi@4: danielebarchiesi@4: foreach ($items as $delta => $item) { danielebarchiesi@4: if ($field_collection = field_collection_field_get_entity($item)) { danielebarchiesi@4: $output = l($field_collection->label(), $field_collection->path()); danielebarchiesi@4: $links = array(); danielebarchiesi@4: foreach (array('edit', 'delete') as $op) { danielebarchiesi@4: if ($settings[$op] && field_collection_item_access($op == 'edit' ? 'update' : $op, $field_collection)) { danielebarchiesi@4: $title = entity_i18n_string("field:{$field['field_name']}:{$instance['bundle']}:setting_$op", $settings[$op]); danielebarchiesi@4: $links[] = l($title, $field_collection->path() . '/' . $op, array('query' => drupal_get_destination())); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($links) { danielebarchiesi@4: $output .= ' (' . implode('|', $links) . ')'; danielebarchiesi@4: } danielebarchiesi@4: $element[$delta] = array('#markup' => $output); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: field_collection_field_formatter_links($element, $entity_type, $entity, $field, $instance, $langcode, $items, $display); danielebarchiesi@4: break; danielebarchiesi@4: danielebarchiesi@4: case 'field_collection_view': danielebarchiesi@4: danielebarchiesi@4: $element['#attached']['css'][] = drupal_get_path('module', 'field_collection') . '/field_collection.theme.css'; danielebarchiesi@4: $view_mode = !empty($display['settings']['view_mode']) ? $display['settings']['view_mode'] : 'full'; danielebarchiesi@4: foreach ($items as $delta => $item) { danielebarchiesi@4: if ($field_collection = field_collection_field_get_entity($item)) { danielebarchiesi@4: $element[$delta]['entity'] = $field_collection->view($view_mode); danielebarchiesi@4: $element[$delta]['#theme_wrappers'] = array('field_collection_view'); danielebarchiesi@4: $element[$delta]['#attributes']['class'][] = 'field-collection-view'; danielebarchiesi@4: $element[$delta]['#attributes']['class'][] = 'clearfix'; danielebarchiesi@4: $element[$delta]['#attributes']['class'][] = drupal_clean_css_identifier('view-mode-' . $view_mode); danielebarchiesi@4: danielebarchiesi@4: $links = array( danielebarchiesi@4: '#theme' => 'links__field_collection_view', danielebarchiesi@4: ); danielebarchiesi@4: $links['#attributes']['class'][] = 'field-collection-view-links'; danielebarchiesi@4: foreach (array('edit', 'delete') as $op) { danielebarchiesi@4: if ($settings[$op] && field_collection_item_access($op == 'edit' ? 'update' : $op, $field_collection)) { danielebarchiesi@4: $links['#links'][$op] = array( danielebarchiesi@4: 'title' => entity_i18n_string("field:{$field['field_name']}:{$instance['bundle']}:setting_$op", $settings[$op]), danielebarchiesi@4: 'href' => $field_collection->path() . '/' . $op, danielebarchiesi@4: 'query' => drupal_get_destination(), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $element[$delta]['links'] = $links; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: field_collection_field_formatter_links($element, $entity_type, $entity, $field, $instance, $langcode, $items, $display); danielebarchiesi@4: break; danielebarchiesi@4: danielebarchiesi@4: case 'field_collection_fields': danielebarchiesi@4: danielebarchiesi@4: $view_mode = !empty($display['settings']['view_mode']) ? $display['settings']['view_mode'] : 'full'; danielebarchiesi@4: foreach ($items as $delta => $item) { danielebarchiesi@4: if ($field_collection = field_collection_field_get_entity($item)) { danielebarchiesi@4: $element[$delta]['entity'] = $field_collection->view($view_mode); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: break; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: return $element; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Helper function to add links to a field collection field. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_formatter_links(&$element, $entity_type, $entity, $field, $instance, $langcode, $items, $display) { danielebarchiesi@4: $settings = $display['settings']; danielebarchiesi@4: danielebarchiesi@4: if ($settings['add'] && ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || count($items) < $field['cardinality'])) { danielebarchiesi@4: // Check whether the current is allowed to create a new item. danielebarchiesi@4: $field_collection_item = entity_create('field_collection_item', array('field_name' => $field['field_name'])); danielebarchiesi@4: $field_collection_item->setHostEntity($entity_type, $entity, LANGUAGE_NONE, FALSE); danielebarchiesi@4: danielebarchiesi@4: if (field_collection_item_access('create', $field_collection_item)) { danielebarchiesi@4: $path = field_collection_field_get_path($field); danielebarchiesi@4: list($id) = entity_extract_ids($entity_type, $entity); danielebarchiesi@4: $element['#suffix'] = ''; danielebarchiesi@4: if (!empty($settings['description'])) { danielebarchiesi@4: $element['#suffix'] .= '
' . field_filter_xss($instance['description']) . '
'; danielebarchiesi@4: } danielebarchiesi@4: $title = entity_i18n_string("field:{$field['field_name']}:{$instance['bundle']}:setting_add", $settings['add']); danielebarchiesi@4: $add_path = $path . '/add/' . $entity_type . '/' . $id; danielebarchiesi@4: $element['#suffix'] .= ''; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: // If there is no add link, add a special class to the last item. danielebarchiesi@4: if (empty($element['#suffix'])) { danielebarchiesi@4: $index = count(element_children($element)) - 1; danielebarchiesi@4: $element[$index]['#attributes']['class'][] = 'field-collection-view-final'; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: $element += array('#prefix' => '', '#suffix' => ''); danielebarchiesi@4: $element['#prefix'] .= '
'; danielebarchiesi@4: $element['#suffix'] .= '
'; danielebarchiesi@4: danielebarchiesi@4: return $element; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Themes field collection items printed using the field_collection_view formatter. danielebarchiesi@4: */ danielebarchiesi@4: function theme_field_collection_view($variables) { danielebarchiesi@4: $element = $variables['element']; danielebarchiesi@4: return '' . $element['#children'] . ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_widget_info(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_widget_info() { danielebarchiesi@4: return array( danielebarchiesi@4: 'field_collection_hidden' => array( danielebarchiesi@4: 'label' => t('Hidden'), danielebarchiesi@4: 'field types' => array('field_collection'), danielebarchiesi@4: 'behaviors' => array( danielebarchiesi@4: 'multiple values' => FIELD_BEHAVIOR_CUSTOM, danielebarchiesi@4: 'default value' => FIELD_BEHAVIOR_NONE, danielebarchiesi@4: ), danielebarchiesi@4: ), danielebarchiesi@4: 'field_collection_embed' => array( danielebarchiesi@4: 'label' => t('Embedded'), danielebarchiesi@4: 'field types' => array('field_collection'), danielebarchiesi@4: 'behaviors' => array( danielebarchiesi@4: 'multiple values' => FIELD_BEHAVIOR_DEFAULT, danielebarchiesi@4: 'default value' => FIELD_BEHAVIOR_NONE, danielebarchiesi@4: ), danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_widget_form(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { danielebarchiesi@4: static $recursion = 0; danielebarchiesi@4: danielebarchiesi@4: switch ($instance['widget']['type']) { danielebarchiesi@4: case 'field_collection_hidden': danielebarchiesi@4: return $element; danielebarchiesi@4: danielebarchiesi@4: case 'field_collection_embed': danielebarchiesi@4: // If the field collection item form contains another field collection, danielebarchiesi@4: // we might ran into a recursive loop. Prevent that. danielebarchiesi@4: if ($recursion++ > 3) { danielebarchiesi@4: drupal_set_message(t('The field collection item form has not been embedded to avoid recursive loops.'), 'error'); danielebarchiesi@4: return $element; danielebarchiesi@4: } danielebarchiesi@4: $field_parents = $element['#field_parents']; danielebarchiesi@4: $field_name = $element['#field_name']; danielebarchiesi@4: $language = $element['#language']; danielebarchiesi@4: danielebarchiesi@4: // Nest the field collection item entity form in a dedicated parent space, danielebarchiesi@4: // by appending [field_name, langcode, delta] to the current parent space. danielebarchiesi@4: // That way the form values of the field collection item are separated. danielebarchiesi@4: $parents = array_merge($field_parents, array($field_name, $language, $delta)); danielebarchiesi@4: danielebarchiesi@4: $element += array( danielebarchiesi@4: '#element_validate' => array('field_collection_field_widget_embed_validate'), danielebarchiesi@4: '#parents' => $parents, danielebarchiesi@4: ); danielebarchiesi@4: danielebarchiesi@4: if ($field['cardinality'] == 1) { danielebarchiesi@4: $element['#type'] = 'fieldset'; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: $field_state = field_form_get_state($field_parents, $field_name, $language, $form_state); danielebarchiesi@4: danielebarchiesi@4: if (!empty($field['settings']['hide_blank_items']) && $delta == $field_state['items_count'] && $delta > 0) { danielebarchiesi@4: // Do not add a blank item. Also see danielebarchiesi@4: // field_collection_field_attach_form() for correcting #max_delta. danielebarchiesi@4: $recursion--; danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: elseif (!empty($field['settings']['hide_blank_items']) && $field_state['items_count'] == 0) { danielebarchiesi@4: // We show one item, so also specify that as item count. So when the danielebarchiesi@4: // add button is pressed the item count will be 2 and we show to items. danielebarchiesi@4: $field_state['items_count'] = 1; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: if (isset($field_state['entity'][$delta])) { danielebarchiesi@4: $field_collection_item = $field_state['entity'][$delta]; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: if (isset($items[$delta])) { danielebarchiesi@4: $field_collection_item = field_collection_field_get_entity($items[$delta], $field_name); danielebarchiesi@4: } danielebarchiesi@4: // Show an empty collection if we have no existing one or it does not danielebarchiesi@4: // load. danielebarchiesi@4: if (empty($field_collection_item)) { danielebarchiesi@4: $field_collection_item = entity_create('field_collection_item', array('field_name' => $field_name)); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Put our entity in the form state, so FAPI callbacks can access it. danielebarchiesi@4: $field_state['entity'][$delta] = $field_collection_item; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: field_form_set_state($field_parents, $field_name, $language, $form_state, $field_state); danielebarchiesi@4: field_attach_form('field_collection_item', $field_collection_item, $element, $form_state, $language); danielebarchiesi@4: danielebarchiesi@4: if (empty($element['#required'])) { danielebarchiesi@4: $element['#after_build'][] = 'field_collection_field_widget_embed_delay_required_validation'; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) { danielebarchiesi@4: $element['remove_button'] = array( danielebarchiesi@4: '#delta' => $delta, danielebarchiesi@4: '#name' => implode('_', $parents) . '_remove_button', danielebarchiesi@4: '#type' => 'submit', danielebarchiesi@4: '#value' => t('Remove'), danielebarchiesi@4: '#validate' => array(), danielebarchiesi@4: '#submit' => array('field_collection_remove_submit'), danielebarchiesi@4: '#limit_validation_errors' => array(), danielebarchiesi@4: '#ajax' => array( danielebarchiesi@4: 'path' => 'field_collection/ajax', danielebarchiesi@4: 'effect' => 'fade', danielebarchiesi@4: ), danielebarchiesi@4: '#weight' => 1000, danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: $recursion--; danielebarchiesi@4: return $element; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_attach_form(). danielebarchiesi@4: * danielebarchiesi@4: * Corrects #max_delta when we hide the blank field collection item. danielebarchiesi@4: * danielebarchiesi@4: * @see field_add_more_js() danielebarchiesi@4: * @see field_collection_field_widget_form() danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { danielebarchiesi@4: danielebarchiesi@4: foreach (field_info_instances($entity_type, $form['#bundle']) as $field_name => $instance) { danielebarchiesi@4: $field = field_info_field($field_name); danielebarchiesi@4: danielebarchiesi@4: if ($field['type'] == 'field_collection' && $field['settings']['hide_blank_items'] danielebarchiesi@4: && field_access('edit', $field, $entity_type) && $instance['widget']['type'] == 'field_collection_embed') { danielebarchiesi@4: danielebarchiesi@4: $element_langcode = $form[$field_name]['#language']; danielebarchiesi@4: if ($form[$field_name][$element_langcode]['#max_delta'] > 0) { danielebarchiesi@4: $form[$field_name][$element_langcode]['#max_delta']--; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Page callback to handle AJAX for removing a field collection item. danielebarchiesi@4: * danielebarchiesi@4: * This is a direct page callback. The actual job of deleting the item is danielebarchiesi@4: * done in the submit handler for the button, so all we really need to danielebarchiesi@4: * do is process the form and then generate output. We generate this danielebarchiesi@4: * output by doing a replace command on the id of the entire form element. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_remove_js() { danielebarchiesi@4: // drupal_html_id() very helpfully ensures that all html IDS are unique danielebarchiesi@4: // on a page. Unfortunately what it doesn't realize is that the IDs danielebarchiesi@4: // we are generating are going to replace IDs that already exist, so danielebarchiesi@4: // this actually works against us. danielebarchiesi@4: if (isset($_POST['ajax_html_ids'])) { danielebarchiesi@4: unset($_POST['ajax_html_ids']); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: list($form, $form_state) = ajax_get_form(); danielebarchiesi@4: drupal_process_form($form['#form_id'], $form, $form_state); danielebarchiesi@4: danielebarchiesi@4: // Get the information on what we're removing. danielebarchiesi@4: $button = $form_state['triggering_element']; danielebarchiesi@4: // Go two levels up in the form, to the whole widget. danielebarchiesi@4: $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -3)); danielebarchiesi@4: // Now send back the proper AJAX command to replace it. danielebarchiesi@4: $return = array( danielebarchiesi@4: '#type' => 'ajax', danielebarchiesi@4: '#commands' => array( danielebarchiesi@4: ajax_command_replace('#' . $element['#id'], drupal_render($element)) danielebarchiesi@4: ), danielebarchiesi@4: ); danielebarchiesi@4: danielebarchiesi@4: // Because we're doing this ourselves, messages aren't automatic. We have danielebarchiesi@4: // to add them. danielebarchiesi@4: $messages = theme('status_messages'); danielebarchiesi@4: if ($messages) { danielebarchiesi@4: $return['#commands'][] = ajax_command_prepend('#' . $element['#id'], $messages); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: return $return; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Submit callback to remove an item from the field UI multiple wrapper. danielebarchiesi@4: * danielebarchiesi@4: * When a remove button is submitted, we need to find the item that it danielebarchiesi@4: * referenced and delete it. Since field UI has the deltas as a straight danielebarchiesi@4: * unbroken array key, we have to renumber everything down. Since we do this danielebarchiesi@4: * we *also* need to move all the deltas around in the $form_state['values'] danielebarchiesi@4: * and $form_state['input'] so that user changed values follow. This is a bit danielebarchiesi@4: * of a complicated process. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_remove_submit($form, &$form_state) { danielebarchiesi@4: $button = $form_state['triggering_element']; danielebarchiesi@4: $delta = $button['#delta']; danielebarchiesi@4: danielebarchiesi@4: // Where in the form we'll find the parent element. danielebarchiesi@4: $address = array_slice($button['#array_parents'], 0, -2); danielebarchiesi@4: danielebarchiesi@4: // Go one level up in the form, to the widgets container. danielebarchiesi@4: $parent_element = drupal_array_get_nested_value($form, $address); danielebarchiesi@4: $field_name = $parent_element['#field_name']; danielebarchiesi@4: $langcode = $parent_element['#language']; danielebarchiesi@4: $parents = $parent_element['#field_parents']; danielebarchiesi@4: danielebarchiesi@4: $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state); danielebarchiesi@4: danielebarchiesi@4: // Go ahead and renumber everything from our delta to the last danielebarchiesi@4: // item down one. This will overwrite the item being removed. danielebarchiesi@4: for ($i = $delta; $i <= $field_state['items_count']; $i++) { danielebarchiesi@4: $old_element_address = array_merge($address, array($i + 1)); danielebarchiesi@4: $new_element_address = array_merge($address, array($i)); danielebarchiesi@4: danielebarchiesi@4: $moving_element = drupal_array_get_nested_value($form, $old_element_address); danielebarchiesi@4: $moving_element_value = drupal_array_get_nested_value($form_state['values'], $old_element_address); danielebarchiesi@4: $moving_element_input = drupal_array_get_nested_value($form_state['input'], $old_element_address); danielebarchiesi@4: danielebarchiesi@4: // Tell the element where it's being moved to. danielebarchiesi@4: $moving_element['#parents'] = $new_element_address; danielebarchiesi@4: danielebarchiesi@4: // Move the element around. danielebarchiesi@4: form_set_value($moving_element, $moving_element_value, $form_state); danielebarchiesi@4: drupal_array_set_nested_value($form_state['input'], $moving_element['#parents'], $moving_element_input); danielebarchiesi@4: danielebarchiesi@4: // Move the entity in our saved state. danielebarchiesi@4: if (isset($field_state['entity'][$i + 1])) { danielebarchiesi@4: $field_state['entity'][$i] = $field_state['entity'][$i + 1]; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: unset($field_state['entity'][$i]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Replace the deleted entity with an empty one. This helps to ensure that danielebarchiesi@4: // trying to add a new entity won't ressurect a deleted entity from the danielebarchiesi@4: // trash bin. danielebarchiesi@4: $count = count($field_state['entity']); danielebarchiesi@4: $field_state['entity'][$count] = entity_create('field_collection_item', array('field_name' => $field_name)); danielebarchiesi@4: danielebarchiesi@4: // Then remove the last item. But we must not go negative. danielebarchiesi@4: if ($field_state['items_count'] > 0) { danielebarchiesi@4: $field_state['items_count']--; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Fix the weights. Field UI lets the weights be in a range of danielebarchiesi@4: // (-1 * item_count) to (item_count). This means that when we remove one, danielebarchiesi@4: // the range shrinks; weights outside of that range then get set to danielebarchiesi@4: // the first item in the select by the browser, floating them to the top. danielebarchiesi@4: // We use a brute force method because we lost weights on both ends danielebarchiesi@4: // and if the user has moved things around, we have to cascade because danielebarchiesi@4: // if I have items weight weights 3 and 4, and I change 4 to 3 but leave danielebarchiesi@4: // the 3, the order of the two 3s now is undefined and may not match what danielebarchiesi@4: // the user had selected. danielebarchiesi@4: $input = drupal_array_get_nested_value($form_state['input'], $address); danielebarchiesi@4: // Sort by weight danielebarchiesi@4: uasort($input, '_field_sort_items_helper'); danielebarchiesi@4: danielebarchiesi@4: // Reweight everything in the correct order. danielebarchiesi@4: $weight = -1 * $field_state['items_count']; danielebarchiesi@4: foreach ($input as $key => $item) { danielebarchiesi@4: if ($item) { danielebarchiesi@4: $input[$key]['_weight'] = $weight++; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: drupal_array_set_nested_value($form_state['input'], $address, $input); danielebarchiesi@4: field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state); danielebarchiesi@4: danielebarchiesi@4: $form_state['rebuild'] = TRUE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Gets a field collection item entity for a given field item. danielebarchiesi@4: * danielebarchiesi@4: * @param $field_name danielebarchiesi@4: * (optional) If given and there is no entity yet, a new entity object is danielebarchiesi@4: * created for the given item. danielebarchiesi@4: * danielebarchiesi@4: * @return danielebarchiesi@4: * The entity object or FALSE. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_get_entity(&$item, $field_name = NULL) { danielebarchiesi@4: if (isset($item['entity'])) { danielebarchiesi@4: return $item['entity']; danielebarchiesi@4: } danielebarchiesi@4: elseif (isset($item['value'])) { danielebarchiesi@4: // By default always load the default revision, so caches get used. danielebarchiesi@4: $entity = field_collection_item_load($item['value']); danielebarchiesi@4: if ($entity->revision_id != $item['revision_id']) { danielebarchiesi@4: // A non-default revision is a referenced, so load this one. danielebarchiesi@4: $entity = field_collection_item_revision_load($item['revision_id']); danielebarchiesi@4: } danielebarchiesi@4: return $entity; danielebarchiesi@4: } danielebarchiesi@4: elseif (!isset($item['entity']) && isset($field_name)) { danielebarchiesi@4: $item['entity'] = entity_create('field_collection_item', array('field_name' => $field_name)); danielebarchiesi@4: return $item['entity']; danielebarchiesi@4: } danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * FAPI #after_build of an individual field collection element to delay the validation of #required. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_widget_embed_delay_required_validation(&$element, &$form_state) { danielebarchiesi@4: // If the process_input flag is set, the form and its input is going to be danielebarchiesi@4: // validated. Prevent #required (sub)fields from throwing errors while danielebarchiesi@4: // their non-#required field collection item is empty. danielebarchiesi@4: if ($form_state['process_input']) { danielebarchiesi@4: _field_collection_collect_required_elements($element, $element['#field_collection_required_elements']); danielebarchiesi@4: } danielebarchiesi@4: return $element; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function _field_collection_collect_required_elements(&$element, &$required_elements) { danielebarchiesi@4: // Recurse through all children. danielebarchiesi@4: foreach (element_children($element) as $key) { danielebarchiesi@4: if (isset($element[$key]) && $element[$key]) { danielebarchiesi@4: _field_collection_collect_required_elements($element[$key], $required_elements); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if (!empty($element['#required'])) { danielebarchiesi@4: $element['#required'] = FALSE; danielebarchiesi@4: $required_elements[] = &$element; danielebarchiesi@4: $element += array('#pre_render' => array()); danielebarchiesi@4: array_unshift($element['#pre_render'], 'field_collection_field_widget_render_required'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * #pre_render callback that ensures the element is rendered as being required. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_widget_render_required($element) { danielebarchiesi@4: $element['#required'] = TRUE; danielebarchiesi@4: return $element; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * FAPI validation of an individual field collection element. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_widget_embed_validate($element, &$form_state, $complete_form) { danielebarchiesi@4: $instance = field_widget_instance($element, $form_state); danielebarchiesi@4: $field = field_widget_field($element, $form_state); danielebarchiesi@4: $field_parents = $element['#field_parents']; danielebarchiesi@4: $field_name = $element['#field_name']; danielebarchiesi@4: $language = $element['#language']; danielebarchiesi@4: danielebarchiesi@4: $field_state = field_form_get_state($field_parents, $field_name, $language, $form_state); danielebarchiesi@4: $field_collection_item = $field_state['entity'][$element['#delta']]; danielebarchiesi@4: danielebarchiesi@4: // Attach field API validation of the embedded form. danielebarchiesi@4: field_attach_form_validate('field_collection_item', $field_collection_item, $element, $form_state); danielebarchiesi@4: danielebarchiesi@4: // Now validate required elements if the entity is not empty. danielebarchiesi@4: if (!field_collection_item_is_empty($field_collection_item) && !empty($element['#field_collection_required_elements'])) { danielebarchiesi@4: foreach ($element['#field_collection_required_elements'] as &$elements) { danielebarchiesi@4: danielebarchiesi@4: // Copied from _form_validate(). danielebarchiesi@4: if (isset($elements['#needs_validation'])) { danielebarchiesi@4: $is_empty_multiple = (!count($elements['#value'])); danielebarchiesi@4: $is_empty_string = (is_string($elements['#value']) && drupal_strlen(trim($elements['#value'])) == 0); danielebarchiesi@4: $is_empty_value = ($elements['#value'] === 0); danielebarchiesi@4: if ($is_empty_multiple || $is_empty_string || $is_empty_value) { danielebarchiesi@4: if (isset($elements['#title'])) { danielebarchiesi@4: form_error($elements, t('!name field is required.', array('!name' => $elements['#title']))); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: form_error($elements); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Only if the form is being submitted, finish the collection entity and danielebarchiesi@4: // prepare it for saving. danielebarchiesi@4: if ($form_state['submitted'] && !form_get_errors()) { danielebarchiesi@4: danielebarchiesi@4: field_attach_submit('field_collection_item', $field_collection_item, $element, $form_state); danielebarchiesi@4: danielebarchiesi@4: // Load initial form values into $item, so any other form values below the danielebarchiesi@4: // same parents are kept. danielebarchiesi@4: $item = drupal_array_get_nested_value($form_state['values'], $element['#parents']); danielebarchiesi@4: danielebarchiesi@4: // Set the _weight if it is a multiple field. danielebarchiesi@4: if (isset($element['_weight']) && ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED)) { danielebarchiesi@4: $item['_weight'] = $element['_weight']['#value']; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Put the field collection item in $item['entity'], so it is saved with danielebarchiesi@4: // the host entity via hook_field_presave() / field API if it is not empty. danielebarchiesi@4: // @see field_collection_field_presave() danielebarchiesi@4: $item['entity'] = $field_collection_item; danielebarchiesi@4: form_set_value($element, $item, $form_state); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_create_field(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_create_field($field) { danielebarchiesi@4: if ($field['type'] == 'field_collection') { danielebarchiesi@4: field_attach_create_bundle('field_collection_item', $field['field_name']); danielebarchiesi@4: danielebarchiesi@4: // Clear caches. danielebarchiesi@4: entity_info_cache_clear(); danielebarchiesi@4: // Do not directly issue menu rebuilds here to avoid potentially multiple danielebarchiesi@4: // rebuilds. Instead, let menu_get_item() issue the rebuild on the next danielebarchiesi@4: // request. danielebarchiesi@4: variable_set('menu_rebuild_needed', TRUE); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_field_delete_field(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_delete_field($field) { danielebarchiesi@4: if ($field['type'] == 'field_collection') { danielebarchiesi@4: // Notify field.module that field collection was deleted. danielebarchiesi@4: field_attach_delete_bundle('field_collection_item', $field['field_name']); danielebarchiesi@4: danielebarchiesi@4: // Clear caches. danielebarchiesi@4: entity_info_cache_clear(); danielebarchiesi@4: // Do not directly issue menu rebuilds here to avoid potentially multiple danielebarchiesi@4: // rebuilds. Instead, let menu_get_item() issue the rebuild on the next danielebarchiesi@4: // request. danielebarchiesi@4: variable_set('menu_rebuild_needed', TRUE); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_i18n_string_list_{textgroup}_alter(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_i18n_string_list_field_alter(&$properties, $type, $instance) { danielebarchiesi@4: if ($type == 'field_instance') { danielebarchiesi@4: $field = field_info_field($instance['field_name']); danielebarchiesi@4: danielebarchiesi@4: if ($field['type'] == 'field_collection' && !empty($instance['display'])) { danielebarchiesi@4: danielebarchiesi@4: foreach ($instance['display'] as $view_mode => $display) { danielebarchiesi@4: if ($display['type'] != 'field_collection_fields') { danielebarchiesi@4: $display['settings'] += array('edit' => 'edit', 'delete' => 'delete', 'add' => 'add'); danielebarchiesi@4: danielebarchiesi@4: $properties['field'][$instance['field_name']][$instance['bundle']]['setting_edit'] = array( danielebarchiesi@4: 'title' => t('Edit link title'), danielebarchiesi@4: 'string' => $display['settings']['edit'], danielebarchiesi@4: ); danielebarchiesi@4: $properties['field'][$instance['field_name']][$instance['bundle']]['setting_delete'] = array( danielebarchiesi@4: 'title' => t('Delete link title'), danielebarchiesi@4: 'string' => $display['settings']['delete'], danielebarchiesi@4: ); danielebarchiesi@4: $properties['field'][$instance['field_name']][$instance['bundle']]['setting_add'] = array( danielebarchiesi@4: 'title' => t('Add link title'), danielebarchiesi@4: 'string' => $display['settings']['add'], danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_views_api(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_views_api() { danielebarchiesi@4: return array( danielebarchiesi@4: 'api' => '3.0-alpha1', danielebarchiesi@4: 'path' => drupal_get_path('module', 'field_collection') . '/views', danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_features_pipe_component_alter() for fields. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_features_pipe_field_alter(&$pipe, $data, $export) { danielebarchiesi@4: // Add the fields of the field collection entity to the pipe. danielebarchiesi@4: foreach ($data as $identifier) { danielebarchiesi@4: if (($field = features_field_load($identifier)) && $field['field_config']['type'] == 'field_collection') { danielebarchiesi@4: $fields = field_info_instances('field_collection_item', $field['field_config']['field_name']); danielebarchiesi@4: foreach ($fields as $name => $field) { danielebarchiesi@4: $pipe['field'][] = "{$field['entity_type']}-{$field['bundle']}-{$field['field_name']}"; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Callback for generating entity metadata property info for our field instances. danielebarchiesi@4: * danielebarchiesi@4: * @see field_collection_field_info() danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_entity_metadata_property_callback(&$info, $entity_type, $field, $instance, $field_type) { danielebarchiesi@4: $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']]; danielebarchiesi@4: // Set the bundle as we know it is the name of the field. danielebarchiesi@4: $property['bundle'] = $field['field_name']; danielebarchiesi@4: $property['getter callback'] = 'field_collection_field_property_get'; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Entity property info setter callback for the host entity property. danielebarchiesi@4: * danielebarchiesi@4: * As the property is of type entity, the value will be passed as a wrapped danielebarchiesi@4: * entity. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_item_set_host_entity($item, $property_name, $wrapper) { danielebarchiesi@4: if (empty($item->is_new)) { danielebarchiesi@4: throw new EntityMetadataWrapperException('The host entity may be set only during creation of a field collection item.'); danielebarchiesi@4: } danielebarchiesi@4: if (!isset($wrapper->{$item->field_name})) { danielebarchiesi@4: throw new EntityMetadataWrapperException('The specified entity has no such field collection field.'); danielebarchiesi@4: } danielebarchiesi@4: $item->setHostEntity($wrapper->type(), $wrapper->value()); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Entity property info getter callback for the host entity property. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_item_get_host_entity($item) { danielebarchiesi@4: // As the property is defined as 'entity', we have to return a wrapped entity. danielebarchiesi@4: return entity_metadata_wrapper($item->hostEntityType(), $item->hostEntity()); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Entity property info getter callback for the field collection items. danielebarchiesi@4: * danielebarchiesi@4: * Like entity_metadata_field_property_get(), but additionally supports getting danielebarchiesi@4: * not-yet saved collection items from @code $item['entity'] @endcode. danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_field_property_get($entity, array $options, $name, $entity_type, $info) { danielebarchiesi@4: $field = field_info_field($name); danielebarchiesi@4: $langcode = field_language($entity_type, $entity, $name, isset($options['language']) ? $options['language']->language : NULL); danielebarchiesi@4: $values = array(); danielebarchiesi@4: if (isset($entity->{$name}[$langcode])) { danielebarchiesi@4: foreach ($entity->{$name}[$langcode] as $delta => $data) { danielebarchiesi@4: // Wrappers do not support multiple entity references being revisions or danielebarchiesi@4: // not yet saved entities. In the case of a single reference we can return danielebarchiesi@4: // the entity object though. danielebarchiesi@4: if ($field['cardinality'] == 1) { danielebarchiesi@4: $values[$delta] = field_collection_field_get_entity($data); danielebarchiesi@4: } danielebarchiesi@4: elseif (isset($data['value'])) { danielebarchiesi@4: $values[$delta] = $data['value']; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: // For an empty single-valued field, we have to return NULL. danielebarchiesi@4: return $field['cardinality'] == 1 ? ($values ? reset($values) : NULL) : $values; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Implements hook_devel_generate(). danielebarchiesi@4: */ danielebarchiesi@4: function field_collection_devel_generate($object, $field, $instance, $bundle) { danielebarchiesi@4: // Create a new field collection object and add fake data to its fields. danielebarchiesi@4: $field_collection = entity_create('field_collection_item', array('field_name' => $field['field_name'])); danielebarchiesi@4: $field_collection->language = $object->language; danielebarchiesi@4: $field_collection->setHostEntity($instance['entity_type'], $object, $object->language, FALSE); danielebarchiesi@4: danielebarchiesi@4: devel_generate_fields($field_collection, 'field_collection_item', $field['field_name']); danielebarchiesi@4: danielebarchiesi@4: $field_collection->save(TRUE); danielebarchiesi@4: danielebarchiesi@4: return array('value' => $field_collection->item_id); danielebarchiesi@4: }