association-1.0.0-alpha2/association.module

association.module
<?php

/**
 * @file
 * Drupal module hooks and global functions for the entity assocation module.
 */

use Drupal\association\Entity\AssociationInterface;
use Drupal\association\EntityUpdater\PathAliasUpdater;
use Drupal\association\EntityUpdater\SearchApiUpdater;
use Drupal\association\Event\AssociatedEntityFormEvent;
use Drupal\association\Event\AssociatedEntityUpdaterAlterEvent;
use Drupal\association\Event\AssociationEvents;
use Drupal\association\Field\AssociationReferenceItemList;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Entity\EntityFormInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Utility\Error;
use Drupal\toolshed\Strategy\Exception\StrategyException;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\Plugin\views\query\Sql;
use Drupal\views\ViewExecutable;

/**
 * Implements hook_module_implements_alter().
 */
function association_module_implements_alter(&$implementations, $hook) {
  // Ensure that the association hook_entity_update() runs last.
  if ($hook === 'entity_update') {
    $callback = $implementations['association'];
    unset($implementations['association']);
    $implementations['association'] = $callback;
  }
}

/**
 * Implements hook_data_type_info_alter().
 */
function association_data_type_info_alter(&$data_types) {
  $entity_type_manager = \Drupal::entityTypeManager();
  $link_type_def = $entity_type_manager->getDefinition('association_link');

  if ($link_type_def && !empty($data_types['entity:association_link'])) {
    /** @var \Drupal\association\EntityAdapterManagerInterface $adapter_manager */
    $adapter_manager = \Drupal::service('plugin.manager.association.entity_adapter');
    $data_type_def = $data_types['entity:association_link'];

    // Prefer the bundle definition as the base if it is available.
    if (!empty($data_types['entity:association_link:association_link'])) {
      $data_type_def = $data_types['entity:association_link:association_link'];
      unset($data_types['entity:association_link:association_link']);
    }

    foreach ($adapter_manager->getEntityTypes() as $entity_type_id) {
      $type_def = $entity_type_manager->getDefinition(($entity_type_id));
      $data_type_id = 'entity:association_link:' . $entity_type_id;
      $data_types[$data_type_id] = $data_type_def;
      $data_types[$data_type_id]['label'] = new TranslatableMarkup('@label association link', [
        '@label' => $type_def ? $type_def->getLabel() : 'Content',
      ]);
    }
  }
}

/**
 * Implements hook_condition_info_alter().
 */
function association_condition_info_alter(&$conditions) {
  // Remove CTools derived entity condition plugin for entity associations as
  // these cause confusion and do not handle "none" condition correctly.
  if (isset($conditions['entity_bundle:association'])) {
    unset($conditions['entity_bundle:association']);
  }
}

/**
 * Implements hook_entity_bundle_field_info().
 */
function association_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
  $adapter_manager = \Drupal::service('plugin.manager.association.entity_adapter');
  $fields = [];

  // If a supported entity type is the entity being worked with, add a
  // association back reference field.
  if ($adapter_manager->isAssociableType($entity_type->id())) {
    $fields['associations'] = BaseFieldDefinition::create('entity_reference')
      ->setTargetEntityTypeId($entity_type->id())
      ->setName(t('Associations'))
      ->setSetting('target_type', 'association_link')
      ->setLabel(t('Associations for @entity_type_label entities.', [
        '@entity_type_label' => $entity_type->getLabel(),
      ]))
      ->setDescription(t('A list of all entity associations this entity belongs to.'))
      ->setRevisionable(FALSE)
      ->setTranslatable(FALSE)
      ->setComputed(TRUE)
      ->setCardinality(1)
      ->setClass(AssociationReferenceItemList::class)
      ->setDisplayConfigurable('view', TRUE);
  }

  return $fields;
}

/**
 * Implements hook_entity_access().
 */
function association_entity_access(EntityInterface $entity, $op, AccountInterface $account) {
  try {
    /** @var \Drupal\association\EntityAdapterManagerInterface $adapter_manager */
    $adapter_manager = \Drupal::service('plugin.manager.association.entity_adapter');

    if ($adapter = $adapter_manager->getAdapter($entity)) {
      return $adapter->checkAccess($entity, $op, $account);
    }
  }
  catch (StrategyException) {
    // Not an associatable entity type.
  }

  return AccessResult::neutral();
}

/**
 * Implements hook_query_TAG_alter() for entity_query.
 */
function association_query_entity_query_alter(AlterableInterface $query) {
  $adapter_manager = \Drupal::service('plugin.manager.association.entity_adapter');
  $entity_type_id = $query->getMetaData('entity_type');

  if ($query->hasTag($entity_type_id . '_access') && $adapter_manager->isAssociableType($entity_type_id)) {
    $assoc_type = \Drupal::entityTypeManager()->getDefinition('association');
    $account = $query->getMetaData('account') ?: \Drupal::currentUser();
    $op = $query->getMetaData('op') ?: 'view';

    // If user account has permission to bypass association access checks,
    // we can skip the query alter as no additional restrictions are needed.
    if (!$account->hasPermission($assoc_type->getAdminPermission())) {
      if ($adapter = $adapter_manager->getAdapterByEntityType($entity_type_id)) {
        // Alter the query to respect permissions from entity associations.
        $adapter->accessQueryAlter($query, $op, $account);
      }
    }
  }
}

/**
 * Implements hook_query_TAG_alter() for views_entity_query.
 */
function association_query_views_entity_query_alter(AlterableInterface $query) {
  $entity_type_id = $query->getMetaData('entity_type');
  $query->addTag($entity_type_id . '_access');

  association_query_entity_query_alter($query);
}

