/**
  * Adds a relationship to the running list of relationships.
  *
  * @param Entity $source            The source entity.
  * @param Entity $destination       The destination entity that the source entity relates to.
  * @param string $field_name        The field on the source entity that the relationship belongs to.
  * @param int    $delta             The delta the relationship belongs to.
  * @param array  $handler_arguments Any arguments to pass to the handler (like the field name).
  * @param bool   $source_vuuid      The VUUID of the source (if false, defaults to the current
  *                                  VUUID).
  * @param bool   $destination_vuuid The VUUID of the destination (if false, the VUUID of the
  *                                  destination isn't taken into account).
  */
 protected function addRelationship(Entity $source, Entity $destination, $field_name, $delta = 0, array $handler_arguments = array(), $source_vuuid = false, $destination_vuuid = false)
 {
     // Get the relationship handler name.
     $handler = $this->getRelationshipHandlerName();
     // Generate the fields for the relationship.
     $record = array();
     $record['source_type'] = $source->type();
     $record['source_uuid'] = $source->uuid();
     $record['destination_type'] = $destination->type();
     $record['destination_uuid'] = $destination->uuid();
     $record['field_name'] = $field_name;
     $record['delta'] = $delta;
     // Add the source_vuuid column.
     if ($source->supportsRevisions()) {
         $record['source_vuuid'] = $source_vuuid === false ? $source->vuuid() : $source_vuuid;
     } else {
         $record['source_vuuid'] = false;
     }
     // Add the destination vuuid column.
     $record['destination_vuuid'] = $destination->supportsRevisions() ? $destination_vuuid : false;
     // Add the relationship handler and arguments.
     $record['relationship_handler'] = $handler;
     $record['relationship_arguments'] = $handler_arguments;
     // Check to see if the relationship already exists...
     foreach ($this->relationships as $relationship) {
         if ($record['source_type'] != $relationship['source_type']) {
             continue;
         }
         if ($record['source_uuid'] != $relationship['source_uuid']) {
             continue;
         }
         if ($record['source_vuuid'] != $relationship['source_vuuid']) {
             continue;
         }
         if ($record['destination_type'] != $relationship['destination_type']) {
             continue;
         }
         if ($record['destination_uuid'] != $relationship['destination_uuid']) {
             continue;
         }
         if ($record['destination_vuuid'] != $relationship['destination_vuuid']) {
             continue;
         }
         if ($record['relationship_handler'] != $relationship['relationship_handler']) {
             continue;
         }
         if (count(array_diff($record['relationship_arguments'], $relationship['relationship_arguments'])) !== 0) {
             continue;
         }
         return;
         // The relationship already exists...
     }
     // Add the relationship to the list.
     $this->relationships[] = $record;
 }
示例#2
0
 public function diff()
 {
     // Get the preparer registry.
     $preparer_registry = new PreparerRegistry();
     if (!$this->revisions_table) {
         // Prepare the entity.
         $preparer_registry->beforeSend($this->entity);
         $resolver = new Resolver($this->entity);
         return array($this->entity->vuuid() => array('additions' => $resolver->resolvedDefinition(), 'deletions' => array()));
     }
     // Get the revisions in between the two mentioned. If the old is blank, get the entire history.
     $revision_ids = $this->getRevisionHistory($this->old_revision_id, $this->new_revision_id);
     if ($revision_ids === false) {
         return false;
     }
     $payload = array();
     $before_id = $this->old_revision_id ? $this->old_revision_id : -1;
     foreach ($revision_ids as $current_id) {
         // Load the entities.
         if ($before_id === -1) {
             $old_entity = null;
         } else {
             $old_entity = clone $this->entity;
             $old_entity->setRevision($before_id);
         }
         $new_entity = clone $this->entity;
         $new_entity->setRevision($current_id);
         // Pass the entity through the preparers to add any additional field metadata.
         if ($old_entity !== null) {
             $preparer_registry->beforeSend($old_entity);
         }
         $preparer_registry->beforeSend($new_entity);
         $payload[$current_id . '|' . $new_entity->vuuid()] = $this->singleDiff($old_entity, $new_entity);
         // Update the before ID.
         $before_id = $current_id;
     }
     return $payload;
 }
示例#3
0
 /**
  * Create reference definition.
  *
  * Creates an entity reference definition, storing the UUID, VUUID and entity
  * type for the provided entity.
  *
  * @param Entity $entity       The entity to generate the definition for.
  *
  * @return array The generated definition.
  */
 public static function createReferenceDefinition(Entity $entity)
 {
     $value = array();
     $value['uuid'] = $entity->uuid();
     $value['vuuid'] = $entity->vuuid();
     $value['entity_type'] = $entity->type();
     $value['original'] = $entity->id();
     $value['original_revision'] = $entity->revision();
     $value['revisions'] = Entity::getAllRevisions($entity->id(), $entity->type());
     return $value;
 }
