Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / lib / Drupal / Core / Extension / ModuleExtensionList.php
1 <?php
2
3 namespace Drupal\Core\Extension;
4
5 use Drupal\Core\Cache\CacheBackendInterface;
6 use Drupal\Core\Config\ConfigFactoryInterface;
7 use Drupal\Core\State\StateInterface;
8 use Drupal\Core\StringTranslation\StringTranslationTrait;
9
10 /**
11  * Provides a list of available modules.
12  */
13 class ModuleExtensionList extends ExtensionList {
14
15   use StringTranslationTrait;
16
17   /**
18    * {@inheritdoc}
19    */
20   protected $defaults = [
21     'dependencies' => [],
22     'description' => '',
23     'package' => 'Other',
24     'version' => NULL,
25     'php' => DRUPAL_MINIMUM_PHP,
26   ];
27
28   /**
29    * The config factory.
30    *
31    * @var \Drupal\Core\Config\ConfigFactoryInterface
32    */
33   protected $configFactory;
34
35   /**
36    * The profile list needed by this module list.
37    *
38    * @var \Drupal\Core\Extension\ExtensionList
39    */
40   protected $profileList;
41
42   /**
43    * Constructs a new ModuleExtensionList instance.
44    *
45    * @param string $root
46    *   The app root.
47    * @param string $type
48    *   The extension type.
49    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
50    *   The cache.
51    * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
52    *   The info parser.
53    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
54    *   The module handler.
55    * @param \Drupal\Core\State\StateInterface $state
56    *   The state.
57    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
58    *   The config factory.
59    * @param \Drupal\Core\Extension\ExtensionList $profile_list
60    *   The site profile listing.
61    * @param string $install_profile
62    *   The install profile used by the site.
63    * @param array[] $container_modules_info
64    *   (optional) The module locations coming from the compiled container.
65    */
66   public function __construct($root, $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, StateInterface $state, ConfigFactoryInterface $config_factory, ExtensionList $profile_list, $install_profile, array $container_modules_info = []) {
67     parent::__construct($root, $type, $cache, $info_parser, $module_handler, $state, $install_profile);
68
69     $this->configFactory = $config_factory;
70     $this->profileList = $profile_list;
71
72     // Use the information from the container. This is an optimization.
73     foreach ($container_modules_info as $module_name => $info) {
74       $this->setPathname($module_name, $info['pathname']);
75     }
76   }
77
78   /**
79    * {@inheritdoc}
80    */
81   protected function getExtensionDiscovery() {
82     $discovery = parent::getExtensionDiscovery();
83
84     if ($active_profile = $this->getActiveProfile()) {
85       $discovery->setProfileDirectories($this->getProfileDirectories($discovery));
86     }
87
88     return $discovery;
89   }
90
91   /**
92    * Finds all installation profile paths.
93    *
94    * @param \Drupal\Core\Extension\ExtensionDiscovery $discovery
95    *   The extension discovery.
96    *
97    * @return string[]
98    *   Paths to all installation profiles.
99    */
100   protected function getProfileDirectories(ExtensionDiscovery $discovery) {
101     $discovery->setProfileDirectories([]);
102     $all_profiles = $discovery->scan('profile');
103     $active_profile = $all_profiles[$this->installProfile];
104     $profiles = array_intersect_key($all_profiles, $this->configFactory->get('core.extension')->get('module') ?: [$active_profile->getName() => 0]);
105
106     // If a module is within a profile directory but specifies another
107     // profile for testing, it needs to be found in the parent profile.
108     $parent_profile = $this->configFactory->get('simpletest.settings')->get('parent_profile');
109
110     if ($parent_profile && !isset($profiles[$parent_profile])) {
111       // In case both profile directories contain the same extension, the
112       // actual profile always has precedence.
113       $profiles = [$parent_profile => $all_profiles[$parent_profile]] + $profiles;
114     }
115
116     $profile_directories = array_map(function (Extension $profile) {
117       return $profile->getPath();
118     }, $profiles);
119     return $profile_directories;
120   }
121
122   /**
123    * Gets the processed active profile object, or null.
124    *
125    * @return \Drupal\Core\Extension\Extension|null
126    *   The active profile, if there is one.
127    */
128   protected function getActiveProfile() {
129     $profiles = $this->profileList->getList();
130     if ($this->installProfile && isset($profiles[$this->installProfile])) {
131       return $profiles[$this->installProfile];
132     }
133     return NULL;
134
135   }
136
137   /**
138    * {@inheritdoc}
139    */
140   protected function doScanExtensions() {
141     $extensions = parent::doScanExtensions();
142
143     $profiles = $this->profileList->getList();
144     // Modify the active profile object that was previously added to the module
145     // list.
146     if ($this->installProfile && isset($profiles[$this->installProfile])) {
147       $extensions[$this->installProfile] = $profiles[$this->installProfile];
148     }
149
150     return $extensions;
151   }
152
153   /**
154    * {@inheritdoc}
155    */
156   protected function doList() {
157     // Find modules.
158     $extensions = parent::doList();
159     // It is possible that a module was marked as required by
160     // hook_system_info_alter() and modules that it depends on are not required.
161     foreach ($extensions as $extension) {
162       $this->ensureRequiredDependencies($extension, $extensions);
163     }
164
165     // Add status, weight, and schema version.
166     $installed_modules = $this->configFactory->get('core.extension')->get('module') ?: [];
167     foreach ($extensions as $name => $module) {
168       $module->weight = isset($installed_modules[$name]) ? $installed_modules[$name] : 0;
169       $module->status = (int) isset($installed_modules[$name]);
170       $module->schema_version = SCHEMA_UNINSTALLED;
171     }
172     $extensions = $this->moduleHandler->buildModuleDependencies($extensions);
173
174     if ($this->installProfile && $extensions[$this->installProfile]) {
175       $active_profile = $extensions[$this->installProfile];
176
177       // Installation profile hooks are always executed last.
178       $active_profile->weight = 1000;
179
180       // Installation profiles are hidden by default, unless explicitly
181       // specified otherwise in the .info.yml file.
182       if (!isset($active_profile->info['hidden'])) {
183         $active_profile->info['hidden'] = TRUE;
184       }
185
186       // The installation profile is required.
187       $active_profile->info['required'] = TRUE;
188       // Add a default distribution name if the profile did not provide one.
189       // @see install_profile_info()
190       // @see drupal_install_profile_distribution_name()
191       if (!isset($active_profile->info['distribution']['name'])) {
192         $active_profile->info['distribution']['name'] = 'Drupal';
193       }
194     }
195
196     return $extensions;
197   }
198
199   /**
200    * {@inheritdoc}
201    */
202   protected function getInstalledExtensionNames() {
203     return array_keys($this->moduleHandler->getModuleList());
204   }
205
206   /**
207    * Marks dependencies of required modules as 'required', recursively.
208    *
209    * @param \Drupal\Core\Extension\Extension $module
210    *   The module extension object.
211    * @param \Drupal\Core\Extension\Extension[] $modules
212    *   Extension objects for all available modules.
213    */
214   protected function ensureRequiredDependencies(Extension $module, array $modules = []) {
215     if (!empty($module->info['required'])) {
216       foreach ($module->info['dependencies'] as $dependency) {
217         $dependency_name = ModuleHandler::parseDependency($dependency)['name'];
218         if (!isset($modules[$dependency_name]->info['required'])) {
219           $modules[$dependency_name]->info['required'] = TRUE;
220           $modules[$dependency_name]->info['explanation'] = $this->t('Dependency of required module @module', ['@module' => $module->info['name']]);
221           // Ensure any dependencies it has are required.
222           $this->ensureRequiredDependencies($modules[$dependency_name], $modules);
223         }
224       }
225     }
226   }
227
228 }