/**
 * Implements hook_views_query_alter().
 *
 * Borrowed from the "group" module, and is skipped if the group module is
 * also installed since it will take care of this. Included here so association
 * module can be used without requiring the groups module.
 */
function association_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if (\Drupal::moduleHandler()->moduleExists('group')) {
    // The group module does this for us if it's installed and running skip.
    return;
  }

  if ($query instanceof Sql && empty($query->options['disable_sql_rewrite'])) {
    $tables = $query->getEntityTableInfo();
    $base_table = reset($tables);

    if (empty($base_table['entity_type']) || $base_table['relationship_id'] != 'none') {
      return;
    }

    // Add a custom tag so we don't trigger all other 'entity_query' tag alters.
    $entity_type_id = $base_table['entity_type'];
    $query->addTag('views_entity_query');

    // Build the Views query already so we can access the DB query.
    $query->build($view);
    $view->built = TRUE;

    // Add metadata to the DB query.
    $query = $view->build_info['query'];
    $count_query = $view->build_info['count_query'];
    $query->addMetaData('entity_type', $entity_type_id);
    $count_query->addMetaData('entity_type', $entity_type_id);
  }
}

/**
 * Implements hook_form_alter().
 *
 * Check if form is an entity form which belongs to an entity association, and
 * if it is apply association specific form alter hooks.
 */
function association_form_alter(array &$form, $form_state, $form_id) {
  $formObj = $form_state->getFormObject();

  if ($formObj instanceof EntityFormInterface) {
    $entity = $formObj->getEntity();
    $operation = $formObj->getOperation();
    $events = AssociatedEntityFormEvent::getEventNames();
    $negotiator = \Drupal::service('association.negotiator');

    if (!empty($events[$operation]) && ($association = $negotiator->byEntity($entity))) {
      $event = new AssociatedEntityFormEvent($operation, $form, $form_state, $entity, $association);
      \Drupal::service('event_dispatcher')->dispatch($event, $event->getEventName());
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for pathauto_pattern_form.
 */
function association_form_pathauto_pattern_form_alter(array &$form, FormStateInterface $form_state) {
  $formObj = $form_state->getFormObject();

  if ($formObj instanceof EntityFormInterface) {
    $entity = $formObj->getEntity();

    // Alters the pathauto pattern to include entity association conditions
    // to target content based on the associations they belong to.
    \Drupal::service('association.pathauto.helper')->alterForm($entity, $form, $form_state);
  }
}

/**
 * Implements hook_entity_update().
 */
function association_entity_update(EntityInterface $entity) {
  // Check if associated content needs to get updated. Depending on the
  // number of items being updated, this update hook attempts to run them
  // in-place and queuing any additional items to cron if the number is large.
  if ($entity instanceof AssociationInterface) {
    $updaters = [];

    if (PathAliasUpdater::applies($entity)) {
      $updaters['path_alias'] = PathAliasUpdater::class;
    }
    if (SearchApiUpdater::applies($entity)) {
      $updaters['search_api'] = SearchApiUpdater::class;
    }

    $event = new AssociatedEntityUpdaterAlterEvent($updaters, $entity);
    \Drupal::service('event_dispatcher')
      ->dispatch($event, AssociationEvents::ENTITY_UPDATER_ALTER);

    if ($updaters) {
      $queueItem = new \stdClass();
      $queueItem->current = 0;
      $queueItem->association = $entity->id();
      $queueItem->updaters = $updaters;

      try {
        // Run one iteration of the queue worker. If exceeds maximum
        // batch size, future updates will happen on the next CRON run.
        \Drupal::service('plugin.manager.queue_worker')
          ->createInstance('association_linked_content_updater')
          ->processItem($queueItem);
      }
      catch (\Exception $e) {
        // If a processing error occurs, we'll be unable to used this queue
        // so just exit log the error.
        $logger = \Drupal::logger('association_update');
        Error::logException($logger, $e);
      }
    }
  }

  $adapter_manager = \Drupal::service('plugin.manager.association.entity_adapter');
  if ($adapter_manager->isAssociable($entity)) {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $tags = [];

    // Invalidate association cache tags when the associated entity is updated.
    // Ensures entity updates propagate to other associated entities and blocks.
    /** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface */
    foreach ($entity->get('associations') as $item) {
      if ($link = $item->entity) {
        $tags = Cache::mergeTags($tags, $link->getCacheTagsToInvalidate());
      }
    }

    if ($tags) {
      Cache::invalidateTags($tags);
    }
  }
}

/**
 * Implements hook_entity_delete().
 */
function association_entity_delete(EntityInterface $entity) {
  $adapter_manager = \Drupal::service('plugin.manager.association.entity_adapter');

  if ($adapter_manager->isAssociable($entity)) {
    /** @var \Drupal\association\Entity\Storage\AssociationLinkStorageInterface */
    $link_storage = \Drupal::entityTypeManager()
      ->getStorage('association_link');

    if ($links = $link_storage->loadByTarget($entity)) {
      // Content is about to be deleted, don't allow association_link entity
      // to clear the target entity during it's clean-up, because this entity
      // is already being deleted.
      foreach ($links as $link) {
        $link->set('target', NULL);
      }

      $link_storage->delete($links);
    }
  }
}

/**
 * Implements hook_theme().
 */
function association_theme($existing, $type, $theme, $path) {
  return [
    'association' => [
      'render element' => 'element',
      'pattern' => 'association__',
      'file' => 'association.theme',
    ],
    'association_manifest_section' => [
      'render element' => 'element',
    ],
  ];
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc