- <?php
- * @file
- * Code required only when comparing available updates to existing data.
- */
-
- * Fetches an array of installed and enabled projects.
- *
- * This is only responsible for generating an array of projects (taking into
- * account projects that include more than one module or theme). Other
- * information like the specific version and install type (official release,
- * dev snapshot, etc) is handled later in update_process_project_info() since
- * that logic is only required when preparing the status report, not for
- * fetching the available release data.
- *
- * This array is fairly expensive to construct, since it involves a lot of disk
- * I/O, so we cache the results into the {cache_update} table using the
- * 'update_project_projects' cache ID. However, since this is not the data about
- * available updates fetched from the network, it is acceptable to invalidate it
- * somewhat quickly. If we keep this data for very long, site administrators are
- * more likely to see incorrect results if they upgrade to a newer version of a
- * module or theme but do not visit certain pages that automatically clear this
- * cache.
- *
- * @return
- * An associative array of currently enabled projects keyed by the
- * machine-readable project short name. Each project contains:
- * - name: The machine-readable project short name.
- * - info: An array with values from the main .info file for this project.
- * - name: The human-readable name of the project.
- * - package: The package that the project is grouped under.
- * - version: The version of the project.
- * - project: The BackdropCMS.org project name.
- * - datestamp: The date stamp of the project's main .info file.
- * - _info_file_ctime: The maximum file change time for all of the .info
- * files included in this project.
- * - datestamp: The date stamp when the project was released, if known.
- * - includes: An associative array containing all projects included with this
- * project, keyed by the machine-readable short name with the human-readable
- * name as value.
- * - project_type: The type of project. Allowed values are 'module' and
- * 'theme'.
- * - project_status: This indicates if the project is enabled and will always
- * be TRUE, as the function only returns enabled projects.
- * - sub_themes: If the project is a theme it contains an associative array of
- * all sub-themes.
- * - base_themes: If the project is a theme it contains an associative array
- * of all base-themes.
- *
- * @see update_process_project_info()
- * @see update_calculate_project_data()
- * @see update_project_cache()
- */
- function update_get_projects() {
- $projects = &backdrop_static(__FUNCTION__, array());
- if (empty($projects)) {
-
- $projects = update_project_cache('update_project_projects');
- if (empty($projects)) {
-
- $module_data = system_rebuild_module_data();
- $theme_data = system_rebuild_theme_data();
- $layout_data = _update_build_layout_data();
- _update_process_info_list($projects, $module_data, 'module', TRUE);
- _update_process_info_list($projects, $theme_data, 'theme', TRUE);
- _update_process_info_list($projects, $layout_data, 'layout', TRUE);
- if (config_get('update.settings', 'update_disabled_extensions')) {
- _update_process_info_list($projects, $module_data, 'module', FALSE);
- _update_process_info_list($projects, $theme_data, 'theme', FALSE);
- _update_process_info_list($projects, $layout_data, 'layout', FALSE);
- }
-
- backdrop_alter('update_projects', $projects);
-
- _update_cache_set('update_project_projects', $projects, REQUEST_TIME + 3600);
- }
- }
- return $projects;
- }
-
- * Helper function to scan and collect layout data.
- *
- * @return
- * An associative array of layout information.
- */
- function _update_build_layout_data() {
- $layout_data = layout_get_layout_template_info();
- foreach($layout_data as $layout) {
- $name = $layout['name'];
- $layouts[$name] = new stdClass();
- $layouts[$name]->name = $name;
- $layouts[$name]->uri = $layout['path'] . '/' . $name . '.info';
- $layouts[$name]->info = $layout;
- $layouts[$name]->status = 1;
- }
- return $layouts;
- }
-
- * Populates an array of project data.
- *
- * This function iterates over the list of installed modules, themes and layout
- * templates, and groups them by project type and status.
- *
- * Some parts of this function assume that enabled projects are always processed
- * first, and if disabled projects are being processed (depending on the
- * 'update_disabled_extensions' setting, as configured under
- * admin/reports/updates/settings), those are only processed after $projects has
- * been populated with information about the enabled code. Projects that are set
- * as hidden are always ignored.
- *
- * This function also records the latest change time on the .info files for each
- * module, theme, or layout template. This timestamp is used when deciding if
- * the cached available update data should be invalidated.
- *
- * @param $projects
- * Reference to the array of project data of what's installed on this site.
- * @param $list
- * Array of data to process to add the relevant info to the $projects array.
- * @param $project_type
- * The kind of data in the list. Can be 'module' or 'theme' or 'layout'.
- * @param $status
- * Boolean that controls what status (enabled or disabled) to process out of
- * the $list and add to the $projects array.
- *
- * @see update_get_projects()
- */
- function _update_process_info_list(&$projects, $list, $project_type, $status) {
- $admin_theme = config_get('system.core', 'admin_theme');
- foreach ($list as $file) {
-
-
- if ($file->name === $admin_theme) {
- $file->status = TRUE;
- }
-
-
- if ($status && !$file->status && !empty($file->sub_themes)) {
- foreach ($file->sub_themes as $key => $name) {
-
- if ($list[$key]->status) {
- $file->enabled_sub_themes[$key] = $name;
- }
- }
-
-
-
- if (empty($file->enabled_sub_themes)) {
- continue;
- }
- }
-
- elseif ($file->status != $status) {
- continue;
- }
-
- if (empty($file->info)) {
- continue;
- }
-
-
- if (!empty($file->info['hidden'])) {
- continue;
- }
-
-
- if (!isset($file->info['project'])) {
- $file->info['project'] = update_get_project_name($file);
- }
-
-
- if (empty($file->info['project'])) {
- continue;
- }
-
-
-
-
-
-
-
-
- if (!isset($file->info['_info_file_ctime'])) {
- $info_filename = dirname($file->uri) . '/' . $file->name . '.info';
- $file->info['_info_file_ctime'] = filectime($info_filename);
- }
-
- if (!isset($file->info['datestamp'])) {
- $file->info['datestamp'] = 0;
- }
-
- $project_name = $file->info['project'];
-
-
-
-
-
- if ($project_name == 'backdrop') {
- $project_display_type = 'core';
- }
- else {
- $project_display_type = $project_type;
- }
-
-
-
- if ($project_name == 'backdrop') {
-
- $sub_themes = array();
- $base_themes = array();
- }
- else {
-
- $sub_themes = !empty($file->enabled_sub_themes) ? $file->enabled_sub_themes : array();
-
- $base_themes = !empty($file->base_themes) ? $file->base_themes : array();
- }
-
-
-
-
-
-
-
- $includes_name = isset($file->info['title']) ? $file->info['title'] : $file->info['name'];
- if (!isset($projects[$project_name])) {
-
-
- $projects[$project_name] = array(
- 'name' => $project_name,
-
-
- 'info' => update_filter_project_info($file->info),
- 'datestamp' => $file->info['datestamp'],
- 'includes' => array($file->name => $includes_name),
- 'project_type' => $project_name == 'backdrop' ? 'core' : $project_display_type,
- 'project_status' => $status,
- 'sub_themes' => $sub_themes,
- 'base_themes' => $base_themes,
- );
- }
- elseif ($projects[$project_name]['project_type'] == $project_display_type) {
-
-
-
-
-
- $projects[$project_name]['includes'][$file->name] = $includes_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']);
- if (!empty($sub_themes)) {
- $projects[$project_name]['sub_themes'] += $sub_themes;
- }
- if (!empty($base_themes)) {
- $projects[$project_name]['base_themes'] += $base_themes;
- }
- }
- elseif (empty($status)) {
-
-
-
-
- $projects[$project_name]['disabled'][$file->name] = $includes_name;
- }
- }
- }
-
- * Determines what project a given file object belongs to.
- *
- * @param $file
- * A file object as returned by system_get_files_database().
- *
- * @return
- * The canonical project short name.
- *
- * @see system_get_files_database()
- */
- function update_get_project_name($file) {
- $project_name = '';
- if (isset($file->info['project'])) {
- $project_name = $file->info['project'];
- }
- elseif (isset($file->info['package']) && (strpos($file->info['package'], 'System') === 0)) {
- $project_name = 'backdrop';
- }
- return $project_name;
- }
-
- * Determines version and type information for currently installed projects.
- *
- * Processes the list of projects on the system to figure out the currently
- * installed versions, and other information that is required before we can
- * compare against the available releases to produce the status report.
- *
- * @param $projects
- * Array of project information from update_get_projects().
- */
- function update_process_project_info(&$projects) {
- foreach ($projects as $key => $project) {
-
- $install_type = 'official';
-
- $info = $project['info'];
-
- if (isset($info['version'])) {
-
- if (preg_match('@(dev|HEAD)@', $info['version'])) {
- $install_type = 'dev';
- }
-
-
-
-
- $matches = array();
- if (preg_match('/^(\d+\.x-)?(\d+)\..*$/', $info['version'], $matches)) {
- $info['major'] = $matches[2];
- }
- elseif (!isset($info['major'])) {
-
-
-
- $info['major'] = -1;
- }
- }
- else {
-
- $install_type = 'unknown';
- $info['version'] = t('Unknown');
- $info['major'] = -1;
- }
-
-
- $projects[$key]['existing_version'] = $info['version'];
- $projects[$key]['existing_major'] = $info['major'];
- $projects[$key]['install_type'] = $install_type;
- }
- }
-
- * Calculates the current update status of all projects on the site.
- *
- * The results of this function are expensive to compute, especially on sites
- * with lots of projects, since it involves a lot of comparisons and other
- * operations. Therefore, we cache the results into the {cache_update} table
- * using the 'update_project_data' cache ID. However, since this is not the data
- * about available updates fetched from the network, it is ok to invalidate it
- * somewhat quickly. If we keep this data for very long, site administrators are
- * more likely to see incorrect results if they upgrade to a newer version of a
- * module, theme, or layout, but do not visit certain pages that automatically
- * clear this cache.
- *
- * @param array $available
- * Data about available project releases.
- *
- * @return
- * An array of installed projects with current update status information.
- *
- * @see update_get_available()
- * @see update_get_projects()
- * @see update_process_project_info()
- * @see update_project_cache()
- */
- function update_calculate_project_data($available) {
-
- $projects = update_project_cache('update_project_data');
-
-
-
- if (!empty($projects)) {
- return $projects;
- }
-
- $projects = update_get_projects();
- update_process_project_info($projects);
- foreach ($projects as $project => $project_info) {
- if (isset($available[$project])) {
- update_calculate_project_update_status($projects[$project], $available[$project]);
- }
- else {
- $projects[$project]['status'] = UPDATE_UNKNOWN;
- $projects[$project]['reason'] = t('No available releases found');
- }
- }
-
-
-
- backdrop_alter('update_status', $projects);
-
-
- _update_cache_set('update_project_data', $projects, REQUEST_TIME + 3600);
-
- return $projects;
- }
-
- * Calculates the current update status of a specific project.
- *
- * This function is the heart of the update status feature. For each project it
- * is invoked for, it first checks if the project has been flagged with a
- * special status like "unsupported" or "insecure", or if the project node
- * itself has been unpublished. In any of those cases, the project is marked
- * with an error, and the next project is considered.
- *
- * If the project itself is valid, the function decides what major release
- * series to consider. The project defines what the currently supported major
- * versions are for each version of core, so the first step is to make sure the
- * current version is still supported. If so, that's the target version. If the
- * current version is unsupported, the project maintainer's recommended major
- * version is used. There's also a check to make sure that this function never
- * recommends an earlier release than the currently installed major version.
- *
- * Given a target major version, the available releases are scanned looking for
- * the specific release to recommend (avoiding beta releases and development
- * snapshots if possible). For the target major version, the highest patch level
- * is found. If there is a release at that patch level with no extra ("beta",
- * etc.), then the release at that patch level with the most recent release date
- * is recommended. If every release at that patch level has extra (only betas),
- * then the latest release from the previous patch level is recommended. For
- * example:
- *
- * - 1.6-bugfix <-- recommended version because 1.6 already exists.
- * - 1.6
- *
- * or
- *
- * - 1.6-beta
- * - 1.5 <-- recommended version because no 1.6 exists.
- * - 1.4
- *
- * Also, the latest release from the same major version is looked for, even beta
- * releases, to display to the user as the "Latest version" option.
- * Additionally, the latest official release from any higher major versions that
- * have been released is searched for to provide a set of "Also available"
- * options.
- *
- * Finally, and most importantly, the release history continues to be scanned
- * until the currently installed release is reached, searching for anything
- * marked as a security update. If any security updates have been found between
- * the recommended release and the installed version, all of the releases that
- * included a security fix are recorded so that the site administrator can be
- * warned their site is insecure, and links pointing to the release notes for
- * each security update can be included (which, in turn, will link to the
- * official security announcements for each vulnerability).
- *
- * This function relies on the fact that the .xml release history data comes
- * sorted based on major version and patch level, then finally by release date
- * if there are multiple releases such as betas from the same major.patch
- * version (e.g., 5.x-1.5-beta1, 5.x-1.5-beta2, and 5.x-1.5). Development
- * snapshots for a given major version are always listed last.
- *
- * @param $project_data
- * An array containing information about a specific project.
- * @param $available
- * Data about available project releases of a specific project.
- */
- function update_calculate_project_update_status(&$project_data, $available) {
- foreach (array('title', 'link') as $attribute) {
- if (!isset($project_data[$attribute]) && isset($available[$attribute])) {
- $project_data[$attribute] = $available[$attribute];
- }
- }
-
-
-
- if (isset($available['project_status'])) {
- switch ($available['project_status']) {
- case 'insecure':
- $project_data['status'] = UPDATE_NOT_SECURE;
- if (empty($project_data['extra'])) {
- $project_data['extra'] = array();
- }
- $project_data['extra'][] = array(
- 'class' => array('project-not-secure'),
- 'label' => t('Project not secure'),
- 'data' => t('This project has been labeled insecure by the Backdrop security team, and is no longer available for download. Immediately disabling everything included by this project is strongly recommended!'),
- );
- break;
- case 'unpublished':
- case 'revoked':
- $project_data['status'] = UPDATE_REVOKED;
- if (empty($project_data['extra'])) {
- $project_data['extra'] = array();
- }
- $project_data['extra'][] = array(
- 'class' => array('project-revoked'),
- 'label' => t('Project revoked'),
- 'data' => t('This project has been revoked, and is no longer available for download. Disabling everything included by this project is strongly recommended!'),
- );
- break;
- case 'unsupported':
- $project_data['status'] = UPDATE_NOT_SUPPORTED;
- if (empty($project_data['extra'])) {
- $project_data['extra'] = array();
- }
- $project_data['extra'][] = array(
- 'class' => array('project-not-supported'),
- 'label' => t('Project not supported'),
- 'data' => t('This project is no longer supported, and is no longer available for download. Disabling everything included by this project is strongly recommended!'),
- );
- break;
- case 'not-fetched':
- $project_data['status'] = UPDATE_NOT_FETCHED;
- $project_data['reason'] = t('Failed to get available update data.');
- break;
- case 'not-implemented':
- $project_data['status'] = UPDATE_NOT_IMPLEMENTED;
- $project_data['reason'] = t('Update server is not yet implemented.');
- $project_data['extra'][] = array(
- 'class' => array('project-not-implemented'),
- 'label' => t('Status unknown'),
- 'data' => t('The update server for this project is not yet implemented. Once completed, the update server will start reporting available updates here. Until then, please check for updates manually.'),
- );
- break;
-
- default:
-
-
- break;
- }
- }
-
- if (!empty($project_data['status'])) {
-
-
- $project_data['project_status'] = $available['project_status'];
- return;
- }
-
-
- $existing_major = $project_data['existing_major'];
- $supported_majors = array();
- if (isset($available['supported_majors'])) {
- $supported_majors = explode(',', $available['supported_majors']);
- }
- elseif (isset($available['default_major'])) {
-
- $supported_majors[] = $available['default_major'];
- }
-
- if (in_array($existing_major, $supported_majors)) {
-
- $target_major = $existing_major;
- }
- elseif (isset($available['recommended_major'])) {
-
-
-
-
- $target_major = $available['recommended_major'];
- $project_data['status'] = UPDATE_NOT_SUPPORTED;
- }
- elseif (isset($available['default_major'])) {
-
-
- $target_major = $available['default_major'];
- }
- else {
-
- $target_major = $existing_major;
- }
-
-
-
-
-
-
-
-
- $target_major = max($existing_major, $target_major);
-
- $release_patch_changed = '';
- $patch = '';
-
-
-
-
-
- if (!empty($available['fetch_status']) && $available['fetch_status'] == UPDATE_FETCH_PENDING) {
- $project_data['status'] = UPDATE_FETCH_PENDING;
- $project_data['reason'] = t('No available update data');
- $project_data['fetch_status'] = $available['fetch_status'];
- return;
- }
-
-
- if (empty($available['releases'])) {
- $project_data['status'] = UPDATE_UNKNOWN;
- $project_data['reason'] = t('No available releases found');
- return;
- }
- foreach ($available['releases'] as $version => $release) {
-
- if ($project_data['existing_version'] === $version) {
- if (isset($release['terms']['Release type']) &&
- in_array('Insecure', $release['terms']['Release type'])) {
- $project_data['status'] = UPDATE_NOT_SECURE;
- }
- elseif ($release['status'] == 'unpublished') {
- $project_data['status'] = UPDATE_REVOKED;
- if (empty($project_data['extra'])) {
- $project_data['extra'] = array();
- }
- $project_data['extra'][] = array(
- 'class' => array('release-revoked'),
- 'label' => t('Release revoked'),
- 'data' => t('Your currently installed release has been revoked, and is no longer available for download. Disabling everything included in this release or upgrading is strongly recommended!'),
- );
- }
- elseif (isset($release['terms']['Release type']) &&
- in_array('Unsupported', $release['terms']['Release type'])) {
- $project_data['status'] = UPDATE_NOT_SUPPORTED;
- if (empty($project_data['extra'])) {
- $project_data['extra'] = array();
- }
- $project_data['extra'][] = array(
- 'class' => array('release-not-supported'),
- 'label' => t('Release not supported'),
- 'data' => t('Your currently installed release is now unsupported, and is no longer available for download. Disabling everything included in this release or upgrading is strongly recommended!'),
- );
- }
- }
-
-
- if ($release['status'] == 'unpublished' ||
- (isset($release['terms']['Release type']) &&
- (in_array('Insecure', $release['terms']['Release type']) ||
- in_array('Unsupported', $release['terms']['Release type'])))) {
- continue;
- }
-
-
-
-
-
-
- if (isset($release['version_major']) && $release['version_major'] > $target_major) {
- if (in_array($release['version_major'], $supported_majors)) {
- if (!isset($project_data['also'])) {
- $project_data['also'] = array();
- }
- if (!isset($project_data['also'][$release['version_major']])) {
- $project_data['also'][$release['version_major']] = $version;
- $project_data['releases'][$version] = $release;
- }
- }
-
-
-
-
-
-
-
- continue;
- }
-
-
-
- if (!isset($project_data['latest_version'])
- && $release['version_major'] == $target_major) {
- $project_data['latest_version'] = $version;
- $project_data['releases'][$version] = $release;
- }
-
-
- if (!isset($project_data['dev_version'])
- && $release['version_major'] == $target_major
- && isset($release['version_extra'])
- && $release['version_extra'] == 'dev') {
- $project_data['dev_version'] = $version;
- $project_data['releases'][$version] = $release;
- }
-
-
-
- if (!isset($project_data['recommended'])
- && $release['version_major'] == $target_major
- && isset($release['version_patch'])) {
- if ($patch != $release['version_patch']) {
- $patch = $release['version_patch'];
- $release_patch_changed = $release;
- }
- if (empty($release['version_extra']) && $patch == $release['version_patch']) {
- $project_data['recommended'] = $release_patch_changed['version'];
- $project_data['releases'][$release_patch_changed['version']] = $release_patch_changed;
- }
- }
-
-
- if ($project_data['existing_version'] === $version) {
- break;
- }
-
-
-
-
-
- if (version_compare($version, $project_data['existing_version'], '<')) {
- break;
- }
-
-
-
-
-
-
-
- if ($project_data['install_type'] == 'dev') {
- if (empty($project_data['datestamp'])) {
-
- continue;
- }
- elseif (isset($release['date']) && ($project_data['datestamp'] + 100 > $release['date'])) {
-
- continue;
- }
- }
-
-
- if (isset($release['terms']['Release type'])
- && in_array('Security update', $release['terms']['Release type'])) {
- $project_data['security updates'][] = $release;
- }
- }
-
-
-
- if (!isset($project_data['recommended']) && isset($project_data['latest_version'])) {
- $project_data['recommended'] = $project_data['latest_version'];
- }
-
-
-
-
-
- if (!empty($project_data['security updates'])) {
-
- $project_data['status'] = UPDATE_NOT_SECURE;
- }
-
- if (isset($project_data['status'])) {
-
- return;
- }
-
-
-
- if (!isset($project_data['recommended'])) {
- $project_data['status'] = UPDATE_UNKNOWN;
- $project_data['reason'] = t('No available releases found');
- return;
- }
-
-
-
-
-
- if ($project_data['install_type'] == 'dev') {
- if (isset($project_data['dev_version']) && $available['releases'][$project_data['dev_version']]['date'] > $available['releases'][$project_data['latest_version']]['date']) {
- $project_data['latest_dev'] = $project_data['dev_version'];
- }
- else {
- $project_data['latest_dev'] = $project_data['latest_version'];
- }
- }
-
-
- switch ($project_data['install_type']) {
- case 'official':
- if ($project_data['existing_version'] === $project_data['recommended'] || $project_data['existing_version'] === $project_data['latest_version']) {
- $project_data['status'] = UPDATE_CURRENT;
- }
- else {
- $project_data['status'] = UPDATE_NOT_CURRENT;
- }
- break;
-
- case 'dev':
- $latest = $available['releases'][$project_data['latest_dev']];
- if (empty($project_data['datestamp'])) {
- $project_data['status'] = UPDATE_NOT_CHECKED;
- $project_data['reason'] = t('Developer version');
- }
- elseif (($project_data['datestamp'] + 100 > $latest['date'])) {
- $project_data['status'] = UPDATE_CURRENT;
- }
- else {
- $project_data['status'] = UPDATE_NOT_CURRENT;
- }
- break;
-
- default:
- $project_data['status'] = UPDATE_UNKNOWN;
- $project_data['reason'] = t('Invalid info');
- }
- }
-
- * Retrieves data from {cache_update} or empties the cache when necessary.
- *
- * Two very expensive arrays computed by this module are the list of all
- * installed modules, themes, and layouts (and .info data, project associations, etc), and
- * the current status of the site relative to the currently available releases.
- * These two arrays are cached in the {cache_update} table and used whenever
- * possible. The cache is cleared whenever the administrator visits the status
- * report, available updates report, or the module, theme, or layout administration
- * pages, since we should always recompute the most current values on any of
- * those pages.
- *
- * Note: while both of these arrays are expensive to compute (in terms of disk
- * I/O and some fairly heavy CPU processing), neither of these is the actual
- * data about available updates that we have to fetch over the network from
- * updates.drupal.org. That information is stored with the
- * 'update_available_releases' cache ID -- it needs to persist longer than 1
- * hour and never get invalidated just by visiting a page on the site.
- *
- * @param $cid
- * The cache ID of data to return from the cache. Valid options are
- * 'update_project_data' and 'update_project_projects'.
- *
- * @return
- * The cached value of the $projects array generated by
- * update_calculate_project_data() or update_get_projects(), or an empty array
- * when the cache is cleared.
- */
- function update_project_cache($cid) {
- $projects = array();
-
-
-
- $q = $_GET['q'];
- $paths = array(
- 'admin/config/system/updates',
- 'admin/modules',
- 'admin/appearance',
- 'admin/structure/layouts',
- 'admin/reports',
- 'admin/reports/updates',
- 'admin/reports/updates/update',
- 'admin/reports/status',
- 'admin/reports/updates/check',
- );
- if (in_array($q, $paths)) {
- _update_cache_clear($cid);
- }
- else {
- $cache = _update_cache_get($cid);
- if (!empty($cache->data) && $cache->expire > REQUEST_TIME) {
- $projects = $cache->data;
- }
- }
- return $projects;
- }
-
- * Filters the project .info data to only save attributes we need.
- *
- * @param array $info
- * Array of .info file data as returned by backdrop_parse_info_file().
- *
- * @return
- * Array of .info file data we need for the update manager.
- *
- * @see _update_process_info_list()
- */
- function update_filter_project_info($info) {
- $allowlist = array(
- '_info_file_ctime',
- 'datestamp',
- 'major',
- 'name',
- 'package',
- 'project',
- 'project status url',
- 'version',
- );
- return array_intersect_key($info, backdrop_map_assoc($allowlist));
- }