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('