3 namespace Drupal\permissions_by_term\Service;
5 use Drupal\Component\Utility\Tags;
6 use Drupal\Core\Database\Connection;
7 use Drupal\Core\Form\FormState;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\Core\Session\AccountInterface;
10 use Drupal\user\Entity\User;
11 use Drupal\user\Entity\Role;
14 * Class AccessStorage.
16 * @package Drupal\permissions_by_term
21 * The database connection.
28 * The term name for which the access is set.
35 * The user ids which gain granted access.
39 protected $aUserIdsGrantedAccess;
42 * The roles with granted access.
46 protected $aSubmittedRolesGrantedAccess;
56 const NODE_ACCESS_REALM = 'permissions_by_term';
61 protected $accessCheck;
64 * AccessStorageService constructor.
66 * @param Connection $database
67 * @param TermHandler $term
68 * @param AccessCheck $accessCheck
70 public function __construct(Connection $database, TermHandler $term, AccessCheck $accessCheck) {
71 $this->database = $database;
73 $this->accessCheck = $accessCheck;
77 * Gets submitted roles with granted access from form.
80 * An array with chosen roles.
82 public function getSubmittedRolesGrantedAccess(FormStateInterface $form_state) {
83 $aRoles = $form_state->getValue('access')['role'];
85 foreach ($aRoles as $sRole) {
87 $aChosenRoles[] = $sRole;
94 * @param FormState $form_state
96 public function checkIfUsersExists(FormState $form_state) {
97 $sAllowedUsers = $form_state->getValue('access')['user'];
98 $aAllowedUsers = Tags::explode($sAllowedUsers);
99 foreach ($aAllowedUsers as $sUserId) {
100 $aUserId = \Drupal::entityQuery('user')
101 ->condition('uid', $sUserId)
103 if (empty($aUserId)) {
104 $form_state->setErrorByName('access][user',
105 t('The user with ID %user_id does not exist.',
106 ['%user_id' => $sUserId]));
112 * @param int $term_id
116 public function getUserTermPermissionsByTid($term_id, $langcode) {
117 return $this->database->select('permissions_by_term_user', 'pu')
118 ->condition('tid', $term_id)
119 ->condition('langcode', $langcode)
120 ->fields('pu', ['uid'])
131 public function getPermittedTids($uid, $rids) {
132 $permittedTids = $this->database->select('permissions_by_term_user', 'pu')
133 ->condition('uid', $uid)
134 ->fields('pu', ['tid'])
138 foreach ($rids as $rid) {
139 $permittedTidsByRid = $this->database->select('permissions_by_term_role', 'pr')
140 ->condition('rid', $rid)
141 ->fields('pr', ['tid'])
145 $permittedTids = array_merge($permittedTidsByRid, $permittedTids);
148 return array_unique($permittedTids);
153 * @param string $langcode
156 public function getUserTermPermissionsByTids($tids, $langcode) {
159 foreach ($tids as $tid) {
160 if (!empty($tmpUids = $this->getUserTermPermissionsByTid($tid, $langcode))) {
161 foreach ($tmpUids as $tmpUid) {
171 * @param int $term_id
172 * @param string $langcode
176 public function getRoleTermPermissionsByTid($term_id, $langcode) {
177 return $this->database->select('permissions_by_term_role', 'pr')
178 ->condition('tid', $term_id)
179 ->condition('langcode', $langcode)
180 ->fields('pr', ['rid'])
187 * @param string $langcode
190 public function getRoleTermPermissionsByTids($tids, $langcode) {
193 foreach ($tids as $tid) {
194 $tmpRids = $this->getRoleTermPermissionsByTid($tid, $langcode);
195 if (!empty($tmpRids)) {
196 foreach ($tmpRids as $tmpRid) {
206 * @param string $sUsername
210 public function getUserIdByName($sUsername) {
211 return $this->database->select('users_field_data', 'ufd')
212 ->condition('name', $sUsername)
213 ->fields('ufd', ['uid'])
219 * @param array $aUserNames
223 public function getUserIdsByNames($aUserNames) {
225 foreach ($aUserNames as $userName) {
226 $iUserId = $this->getUserIdByName($userName)['uid'];
227 $aUserIds[] = $iUserId['uid'];
233 * @param int $term_id
237 public function getAllowedUserIds($term_id, $langcode) {
238 $query = $this->database->select('permissions_by_term_user', 'p')
239 ->fields('p', ['uid'])
240 ->condition('p.tid', $term_id)
241 ->condition('p.langcode', $langcode);
243 // fetchCol() returns all results, fetchAssoc() only "one" result.
244 return $query->execute()
249 * @param array $aUserIdsAccessRemove
250 * @param int $term_id
252 public function deleteTermPermissionsByUserIds($aUserIdsAccessRemove, $term_id, $langcode) {
253 foreach ($aUserIdsAccessRemove as $iUserId) {
254 $this->database->delete('permissions_by_term_user')
255 ->condition('uid', $iUserId, '=')
256 ->condition('tid', $term_id, '=')
257 ->condition('langcode', $langcode, '=')
263 * @param array $aRoleIdsAccessRemove
264 * @param int $term_id
266 public function deleteTermPermissionsByRoleIds($aRoleIdsAccessRemove, $term_id, $langcode) {
267 foreach ($aRoleIdsAccessRemove as $sRoleId) {
268 $this->database->delete('permissions_by_term_role')
269 ->condition('rid', $sRoleId, '=')
270 ->condition('tid', $term_id, '=')
271 ->condition('langcode', $langcode, '=')
279 public function deleteAllTermPermissionsByUserId($userId) {
280 $this->database->delete('permissions_by_term_user')
281 ->condition('uid', $userId, '=')
286 * Delete access storage when a term is removed.
288 * @param int $term_id
289 * The term ID being deleted.
291 public function deleteAllTermPermissionsByTid($term_id) {
292 $this->database->delete('permissions_by_term_user')
293 ->condition('tid', $term_id, '=')
296 $this->database->delete('permissions_by_term_role')
297 ->condition('tid', $term_id, '=')
302 * @param array $aUserIdsGrantedAccess
303 * @param int $term_id
304 * @param string $langcode
308 public function addTermPermissionsByUserIds($aUserIdsGrantedAccess, $term_id, $langcode = '') {
309 $langcode = ($langcode === '') ? \Drupal::languageManager()->getCurrentLanguage()->getId() : $langcode;
311 foreach ($aUserIdsGrantedAccess as $iUserIdGrantedAccess) {
312 $queryResult = $this->database->query("SELECT uid FROM {permissions_by_term_user} WHERE tid = :tid AND uid = :uid AND langcode = :langcode",
313 [':tid' => $term_id, ':uid' => $iUserIdGrantedAccess, ':langcode' => $langcode])->fetchField();
314 if (empty($queryResult)) {
315 $this->database->insert('permissions_by_term_user')
316 ->fields(['tid', 'uid', 'langcode'], [
318 $iUserIdGrantedAccess,
327 * @param array $aRoleIdsGrantedAccess
328 * @param int $term_id
329 * @param string $langcode
333 public function addTermPermissionsByRoleIds($aRoleIdsGrantedAccess, $term_id, $langcode = '') {
334 $langcode = ($langcode === '') ? \Drupal::languageManager()->getCurrentLanguage()->getId() : $langcode;
336 $roles = Role::loadMultiple();
337 foreach ($roles as $role => $roleObj) {
338 if ($roleObj->hasPermission('bypass node access')) {
339 $aRoleIdsGrantedAccess[] = $roleObj->id();
343 $aRoleIdsGrantedAccess = array_unique($aRoleIdsGrantedAccess);
345 foreach ($aRoleIdsGrantedAccess as $sRoleIdGrantedAccess) {
346 $queryResult = $this->database->query("SELECT rid FROM {permissions_by_term_role} WHERE tid = :tid AND rid = :rid AND langcode = :langcode",
347 [':tid' => $term_id, ':rid' => $sRoleIdGrantedAccess, ':langcode' => $langcode])->fetchField();
348 if (empty($queryResult)) {
349 $this->database->insert('permissions_by_term_role')
350 ->fields(['tid', 'rid', 'langcode'], [$term_id, $sRoleIdGrantedAccess, $langcode])
357 * Gets the user ids which have been submitted by form.
359 * Users which will gain granted access to taxonomy terms.
362 * The user ids which have been submitted.
364 public function getSubmittedUserIds($formState) {
365 /* There's a $this->oFormState->getValues() method, but
366 * it is loosing multiple form values. Don't know why.
367 * So there're some custom lines on the $_REQUEST array. */
368 $sRawUsers = $_REQUEST['access']['user'];
370 if (empty($sRawUsers)) {
374 $aRawUsers = Tags::explode($sRawUsers);
377 if (!empty($aRawUsers)) {
378 foreach ($aRawUsers as $sRawUser) {
379 if (preg_match("/.+\s\(([^\)]+)\)/", $sRawUser, $matches)) {
380 $aUserIds[] = $matches[1];
389 * @param FormState $formState
390 * @param int $term_id
395 public function saveTermPermissions(FormStateInterface $formState, $term_id) {
396 $langcode = \Drupal::languageManager()->getCurrentLanguage()->getId();
397 if (!empty($formState->getValue('langcode'))) {
398 $langcode = $formState->getValue('langcode')['0']['value'];
401 $aExistingUserPermissions = $this->getUserTermPermissionsByTid($term_id, $langcode);
402 $aSubmittedUserIdsGrantedAccess = $this->getSubmittedUserIds($formState);
404 $aExistingRoleIdsGrantedAccess = $this->getRoleTermPermissionsByTid($term_id, $langcode);
405 $aSubmittedRolesGrantedAccess = $this->getSubmittedRolesGrantedAccess($formState);
407 $aRet = $this->getPreparedDataForDatabaseQueries($aExistingUserPermissions,
408 $aSubmittedUserIdsGrantedAccess, $aExistingRoleIdsGrantedAccess,
409 $aSubmittedRolesGrantedAccess);
411 $this->deleteTermPermissionsByUserIds($aRet['UserIdPermissionsToRemove'], $term_id, $langcode);
412 $this->addTermPermissionsByUserIds($aRet['UserIdPermissionsToAdd'], $term_id, $langcode);
414 $this->deleteTermPermissionsByRoleIds($aRet['UserRolePermissionsToRemove'], $term_id, $langcode);
415 if (!empty($aRet['aRoleIdPermissionsToAdd'])) {
416 $this->addTermPermissionsByRoleIds($aRet['aRoleIdPermissionsToAdd'], $term_id, $langcode);
423 * Get array items to remove.
425 * The array items which aren't in the new items array, but are in old items
426 * array, will be returned.
428 * @param array $aExistingItems
429 * The existing array items.
430 * @param array|bool $aNewItems
431 * Either false if there're no new items or an array with items.
434 * The array items to remove.
436 private function getArrayItemsToRemove($aExistingItems, $aNewItems) {
439 foreach ($aExistingItems as $existingItem) {
440 if (!in_array($existingItem, $aNewItems)) {
441 $aRet[] = $existingItem;
449 * Get the array items to add.
451 * The items in the new items array, which aren't in the existing items array,
454 * @param array $aNewItems
455 * The new array items.
456 * @param array $aExistingItems
457 * The existing array items.
460 * The items which needs to be added.
462 private function getArrayItemsToAdd($aNewItems, $aExistingItems) {
465 foreach ($aNewItems as $newItem) {
466 if (!in_array($newItem, $aExistingItems)) {
475 * Prepares the data which has to be applied to the database.
477 * @param array $aExistingUserPermissions
478 * The permissions for existing user.
479 * @param array $aSubmittedUserIdsGrantedAccess
480 * The user ids which get access.
481 * @param array $aExistingRoleIdsGrantedAccess
482 * The existing role ids.
483 * @param array $aSubmittedRolesGrantedAccess
484 * The user roles which get access.
487 * User ID and role data.
489 public function getPreparedDataForDatabaseQueries($aExistingUserPermissions,
490 $aSubmittedUserIdsGrantedAccess,
491 $aExistingRoleIdsGrantedAccess,
492 $aSubmittedRolesGrantedAccess) {
493 // Fill array with user ids to remove permission.
494 $aRet['UserIdPermissionsToRemove'] =
495 $this->getArrayItemsToRemove($aExistingUserPermissions,
496 $aSubmittedUserIdsGrantedAccess);
498 // Fill array with user ids to add permission.
499 $aRet['UserIdPermissionsToAdd'] =
500 $this->getArrayItemsToAdd($aSubmittedUserIdsGrantedAccess,
501 $aExistingUserPermissions);
503 // Fill array with user roles to remove permission.
504 $aRet['UserRolePermissionsToRemove'] =
505 $this->getArrayItemsToRemove($aExistingRoleIdsGrantedAccess,
506 $aSubmittedRolesGrantedAccess);
508 // Fill array with user roles to add permission.
509 $aRet['aRoleIdPermissionsToAdd'] =
510 $this->getArrayItemsToAdd($aSubmittedRolesGrantedAccess,
511 $aExistingRoleIdsGrantedAccess);
517 * The form value for allowed users as string to be shown to the user.
519 * @param User[] $aAllowedUsers
520 * An array with the allowed users.
522 * @return null|string
523 * Either null or the user name.
525 public function getUserFormValue($aAllowedUsers) {
529 if (!empty($aAllowedUsers)) {
531 foreach ($aAllowedUsers as $oUser) {
532 $iUid = intval($oUser->id());
534 $sUsername = $oUser->getDisplayName();
537 $sUsername = t('Anonymous User');
540 $sUserInfos .= "$sUsername ($iUid), ";
543 // Remove space and comma at the end of the string.
544 $sUserInfos = substr($sUserInfos, 0, -2);
555 public function getTidsByNid($nid)
557 $node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
560 foreach ($node->getFields() as $field) {
561 if ($field->getFieldDefinition()->getType() == 'entity_reference' && $field->getFieldDefinition()->getSetting('target_type') == 'taxonomy_term') {
562 $aReferencedTaxonomyTerms = $field->getValue();
563 if (!empty($aReferencedTaxonomyTerms)) {
564 foreach ($aReferencedTaxonomyTerms as $aReferencedTerm) {
565 if (isset($aReferencedTerm['target_id'])) {
566 $tids[] = $aReferencedTerm['target_id'];
579 public function getAllUids()
581 $nodes = \Drupal::entityQuery('user')
584 return array_values($nodes);
592 public function getNodeType($nid)
594 $query = $this->database->select('node', 'n')
595 ->fields('n', ['type'])
596 ->condition('n.nid', $nid);
598 return $query->execute()
599 ->fetchAssoc()['type'];
607 public function getLangCode($nid)
609 $query = $this->database->select('node', 'n')
610 ->fields('n', ['langcode'])
611 ->condition('n.nid', $nid);
613 return $query->execute()
614 ->fetchAssoc()['langcode'];
618 * @param AccountInterface $user
622 public function getGids(AccountInterface $user)
626 if (!empty($permittedNids = $this->computePermittedTids($user))) {
627 $query = $this->database->select('node_access', 'na')
628 ->fields('na', ['gid'])
629 ->condition('na.nid', $permittedNids, 'IN')
630 ->condition('na.realm', self::NODE_ACCESS_REALM);
632 $gids = $query->execute()->fetchCol();
634 foreach ($gids as $gid) {
635 $grants[self::NODE_ACCESS_REALM][] = $gid;
642 private function computePermittedTids(AccountInterface $user)
644 $nidsWithNoTidRestriction = $this->getUnrestrictedNids();
645 $nidsByTids = $this->term->getNidsByTids($this->getPermittedTids($user->id(), $user->getRoles()));
647 if (\Drupal::config('permissions_by_term.settings.single_term_restriction')->get('value')) {
649 foreach ($nidsByTids as $nid) {
650 if($this->accessCheck->canUserAccessByNodeId($nid, $user->id(), $this->getLangCode($nid))) {
651 $permittedNids[] = $nid;
654 $nidsByTids = $permittedNids;
657 if (!empty($nidsByTids)) {
659 $this->getUnrestrictedNids(),
664 return $nidsWithNoTidRestriction;
667 private function getUnrestrictedNids() {
668 $tidsRestrictedUserQuery = $this->database->select('permissions_by_term_user', 'u')
669 ->fields('u', ['tid']);
671 $restrictedTids = $this->database->select('permissions_by_term_role', 'r')
672 ->fields('r', ['tid'])
673 ->union($tidsRestrictedUserQuery)
677 if (empty($restrictedTids)) {
678 return $this->getAllNids();
681 $restrictedNids = $this->database->select('taxonomy_index', 't')
682 ->fields('t', ['nid'])
683 ->condition('t.tid', $restrictedTids, 'IN')
688 if (empty($restrictedNids)) {
689 return $this->getAllNids();
692 $unrestrictedNids = $this->database->select('taxonomy_index', 't')
693 ->fields('t', ['nid'])
694 ->condition('t.nid', $restrictedNids, 'NOT IN')
699 return $unrestrictedNids;
705 public function getAllNids() {
706 return $this->database->select('node', 'n')
707 ->fields('n', ['nid'])
717 public function getAllNidsUserCanAccess($uid)
719 $query = $this->database->select('node_access', 'na')
720 ->fields('na', ['nid'])
721 ->condition('na.realm', 'permissions_by_term__uid_' . $uid);
723 return $query->execute()
732 public function getNidsByTid($tid)
734 $query = $this->database->select('taxonomy_index', 'ti')
735 ->fields('ti', ['nid'])
736 ->condition('ti.tid', $tid);
738 return $query->execute()->fetchCol();