annotate modules/update/update.compare.inc @ 13:134d4b2e75f6

updated quicktabs and google analytics modules
author danieleb <danielebarchiesi@me.com>
date Tue, 29 Oct 2013 13:48:59 +0000
parents ff03f76ab3fe
children
rev   line source
danielebarchiesi@0 1 <?php
danielebarchiesi@0 2
danielebarchiesi@0 3 /**
danielebarchiesi@0 4 * @file
danielebarchiesi@0 5 * Code required only when comparing available updates to existing data.
danielebarchiesi@0 6 */
danielebarchiesi@0 7
danielebarchiesi@0 8 /**
danielebarchiesi@0 9 * Fetches an array of installed and enabled projects.
danielebarchiesi@0 10 *
danielebarchiesi@0 11 * This is only responsible for generating an array of projects (taking into
danielebarchiesi@0 12 * account projects that include more than one module or theme). Other
danielebarchiesi@0 13 * information like the specific version and install type (official release,
danielebarchiesi@0 14 * dev snapshot, etc) is handled later in update_process_project_info() since
danielebarchiesi@0 15 * that logic is only required when preparing the status report, not for
danielebarchiesi@0 16 * fetching the available release data.
danielebarchiesi@0 17 *
danielebarchiesi@0 18 * This array is fairly expensive to construct, since it involves a lot of disk
danielebarchiesi@0 19 * I/O, so we cache the results into the {cache_update} table using the
danielebarchiesi@0 20 * 'update_project_projects' cache ID. However, since this is not the data about
danielebarchiesi@0 21 * available updates fetched from the network, it is acceptable to invalidate it
danielebarchiesi@0 22 * somewhat quickly. If we keep this data for very long, site administrators are
danielebarchiesi@0 23 * more likely to see incorrect results if they upgrade to a newer version of a
danielebarchiesi@0 24 * module or theme but do not visit certain pages that automatically clear this
danielebarchiesi@0 25 * cache.
danielebarchiesi@0 26 *
danielebarchiesi@0 27 * @return
danielebarchiesi@0 28 * An associative array of currently enabled projects keyed by the
danielebarchiesi@0 29 * machine-readable project short name. Each project contains:
danielebarchiesi@0 30 * - name: The machine-readable project short name.
danielebarchiesi@0 31 * - info: An array with values from the main .info file for this project.
danielebarchiesi@0 32 * - name: The human-readable name of the project.
danielebarchiesi@0 33 * - package: The package that the project is grouped under.
danielebarchiesi@0 34 * - version: The version of the project.
danielebarchiesi@0 35 * - project: The Drupal.org project name.
danielebarchiesi@0 36 * - datestamp: The date stamp of the project's main .info file.
danielebarchiesi@0 37 * - _info_file_ctime: The maximum file change time for all of the .info
danielebarchiesi@0 38 * files included in this project.
danielebarchiesi@0 39 * - datestamp: The date stamp when the project was released, if known.
danielebarchiesi@0 40 * - includes: An associative array containing all projects included with this
danielebarchiesi@0 41 * project, keyed by the machine-readable short name with the human-readable
danielebarchiesi@0 42 * name as value.
danielebarchiesi@0 43 * - project_type: The type of project. Allowed values are 'module' and
danielebarchiesi@0 44 * 'theme'.
danielebarchiesi@0 45 * - project_status: This indicates if the project is enabled and will always
danielebarchiesi@0 46 * be TRUE, as the function only returns enabled projects.
danielebarchiesi@0 47 * - sub_themes: If the project is a theme it contains an associative array of
danielebarchiesi@0 48 * all sub-themes.
danielebarchiesi@0 49 * - base_themes: If the project is a theme it contains an associative array
danielebarchiesi@0 50 * of all base-themes.
danielebarchiesi@0 51 *
danielebarchiesi@0 52 * @see update_process_project_info()
danielebarchiesi@0 53 * @see update_calculate_project_data()
danielebarchiesi@0 54 * @see update_project_cache()
danielebarchiesi@0 55 */
danielebarchiesi@0 56 function update_get_projects() {
danielebarchiesi@0 57 $projects = &drupal_static(__FUNCTION__, array());
danielebarchiesi@0 58 if (empty($projects)) {
danielebarchiesi@0 59 // Retrieve the projects from cache, if present.
danielebarchiesi@0 60 $projects = update_project_cache('update_project_projects');
danielebarchiesi@0 61 if (empty($projects)) {
danielebarchiesi@0 62 // Still empty, so we have to rebuild the cache.
danielebarchiesi@0 63 $module_data = system_rebuild_module_data();
danielebarchiesi@0 64 $theme_data = system_rebuild_theme_data();
danielebarchiesi@0 65 _update_process_info_list($projects, $module_data, 'module', TRUE);
danielebarchiesi@0 66 _update_process_info_list($projects, $theme_data, 'theme', TRUE);
danielebarchiesi@0 67 if (variable_get('update_check_disabled', FALSE)) {
danielebarchiesi@0 68 _update_process_info_list($projects, $module_data, 'module', FALSE);
danielebarchiesi@0 69 _update_process_info_list($projects, $theme_data, 'theme', FALSE);
danielebarchiesi@0 70 }
danielebarchiesi@0 71 // Allow other modules to alter projects before fetching and comparing.
danielebarchiesi@0 72 drupal_alter('update_projects', $projects);
danielebarchiesi@0 73 // Cache the site's project data for at most 1 hour.
danielebarchiesi@0 74 _update_cache_set('update_project_projects', $projects, REQUEST_TIME + 3600);
danielebarchiesi@0 75 }
danielebarchiesi@0 76 }
danielebarchiesi@0 77 return $projects;
danielebarchiesi@0 78 }
danielebarchiesi@0 79
danielebarchiesi@0 80 /**
danielebarchiesi@0 81 * Populates an array of project data.
danielebarchiesi@0 82 *
danielebarchiesi@0 83 * This iterates over a list of the installed modules or themes and groups them
danielebarchiesi@0 84 * by project and status. A few parts of this function assume that enabled
danielebarchiesi@0 85 * modules and themes are always processed first, and if disabled modules or
danielebarchiesi@0 86 * themes are being processed (there is a setting to control if disabled code
danielebarchiesi@0 87 * should be included or not in the 'Available updates' report), those are only
danielebarchiesi@0 88 * processed after $projects has been populated with information about the
danielebarchiesi@0 89 * enabled code. Modules and themes set as hidden are always ignored. This
danielebarchiesi@0 90 * function also records the latest change time on the .info files for each
danielebarchiesi@0 91 * module or theme, which is important data which is used when deciding if the
danielebarchiesi@0 92 * cached available update data should be invalidated.
danielebarchiesi@0 93 *
danielebarchiesi@0 94 * @param $projects
danielebarchiesi@0 95 * Reference to the array of project data of what's installed on this site.
danielebarchiesi@0 96 * @param $list
danielebarchiesi@0 97 * Array of data to process to add the relevant info to the $projects array.
danielebarchiesi@0 98 * @param $project_type
danielebarchiesi@0 99 * The kind of data in the list. Can be 'module' or 'theme'.
danielebarchiesi@0 100 * @param $status
danielebarchiesi@0 101 * Boolean that controls what status (enabled or disabled) to process out of
danielebarchiesi@0 102 * the $list and add to the $projects array.
danielebarchiesi@0 103 *
danielebarchiesi@0 104 * @see update_get_projects()
danielebarchiesi@0 105 */
danielebarchiesi@0 106 function _update_process_info_list(&$projects, $list, $project_type, $status) {
danielebarchiesi@0 107 foreach ($list as $file) {
danielebarchiesi@0 108 // A disabled base theme of an enabled sub-theme still has all of its code
danielebarchiesi@0 109 // run by the sub-theme, so we include it in our "enabled" projects list.
danielebarchiesi@0 110 if ($status && !$file->status && !empty($file->sub_themes)) {
danielebarchiesi@0 111 foreach ($file->sub_themes as $key => $name) {
danielebarchiesi@0 112 // Build a list of enabled sub-themes.
danielebarchiesi@0 113 if ($list[$key]->status) {
danielebarchiesi@0 114 $file->enabled_sub_themes[$key] = $name;
danielebarchiesi@0 115 }
danielebarchiesi@0 116 }
danielebarchiesi@0 117 // If there are no enabled subthemes, we should ignore this base theme
danielebarchiesi@0 118 // for the enabled case. If the site is trying to display disabled
danielebarchiesi@0 119 // themes, we'll catch it then.
danielebarchiesi@0 120 if (empty($file->enabled_sub_themes)) {
danielebarchiesi@0 121 continue;
danielebarchiesi@0 122 }
danielebarchiesi@0 123 }
danielebarchiesi@0 124 // Otherwise, just add projects of the proper status to our list.
danielebarchiesi@0 125 elseif ($file->status != $status) {
danielebarchiesi@0 126 continue;
danielebarchiesi@0 127 }
danielebarchiesi@0 128
danielebarchiesi@0 129 // Skip if the .info file is broken.
danielebarchiesi@0 130 if (empty($file->info)) {
danielebarchiesi@0 131 continue;
danielebarchiesi@0 132 }
danielebarchiesi@0 133
danielebarchiesi@0 134 // Skip if it's a hidden module or theme.
danielebarchiesi@0 135 if (!empty($file->info['hidden'])) {
danielebarchiesi@0 136 continue;
danielebarchiesi@0 137 }
danielebarchiesi@0 138
danielebarchiesi@0 139 // If the .info doesn't define the 'project', try to figure it out.
danielebarchiesi@0 140 if (!isset($file->info['project'])) {
danielebarchiesi@0 141 $file->info['project'] = update_get_project_name($file);
danielebarchiesi@0 142 }
danielebarchiesi@0 143
danielebarchiesi@0 144 // If we still don't know the 'project', give up.
danielebarchiesi@0 145 if (empty($file->info['project'])) {
danielebarchiesi@0 146 continue;
danielebarchiesi@0 147 }
danielebarchiesi@0 148
danielebarchiesi@0 149 // If we don't already know it, grab the change time on the .info file
danielebarchiesi@0 150 // itself. Note: we need to use the ctime, not the mtime (modification
danielebarchiesi@0 151 // time) since many (all?) tar implementations will go out of their way to
danielebarchiesi@0 152 // set the mtime on the files it creates to the timestamps recorded in the
danielebarchiesi@0 153 // tarball. We want to see the last time the file was changed on disk,
danielebarchiesi@0 154 // which is left alone by tar and correctly set to the time the .info file
danielebarchiesi@0 155 // was unpacked.
danielebarchiesi@0 156 if (!isset($file->info['_info_file_ctime'])) {
danielebarchiesi@0 157 $info_filename = dirname($file->uri) . '/' . $file->name . '.info';
danielebarchiesi@0 158 $file->info['_info_file_ctime'] = filectime($info_filename);
danielebarchiesi@0 159 }
danielebarchiesi@0 160
danielebarchiesi@0 161 if (!isset($file->info['datestamp'])) {
danielebarchiesi@0 162 $file->info['datestamp'] = 0;
danielebarchiesi@0 163 }
danielebarchiesi@0 164
danielebarchiesi@0 165 $project_name = $file->info['project'];
danielebarchiesi@0 166
danielebarchiesi@0 167 // Figure out what project type we're going to use to display this module
danielebarchiesi@0 168 // or theme. If the project name is 'drupal', we don't want it to show up
danielebarchiesi@0 169 // under the usual "Modules" section, we put it at a special "Drupal Core"
danielebarchiesi@0 170 // section at the top of the report.
danielebarchiesi@0 171 if ($project_name == 'drupal') {
danielebarchiesi@0 172 $project_display_type = 'core';
danielebarchiesi@0 173 }
danielebarchiesi@0 174 else {
danielebarchiesi@0 175 $project_display_type = $project_type;
danielebarchiesi@0 176 }
danielebarchiesi@0 177 if (empty($status) && empty($file->enabled_sub_themes)) {
danielebarchiesi@0 178 // If we're processing disabled modules or themes, append a suffix.
danielebarchiesi@0 179 // However, we don't do this to a a base theme with enabled
danielebarchiesi@0 180 // subthemes, since we treat that case as if it is enabled.
danielebarchiesi@0 181 $project_display_type .= '-disabled';
danielebarchiesi@0 182 }
danielebarchiesi@0 183 // Add a list of sub-themes that "depend on" the project and a list of base
danielebarchiesi@0 184 // themes that are "required by" the project.
danielebarchiesi@0 185 if ($project_name == 'drupal') {
danielebarchiesi@0 186 // Drupal core is always required, so this extra info would be noise.
danielebarchiesi@0 187 $sub_themes = array();
danielebarchiesi@0 188 $base_themes = array();
danielebarchiesi@0 189 }
danielebarchiesi@0 190 else {
danielebarchiesi@0 191 // Add list of enabled sub-themes.
danielebarchiesi@0 192 $sub_themes = !empty($file->enabled_sub_themes) ? $file->enabled_sub_themes : array();
danielebarchiesi@0 193 // Add list of base themes.
danielebarchiesi@0 194 $base_themes = !empty($file->base_themes) ? $file->base_themes : array();
danielebarchiesi@0 195 }
danielebarchiesi@0 196 if (!isset($projects[$project_name])) {
danielebarchiesi@0 197 // Only process this if we haven't done this project, since a single
danielebarchiesi@0 198 // project can have multiple modules or themes.
danielebarchiesi@0 199 $projects[$project_name] = array(
danielebarchiesi@0 200 'name' => $project_name,
danielebarchiesi@0 201 // Only save attributes from the .info file we care about so we do not
danielebarchiesi@0 202 // bloat our RAM usage needlessly.
danielebarchiesi@0 203 'info' => update_filter_project_info($file->info),
danielebarchiesi@0 204 'datestamp' => $file->info['datestamp'],
danielebarchiesi@0 205 'includes' => array($file->name => $file->info['name']),
danielebarchiesi@0 206 'project_type' => $project_display_type,
danielebarchiesi@0 207 'project_status' => $status,
danielebarchiesi@0 208 'sub_themes' => $sub_themes,
danielebarchiesi@0 209 'base_themes' => $base_themes,
danielebarchiesi@0 210 );
danielebarchiesi@0 211 }
danielebarchiesi@0 212 elseif ($projects[$project_name]['project_type'] == $project_display_type) {
danielebarchiesi@0 213 // Only add the file we're processing to the 'includes' array for this
danielebarchiesi@0 214 // project if it is of the same type and status (which is encoded in the
danielebarchiesi@0 215 // $project_display_type). This prevents listing all the disabled
danielebarchiesi@0 216 // modules included with an enabled project if we happen to be checking
danielebarchiesi@0 217 // for disabled modules, too.
danielebarchiesi@0 218 $projects[$project_name]['includes'][$file->name] = $file->info['name'];
danielebarchiesi@0 219 $projects[$project_name]['info']['_info_file_ctime'] = max($projects[$project_name]['info']['_info_file_ctime'], $file->info['_info_file_ctime']);
danielebarchiesi@0 220 $projects[$project_name]['datestamp'] = max($projects[$project_name]['datestamp'], $file->info['datestamp']);
danielebarchiesi@0 221 if (!empty($sub_themes)) {
danielebarchiesi@0 222 $projects[$project_name]['sub_themes'] += $sub_themes;
danielebarchiesi@0 223 }
danielebarchiesi@0 224 if (!empty($base_themes)) {
danielebarchiesi@0 225 $projects[$project_name]['base_themes'] += $base_themes;
danielebarchiesi@0 226 }
danielebarchiesi@0 227 }
danielebarchiesi@0 228 elseif (empty($status)) {
danielebarchiesi@0 229 // If we have a project_name that matches, but the project_display_type
danielebarchiesi@0 230 // does not, it means we're processing a disabled module or theme that
danielebarchiesi@0 231 // belongs to a project that has some enabled code. In this case, we add
danielebarchiesi@0 232 // the disabled thing into a separate array for separate display.
danielebarchiesi@0 233 $projects[$project_name]['disabled'][$file->name] = $file->info['name'];
danielebarchiesi@0 234 }
danielebarchiesi@0 235 }
danielebarchiesi@0 236 }
danielebarchiesi@0 237
danielebarchiesi@0 238 /**
danielebarchiesi@0 239 * Determines what project a given file object belongs to.
danielebarchiesi@0 240 *
danielebarchiesi@0 241 * @param $file
danielebarchiesi@0 242 * A file object as returned by system_get_files_database().
danielebarchiesi@0 243 *
danielebarchiesi@0 244 * @return
danielebarchiesi@0 245 * The canonical project short name.
danielebarchiesi@0 246 *
danielebarchiesi@0 247 * @see system_get_files_database()
danielebarchiesi@0 248 */
danielebarchiesi@0 249 function update_get_project_name($file) {
danielebarchiesi@0 250 $project_name = '';
danielebarchiesi@0 251 if (isset($file->info['project'])) {
danielebarchiesi@0 252 $project_name = $file->info['project'];
danielebarchiesi@0 253 }
danielebarchiesi@0 254 elseif (isset($file->info['package']) && (strpos($file->info['package'], 'Core') === 0)) {
danielebarchiesi@0 255 $project_name = 'drupal';
danielebarchiesi@0 256 }
danielebarchiesi@0 257 return $project_name;
danielebarchiesi@0 258 }
danielebarchiesi@0 259
danielebarchiesi@0 260 /**
danielebarchiesi@0 261 * Determines version and type information for currently installed projects.
danielebarchiesi@0 262 *
danielebarchiesi@0 263 * Processes the list of projects on the system to figure out the currently
danielebarchiesi@0 264 * installed versions, and other information that is required before we can
danielebarchiesi@0 265 * compare against the available releases to produce the status report.
danielebarchiesi@0 266 *
danielebarchiesi@0 267 * @param $projects
danielebarchiesi@0 268 * Array of project information from update_get_projects().
danielebarchiesi@0 269 */
danielebarchiesi@0 270 function update_process_project_info(&$projects) {
danielebarchiesi@0 271 foreach ($projects as $key => $project) {
danielebarchiesi@0 272 // Assume an official release until we see otherwise.
danielebarchiesi@0 273 $install_type = 'official';
danielebarchiesi@0 274
danielebarchiesi@0 275 $info = $project['info'];
danielebarchiesi@0 276
danielebarchiesi@0 277 if (isset($info['version'])) {
danielebarchiesi@0 278 // Check for development snapshots
danielebarchiesi@0 279 if (preg_match('@(dev|HEAD)@', $info['version'])) {
danielebarchiesi@0 280 $install_type = 'dev';
danielebarchiesi@0 281 }
danielebarchiesi@0 282
danielebarchiesi@0 283 // Figure out what the currently installed major version is. We need
danielebarchiesi@0 284 // to handle both contribution (e.g. "5.x-1.3", major = 1) and core
danielebarchiesi@0 285 // (e.g. "5.1", major = 5) version strings.
danielebarchiesi@0 286 $matches = array();
danielebarchiesi@0 287 if (preg_match('/^(\d+\.x-)?(\d+)\..*$/', $info['version'], $matches)) {
danielebarchiesi@0 288 $info['major'] = $matches[2];
danielebarchiesi@0 289 }
danielebarchiesi@0 290 elseif (!isset($info['major'])) {
danielebarchiesi@0 291 // This would only happen for version strings that don't follow the
danielebarchiesi@0 292 // drupal.org convention. We let contribs define "major" in their
danielebarchiesi@0 293 // .info in this case, and only if that's missing would we hit this.
danielebarchiesi@0 294 $info['major'] = -1;
danielebarchiesi@0 295 }
danielebarchiesi@0 296 }
danielebarchiesi@0 297 else {
danielebarchiesi@0 298 // No version info available at all.
danielebarchiesi@0 299 $install_type = 'unknown';
danielebarchiesi@0 300 $info['version'] = t('Unknown');
danielebarchiesi@0 301 $info['major'] = -1;
danielebarchiesi@0 302 }
danielebarchiesi@0 303
danielebarchiesi@0 304 // Finally, save the results we care about into the $projects array.
danielebarchiesi@0 305 $projects[$key]['existing_version'] = $info['version'];
danielebarchiesi@0 306 $projects[$key]['existing_major'] = $info['major'];
danielebarchiesi@0 307 $projects[$key]['install_type'] = $install_type;
danielebarchiesi@0 308 }
danielebarchiesi@0 309 }
danielebarchiesi@0 310
danielebarchiesi@0 311 /**
danielebarchiesi@0 312 * Calculates the current update status of all projects on the site.
danielebarchiesi@0 313 *
danielebarchiesi@0 314 * The results of this function are expensive to compute, especially on sites
danielebarchiesi@0 315 * with lots of modules or themes, since it involves a lot of comparisons and
danielebarchiesi@0 316 * other operations. Therefore, we cache the results into the {cache_update}
danielebarchiesi@0 317 * table using the 'update_project_data' cache ID. However, since this is not
danielebarchiesi@0 318 * the data about available updates fetched from the network, it is ok to
danielebarchiesi@0 319 * invalidate it somewhat quickly. If we keep this data for very long, site
danielebarchiesi@0 320 * administrators are more likely to see incorrect results if they upgrade to a
danielebarchiesi@0 321 * newer version of a module or theme but do not visit certain pages that
danielebarchiesi@0 322 * automatically clear this cache.
danielebarchiesi@0 323 *
danielebarchiesi@0 324 * @param array $available
danielebarchiesi@0 325 * Data about available project releases.
danielebarchiesi@0 326 *
danielebarchiesi@0 327 * @return
danielebarchiesi@0 328 * An array of installed projects with current update status information.
danielebarchiesi@0 329 *
danielebarchiesi@0 330 * @see update_get_available()
danielebarchiesi@0 331 * @see update_get_projects()
danielebarchiesi@0 332 * @see update_process_project_info()
danielebarchiesi@0 333 * @see update_project_cache()
danielebarchiesi@0 334 */
danielebarchiesi@0 335 function update_calculate_project_data($available) {
danielebarchiesi@0 336 // Retrieve the projects from cache, if present.
danielebarchiesi@0 337 $projects = update_project_cache('update_project_data');
danielebarchiesi@0 338 // If $projects is empty, then the cache must be rebuilt.
danielebarchiesi@0 339 // Otherwise, return the cached data and skip the rest of the function.
danielebarchiesi@0 340 if (!empty($projects)) {
danielebarchiesi@0 341 return $projects;
danielebarchiesi@0 342 }
danielebarchiesi@0 343 $projects = update_get_projects();
danielebarchiesi@0 344 update_process_project_info($projects);
danielebarchiesi@0 345 foreach ($projects as $project => $project_info) {
danielebarchiesi@0 346 if (isset($available[$project])) {
danielebarchiesi@0 347 update_calculate_project_update_status($project, $projects[$project], $available[$project]);
danielebarchiesi@0 348 }
danielebarchiesi@0 349 else {
danielebarchiesi@0 350 $projects[$project]['status'] = UPDATE_UNKNOWN;
danielebarchiesi@0 351 $projects[$project]['reason'] = t('No available releases found');
danielebarchiesi@0 352 }
danielebarchiesi@0 353 }
danielebarchiesi@0 354 // Give other modules a chance to alter the status (for example, to allow a
danielebarchiesi@0 355 // contrib module to provide fine-grained settings to ignore specific
danielebarchiesi@0 356 // projects or releases).
danielebarchiesi@0 357 drupal_alter('update_status', $projects);
danielebarchiesi@0 358
danielebarchiesi@0 359 // Cache the site's update status for at most 1 hour.
danielebarchiesi@0 360 _update_cache_set('update_project_data', $projects, REQUEST_TIME + 3600);
danielebarchiesi@0 361 return $projects;
danielebarchiesi@0 362 }
danielebarchiesi@0 363
danielebarchiesi@0 364 /**
danielebarchiesi@0 365 * Calculates the current update status of a specific project.
danielebarchiesi@0 366 *
danielebarchiesi@0 367 * This function is the heart of the update status feature. For each project it
danielebarchiesi@0 368 * is invoked with, it first checks if the project has been flagged with a
danielebarchiesi@0 369 * special status like "unsupported" or "insecure", or if the project node
danielebarchiesi@0 370 * itself has been unpublished. In any of those cases, the project is marked
danielebarchiesi@0 371 * with an error and the next project is considered.
danielebarchiesi@0 372 *
danielebarchiesi@0 373 * If the project itself is valid, the function decides what major release
danielebarchiesi@0 374 * series to consider. The project defines what the currently supported major
danielebarchiesi@0 375 * versions are for each version of core, so the first step is to make sure the
danielebarchiesi@0 376 * current version is still supported. If so, that's the target version. If the
danielebarchiesi@0 377 * current version is unsupported, the project maintainer's recommended major
danielebarchiesi@0 378 * version is used. There's also a check to make sure that this function never
danielebarchiesi@0 379 * recommends an earlier release than the currently installed major version.
danielebarchiesi@0 380 *
danielebarchiesi@0 381 * Given a target major version, the available releases are scanned looking for
danielebarchiesi@0 382 * the specific release to recommend (avoiding beta releases and development
danielebarchiesi@0 383 * snapshots if possible). For the target major version, the highest patch level
danielebarchiesi@0 384 * is found. If there is a release at that patch level with no extra ("beta",
danielebarchiesi@0 385 * etc.), then the release at that patch level with the most recent release date
danielebarchiesi@0 386 * is recommended. If every release at that patch level has extra (only betas),
danielebarchiesi@0 387 * then the latest release from the previous patch level is recommended. For
danielebarchiesi@0 388 * example:
danielebarchiesi@0 389 *
danielebarchiesi@0 390 * - 1.6-bugfix <-- recommended version because 1.6 already exists.
danielebarchiesi@0 391 * - 1.6
danielebarchiesi@0 392 *
danielebarchiesi@0 393 * or
danielebarchiesi@0 394 *
danielebarchiesi@0 395 * - 1.6-beta
danielebarchiesi@0 396 * - 1.5 <-- recommended version because no 1.6 exists.
danielebarchiesi@0 397 * - 1.4
danielebarchiesi@0 398 *
danielebarchiesi@0 399 * Also, the latest release from the same major version is looked for, even beta
danielebarchiesi@0 400 * releases, to display to the user as the "Latest version" option.
danielebarchiesi@0 401 * Additionally, the latest official release from any higher major versions that
danielebarchiesi@0 402 * have been released is searched for to provide a set of "Also available"
danielebarchiesi@0 403 * options.
danielebarchiesi@0 404 *
danielebarchiesi@0 405 * Finally, and most importantly, the release history continues to be scanned
danielebarchiesi@0 406 * until the currently installed release is reached, searching for anything
danielebarchiesi@0 407 * marked as a security update. If any security updates have been found between
danielebarchiesi@0 408 * the recommended release and the installed version, all of the releases that
danielebarchiesi@0 409 * included a security fix are recorded so that the site administrator can be
danielebarchiesi@0 410 * warned their site is insecure, and links pointing to the release notes for
danielebarchiesi@0 411 * each security update can be included (which, in turn, will link to the
danielebarchiesi@0 412 * official security announcements for each vulnerability).
danielebarchiesi@0 413 *
danielebarchiesi@0 414 * This function relies on the fact that the .xml release history data comes
danielebarchiesi@0 415 * sorted based on major version and patch level, then finally by release date
danielebarchiesi@0 416 * if there are multiple releases such as betas from the same major.patch
danielebarchiesi@0 417 * version (e.g., 5.x-1.5-beta1, 5.x-1.5-beta2, and 5.x-1.5). Development
danielebarchiesi@0 418 * snapshots for a given major version are always listed last.
danielebarchiesi@0 419 *
danielebarchiesi@0 420 * @param $project
danielebarchiesi@0 421 * An array containing information about a specific project.
danielebarchiesi@0 422 * @param $project_data
danielebarchiesi@0 423 * An array containing information about a specific project.
danielebarchiesi@0 424 * @param $available
danielebarchiesi@0 425 * Data about available project releases of a specific project.
danielebarchiesi@0 426 */
danielebarchiesi@0 427 function update_calculate_project_update_status($project, &$project_data, $available) {
danielebarchiesi@0 428 foreach (array('title', 'link') as $attribute) {
danielebarchiesi@0 429 if (!isset($project_data[$attribute]) && isset($available[$attribute])) {
danielebarchiesi@0 430 $project_data[$attribute] = $available[$attribute];
danielebarchiesi@0 431 }
danielebarchiesi@0 432 }
danielebarchiesi@0 433
danielebarchiesi@0 434 // If the project status is marked as something bad, there's nothing else
danielebarchiesi@0 435 // to consider.
danielebarchiesi@0 436 if (isset($available['project_status'])) {
danielebarchiesi@0 437 switch ($available['project_status']) {
danielebarchiesi@0 438 case 'insecure':
danielebarchiesi@0 439 $project_data['status'] = UPDATE_NOT_SECURE;
danielebarchiesi@0 440 if (empty($project_data['extra'])) {
danielebarchiesi@0 441 $project_data['extra'] = array();
danielebarchiesi@0 442 }
danielebarchiesi@0 443 $project_data['extra'][] = array(
danielebarchiesi@0 444 'class' => array('project-not-secure'),
danielebarchiesi@0 445 'label' => t('Project not secure'),
danielebarchiesi@0 446 'data' => t('This project has been labeled insecure by the Drupal security team, and is no longer available for download. Immediately disabling everything included by this project is strongly recommended!'),
danielebarchiesi@0 447 );
danielebarchiesi@0 448 break;
danielebarchiesi@0 449 case 'unpublished':
danielebarchiesi@0 450 case 'revoked':
danielebarchiesi@0 451 $project_data['status'] = UPDATE_REVOKED;
danielebarchiesi@0 452 if (empty($project_data['extra'])) {
danielebarchiesi@0 453 $project_data['extra'] = array();
danielebarchiesi@0 454 }
danielebarchiesi@0 455 $project_data['extra'][] = array(
danielebarchiesi@0 456 'class' => array('project-revoked'),
danielebarchiesi@0 457 'label' => t('Project revoked'),
danielebarchiesi@0 458 'data' => t('This project has been revoked, and is no longer available for download. Disabling everything included by this project is strongly recommended!'),
danielebarchiesi@0 459 );
danielebarchiesi@0 460 break;
danielebarchiesi@0 461 case 'unsupported':
danielebarchiesi@0 462 $project_data['status'] = UPDATE_NOT_SUPPORTED;
danielebarchiesi@0 463 if (empty($project_data['extra'])) {
danielebarchiesi@0 464 $project_data['extra'] = array();
danielebarchiesi@0 465 }
danielebarchiesi@0 466 $project_data['extra'][] = array(
danielebarchiesi@0 467 'class' => array('project-not-supported'),
danielebarchiesi@0 468 'label' => t('Project not supported'),
danielebarchiesi@0 469 'data' => t('This project is no longer supported, and is no longer available for download. Disabling everything included by this project is strongly recommended!'),
danielebarchiesi@0 470 );
danielebarchiesi@0 471 break;
danielebarchiesi@0 472 case 'not-fetched':
danielebarchiesi@0 473 $project_data['status'] = UPDATE_NOT_FETCHED;
danielebarchiesi@0 474 $project_data['reason'] = t('Failed to get available update data.');
danielebarchiesi@0 475 break;
danielebarchiesi@0 476
danielebarchiesi@0 477 default:
danielebarchiesi@0 478 // Assume anything else (e.g. 'published') is valid and we should
danielebarchiesi@0 479 // perform the rest of the logic in this function.
danielebarchiesi@0 480 break;
danielebarchiesi@0 481 }
danielebarchiesi@0 482 }
danielebarchiesi@0 483
danielebarchiesi@0 484 if (!empty($project_data['status'])) {
danielebarchiesi@0 485 // We already know the status for this project, so there's nothing else to
danielebarchiesi@0 486 // compute. Record the project status into $project_data and we're done.
danielebarchiesi@0 487 $project_data['project_status'] = $available['project_status'];
danielebarchiesi@0 488 return;
danielebarchiesi@0 489 }
danielebarchiesi@0 490
danielebarchiesi@0 491 // Figure out the target major version.
danielebarchiesi@0 492 $existing_major = $project_data['existing_major'];
danielebarchiesi@0 493 $supported_majors = array();
danielebarchiesi@0 494 if (isset($available['supported_majors'])) {
danielebarchiesi@0 495 $supported_majors = explode(',', $available['supported_majors']);
danielebarchiesi@0 496 }
danielebarchiesi@0 497 elseif (isset($available['default_major'])) {
danielebarchiesi@0 498 // Older release history XML file without supported or recommended.
danielebarchiesi@0 499 $supported_majors[] = $available['default_major'];
danielebarchiesi@0 500 }
danielebarchiesi@0 501
danielebarchiesi@0 502 if (in_array($existing_major, $supported_majors)) {
danielebarchiesi@0 503 // Still supported, stay at the current major version.
danielebarchiesi@0 504 $target_major = $existing_major;
danielebarchiesi@0 505 }
danielebarchiesi@0 506 elseif (isset($available['recommended_major'])) {
danielebarchiesi@0 507 // Since 'recommended_major' is defined, we know this is the new XML
danielebarchiesi@0 508 // format. Therefore, we know the current release is unsupported since
danielebarchiesi@0 509 // its major version was not in the 'supported_majors' list. We should
danielebarchiesi@0 510 // find the best release from the recommended major version.
danielebarchiesi@0 511 $target_major = $available['recommended_major'];
danielebarchiesi@0 512 $project_data['status'] = UPDATE_NOT_SUPPORTED;
danielebarchiesi@0 513 }
danielebarchiesi@0 514 elseif (isset($available['default_major'])) {
danielebarchiesi@0 515 // Older release history XML file without recommended, so recommend
danielebarchiesi@0 516 // the currently defined "default_major" version.
danielebarchiesi@0 517 $target_major = $available['default_major'];
danielebarchiesi@0 518 }
danielebarchiesi@0 519 else {
danielebarchiesi@0 520 // Malformed XML file? Stick with the current version.
danielebarchiesi@0 521 $target_major = $existing_major;
danielebarchiesi@0 522 }
danielebarchiesi@0 523
danielebarchiesi@0 524 // Make sure we never tell the admin to downgrade. If we recommended an
danielebarchiesi@0 525 // earlier version than the one they're running, they'd face an
danielebarchiesi@0 526 // impossible data migration problem, since Drupal never supports a DB
danielebarchiesi@0 527 // downgrade path. In the unfortunate case that what they're running is
danielebarchiesi@0 528 // unsupported, and there's nothing newer for them to upgrade to, we
danielebarchiesi@0 529 // can't print out a "Recommended version", but just have to tell them
danielebarchiesi@0 530 // what they have is unsupported and let them figure it out.
danielebarchiesi@0 531 $target_major = max($existing_major, $target_major);
danielebarchiesi@0 532
danielebarchiesi@0 533 $release_patch_changed = '';
danielebarchiesi@0 534 $patch = '';
danielebarchiesi@0 535
danielebarchiesi@0 536 // If the project is marked as UPDATE_FETCH_PENDING, it means that the
danielebarchiesi@0 537 // data we currently have (if any) is stale, and we've got a task queued
danielebarchiesi@0 538 // up to (re)fetch the data. In that case, we mark it as such, merge in
danielebarchiesi@0 539 // whatever data we have (e.g. project title and link), and move on.
danielebarchiesi@0 540 if (!empty($available['fetch_status']) && $available['fetch_status'] == UPDATE_FETCH_PENDING) {
danielebarchiesi@0 541 $project_data['status'] = UPDATE_FETCH_PENDING;
danielebarchiesi@0 542 $project_data['reason'] = t('No available update data');
danielebarchiesi@0 543 $project_data['fetch_status'] = $available['fetch_status'];
danielebarchiesi@0 544 return;
danielebarchiesi@0 545 }
danielebarchiesi@0 546
danielebarchiesi@0 547 // Defend ourselves from XML history files that contain no releases.
danielebarchiesi@0 548 if (empty($available['releases'])) {
danielebarchiesi@0 549 $project_data['status'] = UPDATE_UNKNOWN;
danielebarchiesi@0 550 $project_data['reason'] = t('No available releases found');
danielebarchiesi@0 551 return;
danielebarchiesi@0 552 }
danielebarchiesi@0 553 foreach ($available['releases'] as $version => $release) {
danielebarchiesi@0 554 // First, if this is the existing release, check a few conditions.
danielebarchiesi@0 555 if ($project_data['existing_version'] === $version) {
danielebarchiesi@0 556 if (isset($release['terms']['Release type']) &&
danielebarchiesi@0 557 in_array('Insecure', $release['terms']['Release type'])) {
danielebarchiesi@0 558 $project_data['status'] = UPDATE_NOT_SECURE;
danielebarchiesi@0 559 }
danielebarchiesi@0 560 elseif ($release['status'] == 'unpublished') {
danielebarchiesi@0 561 $project_data['status'] = UPDATE_REVOKED;
danielebarchiesi@0 562 if (empty($project_data['extra'])) {
danielebarchiesi@0 563 $project_data['extra'] = array();
danielebarchiesi@0 564 }
danielebarchiesi@0 565 $project_data['extra'][] = array(
danielebarchiesi@0 566 'class' => array('release-revoked'),
danielebarchiesi@0 567 'label' => t('Release revoked'),
danielebarchiesi@0 568 '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!'),
danielebarchiesi@0 569 );
danielebarchiesi@0 570 }
danielebarchiesi@0 571 elseif (isset($release['terms']['Release type']) &&
danielebarchiesi@0 572 in_array('Unsupported', $release['terms']['Release type'])) {
danielebarchiesi@0 573 $project_data['status'] = UPDATE_NOT_SUPPORTED;
danielebarchiesi@0 574 if (empty($project_data['extra'])) {
danielebarchiesi@0 575 $project_data['extra'] = array();
danielebarchiesi@0 576 }
danielebarchiesi@0 577 $project_data['extra'][] = array(
danielebarchiesi@0 578 'class' => array('release-not-supported'),
danielebarchiesi@0 579 'label' => t('Release not supported'),
danielebarchiesi@0 580 '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!'),
danielebarchiesi@0 581 );
danielebarchiesi@0 582 }
danielebarchiesi@0 583 }
danielebarchiesi@0 584
danielebarchiesi@0 585 // Otherwise, ignore unpublished, insecure, or unsupported releases.
danielebarchiesi@0 586 if ($release['status'] == 'unpublished' ||
danielebarchiesi@0 587 (isset($release['terms']['Release type']) &&
danielebarchiesi@0 588 (in_array('Insecure', $release['terms']['Release type']) ||
danielebarchiesi@0 589 in_array('Unsupported', $release['terms']['Release type'])))) {
danielebarchiesi@0 590 continue;
danielebarchiesi@0 591 }
danielebarchiesi@0 592
danielebarchiesi@0 593 // See if this is a higher major version than our target and yet still
danielebarchiesi@0 594 // supported. If so, record it as an "Also available" release.
danielebarchiesi@0 595 // Note: some projects have a HEAD release from CVS days, which could
danielebarchiesi@0 596 // be one of those being compared. They would not have version_major
danielebarchiesi@0 597 // set, so we must call isset first.
danielebarchiesi@0 598 if (isset($release['version_major']) && $release['version_major'] > $target_major) {
danielebarchiesi@0 599 if (in_array($release['version_major'], $supported_majors)) {
danielebarchiesi@0 600 if (!isset($project_data['also'])) {
danielebarchiesi@0 601 $project_data['also'] = array();
danielebarchiesi@0 602 }
danielebarchiesi@0 603 if (!isset($project_data['also'][$release['version_major']])) {
danielebarchiesi@0 604 $project_data['also'][$release['version_major']] = $version;
danielebarchiesi@0 605 $project_data['releases'][$version] = $release;
danielebarchiesi@0 606 }
danielebarchiesi@0 607 }
danielebarchiesi@0 608 // Otherwise, this release can't matter to us, since it's neither
danielebarchiesi@0 609 // from the release series we're currently using nor the recommended
danielebarchiesi@0 610 // release. We don't even care about security updates for this
danielebarchiesi@0 611 // branch, since if a project maintainer puts out a security release
danielebarchiesi@0 612 // at a higher major version and not at the lower major version,
danielebarchiesi@0 613 // they must remove the lower version from the supported major
danielebarchiesi@0 614 // versions at the same time, in which case we won't hit this code.
danielebarchiesi@0 615 continue;
danielebarchiesi@0 616 }
danielebarchiesi@0 617
danielebarchiesi@0 618 // Look for the 'latest version' if we haven't found it yet. Latest is
danielebarchiesi@0 619 // defined as the most recent version for the target major version.
danielebarchiesi@0 620 if (!isset($project_data['latest_version'])
danielebarchiesi@0 621 && $release['version_major'] == $target_major) {
danielebarchiesi@0 622 $project_data['latest_version'] = $version;
danielebarchiesi@0 623 $project_data['releases'][$version] = $release;
danielebarchiesi@0 624 }
danielebarchiesi@0 625
danielebarchiesi@0 626 // Look for the development snapshot release for this branch.
danielebarchiesi@0 627 if (!isset($project_data['dev_version'])
danielebarchiesi@0 628 && $release['version_major'] == $target_major
danielebarchiesi@0 629 && isset($release['version_extra'])
danielebarchiesi@0 630 && $release['version_extra'] == 'dev') {
danielebarchiesi@0 631 $project_data['dev_version'] = $version;
danielebarchiesi@0 632 $project_data['releases'][$version] = $release;
danielebarchiesi@0 633 }
danielebarchiesi@0 634
danielebarchiesi@0 635 // Look for the 'recommended' version if we haven't found it yet (see
danielebarchiesi@0 636 // phpdoc at the top of this function for the definition).
danielebarchiesi@0 637 if (!isset($project_data['recommended'])
danielebarchiesi@0 638 && $release['version_major'] == $target_major
danielebarchiesi@0 639 && isset($release['version_patch'])) {
danielebarchiesi@0 640 if ($patch != $release['version_patch']) {
danielebarchiesi@0 641 $patch = $release['version_patch'];
danielebarchiesi@0 642 $release_patch_changed = $release;
danielebarchiesi@0 643 }
danielebarchiesi@0 644 if (empty($release['version_extra']) && $patch == $release['version_patch']) {
danielebarchiesi@0 645 $project_data['recommended'] = $release_patch_changed['version'];
danielebarchiesi@0 646 $project_data['releases'][$release_patch_changed['version']] = $release_patch_changed;
danielebarchiesi@0 647 }
danielebarchiesi@0 648 }
danielebarchiesi@0 649
danielebarchiesi@0 650 // Stop searching once we hit the currently installed version.
danielebarchiesi@0 651 if ($project_data['existing_version'] === $version) {
danielebarchiesi@0 652 break;
danielebarchiesi@0 653 }
danielebarchiesi@0 654
danielebarchiesi@0 655 // If we're running a dev snapshot and have a timestamp, stop
danielebarchiesi@0 656 // searching for security updates once we hit an official release
danielebarchiesi@0 657 // older than what we've got. Allow 100 seconds of leeway to handle
danielebarchiesi@0 658 // differences between the datestamp in the .info file and the
danielebarchiesi@0 659 // timestamp of the tarball itself (which are usually off by 1 or 2
danielebarchiesi@0 660 // seconds) so that we don't flag that as a new release.
danielebarchiesi@0 661 if ($project_data['install_type'] == 'dev') {
danielebarchiesi@0 662 if (empty($project_data['datestamp'])) {
danielebarchiesi@0 663 // We don't have current timestamp info, so we can't know.
danielebarchiesi@0 664 continue;
danielebarchiesi@0 665 }
danielebarchiesi@0 666 elseif (isset($release['date']) && ($project_data['datestamp'] + 100 > $release['date'])) {
danielebarchiesi@0 667 // We're newer than this, so we can skip it.
danielebarchiesi@0 668 continue;
danielebarchiesi@0 669 }
danielebarchiesi@0 670 }
danielebarchiesi@0 671
danielebarchiesi@0 672 // See if this release is a security update.
danielebarchiesi@0 673 if (isset($release['terms']['Release type'])
danielebarchiesi@0 674 && in_array('Security update', $release['terms']['Release type'])) {
danielebarchiesi@0 675 $project_data['security updates'][] = $release;
danielebarchiesi@0 676 }
danielebarchiesi@0 677 }
danielebarchiesi@0 678
danielebarchiesi@0 679 // If we were unable to find a recommended version, then make the latest
danielebarchiesi@0 680 // version the recommended version if possible.
danielebarchiesi@0 681 if (!isset($project_data['recommended']) && isset($project_data['latest_version'])) {
danielebarchiesi@0 682 $project_data['recommended'] = $project_data['latest_version'];
danielebarchiesi@0 683 }
danielebarchiesi@0 684
danielebarchiesi@0 685 //
danielebarchiesi@0 686 // Check to see if we need an update or not.
danielebarchiesi@0 687 //
danielebarchiesi@0 688
danielebarchiesi@0 689 if (!empty($project_data['security updates'])) {
danielebarchiesi@0 690 // If we found security updates, that always trumps any other status.
danielebarchiesi@0 691 $project_data['status'] = UPDATE_NOT_SECURE;
danielebarchiesi@0 692 }
danielebarchiesi@0 693
danielebarchiesi@0 694 if (isset($project_data['status'])) {
danielebarchiesi@0 695 // If we already know the status, we're done.
danielebarchiesi@0 696 return;
danielebarchiesi@0 697 }
danielebarchiesi@0 698
danielebarchiesi@0 699 // If we don't know what to recommend, there's nothing we can report.
danielebarchiesi@0 700 // Bail out early.
danielebarchiesi@0 701 if (!isset($project_data['recommended'])) {
danielebarchiesi@0 702 $project_data['status'] = UPDATE_UNKNOWN;
danielebarchiesi@0 703 $project_data['reason'] = t('No available releases found');
danielebarchiesi@0 704 return;
danielebarchiesi@0 705 }
danielebarchiesi@0 706
danielebarchiesi@0 707 // If we're running a dev snapshot, compare the date of the dev snapshot
danielebarchiesi@0 708 // with the latest official version, and record the absolute latest in
danielebarchiesi@0 709 // 'latest_dev' so we can correctly decide if there's a newer release
danielebarchiesi@0 710 // than our current snapshot.
danielebarchiesi@0 711 if ($project_data['install_type'] == 'dev') {
danielebarchiesi@0 712 if (isset($project_data['dev_version']) && $available['releases'][$project_data['dev_version']]['date'] > $available['releases'][$project_data['latest_version']]['date']) {
danielebarchiesi@0 713 $project_data['latest_dev'] = $project_data['dev_version'];
danielebarchiesi@0 714 }
danielebarchiesi@0 715 else {
danielebarchiesi@0 716 $project_data['latest_dev'] = $project_data['latest_version'];
danielebarchiesi@0 717 }
danielebarchiesi@0 718 }
danielebarchiesi@0 719
danielebarchiesi@0 720 // Figure out the status, based on what we've seen and the install type.
danielebarchiesi@0 721 switch ($project_data['install_type']) {
danielebarchiesi@0 722 case 'official':
danielebarchiesi@0 723 if ($project_data['existing_version'] === $project_data['recommended'] || $project_data['existing_version'] === $project_data['latest_version']) {
danielebarchiesi@0 724 $project_data['status'] = UPDATE_CURRENT;
danielebarchiesi@0 725 }
danielebarchiesi@0 726 else {
danielebarchiesi@0 727 $project_data['status'] = UPDATE_NOT_CURRENT;
danielebarchiesi@0 728 }
danielebarchiesi@0 729 break;
danielebarchiesi@0 730
danielebarchiesi@0 731 case 'dev':
danielebarchiesi@0 732 $latest = $available['releases'][$project_data['latest_dev']];
danielebarchiesi@0 733 if (empty($project_data['datestamp'])) {
danielebarchiesi@0 734 $project_data['status'] = UPDATE_NOT_CHECKED;
danielebarchiesi@0 735 $project_data['reason'] = t('Unknown release date');
danielebarchiesi@0 736 }
danielebarchiesi@0 737 elseif (($project_data['datestamp'] + 100 > $latest['date'])) {
danielebarchiesi@0 738 $project_data['status'] = UPDATE_CURRENT;
danielebarchiesi@0 739 }
danielebarchiesi@0 740 else {
danielebarchiesi@0 741 $project_data['status'] = UPDATE_NOT_CURRENT;
danielebarchiesi@0 742 }
danielebarchiesi@0 743 break;
danielebarchiesi@0 744
danielebarchiesi@0 745 default:
danielebarchiesi@0 746 $project_data['status'] = UPDATE_UNKNOWN;
danielebarchiesi@0 747 $project_data['reason'] = t('Invalid info');
danielebarchiesi@0 748 }
danielebarchiesi@0 749 }
danielebarchiesi@0 750
danielebarchiesi@0 751 /**
danielebarchiesi@0 752 * Retrieves data from {cache_update} or empties the cache when necessary.
danielebarchiesi@0 753 *
danielebarchiesi@0 754 * Two very expensive arrays computed by this module are the list of all
danielebarchiesi@0 755 * installed modules and themes (and .info data, project associations, etc), and
danielebarchiesi@0 756 * the current status of the site relative to the currently available releases.
danielebarchiesi@0 757 * These two arrays are cached in the {cache_update} table and used whenever
danielebarchiesi@0 758 * possible. The cache is cleared whenever the administrator visits the status
danielebarchiesi@0 759 * report, available updates report, or the module or theme administration
danielebarchiesi@0 760 * pages, since we should always recompute the most current values on any of
danielebarchiesi@0 761 * those pages.
danielebarchiesi@0 762 *
danielebarchiesi@0 763 * Note: while both of these arrays are expensive to compute (in terms of disk
danielebarchiesi@0 764 * I/O and some fairly heavy CPU processing), neither of these is the actual
danielebarchiesi@0 765 * data about available updates that we have to fetch over the network from
danielebarchiesi@0 766 * updates.drupal.org. That information is stored with the
danielebarchiesi@0 767 * 'update_available_releases' cache ID -- it needs to persist longer than 1
danielebarchiesi@0 768 * hour and never get invalidated just by visiting a page on the site.
danielebarchiesi@0 769 *
danielebarchiesi@0 770 * @param $cid
danielebarchiesi@0 771 * The cache ID of data to return from the cache. Valid options are
danielebarchiesi@0 772 * 'update_project_data' and 'update_project_projects'.
danielebarchiesi@0 773 *
danielebarchiesi@0 774 * @return
danielebarchiesi@0 775 * The cached value of the $projects array generated by
danielebarchiesi@0 776 * update_calculate_project_data() or update_get_projects(), or an empty array
danielebarchiesi@0 777 * when the cache is cleared.
danielebarchiesi@0 778 */
danielebarchiesi@0 779 function update_project_cache($cid) {
danielebarchiesi@0 780 $projects = array();
danielebarchiesi@0 781
danielebarchiesi@0 782 // On certain paths, we should clear the cache and recompute the projects for
danielebarchiesi@0 783 // update status of the site to avoid presenting stale information.
danielebarchiesi@0 784 $q = $_GET['q'];
danielebarchiesi@0 785 $paths = array(
danielebarchiesi@0 786 'admin/modules',
danielebarchiesi@0 787 'admin/modules/update',
danielebarchiesi@0 788 'admin/appearance',
danielebarchiesi@0 789 'admin/appearance/update',
danielebarchiesi@0 790 'admin/reports',
danielebarchiesi@0 791 'admin/reports/updates',
danielebarchiesi@0 792 'admin/reports/updates/update',
danielebarchiesi@0 793 'admin/reports/status',
danielebarchiesi@0 794 'admin/reports/updates/check',
danielebarchiesi@0 795 );
danielebarchiesi@0 796 if (in_array($q, $paths)) {
danielebarchiesi@0 797 _update_cache_clear($cid);
danielebarchiesi@0 798 }
danielebarchiesi@0 799 else {
danielebarchiesi@0 800 $cache = _update_cache_get($cid);
danielebarchiesi@0 801 if (!empty($cache->data) && $cache->expire > REQUEST_TIME) {
danielebarchiesi@0 802 $projects = $cache->data;
danielebarchiesi@0 803 }
danielebarchiesi@0 804 }
danielebarchiesi@0 805 return $projects;
danielebarchiesi@0 806 }
danielebarchiesi@0 807
danielebarchiesi@0 808 /**
danielebarchiesi@0 809 * Filters the project .info data to only save attributes we need.
danielebarchiesi@0 810 *
danielebarchiesi@0 811 * @param array $info
danielebarchiesi@0 812 * Array of .info file data as returned by drupal_parse_info_file().
danielebarchiesi@0 813 *
danielebarchiesi@0 814 * @return
danielebarchiesi@0 815 * Array of .info file data we need for the update manager.
danielebarchiesi@0 816 *
danielebarchiesi@0 817 * @see _update_process_info_list()
danielebarchiesi@0 818 */
danielebarchiesi@0 819 function update_filter_project_info($info) {
danielebarchiesi@0 820 $whitelist = array(
danielebarchiesi@0 821 '_info_file_ctime',
danielebarchiesi@0 822 'datestamp',
danielebarchiesi@0 823 'major',
danielebarchiesi@0 824 'name',
danielebarchiesi@0 825 'package',
danielebarchiesi@0 826 'project',
danielebarchiesi@0 827 'project status url',
danielebarchiesi@0 828 'version',
danielebarchiesi@0 829 );
danielebarchiesi@0 830 return array_intersect_key($info, drupal_map_assoc($whitelist));
danielebarchiesi@0 831 }