--- /dev/null
+<?php
+
+/**
+ * @file
+ * Allows access to terms in a vocabulary to be limited by user or role.
+ */
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Form\FormState;
+use Drupal\permissions_by_term\Controller\PermissionsByTermController;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\node\NodeInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\taxonomy\Entity\Term;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Cache\Cache;
+
+/**
+ * Implements hook_help().
+ */
+function permissions_by_term_help($route_name, RouteMatchInterface $arg) {
+ switch ($route_name) {
+ case 'help.page.permissions_by_term':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The "Permissions by Term" (PbT) module allows taxonomy administrators the
+ ability to restrict setting individual terms on nodes by user
+ or role. If a user is unable to set any terms for a required
+ vocabulary, they are blocked from adding or editing content with
+ that vocabulary. For more information, see the online documentation for <a href=":PbT-documentation" target="_blan" title="Online Documentation">Permissions by Term</a>.', [':PbT-documentation' => 'https://www.drupal.org/project/permissions_by_term']) . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('General') . '</dt>';
+ $output .= '<dd>' . t('Use Permissions by Term to easily build access-restricted content areas on your websites.') . '</dd>';
+ $output .= '<dt>' . t('Lightweight Access Control') . '</dt>';
+ $output .= '<dd>' . t('Permissions by Term restricts user access to specified Drupal nodes based on taxonomy terms - a core part of Drupal’s functionality. PbT lets you restrict content access while relying on very little contributed code.') . '</dd>';
+ $output .= '<dt>' . t('Example use cases') . '</dt>';
+ $output .= '<dd>' . t('A club or service site with premium- or member-only content.') . '</dd>';
+ $output .= '<dd>' . t('School websites with content intended for teachers only and content aimed at individual classes within the school.') . '</dd>';
+ $output .= '<dd>' . t('Company intranets with sensitive or proprietary content alongside non-restricted content.') . '</dd>';
+ $output .= '</dl>';
+
+ return $output;
+ }
+}
+
+/**
+ * Validation handler for permissions_by_term_form_alter().
+ */
+function permissions_by_term_validate($form, FormState $oFormState) {
+ foreach ($form as $field) {
+ if (!is_object($field) && !empty($field['widget']['target_id']['#target_type']) && $field['widget']['target_id']['#target_type'] == 'taxonomy_term') {
+ $field_name = $field['widget']['#field_name'];
+ $terms = $oFormState->getValues()[$field_name]['target_id'];
+ $not_allowed_term_names = [];
+ if (!empty($terms)) {
+ foreach ($terms as $term) {
+ $term_id = $term['target_id'];
+ /* @var \Drupal\permissions_by_term\AccessCheck $access_check_service */
+ $access_check_service = \Drupal::service('permissions_by_term.access_check');
+ if (!$access_check_service->isAccessAllowedByDatabase($term_id)) {
+ $term = Term::load($term_id);
+ $not_allowed_term_names[] = $term->getName();
+ }
+ }
+ }
+ }
+ }
+ if (!empty($not_allowed_term_names)) {
+ if (count($not_allowed_term_names) > 1) {
+ $term_names = implode(', ', $not_allowed_term_names);
+ }
+ else {
+ $term_names = $not_allowed_term_names['0'];
+ }
+ $oFormState->setErrorByName('field_tags', t('You are not allowed to use specific taxonomy terms like the following: "term-names". Remove the restricted taxonomy terms from the form field and try again.',
+ ['term-names' => $term_names]));
+ }
+}
+
+/**
+ * Submit handler for permissions_by_term_form_alter().
+ */
+function permissions_by_term_submit($form, FormState $formState) {
+ $termId = $formState->getFormObject()->getEntity()->id();
+ /* @var \Drupal\permissions_by_term\AccessStorage $access_storage */
+ $access_storage = \Drupal::service('permissions_by_term.access_storage');
+ $access_storage->saveTermPermissions($formState, $termId);
+ /**
+ * @var \Drupal\permissions_by_term\NodeAccess $nodeAccess
+ */
+ $nodeAccess = \Drupal::service('permissions_by_term.node_access');
+ $nodeAccess->rebuildByTid($termId, $formState);
+ Cache::invalidateTags(['search_index:node_search']);
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function permissions_by_term_form_taxonomy_term_form_alter(&$form, FormStateInterface $oFormState, $form_id) {
+ if (\Drupal::currentUser()->hasPermission('show term permission form on term page')) {
+ $iTermId = $oFormState->getFormObject()->getEntity()->id();
+
+ /* @var \Drupal\permissions_by_term\AccessStorage $access_storage */
+ $access_storage = \Drupal::service('permissions_by_term.access_storage');
+
+ $form['access'] = [
+ '#type' => 'fieldset',
+ '#title' => t('Permissions'),
+ '#description' => t('To limit access to this term by user or roles,
+ add users or roles to the following lists. Leave empty to allow
+ node access by single node view, node listing in views and taxonomy
+ term selection by all users.'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#attributes' => ['id' => 'fieldset_term_access'],
+ '#weight' => -5,
+ '#tree' => TRUE,
+ ];
+
+ $aAllowedUsers = $access_storage->getAllowedUserIds($iTermId);
+ if (!empty($aAllowedUsers)) {
+ $aAllowedUsers = user_load_multiple($aAllowedUsers);
+ $sUserFormValue = $access_storage->getUserFormValue($aAllowedUsers);
+ }
+ else {
+ $sUserFormValue = NULL;
+ }
+
+ // Note that the autocomplete widget will only enable for users with the
+ // 'access profiles' permission. Other users will have to specify the name
+ // manually.
+ $form['access']['user'] = [
+ '#type' => 'entity_autocomplete',
+ '#target_type' => 'user',
+ '#title' => t('Allowed users'),
+ '#description' => t('Enter a comma-seperated list of user names to give') . ' ' .
+ t('them permission to use this term and access related nodes in single node view
+ and views listings.'),
+ '#value' => $sUserFormValue,
+ '#size' => 60,
+ '#autocomplete_route_name' => 'permissions_by_term.autocomplete_multiple',
+ '#weight' => -10,
+ ];
+
+ $aAllowedRoles = $access_storage->getExistingRoleTermPermissionsByTid($iTermId);
+
+ // Firstly fetch all translated allowed role names.
+ $aTranslatedAllowedRoleNames = [];
+ foreach ($aAllowedRoles as $role) {
+ $aTranslatedAllowedRoleNames[] = $role;
+ }
+
+ // Get all roles for the complete form and translate them.
+ $aTranslatedUserRoles = [];
+ $array_key_counter = 1;
+ foreach (user_roles() as $user_role_id => $user_role_name) {
+ $aTranslatedUserRoles[$user_role_id] = $user_role_name->label();
+ $array_key_counter++;
+ }
+
+ // Generate the default values for the form.
+ $aSetRoles = [];
+ if (!empty($aTranslatedAllowedRoleNames)) {
+ foreach ($aTranslatedAllowedRoleNames as $role_name) {
+ $aSetRoles[] = $role_name;
+ }
+ }
+
+ // Now, lets do the Roles table.
+ $form['access']['role'] = [
+ '#type' => 'checkboxes',
+ '#title' => t('Allowed roles'),
+ '#description' => t('Select a role to allow all members of this role to
+ use this term and access related nodes in single node view and views
+ listings.'),
+ '#default_value' => $aSetRoles,
+ '#options' => $aTranslatedUserRoles,
+ '#multiple' => FALSE,
+ '#weight' => 5,
+ ];
+
+ $form['#validate'][] = 'permissions_by_term_validate';
+ $form['actions']['submit']['#submit'][] = 'permissions_by_term_submit';
+ }
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function permissions_by_term_form_alter(&$form, FormStateInterface $oFormState, $form_id) {
+ $form['#validate'][] = 'permissions_by_term_validate';
+}
+
+/**
+ * Implements hook_node_access().
+ *
+ * Forwards user by drupal_access_denied(); to an access denied page, if a
+ * single restricted node is called.
+ *
+ * This hook is not fired if admin is logged in. Users with the
+ * "bypass node access" permission may always view and edit content
+ * through the administrative interface.
+ */
+function permissions_by_term_node_access(NodeInterface $node, $op, AccountInterface $account) {
+ if (method_exists($node, 'id') && $op == 'view') {
+ if (!$node->isPublished() && !$account->hasPermission('Bypass content access control', $account)) {
+ return AccessResult::forbidden();
+ }
+
+ /* @var \Drupal\permissions_by_term\AccessCheck $access_check_service */
+ $access_check_service = \Drupal::service('permissions_by_term.access_check');
+ $oPermissionsByTermController = new PermissionsByTermController($access_check_service);
+
+ return $oPermissionsByTermController->handleNode($node->id());
+ }
+}
+
+/**
+ * Implements hook_node_grants().
+ */
+function permissions_by_term_node_grants(\Drupal\Core\Session\AccountInterface $account, $op)
+{
+ if ($op == 'view') {
+ /**
+ * @var \Drupal\permissions_by_term\AccessStorage $accessStorage
+ */
+ $accessStorage = \Drupal::service('permissions_by_term.access_storage');
+ $grants = $accessStorage->getGidsByRealm('permissions_by_term__uid_' . \Drupal::currentUser()->id());
+
+ return $grants;
+ }
+}
+
+/**
+ * Implements hook_node_access_records().
+ *
+ * Permissions can be rebuild at /admin/reports/status/rebuild.
+ */
+function permissions_by_term_node_access_records(\Drupal\node\NodeInterface $node) {
+ /**
+ * @var \Drupal\permissions_by_term\NodeAccess $nodeAccess
+ */
+ $nodeAccess = \Drupal::service('permissions_by_term.node_access');
+ $grantsForThisNode = $nodeAccess->createGrants($node->id());
+
+ $grants = [];
+ if (!empty($grantsForThisNode)) {
+ foreach ($grantsForThisNode as $grantObject) {
+ $grants[] = [
+ 'realm' => $grantObject->realm,
+ 'gid' => $grantObject->gid,
+ 'grant_view' => $grantObject->grant_view,
+ 'grant_update' => $grantObject->grant_update,
+ 'grant_delete' => $grantObject->grant_delete,
+ 'langcode' => $grantObject->langcode,
+ 'fallback' => 1,
+ 'nid' => $node->id(),
+ ];
+ }
+ }
+
+ return $grants;
+}
+
+/**
+ * Implements hook_user_insert().
+ */
+function permissions_by_term_user_insert($user) {
+ /**
+ * @var \Drupal\permissions_by_term\NodeAccess $nodeAccess
+ */
+ $nodeAccess = \Drupal::service('permissions_by_term.node_access');
+ $nodeAccess->rebuildByUid($user->id(), TRUE);
+ Cache::invalidateTags(['search_index:node_search']);
+}
+
+/**
+ * Implements hook_user_update().
+ */
+function permissions_by_term_user_update($user) {
+ /**
+ * @var \Drupal\permissions_by_term\NodeAccess $nodeAccess
+ */
+ $nodeAccess = \Drupal::service('permissions_by_term.node_access');
+ $nodeAccess->rebuildByUid($user->id());
+ Cache::invalidateTags(['search_index:node_search']);
+}
+
+/**
+ * Implements hook_node_insert().
+ */
+function permissions_by_term_node_insert($node) {
+ /**
+ * @var \Drupal\permissions_by_term\NodeAccess $nodeAccess
+ */
+ $nodeAccess = \Drupal::service('permissions_by_term.node_access');
+ $nodeAccess->rebuildByNid($node->id());
+ Cache::invalidateTags(['search_index:node_search']);
+}