diff core/lib/Drupal/Core/Utility/ProjectInfo.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/lib/Drupal/Core/Utility/ProjectInfo.php	Wed Nov 29 16:09:58 2017 +0000
@@ -0,0 +1,192 @@
+<?php
+
+namespace Drupal\Core\Utility;
+
+use Drupal\Core\Extension\Extension;
+
+/**
+ * Performs operations on drupal.org project data.
+ */
+class ProjectInfo {
+
+  /**
+   * Populates an array of project data.
+   *
+   * This iterates over a list of the installed modules or themes and groups
+   * them by project and status. A few parts of this function assume that
+   * enabled modules and themes are always processed first, and if uninstalled
+   * modules or themes are being processed (there is a setting to control if
+   * uninstalled code should be included in the Available updates report or
+   * not),those are only processed after $projects has been populated with
+   * information about the enabled code. 'Hidden' modules and themes are
+   * ignored if they are not installed. 'Hidden' Modules and themes in the
+   * "Testing" package are ignored regardless of installation status.
+   *
+   * This function also records the latest change time on the .info.yml files
+   * for each module or theme, which is important data which is used when
+   * deciding if the available update data should be invalidated.
+   *
+   * @param array $projects
+   *   Reference to the array of project data of what's installed on this site.
+   * @param \Drupal\Core\Extension\Extension[] $list
+   *   Array of data to process to add the relevant info to the $projects array.
+   * @param string $project_type
+   *   The kind of data in the list. Can be 'module' or 'theme'.
+   * @param bool $status
+   *   Boolean that controls what status (enabled or uninstalled) to process out
+   *   of the $list and add to the $projects array.
+   * @param array $additional_whitelist
+   *   (optional) Array of additional elements to be collected from the .info.yml
+   *   file. Defaults to array().
+   */
+  public function processInfoList(array &$projects, array $list, $project_type, $status, array $additional_whitelist = []) {
+    foreach ($list as $file) {
+      // Just projects with a matching status should be listed.
+      if ($file->status != $status) {
+        continue;
+      }
+
+      // Skip if the .info.yml file is broken.
+      if (empty($file->info)) {
+        continue;
+      }
+
+      // Skip if it's a hidden project and the project is not installed.
+      if (!empty($file->info['hidden']) && empty($status)) {
+        continue;
+      }
+
+      // Skip if it's a hidden project and the project is a test project. Tests
+      // should use hook_system_info_alter() to test ProjectInfo's
+      // functionality.
+      if (!empty($file->info['hidden']) && isset($file->info['package']) && $file->info['package'] == 'Testing') {
+        continue;
+      }
+
+      // If the .info.yml doesn't define the 'project', try to figure it out.
+      if (!isset($file->info['project'])) {
+        $file->info['project'] = $this->getProjectName($file);
+      }
+
+      // If we still don't know the 'project', give up.
+      if (empty($file->info['project'])) {
+        continue;
+      }
+
+      // If we don't already know it, grab the change time on the .info.yml file
+      // itself. Note: we need to use the ctime, not the mtime (modification
+      // time) since many (all?) tar implementations will go out of their way to
+      // set the mtime on the files it creates to the timestamps recorded in the
+      // tarball. We want to see the last time the file was changed on disk,
+      // which is left alone by tar and correctly set to the time the .info.yml
+      // file was unpacked.
+      if (!isset($file->info['_info_file_ctime'])) {
+        $file->info['_info_file_ctime'] = $file->getCTime();
+      }
+
+      if (!isset($file->info['datestamp'])) {
+        $file->info['datestamp'] = 0;
+      }
+
+      $project_name = $file->info['project'];
+
+      // Figure out what project type we're going to use to display this module
+      // or theme. If the project name is 'drupal', we don't want it to show up
+      // under the usual "Modules" section, we put it at a special "Drupal Core"
+      // section at the top of the report.
+      if ($project_name == 'drupal') {
+        $project_display_type = 'core';
+      }
+      else {
+        $project_display_type = $project_type;
+      }
+      if (empty($status)) {
+        // If we're processing uninstalled modules or themes, append a suffix.
+        $project_display_type .= '-disabled';
+      }
+      if (!isset($projects[$project_name])) {
+        // Only process this if we haven't done this project, since a single
+        // project can have multiple modules or themes.
+        $projects[$project_name] = [
+          'name' => $project_name,
+          // Only save attributes from the .info.yml file we care about so we do
+          // not bloat our RAM usage needlessly.
+          'info' => $this->filterProjectInfo($file->info, $additional_whitelist),
+          'datestamp' => $file->info['datestamp'],
+          'includes' => [$file->getName() => $file->info['name']],
+          'project_type' => $project_display_type,
+          'project_status' => $status,
+        ];
+      }
+      elseif ($projects[$project_name]['project_type'] == $project_display_type) {
+        // Only add the file we're processing to the 'includes' array for this
+        // project if it is of the same type and status (which is encoded in the
+        // $project_display_type). This prevents listing all the uninstalled
+        // modules included with an enabled project if we happen to be checking
+        // for uninstalled modules, too.
+        $projects[$project_name]['includes'][$file->getName()] = $file->info['name'];
+        $projects[$project_name]['info']['_info_file_ctime'] = max($projects[$project_name]['info']['_info_file_ctime'], $file->info['_info_file_ctime']);
+        $projects[$project_name]['datestamp'] = max($projects[$project_name]['datestamp'], $file->info['datestamp']);
+      }
+      elseif (empty($status)) {
+        // If we have a project_name that matches, but the project_display_type
+        // does not, it means we're processing a uninstalled module or theme
+        // that belongs to a project that has some enabled code. In this case,
+        // we add the uninstalled thing into a separate array for separate
+        // display.
+        $projects[$project_name]['disabled'][$file->getName()] = $file->info['name'];
+      }
+    }
+  }
+
+  /**
+   * Determines what project a given file object belongs to.
+   *
+   * @param \Drupal\Core\Extension\Extension $file
+   *   An extension object.
+   *
+   * @return string
+   *   The canonical project short name.
+   */
+  public function getProjectName(Extension $file) {
+    $project_name = '';
+    if (isset($file->info['project'])) {
+      $project_name = $file->info['project'];
+    }
+    elseif (strpos($file->getPath(), 'core/modules') === 0) {
+      $project_name = 'drupal';
+    }
+    return $project_name;
+  }
+
+  /**
+   * Filters the project .info.yml data to only save attributes we need.
+   *
+   * @param array $info
+   *   Array of .info.yml file data as returned by
+   *   \Drupal\Core\Extension\InfoParser.
+   * @param $additional_whitelist
+   *   (optional) Array of additional elements to be collected from the .info.yml
+   *   file. Defaults to array().
+   *
+   * @return
+   *   Array of .info.yml file data we need for the update manager.
+   *
+   * @see \Drupal\Core\Utility\ProjectInfo::processInfoList()
+   */
+  public function filterProjectInfo($info, $additional_whitelist = []) {
+    $whitelist = [
+      '_info_file_ctime',
+      'datestamp',
+      'major',
+      'name',
+      'package',
+      'project',
+      'project status url',
+      'version',
+    ];
+    $whitelist = array_merge($whitelist, $additional_whitelist);
+    return array_intersect_key($info, array_combine($whitelist, $whitelist));
+  }
+
+}