Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / permissions_by_term / permissions_by_term.module
1 <?php
2
3 /**
4  * @file
5  * Allows access to terms in a vocabulary to be limited by user or role.
6  */
7
8 use Drupal\Core\Cache\Cache;
9 use Drupal\Core\Entity\EntityInterface;
10 use Drupal\Core\Form\FormState;
11 use Drupal\Core\Form\FormStateInterface;
12 use Drupal\Core\Routing\RouteMatchInterface;
13 use Drupal\Core\Session\AccountInterface;
14 use Drupal\node\NodeInterface;
15 use Drupal\taxonomy\Entity\Term;
16
17 /**
18  * Implements hook_help().
19  */
20 function permissions_by_term_help($route_name, RouteMatchInterface $arg) {
21   switch ($route_name) {
22     case 'help.page.permissions_by_term':
23       $output = '';
24       $output .= '<h3>' . t('About') . '</h3>';
25       $output .= '<p>' . t('The "Permissions by Term" (PbT) module allows taxonomy administrators the
26         ability to restrict setting individual terms on nodes by user
27         or role. If a user is unable to set any terms for a required
28         vocabulary, they are blocked from adding or editing content with
29         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/docs/8/modules/permissions-by-term']) . '</p>';
30       $output .= '<h3>' . t('Uses') . '</h3>';
31       $output .= '<dl>';
32       $output .= '<dt>' . t('General') . '</dt>';
33       $output .= '<dd>' . t('Use Permissions by Term to easily build access-restricted content areas on your websites.') . '</dd>';
34       $output .= '<dt>' . t('Lightweight Access Control') . '</dt>';
35       $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>';
36       $output .= '<dt>' . t('Example use cases') . '</dt>';
37       $output .= '<dd>' . t('A club or service site with premium- or member-only content.') . '</dd>';
38       $output .= '<dd>' . t('School websites with content intended for teachers only and content aimed at individual classes within the school.') . '</dd>';
39       $output .= '<dd>' . t('Company intranets with sensitive or proprietary content alongside non-restricted content.') . '</dd>';
40       $output .= '</dl>';
41
42       return $output;
43   }
44 }
45
46 /**
47  * Validation handler for permissions_by_term_form_alter().
48  */
49 function permissions_by_term_validate($form, FormState $oFormState) {
50   foreach ($form as $field) {
51     if (!is_object($field) && !empty($field['widget']['target_id']['#target_type']) && $field['widget']['target_id']['#target_type'] == 'taxonomy_term') {
52       $field_name = $field['widget']['#field_name'];
53       $terms = $oFormState->getValues()[$field_name]['target_id'];
54       $not_allowed_term_names = [];
55       if (!empty($terms)) {
56         foreach ($terms as $term) {
57           if (!empty($term['target_id'])) {
58             $term_id = $term['target_id'];
59             /* @var \Drupal\permissions_by_term\Service\AccessCheck $access_check_service */
60             $access_check_service = \Drupal::service('permissions_by_term.access_check');
61             if (!$access_check_service->isAccessAllowedByDatabase($term_id)) {
62               $term = Term::load($term_id);
63               $not_allowed_term_names[] = $term->getName();
64             }
65           }
66         }
67       }
68     }
69   }
70   if (!empty($not_allowed_term_names)) {
71     if (count($not_allowed_term_names) > 1) {
72       $term_names = implode(', ', $not_allowed_term_names);
73     }
74     else {
75       $term_names = $not_allowed_term_names['0'];
76     }
77     $oFormState->setErrorByName('field_tags', t('You are not allowed to use taxonomy terms like: "@termNames". Remove the restricted taxonomy terms from the form field and try again.',
78       ['@termNames' => $term_names]));
79   }
80 }
81
82 /**
83  * Submit handler for permissions_by_term_form_alter().
84  */
85 function permissions_by_term_submit($form, FormState $formState) {
86   $termId = $formState->getFormObject()->getEntity()->id();
87   /* @var \Drupal\permissions_by_term\Service\AccessStorage $access_storage */
88   $access_storage = \Drupal::service('permissions_by_term.access_storage');
89   $access_update = $access_storage->saveTermPermissions($formState, $termId);
90
91   // Check if we need to rebuild cache and node_access
92   $rebuild_cache_and_node_access = false;
93
94   // Has anything has changed?
95   foreach($access_update as $values) {
96     if(!empty($values)) {
97       $rebuild_cache_and_node_access = true;
98       break;
99     }
100   }
101
102   // Do we need to flush the cache and the node access records?
103   if($rebuild_cache_and_node_access === true) {
104     node_access_rebuild(TRUE);
105     Cache::invalidateTags(['search_index:node_search']);
106   }
107 }
108
109 /**
110  * Implements hook_form_alter().
111  */
112 function permissions_by_term_form_taxonomy_term_form_alter(&$form, FormStateInterface $formState, $form_id) {
113   if (\Drupal::currentUser()->hasPermission('show term permission form on term page')) {
114     $termId = $formState->getFormObject()->getEntity()->id();
115
116     /* @var \Drupal\permissions_by_term\Service\AccessStorage $access_storage */
117     $access_storage = \Drupal::service('permissions_by_term.access_storage');
118
119     $description = <<<EOT
120 To limit access to this term by user(s) or role(s), select users or roles above.
121 If left empty, all users will have access to content, related to this taxonomy term
122 and this taxonomy term itself.
123 EOT;
124
125     $form['access'] = [
126       '#type'        => 'details',
127       '#title'       => t('Permissions'),
128       '#description' => t($description),
129       '#attributes'  => ['id' => 'fieldset_term_access'],
130       '#weight'      => -5,
131       '#tree'        => TRUE,
132     ];
133
134     $langcode = \Drupal::languageManager()->getCurrentLanguage()->getId();
135     if (!empty($formState->getValue('langcode'))) {
136       $langcode = $formState->getValue('langcode')['0']['value'];
137     }
138
139     $aAllowedUsers = $access_storage->getAllowedUserIds($termId, $langcode);
140     if (!empty($aAllowedUsers)) {
141       $aAllowedUsers = user_load_multiple($aAllowedUsers);
142       $sUserFormValue = $access_storage->getUserFormValue($aAllowedUsers);
143     }
144     else {
145       $sUserFormValue = NULL;
146     }
147
148     $description = <<<EOT
149 Enter a comma-separated list of user names who will be able to access content,
150 related to this taxonomy term.
151 EOT;
152
153     // Note that the autocomplete widget will only enable for users with the
154     // 'access profiles' permission. Other users will have to specify the name
155     // manually.
156     $form['access']['user'] = [
157       '#type'                    => 'entity_autocomplete',
158       '#target_type'             => 'user',
159       '#title'                   => t('Allowed users'),
160       '#description'             => t($description),
161       '#value'                   => $sUserFormValue,
162       '#size'                    => 60,
163       '#autocomplete_route_name' => 'permissions_by_term.autocomplete_multiple',
164       '#weight'                  => -10,
165     ];
166
167     $aAllowedRoles = $access_storage->getRoleTermPermissionsByTid($termId, $langcode);
168
169     // Firstly fetch all translated allowed role names.
170     $aTranslatedAllowedRoleNames = [];
171     foreach ($aAllowedRoles as $role) {
172       $aTranslatedAllowedRoleNames[] = $role;
173     }
174
175     // Get all roles for the complete form and translate them.
176     $aTranslatedUserRoles = [];
177     $array_key_counter = 1;
178     foreach (user_roles() as $user_role_id => $user_role_name) {
179       $aTranslatedUserRoles[$user_role_id] = $user_role_name->label();
180       $array_key_counter++;
181     }
182
183     // Generate the default values for the form.
184     $aSetRoles = [];
185     if (!empty($aTranslatedAllowedRoleNames)) {
186       foreach ($aTranslatedAllowedRoleNames as $role_name) {
187         $aSetRoles[] = $role_name;
188       }
189     }
190
191     $description = <<<EOT
192 Select user roles who will be able to access content, related to this taxonomy term.
193 EOT;
194
195     // Now, lets do the Roles table.
196     $form['access']['role'] = [
197       '#type'          => 'checkboxes',
198       '#title'         => t('Allowed roles'),
199       '#description'   => t($description),
200       '#default_value' => $aSetRoles,
201       '#options'       => $aTranslatedUserRoles,
202       '#multiple'      => FALSE,
203       '#weight'        => 5,
204     ];
205
206     $form['#validate'][] = 'permissions_by_term_validate';
207     $form['actions']['submit']['#submit'][] = 'permissions_by_term_submit';
208   }
209 }
210
211 /**
212  * Implements hook_form_alter().
213  */
214 function permissions_by_term_form_alter(&$form, FormStateInterface $formState, $form_id) {
215   $form['#validate'][] = 'permissions_by_term_validate';
216   if (isNodeEditForm()) {
217     $form['permissions_by_term_info'] = [
218       '#type' => 'details',
219       '#group' => 'advanced',
220       '#title' => t('Permissions by Term'),
221       '#access' => \Drupal::currentUser()->hasPermission('show term permissions on node edit page'),
222     ];
223
224     $langcode = \Drupal::languageManager()->getCurrentLanguage()->getId();
225     if (!empty($formState->getUserInput()['langcode']['0']['value'])) {
226       $langcode = $formState->getUserInput()['langcode']['0']['value'];
227     }
228
229     $nid = null;
230     if (!empty($node = \Drupal::routeMatch()->getParameter('node'))) {
231       $nid = $node->id();
232     }
233
234     $viewFilePath = drupal_get_path('module', 'permissions_by_term') . '/src/View/node-details.html.twig';
235     /**
236      * @var \Drupal\permissions_by_term\Service\NodeEntityBundleInfo $nodeEntityBundleInfo
237      */
238     $nodeEntityBundleInfo = \Drupal::service('permissions_by_term.node_entity_bundle_info');
239
240     $form['permissions_by_term_info']['revision'] = array(
241       '#type' => 'item',
242       '#markup' => $nodeEntityBundleInfo->renderNodeDetails($viewFilePath, $langcode, $nid),
243     );
244
245     $form['#attached']['library'][] = 'permissions_by_term/nodeForm';
246   }
247 }
248
249 function isNodeEditForm() {
250   $currentPath = \Drupal::service('path.current')->getPath();
251   if (is_numeric(strpos($currentPath, '/node/'))
252     && (is_numeric(strpos($currentPath, '/edit')) || is_numeric(strpos($currentPath, '/add')))) {
253     return TRUE;
254   }
255   return FALSE;
256 }
257
258 /**
259  * Implements hook_node_access().
260  *
261  * Forwards user by drupal_access_denied(); to an access denied page, if a
262  * single restricted node is called.
263  *
264  * This hook is not fired if admin is logged in. Users with the
265  * "bypass node access" permission may always view and edit content
266  * through the administrative interface.
267  */
268 function permissions_by_term_node_access(NodeInterface $node, $op, AccountInterface $account) {
269   /* @var \Drupal\permissions_by_term\Service\AccessCheck $accessCheck */
270   $accessCheck = \Drupal::service('permissions_by_term.access_check');
271
272   return $accessCheck->handleNode($node->id(), $node->language()->getId());
273 }
274
275 /**
276  * Implements hook_node_grants().
277  */
278 function permissions_by_term_node_grants(\Drupal\Core\Session\AccountInterface $account, $op)
279 {
280     if ($op == 'view') {
281       /**
282        * @var \Drupal\permissions_by_term\Service\AccessStorage $accessStorage
283        */
284       $accessStorage = \Drupal::service('permissions_by_term.access_storage');
285       $grants = $accessStorage->getGids(\Drupal::currentUser());
286
287       return $grants;
288     }
289 }
290
291 /**
292  * Implements hook_node_access_records().
293  *
294  * Permissions can be rebuild at /admin/reports/status/rebuild.
295  */
296 function permissions_by_term_node_access_records(\Drupal\node\NodeInterface $node) {
297   // Do not return any grants for nodes that this module doesn't manage.
298   if (!$node->isPublished()) {
299     return;
300   }
301   $has_term_access_restrictions = FALSE;
302   /* @var \Drupal\permissions_by_term\Service\AccessStorage $access_storage */
303   $access_storage = \Drupal::service('permissions_by_term.access_storage');
304   foreach ($access_storage->getTidsByNid($node->id()) as $tid) {
305     /* @var \Drupal\permissions_by_term\Service\AccessCheck $access_check_service */
306     $access_check_service = \Drupal::service('permissions_by_term.access_check');
307     if($node->language()->getId() == 'und'){
308       // Current system default language
309       $language = \Drupal::languageManager()->getCurrentLanguage()->getId();
310     }
311     else {
312       $language = $node->language()->getId();
313     }
314     if ($access_check_service->isAnyPermissionSetForTerm($tid, $language)) {
315       $has_term_access_restrictions = TRUE;
316       break;
317     }
318   }
319   if (!$has_term_access_restrictions) {
320     return;
321   }
322
323   /**
324    * @var \Drupal\permissions_by_term\Service\NodeAccess $nodeAccess
325    */
326   $nodeAccess = \Drupal::service('permissions_by_term.node_access');
327   $grantObject = $nodeAccess->createGrant($node->id(), $node->id());
328
329   $grants[] = [
330     'realm'        => $grantObject->realm,
331     'gid'          => $grantObject->gid,
332     'grant_view'   => $grantObject->grant_view,
333     'grant_update' => $grantObject->grant_update,
334     'grant_delete' => $grantObject->grant_delete,
335     'nid'          => $node->id(),
336   ];
337
338   return $grants;
339 }
340
341 /**
342  * Implements hook_user_insert().
343  */
344 function permissions_by_term_user_insert($user) {
345   Cache::invalidateTags(['search_index:node_search']);
346 }
347
348 /**
349  * Implements hook_user_update().
350  */
351 function permissions_by_term_user_update($user) {
352   if (\Drupal::currentUser()->hasPermission('administer permissions')) {
353     Cache::invalidateTags(['search_index:node_search']);
354   }
355 }
356
357 /**
358  * Implements hook_node_insert().
359  */
360 function permissions_by_term_node_insert($node) {
361   Cache::invalidateTags(['search_index:node_search']);
362 }
363
364 /**
365  * Implements hook_options_list_alter().
366  */
367 function permissions_by_term_options_list_alter(array &$options, array $context) {
368   $fieldDefinitionSettings = $context['fieldDefinition']->getFieldStorageDefinition()->getSettings();
369   if (!empty($fieldDefinitionSettings['target_type']) && $fieldDefinitionSettings['target_type'] == 'taxonomy_term') {
370     foreach ($options as $id => $names) {
371       if ($id !== '_none') {
372         /**
373          * @var \Drupal\permissions_by_term\Service\AccessCheck $accessCheck
374          */
375         $accessCheck = \Drupal::service('permissions_by_term.access_check');
376
377         if (is_array($names)) {
378           foreach ($names as $group_id => $name) {
379             if (!$accessCheck->isAccessAllowedByDatabase($group_id)) {
380               unset($options[$id]);
381             }
382           }
383         } elseif(is_string($names)) {
384           if (!$accessCheck->isAccessAllowedByDatabase($id)) {
385             unset($options[$id]);
386           }
387         }
388       }
389
390     }
391   }
392 }
393
394 /**
395  * Implements hook_user_cancel().
396  *
397  * Deletes all term permissions for a user when their account is cancelled.
398  */
399 function permissions_by_term_user_cancel($edit, $account, $method) {
400   $deleted_user_id = $account->id();
401
402   /* @var \Drupal\permissions_by_term\Service\AccessStorage $access_storage */
403   $access_storage = \Drupal::service('permissions_by_term.access_storage');
404   $access_storage->deleteAllTermPermissionsByUserId($deleted_user_id);
405 }
406
407 /**
408  * Implements hook_ENTITY_TYPE_delete().
409  *
410  * Deletes all term permissions from storage when a term is deleted.
411  */
412 function permissions_by_term_taxonomy_term_delete(EntityInterface $entity) {
413   /* @var \Drupal\permissions_by_term\Service\AccessStorage $access_storage */
414   $access_storage = \Drupal::service('permissions_by_term.access_storage');
415   $access_storage->deleteAllTermPermissionsByTid($entity->id());
416 }