Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / permissions_by_term / src / Service / AccessStorage.php
1 <?php
2
3 namespace Drupal\permissions_by_term\Service;
4
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;
12
13 /**
14  * Class AccessStorage.
15  *
16  * @package Drupal\permissions_by_term
17  */
18 class AccessStorage {
19
20   /**
21    * The database connection.
22    *
23    * @var Connection
24    */
25   protected $database;
26
27   /**
28    * The term name for which the access is set.
29    *
30    * @var string
31    */
32   protected $sTermName;
33
34   /**
35    * The user ids which gain granted access.
36    *
37    * @var array
38    */
39   protected $aUserIdsGrantedAccess;
40
41   /**
42    * The roles with granted access.
43    *
44    * @var array
45    */
46   protected $aSubmittedRolesGrantedAccess;
47
48   /**
49    * @var TermHandler
50    */
51   protected $term;
52
53   /**
54    * @var string
55    */
56   const NODE_ACCESS_REALM = 'permissions_by_term';
57
58   /**
59    * @var AccessCheck
60    */
61   protected $accessCheck;
62
63   /**
64    * AccessStorageService constructor.
65    *
66    * @param Connection  $database
67    * @param TermHandler        $term
68    * @param AccessCheck $accessCheck
69    */
70   public function __construct(Connection $database, TermHandler $term, AccessCheck $accessCheck) {
71     $this->database  = $database;
72     $this->term = $term;
73     $this->accessCheck = $accessCheck;
74   }
75
76   /**
77    * Gets submitted roles with granted access from form.
78    *
79    * @return array
80    *   An array with chosen roles.
81    */
82   public function getSubmittedRolesGrantedAccess(FormStateInterface $form_state) {
83     $aRoles       = $form_state->getValue('access')['role'];
84     $aChosenRoles = [];
85     foreach ($aRoles as $sRole) {
86       if ($sRole !== 0) {
87         $aChosenRoles[] = $sRole;
88       }
89     }
90     return $aChosenRoles;
91   }
92
93   /**
94    * @param FormState $form_state
95    */
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)
102         ->execute();
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]));
107       }
108     }
109   }
110
111   /**
112    * @param int $term_id
113    *
114    * @return array
115    */
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'])
121       ->execute()
122       ->fetchCol();
123   }
124
125   /**
126    * @param int   $uid
127    * @param array $rids
128    *
129    * @return array
130    */
131   public function getPermittedTids($uid, $rids) {
132     $permittedTids = $this->database->select('permissions_by_term_user', 'pu')
133       ->condition('uid', $uid)
134       ->fields('pu', ['tid'])
135       ->execute()
136       ->fetchCol();
137
138     foreach ($rids as $rid) {
139       $permittedTidsByRid = $this->database->select('permissions_by_term_role', 'pr')
140         ->condition('rid', $rid)
141         ->fields('pr', ['tid'])
142         ->execute()
143         ->fetchCol();
144
145       $permittedTids = array_merge($permittedTidsByRid, $permittedTids);
146     }
147
148     return array_unique($permittedTids);
149   }
150
151   /**
152    * @param array  $tids
153    * @param string $langcode
154    * @return array
155    */
156   public function getUserTermPermissionsByTids($tids, $langcode) {
157     $uids = [];
158
159     foreach ($tids as $tid) {
160       if (!empty($tmpUids = $this->getUserTermPermissionsByTid($tid, $langcode))) {
161         foreach ($tmpUids as $tmpUid) {
162           $uids[] = $tmpUid;
163         }
164       }
165     }
166
167     return $uids;
168   }
169
170   /**
171    * @param int $term_id
172    * @param string $langcode
173    *
174    * @return array
175    */
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'])
181       ->execute()
182       ->fetchCol();
183   }
184
185   /**
186    * @param array  $tids
187    * @param string $langcode
188    * @return array
189    */
190   public function getRoleTermPermissionsByTids($tids, $langcode) {
191     $rids = [];
192
193     foreach ($tids as $tid) {
194       $tmpRids = $this->getRoleTermPermissionsByTid($tid, $langcode);
195       if (!empty($tmpRids)) {
196         foreach ($tmpRids as $tmpRid) {
197           $rids[] = $tmpRid;
198         }
199       }
200     }
201
202     return $rids;
203   }
204
205   /**
206    * @param string $sUsername
207    *
208    * @return int
209    */
210   public function getUserIdByName($sUsername) {
211     return $this->database->select('users_field_data', 'ufd')
212       ->condition('name', $sUsername)
213       ->fields('ufd', ['uid'])
214       ->execute()
215       ->fetchAssoc();
216   }
217
218   /**
219    * @param array $aUserNames
220    *
221    * @return array
222    */
223   public function getUserIdsByNames($aUserNames) {
224     $aUserIds = [];
225     foreach ($aUserNames as $userName) {
226       $iUserId    = $this->getUserIdByName($userName)['uid'];
227       $aUserIds[] = $iUserId['uid'];
228     }
229     return $aUserIds;
230   }
231
232   /**
233    * @param int $term_id
234    *
235    * @return array
236    */
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);
242
243     // fetchCol() returns all results, fetchAssoc() only "one" result.
244     return $query->execute()
245       ->fetchCol();
246   }
247
248   /**
249    * @param array $aUserIdsAccessRemove
250    * @param int   $term_id
251    */
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, '=')
258         ->execute();
259     }
260   }
261
262   /**
263    * @param array $aRoleIdsAccessRemove
264    * @param int   $term_id
265    */
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, '=')
272         ->execute();
273     }
274   }
275
276   /**
277    * @param int $userId
278    */
279   public function deleteAllTermPermissionsByUserId($userId) {
280     $this->database->delete('permissions_by_term_user')
281       ->condition('uid', $userId, '=')
282       ->execute();
283   }
284
285   /**
286    * Delete access storage when a term is removed.
287    *
288    * @param int $term_id
289    *   The term ID being deleted.
290    */
291   public function deleteAllTermPermissionsByTid($term_id) {
292     $this->database->delete('permissions_by_term_user')
293       ->condition('tid', $term_id, '=')
294       ->execute();
295
296     $this->database->delete('permissions_by_term_role')
297       ->condition('tid', $term_id, '=')
298       ->execute();
299   }
300
301   /**
302    * @param array  $aUserIdsGrantedAccess
303    * @param int    $term_id
304    * @param string $langcode
305    *
306    * @throws \Exception
307    */
308   public function addTermPermissionsByUserIds($aUserIdsGrantedAccess, $term_id, $langcode = '') {
309                 $langcode = ($langcode === '') ? \Drupal::languageManager()->getCurrentLanguage()->getId() : $langcode;
310
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'], [
317             $term_id,
318             $iUserIdGrantedAccess,
319             $langcode
320           ])
321           ->execute();
322       }
323     }
324   }
325
326   /**
327    * @param array  $aRoleIdsGrantedAccess
328    * @param int    $term_id
329    * @param string $langcode
330    *
331    * @throws \Exception
332    */
333   public function addTermPermissionsByRoleIds($aRoleIdsGrantedAccess, $term_id, $langcode = '') {
334         $langcode = ($langcode === '') ? \Drupal::languageManager()->getCurrentLanguage()->getId() : $langcode;
335
336     $roles = Role::loadMultiple();
337     foreach ($roles as $role => $roleObj) {
338       if ($roleObj->hasPermission('bypass node access')) {
339         $aRoleIdsGrantedAccess[] = $roleObj->id();
340       }
341     }
342
343     $aRoleIdsGrantedAccess = array_unique($aRoleIdsGrantedAccess);
344
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])
351           ->execute();
352       }
353     }
354   }
355
356   /**
357    * Gets the user ids which have been submitted by form.
358    *
359    * Users which will gain granted access to taxonomy terms.
360    *
361    * @return array
362    *   The user ids which have been submitted.
363    */
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'];
369
370     if (empty($sRawUsers)) {
371       return [];
372     }
373
374     $aRawUsers = Tags::explode($sRawUsers);
375
376     $aUserIds = [];
377     if (!empty($aRawUsers)) {
378       foreach ($aRawUsers as $sRawUser) {
379         if (preg_match("/.+\s\(([^\)]+)\)/", $sRawUser, $matches)) {
380           $aUserIds[] = $matches[1];
381         }
382       }
383     }
384
385     return $aUserIds;
386   }
387
388         /**
389          * @param FormState $formState
390    * @param int $term_id
391          *
392          * @return array
393          * @throws \Exception
394          */
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'];
399     }
400
401     $aExistingUserPermissions       = $this->getUserTermPermissionsByTid($term_id, $langcode);
402     $aSubmittedUserIdsGrantedAccess = $this->getSubmittedUserIds($formState);
403
404     $aExistingRoleIdsGrantedAccess = $this->getRoleTermPermissionsByTid($term_id, $langcode);
405     $aSubmittedRolesGrantedAccess  = $this->getSubmittedRolesGrantedAccess($formState);
406
407     $aRet = $this->getPreparedDataForDatabaseQueries($aExistingUserPermissions,
408       $aSubmittedUserIdsGrantedAccess, $aExistingRoleIdsGrantedAccess,
409       $aSubmittedRolesGrantedAccess);
410
411     $this->deleteTermPermissionsByUserIds($aRet['UserIdPermissionsToRemove'], $term_id, $langcode);
412     $this->addTermPermissionsByUserIds($aRet['UserIdPermissionsToAdd'], $term_id, $langcode);
413
414     $this->deleteTermPermissionsByRoleIds($aRet['UserRolePermissionsToRemove'], $term_id, $langcode);
415     if (!empty($aRet['aRoleIdPermissionsToAdd'])) {
416       $this->addTermPermissionsByRoleIds($aRet['aRoleIdPermissionsToAdd'], $term_id, $langcode);
417     }
418
419     return $aRet;
420   }
421
422   /**
423    * Get array items to remove.
424    *
425    * The array items which aren't in the new items array, but are in old items
426    * array, will be returned.
427    *
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.
432    *
433    * @return array
434    *   The array items to remove.
435    */
436   private function getArrayItemsToRemove($aExistingItems, $aNewItems) {
437     $aRet = [];
438
439     foreach ($aExistingItems as $existingItem) {
440       if (!in_array($existingItem, $aNewItems)) {
441         $aRet[] = $existingItem;
442       }
443     }
444
445     return $aRet;
446   }
447
448   /**
449    * Get the array items to add.
450    *
451    * The items in the new items array, which aren't in the existing items array,
452    * will be returned.
453    *
454    * @param array $aNewItems
455    *   The new array items.
456    * @param array $aExistingItems
457    *   The existing array items.
458    *
459    * @return array
460    *   The items which needs to be added.
461    */
462   private function getArrayItemsToAdd($aNewItems, $aExistingItems) {
463     $aRet = [];
464
465     foreach ($aNewItems as $newItem) {
466       if (!in_array($newItem, $aExistingItems)) {
467         $aRet[] = $newItem;
468       }
469     }
470
471     return $aRet;
472   }
473
474   /**
475    * Prepares the data which has to be applied to the database.
476    *
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.
485    *
486    * @return array
487    *   User ID and role data.
488    */
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);
497
498     // Fill array with user ids to add permission.
499     $aRet['UserIdPermissionsToAdd'] =
500       $this->getArrayItemsToAdd($aSubmittedUserIdsGrantedAccess,
501         $aExistingUserPermissions);
502
503     // Fill array with user roles to remove permission.
504     $aRet['UserRolePermissionsToRemove'] =
505       $this->getArrayItemsToRemove($aExistingRoleIdsGrantedAccess,
506         $aSubmittedRolesGrantedAccess);
507
508     // Fill array with user roles to add permission.
509     $aRet['aRoleIdPermissionsToAdd'] =
510       $this->getArrayItemsToAdd($aSubmittedRolesGrantedAccess,
511         $aExistingRoleIdsGrantedAccess);
512
513     return $aRet;
514   }
515
516   /**
517    * The form value for allowed users as string to be shown to the user.
518    *
519    * @param User[] $aAllowedUsers
520    *   An array with the allowed users.
521    *
522    * @return null|string
523    *   Either null or the user name.
524    */
525   public function getUserFormValue($aAllowedUsers) {
526
527     $sUserInfos = '';
528
529     if (!empty($aAllowedUsers)) {
530
531       foreach ($aAllowedUsers as $oUser) {
532         $iUid = intval($oUser->id());
533         if ($iUid !== 0) {
534           $sUsername = $oUser->getDisplayName();
535         }
536         else {
537           $sUsername = t('Anonymous User');
538         }
539
540         $sUserInfos .= "$sUsername ($iUid), ";
541       }
542
543       // Remove space and comma at the end of the string.
544       $sUserInfos = substr($sUserInfos, 0, -2);
545     }
546
547     return $sUserInfos;
548   }
549
550   /**
551    * @param $nid
552    *
553    * @return array
554    */
555   public function getTidsByNid($nid)
556   {
557     $node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
558     $tids = [];
559
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'];
567             }
568           }
569         }
570       }
571     }
572
573     return $tids;
574   }
575
576   /**
577    * @return array
578    */
579   public function getAllUids()
580   {
581     $nodes = \Drupal::entityQuery('user')
582       ->execute();
583
584     return array_values($nodes);
585   }
586
587   /**
588    * @param $nid
589    *
590    * @return array
591    */
592   public function getNodeType($nid)
593   {
594     $query = $this->database->select('node', 'n')
595       ->fields('n', ['type'])
596       ->condition('n.nid', $nid);
597
598     return $query->execute()
599       ->fetchAssoc()['type'];
600   }
601
602   /**
603    * @param $nid
604    *
605    * @return array
606    */
607   public function getLangCode($nid)
608   {
609     $query = $this->database->select('node', 'n')
610       ->fields('n', ['langcode'])
611       ->condition('n.nid', $nid);
612
613     return $query->execute()
614       ->fetchAssoc()['langcode'];
615   }
616
617   /**
618    * @param AccountInterface $user
619    *
620    * @return array
621    */
622   public function getGids(AccountInterface $user)
623   {
624     $grants = null;
625
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);
631
632       $gids = $query->execute()->fetchCol();
633
634       foreach ($gids as $gid) {
635         $grants[self::NODE_ACCESS_REALM][] = $gid;
636       }
637     }
638
639     return $grants;
640   }
641
642   private function computePermittedTids(AccountInterface $user)
643   {
644     $nidsWithNoTidRestriction = $this->getUnrestrictedNids();
645     $nidsByTids = $this->term->getNidsByTids($this->getPermittedTids($user->id(), $user->getRoles()));
646
647     if (\Drupal::config('permissions_by_term.settings.single_term_restriction')->get('value')) {
648       $permittedNids = [];
649       foreach ($nidsByTids as $nid) {
650         if($this->accessCheck->canUserAccessByNodeId($nid, $user->id(), $this->getLangCode($nid))) {
651           $permittedNids[] = $nid;
652         }
653       }
654       $nidsByTids = $permittedNids;
655     }
656
657     if (!empty($nidsByTids)) {
658       return array_merge(
659         $this->getUnrestrictedNids(),
660         $nidsByTids
661       );
662     }
663
664     return $nidsWithNoTidRestriction;
665   }
666
667   private function getUnrestrictedNids() {
668     $tidsRestrictedUserQuery = $this->database->select('permissions_by_term_user', 'u')
669       ->fields('u', ['tid']);
670
671     $restrictedTids = $this->database->select('permissions_by_term_role', 'r')
672       ->fields('r', ['tid'])
673       ->union($tidsRestrictedUserQuery)
674       ->execute()
675       ->fetchCol();
676
677     if (empty($restrictedTids)) {
678       return $this->getAllNids();
679     }
680
681     $restrictedNids = $this->database->select('taxonomy_index', 't')
682       ->fields('t', ['nid'])
683       ->condition('t.tid', $restrictedTids, 'IN')
684       ->distinct(TRUE)
685       ->execute()
686       ->fetchCol();
687
688     if (empty($restrictedNids)) {
689       return $this->getAllNids();
690     }
691
692     $unrestrictedNids = $this->database->select('taxonomy_index', 't')
693       ->fields('t', ['nid'])
694       ->condition('t.nid', $restrictedNids, 'NOT IN')
695       ->distinct(TRUE)
696       ->execute()
697       ->fetchCol();
698
699     return $unrestrictedNids;
700   }
701
702   /**
703    * @return array
704    */
705   public function getAllNids() {
706     return $this->database->select('node', 'n')
707       ->fields('n', ['nid'])
708       ->execute()
709       ->fetchCol();
710   }
711
712   /**
713    * @param $uid
714    *
715    * @return array
716    */
717   public function getAllNidsUserCanAccess($uid)
718   {
719     $query = $this->database->select('node_access', 'na')
720       ->fields('na', ['nid'])
721       ->condition('na.realm', 'permissions_by_term__uid_' . $uid);
722
723     return $query->execute()
724       ->fetchCol();
725   }
726
727   /**
728    * @param $tid
729    *
730    * @return array
731    */
732   public function getNidsByTid($tid)
733   {
734       $query = $this->database->select('taxonomy_index', 'ti')
735         ->fields('ti', ['nid'])
736         ->condition('ti.tid', $tid);
737
738       return $query->execute()->fetchCol();
739   }
740
741 }