Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / user / src / PermissionHandler.php
1 <?php
2
3 namespace Drupal\user;
4
5 use Drupal\Core\Discovery\YamlDiscovery;
6 use Drupal\Core\Controller\ControllerResolverInterface;
7 use Drupal\Core\Extension\ModuleHandlerInterface;
8 use Drupal\Core\StringTranslation\StringTranslationTrait;
9 use Drupal\Core\StringTranslation\TranslationInterface;
10
11 /**
12  * Provides the available permissions based on yml files.
13  *
14  * To define permissions you can use a $module.permissions.yml file. This file
15  * defines machine names, human-readable names, restrict access (if required for
16  * security warning), and optionally descriptions for each permission type. The
17  * machine names are the canonical way to refer to permissions for access
18  * checking.
19  *
20  * If your module needs to define dynamic permissions you can use the
21  * permission_callbacks key to declare a callable that will return an array of
22  * permissions, keyed by machine name. Each item in the array can contain the
23  * same keys as an entry in $module.permissions.yml.
24  *
25  * Here is an example from the core filter module (comments have been added):
26  * @code
27  * # The key is the permission machine name, and is required.
28  * administer filters:
29  *   # (required) Human readable name of the permission used in the UI.
30  *   title: 'Administer text formats and filters'
31  *   # (optional) Additional description fo the permission used in the UI.
32  *   description: 'Define how text is handled by combining filters into text formats.'
33  *   # (optional) Boolean, when set to true a warning about site security will
34  *   # be displayed on the Permissions page. Defaults to false.
35  *   restrict access: false
36  *
37  * # An array of callables used to generate dynamic permissions.
38  * permission_callbacks:
39  *   # Each item in the array should return an associative array with one or
40  *   # more permissions following the same keys as the permission defined above.
41  *   - Drupal\filter\FilterPermissions::permissions
42  * @endcode
43  *
44  * @see filter.permissions.yml
45  * @see \Drupal\filter\FilterPermissions
46  * @see user_api
47  */
48 class PermissionHandler implements PermissionHandlerInterface {
49
50   use StringTranslationTrait;
51
52   /**
53    * The module handler.
54    *
55    * @var \Drupal\Core\Extension\ModuleHandlerInterface
56    */
57   protected $moduleHandler;
58
59   /**
60    * The YAML discovery class to find all .permissions.yml files.
61    *
62    * @var \Drupal\Core\Discovery\YamlDiscovery
63    */
64   protected $yamlDiscovery;
65
66   /**
67    * The controller resolver.
68    *
69    * @var \Drupal\Core\Controller\ControllerResolverInterface
70    */
71   protected $controllerResolver;
72
73   /**
74    * Constructs a new PermissionHandler.
75    *
76    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
77    *   The module handler.
78    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
79    *   The string translation.
80    * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
81    *   The controller resolver.
82    */
83   public function __construct(ModuleHandlerInterface $module_handler, TranslationInterface $string_translation, ControllerResolverInterface $controller_resolver) {
84     // @todo It would be nice if you could pull all module directories from the
85     //   container.
86     $this->moduleHandler = $module_handler;
87     $this->stringTranslation = $string_translation;
88     $this->controllerResolver = $controller_resolver;
89   }
90
91   /**
92    * Gets the YAML discovery.
93    *
94    * @return \Drupal\Core\Discovery\YamlDiscovery
95    *   The YAML discovery.
96    */
97   protected function getYamlDiscovery() {
98     if (!isset($this->yamlDiscovery)) {
99       $this->yamlDiscovery = new YamlDiscovery('permissions', $this->moduleHandler->getModuleDirectories());
100     }
101     return $this->yamlDiscovery;
102   }
103
104   /**
105    * {@inheritdoc}
106    */
107   public function getPermissions() {
108     $all_permissions = $this->buildPermissionsYaml();
109
110     return $this->sortPermissions($all_permissions);
111   }
112
113   /**
114    * {@inheritdoc}
115    */
116   public function moduleProvidesPermissions($module_name) {
117     // @TODO Static cache this information, see
118     // https://www.drupal.org/node/2339487
119     $permissions = $this->getPermissions();
120
121     foreach ($permissions as $permission) {
122       if ($permission['provider'] == $module_name) {
123         return TRUE;
124       }
125     }
126     return FALSE;
127   }
128
129   /**
130    * Builds all permissions provided by .permissions.yml files.
131    *
132    * @return array[]
133    *   Each return permission is an array with the following keys:
134    *   - title: The title of the permission.
135    *   - description: The description of the permission, defaults to NULL.
136    *   - provider: The provider of the permission.
137    */
138   protected function buildPermissionsYaml() {
139     $all_permissions = [];
140     $all_callback_permissions = [];
141
142     foreach ($this->getYamlDiscovery()->findAll() as $provider => $permissions) {
143       // The top-level 'permissions_callback' is a list of methods in controller
144       // syntax, see \Drupal\Core\Controller\ControllerResolver. These methods
145       // should return an array of permissions in the same structure.
146       if (isset($permissions['permission_callbacks'])) {
147         foreach ($permissions['permission_callbacks'] as $permission_callback) {
148           $callback = $this->controllerResolver->getControllerFromDefinition($permission_callback);
149           if ($callback_permissions = call_user_func($callback)) {
150             // Add any callback permissions to the array of permissions. Any
151             // defaults can then get processed below.
152             foreach ($callback_permissions as $name => $callback_permission) {
153               if (!is_array($callback_permission)) {
154                 $callback_permission = [
155                   'title' => $callback_permission,
156                 ];
157               }
158
159               $callback_permission += [
160                 'description' => NULL,
161                 'provider' => $provider,
162               ];
163
164               $all_callback_permissions[$name] = $callback_permission;
165             }
166           }
167         }
168
169         unset($permissions['permission_callbacks']);
170       }
171
172       foreach ($permissions as &$permission) {
173         if (!is_array($permission)) {
174           $permission = [
175             'title' => $permission,
176           ];
177         }
178         $permission['title'] = $this->t($permission['title']);
179         $permission['description'] = isset($permission['description']) ? $this->t($permission['description']) : NULL;
180         $permission['provider'] = !empty($permission['provider']) ? $permission['provider'] : $provider;
181       }
182
183       $all_permissions += $permissions;
184     }
185
186     return $all_permissions + $all_callback_permissions;
187   }
188
189   /**
190    * Sorts the given permissions by provider name and title.
191    *
192    * @param array $all_permissions
193    *   The permissions to be sorted.
194    *
195    * @return array[]
196    *   Each return permission is an array with the following keys:
197    *   - title: The title of the permission.
198    *   - description: The description of the permission, defaults to NULL.
199    *   - provider: The provider of the permission.
200    */
201   protected function sortPermissions(array $all_permissions = []) {
202     // Get a list of all the modules providing permissions and sort by
203     // display name.
204     $modules = $this->getModuleNames();
205
206     uasort($all_permissions, function (array $permission_a, array $permission_b) use ($modules) {
207       if ($modules[$permission_a['provider']] == $modules[$permission_b['provider']]) {
208         return $permission_a['title'] > $permission_b['title'];
209       }
210       else {
211         return $modules[$permission_a['provider']] > $modules[$permission_b['provider']];
212       }
213     });
214     return $all_permissions;
215   }
216
217   /**
218    * Returns all module names.
219    *
220    * @return string[]
221    *   Returns the human readable names of all modules keyed by machine name.
222    */
223   protected function getModuleNames() {
224     $modules = [];
225     foreach (array_keys($this->moduleHandler->getModuleList()) as $module) {
226       $modules[$module] = $this->moduleHandler->getName($module);
227     }
228     asort($modules);
229     return $modules;
230   }
231
232   /**
233    * Wraps system_rebuild_module_data()
234    *
235    * @return \Drupal\Core\Extension\Extension[]
236    */
237   protected function systemRebuildModuleData() {
238     return system_rebuild_module_data();
239   }
240
241 }