示例#4
0
 public function resolveDependencies($recurse = true, $subset = false, $child = false, $subtype = false)
 {
     // Check to see if the resolved definition already exists in the cached list.
     $cached_resolutions =& drupal_static('publisher_cached_resolutions', array());
     $key = $this->base_entity->uuid() . '|' . $this->base_entity->type();
     if ($this->base_entity->supportsRevisions()) {
         $key .= '|' . $this->base_entity->vuuid();
     }
     $key .= $recurse ? '|recurse' : '';
     if (array_key_exists($key, $cached_resolutions) && $child === false && $subset === false) {
         $this->dependencies = array_replace_recursive($this->dependencies, $cached_resolutions[$key]['dependencies']);
         $this->relationships = array_merge($this->relationships, $cached_resolutions[$key]['relationships']);
         $this->resolved_definition = $cached_resolutions[$key]['resolved_definition'];
         return;
     }
     if (count($cached_resolutions) > 200) {
         // For memory consumption purposes.
         drupal_static_reset('publisher_cached_resolutions');
     }
     // Create a clone of the definition so we don't muck up the entity cache.
     $this->resolved_definition = $subset !== false ? $subset : clone $this->entity->definition;
     if (!is_object($this->resolved_definition)) {
         $this->resolved_definition = (object) $this->resolved_definition;
     }
     // Generate the list of handlers for each of the fields.
     $handlers = DefinitionHandlerRegistry::getFieldHandlers($this->entity, $this->resolved_definition, $subtype);
     foreach ($handlers as $field_name => $handler) {
         if (!isset($this->resolved_definition->{$field_name})) {
             continue;
         }
         foreach ($handler['handlers'] as $single_handler) {
             if (!$single_handler instanceof DefinitionHandlerBase) {
                 continue;
             }
             try {
                 $single_handler->dependencies =& $this->dependencies;
                 $single_handler->entity =& $this->entity;
                 $single_handler->original_entity = $this->base_entity;
                 if (property_exists($single_handler, 'relationships')) {
                     $single_handler->relationships =& $this->relationships;
                 }
                 $single_handler->handleField($this->entity->type(), $handler['type'], $field_name, $this->resolved_definition->{$field_name});
             } catch (\Exception $ex) {
                 $message = t('Error processing field "@fieldName" - "@message"', array('@fieldName' => $field_name, '@message' => $ex->getMessage()));
                 \watchdog('publisher', $message, array(), WATCHDOG_WARNING);
                 $this->errors[] = $ex;
             }
         }
     }
     // Now, recursion.
     if ($recurse) {
         foreach ($this->dependencies as $identifier => $dependency) {
             // If the dependency has already been handled, skip it.
             if (array_key_exists($identifier, self::$handled_dependencies)) {
                 continue;
             }
             // Add the dependency to the list of handled ones.
             self::$handled_dependencies[$identifier] = $dependency;
             // Load the entity and get its dependencies.
             $entity = Entity::loadByUUID($dependency['uuid'], $dependency['entity_type']);
             // If for some reason the entity fails, we need to just skip it.
             if (!$entity) {
                 continue;
             }
             // Make sure the entity is not the current entity to prevent infinite loop.
             if ($entity->uuid() == $this->entity->uuid()) {
                 continue;
             }
             $resolver = new self($entity, false, $this->dependencies);
             $resolver->relationships =& $this->relationships;
             $resolver->metadata =& $this->metadata;
             $resolver->resolveDependencies(true, false, true);
             $this->dependencies = $resolver->dependencies();
         }
     }
     // Add a dependency for the entity itself if it doesn't already exist.
     if (!array_key_exists($this->base_entity->uuid(), $this->dependencies) || $this->base_entity->supportsRevisions() && !empty($this->dependencies[$this->base_entity->uuid()]['original_revision']) && $this->base_entity->revision() > $this->dependencies[$this->base_entity->uuid()]['original_revision']) {
         $this->dependencies[$this->base_entity->uuid()] = HandlerBase::createReferenceDefinition($this->base_entity);
     }
     // Pass the entity through the entity handlers.
     if (!array_key_exists($this->base_entity->uuid(), $this->metadata) && $recurse) {
         $this->metadata[$this->base_entity->uuid()] = array();
         foreach (EntityHandlerRegistry::getEntityHandlers($this->base_entity) as $handler) {
             if (!$handler instanceof EntityHandlerBase) {
                 continue;
             }
             $handler->entity =& $this->entity;
             $handler->original_entity = $this->base_entity;
             $handler->dependencies =& $this->dependencies;
             $handler->handleEntity($this->metadata[$this->base_entity->uuid()]);
         }
     }
     // Update the cache record.
     $cached_resolutions[$key] = array('dependencies' => $this->dependencies, 'relationships' => $this->relationships, 'resolved_definition' => $this->resolved_definition);
     if (!$child) {
         $dependency_dependencies =& drupal_static('publisher_dependency_dependencies', array());
         foreach ($dependency_dependencies as $source => $dependencies) {
             if (array_key_exists($source, $this->dependencies)) {
                 if (!array_key_exists('dependencies', $this->dependencies[$source]) || !is_array($this->dependencies[$source]['dependencies'])) {
                     $this->dependencies[$source]['dependencies'] = array();
                 }
                 $this->dependencies[$source]['dependencies'] = array_replace_recursive($this->dependencies[$source]['dependencies'], $dependencies);
                 $this->dependencies[$source]['dependencies'] = array_unique($this->dependencies[$source]['dependencies']);
             }
         }
         // Update the relationships.
         $this->updateRelationships();
         // Loop through all the dependencies and perform cleanup tasks.
         foreach ($this->dependencies as $dependency_key => &$dependency) {
             // Cleanup the sources.
             if (array_key_exists('sources', $dependency) && is_array($dependency['sources'])) {
                 $dependency['sources'] = array_unique($dependency['sources']);
             }
         }
         // Loop through again for the required stuff.
         foreach ($this->dependencies as $dependency_key => &$dependency) {
             // Create the 'required' key.
             if (array_key_exists('has_relationship', $dependency) && $dependency['has_relationship']) {
                 $dependency['required'] = false;
                 continue;
             }
             $this->dependencies[$dependency_key]['required'] = true;
             if (array_key_exists('sources', $this->dependencies[$dependency_key]) && is_array($this->dependencies[$dependency_key]['sources']) && count($this->dependencies[$dependency_key]['sources']) > 0) {
                 // Mark dependencies as not required only if all of their parent paths
                 // are marked as having relationships.
                 $sources = array();
                 foreach ($this->dependencies[$dependency_key]['sources'] as $source_uuid) {
                     $sources[] = $this->dependencies[$source_uuid];
                 }
                 $paths = $this->getAllParentTrails($sources);
                 $paths_with_notrequired = 0;
                 foreach ($paths as $path) {
                     foreach ($path as $item) {
                         if (!array_key_exists('required', $this->dependencies[$item]) || !$this->dependencies[$item]['required']) {
                             $paths_with_notrequired++;
                             break;
                         }
                     }
                 }
                 if ($paths_with_notrequired == count($paths)) {
                     $this->dependencies[$dependency_key]['required'] = false;
                 }
             } else {
                 // If the dependency doesn't have any sources, it is not
                 // required.
                 $this->dependencies[$dependency_key]['required'] = false;
             }
         }
         // Now, loop through the dependencies again and fill in their
         // 'required_if' values. If a dependency is marked as not required,
         // go through its trails and find the lowest parent that is marked
         // as having a relationship and add it to the 'required_if' list
         // if it isn't already there.
         foreach ($this->dependencies as $dependency_key => &$dependency) {
             $dependency['required_if'] = array();
             // If the dependency is marked as required already, is a relationship,
             // or doesn't have any sources, skip it.
             if ($dependency['required']) {
                 continue;
             }
             if (!array_key_exists('sources', $dependency) || !is_array($dependency['sources'])) {
                 continue;
             }
             // Loop through its parent trails to find the non-required items.
             $required_if = array();
             $sources = array();
             foreach ($dependency['sources'] as $source_uuid) {
                 // If the source UUID is the owner of the relationship, don't add it.
                 $skip = false;
                 foreach ($this->relationships as $relationship) {
                     if ($relationship['source_uuid'] == $source_uuid && $relationship['destination_uuid'] == $dependency['uuid']) {
                         $skip = true;
                         break;
                     }
                 }
                 if ($skip) {
                     continue;
                 }
                 $sources[] = $this->dependencies[$source_uuid];
             }
             $paths = $this->getAllParentTrails($sources);
             foreach ($paths as $path) {
                 $found = false;
                 foreach ($path as $index => $item) {
                     if (array_key_exists('has_relationship', $this->dependencies[$item]) && $this->dependencies[$item]['has_relationship']) {
                         $found = true;
                         $required_if[] = array_slice($path, 0, $index + 1);
                         break;
                     }
                 }
                 if (!$found) {
                     if (count($path) === 1) {
                         $required_if[] = $path;
                     } else {
                         for ($i = 1; $i < count($path); $i++) {
                             $required_if[] = array_slice($path, 0, $i);
                         }
                     }
                 }
             }
             // Add it to the dependency.
             $required_if_entities = array();
             foreach ($required_if as $path) {
                 $required_if_entities[] = end($path);
             }
             $dependency['required_if'] = array_unique($required_if_entities);
         }
         // Reset the dependency dependencies cache.
         drupal_static_reset('publisher_dependency_dependencies');
         // Finally, perform the topological sort on the dependencies.
         $this->topologicalSortDependencies();
     }
 }