/[drupal]/drupal/modules/update/update.compare.inc
ViewVC logotype

Contents of /drupal/modules/update/update.compare.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


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

  ViewVC Help
Powered by ViewVC 1.1.2