Chris@0: status != $status) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: // Skip if the .info.yml file is broken. Chris@0: if (empty($file->info)) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: // Skip if it's a hidden project and the project is not installed. Chris@0: if (!empty($file->info['hidden']) && empty($status)) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: // Skip if it's a hidden project and the project is a test project. Tests Chris@0: // should use hook_system_info_alter() to test ProjectInfo's Chris@0: // functionality. Chris@0: if (!empty($file->info['hidden']) && isset($file->info['package']) && $file->info['package'] == 'Testing') { Chris@0: continue; Chris@0: } Chris@0: Chris@0: // If the .info.yml doesn't define the 'project', try to figure it out. Chris@0: if (!isset($file->info['project'])) { Chris@0: $file->info['project'] = $this->getProjectName($file); Chris@0: } Chris@0: Chris@0: // If we still don't know the 'project', give up. Chris@0: if (empty($file->info['project'])) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: // If we don't already know it, grab the change time on the .info.yml file Chris@0: // itself. Note: we need to use the ctime, not the mtime (modification Chris@0: // time) since many (all?) tar implementations will go out of their way to Chris@0: // set the mtime on the files it creates to the timestamps recorded in the Chris@0: // tarball. We want to see the last time the file was changed on disk, Chris@0: // which is left alone by tar and correctly set to the time the .info.yml Chris@0: // file was unpacked. Chris@0: if (!isset($file->info['_info_file_ctime'])) { Chris@0: $file->info['_info_file_ctime'] = $file->getCTime(); Chris@0: } Chris@0: Chris@0: if (!isset($file->info['datestamp'])) { Chris@0: $file->info['datestamp'] = 0; Chris@0: } Chris@0: Chris@0: $project_name = $file->info['project']; Chris@0: Chris@0: // Figure out what project type we're going to use to display this module Chris@0: // or theme. If the project name is 'drupal', we don't want it to show up Chris@0: // under the usual "Modules" section, we put it at a special "Drupal Core" Chris@0: // section at the top of the report. Chris@0: if ($project_name == 'drupal') { Chris@0: $project_display_type = 'core'; Chris@0: } Chris@0: else { Chris@0: $project_display_type = $project_type; Chris@0: } Chris@0: if (empty($status)) { Chris@0: // If we're processing uninstalled modules or themes, append a suffix. Chris@0: $project_display_type .= '-disabled'; Chris@0: } Chris@0: if (!isset($projects[$project_name])) { Chris@0: // Only process this if we haven't done this project, since a single Chris@0: // project can have multiple modules or themes. Chris@0: $projects[$project_name] = [ Chris@0: 'name' => $project_name, Chris@0: // Only save attributes from the .info.yml file we care about so we do Chris@0: // not bloat our RAM usage needlessly. Chris@0: 'info' => $this->filterProjectInfo($file->info, $additional_whitelist), Chris@0: 'datestamp' => $file->info['datestamp'], Chris@0: 'includes' => [$file->getName() => $file->info['name']], Chris@0: 'project_type' => $project_display_type, Chris@0: 'project_status' => $status, Chris@0: ]; Chris@0: } Chris@0: elseif ($projects[$project_name]['project_type'] == $project_display_type) { Chris@0: // Only add the file we're processing to the 'includes' array for this Chris@0: // project if it is of the same type and status (which is encoded in the Chris@0: // $project_display_type). This prevents listing all the uninstalled Chris@0: // modules included with an enabled project if we happen to be checking Chris@0: // for uninstalled modules, too. Chris@0: $projects[$project_name]['includes'][$file->getName()] = $file->info['name']; Chris@0: $projects[$project_name]['info']['_info_file_ctime'] = max($projects[$project_name]['info']['_info_file_ctime'], $file->info['_info_file_ctime']); Chris@0: $projects[$project_name]['datestamp'] = max($projects[$project_name]['datestamp'], $file->info['datestamp']); Chris@0: } Chris@0: elseif (empty($status)) { Chris@0: // If we have a project_name that matches, but the project_display_type Chris@0: // does not, it means we're processing a uninstalled module or theme Chris@0: // that belongs to a project that has some enabled code. In this case, Chris@0: // we add the uninstalled thing into a separate array for separate Chris@0: // display. Chris@0: $projects[$project_name]['disabled'][$file->getName()] = $file->info['name']; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Determines what project a given file object belongs to. Chris@0: * Chris@0: * @param \Drupal\Core\Extension\Extension $file Chris@0: * An extension object. Chris@0: * Chris@0: * @return string Chris@0: * The canonical project short name. Chris@0: */ Chris@0: public function getProjectName(Extension $file) { Chris@0: $project_name = ''; Chris@0: if (isset($file->info['project'])) { Chris@0: $project_name = $file->info['project']; Chris@0: } Chris@0: elseif (strpos($file->getPath(), 'core/modules') === 0) { Chris@0: $project_name = 'drupal'; Chris@0: } Chris@0: return $project_name; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Filters the project .info.yml data to only save attributes we need. Chris@0: * Chris@0: * @param array $info Chris@0: * Array of .info.yml file data as returned by Chris@0: * \Drupal\Core\Extension\InfoParser. Chris@0: * @param $additional_whitelist Chris@0: * (optional) Array of additional elements to be collected from the .info.yml Chris@0: * file. Defaults to array(). Chris@0: * Chris@0: * @return Chris@0: * Array of .info.yml file data we need for the update manager. Chris@0: * Chris@0: * @see \Drupal\Core\Utility\ProjectInfo::processInfoList() Chris@0: */ Chris@0: public function filterProjectInfo($info, $additional_whitelist = []) { Chris@0: $whitelist = [ Chris@0: '_info_file_ctime', Chris@0: 'datestamp', Chris@0: 'major', Chris@0: 'name', Chris@0: 'package', Chris@0: 'project', Chris@0: 'project status url', Chris@0: 'version', Chris@0: ]; Chris@0: $whitelist = array_merge($whitelist, $additional_whitelist); Chris@0: return array_intersect_key($info, array_combine($whitelist, $whitelist)); Chris@0: } Chris@0: Chris@0: }