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