Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / lib / Drupal / Core / Utility / ProjectInfo.php
1 <?php
2
3 namespace Drupal\Core\Utility;
4
5 use Drupal\Core\Extension\Extension;
6
7 /**
8  * Performs operations on drupal.org project data.
9  */
10 class ProjectInfo {
11
12   /**
13    * Populates an array of project data.
14    *
15    * This iterates over a list of the installed modules or themes and groups
16    * them by project and status. A few parts of this function assume that
17    * enabled modules and themes are always processed first, and if uninstalled
18    * modules or themes are being processed (there is a setting to control if
19    * uninstalled code should be included in the Available updates report or
20    * not),those are only processed after $projects has been populated with
21    * information about the enabled code. 'Hidden' modules and themes are
22    * ignored if they are not installed. 'Hidden' Modules and themes in the
23    * "Testing" package are ignored regardless of installation status.
24    *
25    * This function also records the latest change time on the .info.yml files
26    * for each module or theme, which is important data which is used when
27    * deciding if the available update data should be invalidated.
28    *
29    * @param array $projects
30    *   Reference to the array of project data of what's installed on this site.
31    * @param \Drupal\Core\Extension\Extension[] $list
32    *   Array of data to process to add the relevant info to the $projects array.
33    * @param string $project_type
34    *   The kind of data in the list. Can be 'module' or 'theme'.
35    * @param bool $status
36    *   Boolean that controls what status (enabled or uninstalled) to process out
37    *   of the $list and add to the $projects array.
38    * @param array $additional_whitelist
39    *   (optional) Array of additional elements to be collected from the .info.yml
40    *   file. Defaults to array().
41    */
42   public function processInfoList(array &$projects, array $list, $project_type, $status, array $additional_whitelist = []) {
43     foreach ($list as $file) {
44       // Just projects with a matching status should be listed.
45       if ($file->status != $status) {
46         continue;
47       }
48
49       // Skip if the .info.yml file is broken.
50       if (empty($file->info)) {
51         continue;
52       }
53
54       // Skip if it's a hidden project and the project is not installed.
55       if (!empty($file->info['hidden']) && empty($status)) {
56         continue;
57       }
58
59       // Skip if it's a hidden project and the project is a test project. Tests
60       // should use hook_system_info_alter() to test ProjectInfo's
61       // functionality.
62       if (!empty($file->info['hidden']) && isset($file->info['package']) && $file->info['package'] == 'Testing') {
63         continue;
64       }
65
66       // If the .info.yml doesn't define the 'project', try to figure it out.
67       if (!isset($file->info['project'])) {
68         $file->info['project'] = $this->getProjectName($file);
69       }
70
71       // If we still don't know the 'project', give up.
72       if (empty($file->info['project'])) {
73         continue;
74       }
75
76       // If we don't already know it, grab the change time on the .info.yml file
77       // itself. Note: we need to use the ctime, not the mtime (modification
78       // time) since many (all?) tar implementations will go out of their way to
79       // set the mtime on the files it creates to the timestamps recorded in the
80       // tarball. We want to see the last time the file was changed on disk,
81       // which is left alone by tar and correctly set to the time the .info.yml
82       // file was unpacked.
83       if (!isset($file->info['_info_file_ctime'])) {
84         $file->info['_info_file_ctime'] = $file->getCTime();
85       }
86
87       if (!isset($file->info['datestamp'])) {
88         $file->info['datestamp'] = 0;
89       }
90
91       $project_name = $file->info['project'];
92
93       // Figure out what project type we're going to use to display this module
94       // or theme. If the project name is 'drupal', we don't want it to show up
95       // under the usual "Modules" section, we put it at a special "Drupal Core"
96       // section at the top of the report.
97       if ($project_name == 'drupal') {
98         $project_display_type = 'core';
99       }
100       else {
101         $project_display_type = $project_type;
102       }
103       if (empty($status)) {
104         // If we're processing uninstalled modules or themes, append a suffix.
105         $project_display_type .= '-disabled';
106       }
107       if (!isset($projects[$project_name])) {
108         // Only process this if we haven't done this project, since a single
109         // project can have multiple modules or themes.
110         $projects[$project_name] = [
111           'name' => $project_name,
112           // Only save attributes from the .info.yml file we care about so we do
113           // not bloat our RAM usage needlessly.
114           'info' => $this->filterProjectInfo($file->info, $additional_whitelist),
115           'datestamp' => $file->info['datestamp'],
116           'includes' => [$file->getName() => $file->info['name']],
117           'project_type' => $project_display_type,
118           'project_status' => $status,
119         ];
120       }
121       elseif ($projects[$project_name]['project_type'] == $project_display_type) {
122         // Only add the file we're processing to the 'includes' array for this
123         // project if it is of the same type and status (which is encoded in the
124         // $project_display_type). This prevents listing all the uninstalled
125         // modules included with an enabled project if we happen to be checking
126         // for uninstalled modules, too.
127         $projects[$project_name]['includes'][$file->getName()] = $file->info['name'];
128         $projects[$project_name]['info']['_info_file_ctime'] = max($projects[$project_name]['info']['_info_file_ctime'], $file->info['_info_file_ctime']);
129         $projects[$project_name]['datestamp'] = max($projects[$project_name]['datestamp'], $file->info['datestamp']);
130       }
131       elseif (empty($status)) {
132         // If we have a project_name that matches, but the project_display_type
133         // does not, it means we're processing a uninstalled module or theme
134         // that belongs to a project that has some enabled code. In this case,
135         // we add the uninstalled thing into a separate array for separate
136         // display.
137         $projects[$project_name]['disabled'][$file->getName()] = $file->info['name'];
138       }
139     }
140   }
141
142   /**
143    * Determines what project a given file object belongs to.
144    *
145    * @param \Drupal\Core\Extension\Extension $file
146    *   An extension object.
147    *
148    * @return string
149    *   The canonical project short name.
150    */
151   public function getProjectName(Extension $file) {
152     $project_name = '';
153     if (isset($file->info['project'])) {
154       $project_name = $file->info['project'];
155     }
156     elseif (strpos($file->getPath(), 'core/modules') === 0) {
157       $project_name = 'drupal';
158     }
159     return $project_name;
160   }
161
162   /**
163    * Filters the project .info.yml data to only save attributes we need.
164    *
165    * @param array $info
166    *   Array of .info.yml file data as returned by
167    *   \Drupal\Core\Extension\InfoParser.
168    * @param $additional_whitelist
169    *   (optional) Array of additional elements to be collected from the .info.yml
170    *   file. Defaults to array().
171    *
172    * @return
173    *   Array of .info.yml file data we need for the update manager.
174    *
175    * @see \Drupal\Core\Utility\ProjectInfo::processInfoList()
176    */
177   public function filterProjectInfo($info, $additional_whitelist = []) {
178     $whitelist = [
179       '_info_file_ctime',
180       'datestamp',
181       'major',
182       'name',
183       'package',
184       'project',
185       'project status url',
186       'version',
187     ];
188     $whitelist = array_merge($whitelist, $additional_whitelist);
189     return array_intersect_key($info, array_combine($whitelist, $whitelist));
190   }
191
192 }