396 lines
16 KiB
Plaintext
396 lines
16 KiB
Plaintext
<?php
|
|
// $Id$
|
|
|
|
/**
|
|
* @file
|
|
* The "Update status" module checks for available updates of Drupal core and
|
|
* any installed contributed modules and themes. It warns site administrators
|
|
* if newer releases are available via the system status report
|
|
* (admin/reports/status), the module and theme pages, and optionally via email.
|
|
*/
|
|
|
|
/**
|
|
* URL to check for updates, if a given project doesn't define its own.
|
|
*/
|
|
define('UPDATE_DEFAULT_URL', 'http://updates.drupal.org/release-history');
|
|
|
|
// These are internally used constants for this code, do not modify.
|
|
|
|
/**
|
|
* Project is up to date.
|
|
*/
|
|
define('UPDATE_CURRENT', 1);
|
|
|
|
/**
|
|
* Project is missing security update(s).
|
|
*/
|
|
define('UPDATE_NOT_SECURE', 2);
|
|
|
|
/**
|
|
* Project has a new release available, but it is not a security release.
|
|
*/
|
|
define('UPDATE_NOT_CURRENT', 3);
|
|
|
|
/**
|
|
* Project's status cannot be checked.
|
|
*/
|
|
define('UPDATE_NOT_CHECKED', 4);
|
|
|
|
/**
|
|
* No available update data was found for project.
|
|
*/
|
|
define('UPDATE_UNKNOWN', 5);
|
|
|
|
/**
|
|
* Implementation of hook_help().
|
|
*/
|
|
function update_help($path, $arg) {
|
|
switch ($path) {
|
|
case 'admin/reports/updates':
|
|
$output = '<p>'. t('Here you can find information about available updates for your installed modules and themes. Note that each module or theme is part of a "project", which may or may not have the same name, and might include multiple modules or themes within it.') .'</p>';
|
|
$output .= '<p>'. t('To extend the functionality or to change the look of your site, a number of contributed <a href="@modules">modules</a> and <a href="@themes">themes</a> are available.', array('@modules' => 'http://drupal.org/project/modules', '@themes' => 'http://drupal.org/project/themes')) .'</p>';
|
|
return $output;
|
|
case 'admin/build/themes':
|
|
case 'admin/build/modules':
|
|
include_once './includes/install.inc';
|
|
$status = update_requirements('runtime');
|
|
foreach (array('core', 'contrib') as $report_type) {
|
|
$type = 'update_'. $report_type;
|
|
if (isset($status[$type]['severity'])) {
|
|
if ($status[$type]['severity'] == REQUIREMENT_ERROR) {
|
|
drupal_set_message($status[$type]['description'], 'error');
|
|
}
|
|
elseif ($status[$type]['severity'] == REQUIREMENT_WARNING) {
|
|
drupal_set_message($status[$type]['description']);
|
|
}
|
|
}
|
|
}
|
|
return '<p>'. t('See the <a href="@available_updates">available updates</a> page for information on installed modules and themes with new versions released.', array('@available_updates' => url('admin/reports/updates'))) .'</p>';
|
|
|
|
case 'admin/reports/updates/settings':
|
|
case 'admin/reports/status':
|
|
// These two pages don't need additional nagging.
|
|
break;
|
|
|
|
case 'admin/help#update':
|
|
$output = '<p>'. t("The Update status module periodically checks for new versions of your site's software (including contributed modules and themes), and alerts you to available updates.") .'</p>';
|
|
$output .= '<p>'. t('The <a href="@update-report">report of available updates</a> will alert you when new releases are available for download. You may configure options for update checking frequency and notifications at the <a href="@update-settings">Update status module settings page</a>.', array('@update-report' => url('admin/logs/updates'), '@update-settings' => url('admin/logs/updates/settings'))) .'</p>';
|
|
$output .= '<p>'. t('Please note that in order to provide this information, anonymous usage statistics are sent to drupal.org. If desired, you may disable the Update status module from the <a href="@modules">module administration page</a>.', array('@modules' => url('admin/build/modules'))) .'</p>';
|
|
$output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@update">Update status page</a>.', array('@update' => 'http://drupal.org/handbook/modules/update')) .'</p>';
|
|
return $output;
|
|
|
|
default:
|
|
// Otherwise, if we're on *any* admin page and there's a security
|
|
// update missing, print an error message about it.
|
|
if (arg(0) == 'admin' && strpos($path, '#') === FALSE
|
|
&& user_access('administer site configuration')) {
|
|
include_once './includes/install.inc';
|
|
$status = update_requirements('runtime');
|
|
foreach (array('core', 'contrib') as $report_type) {
|
|
$type = 'update_'. $report_type;
|
|
if (isset($status[$type])
|
|
&& isset($status[$type]['reason'])
|
|
&& $status[$type]['reason'] === UPDATE_NOT_SECURE) {
|
|
drupal_set_message($status[$type]['description'], 'error');
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_menu().
|
|
*/
|
|
function update_menu() {
|
|
$items = array();
|
|
|
|
$items['admin/reports/updates'] = array(
|
|
'title' => 'Available updates',
|
|
'description' => 'Get a status report about available updates for your installed modules and themes.',
|
|
'page callback' => 'update_status',
|
|
'access arguments' => array('administer site configuration'),
|
|
'file' => 'update.report.inc',
|
|
'weight' => 10,
|
|
);
|
|
$items['admin/reports/updates/list'] = array(
|
|
'title' => 'List',
|
|
'page callback' => 'update_status',
|
|
'access arguments' => array('administer site configuration'),
|
|
'file' => 'update.report.inc',
|
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
|
);
|
|
$items['admin/reports/updates/settings'] = array(
|
|
'title' => 'Settings',
|
|
'page callback' => 'drupal_get_form',
|
|
'page arguments' => array('update_settings'),
|
|
'access arguments' => array('administer site configuration'),
|
|
'file' => 'update.settings.inc',
|
|
'type' => MENU_LOCAL_TASK,
|
|
);
|
|
$items['admin/reports/updates/check'] = array(
|
|
'title' => 'Manual update check',
|
|
'page callback' => 'update_manual_status',
|
|
'access arguments' => array('administer site configuration'),
|
|
'file' => 'update.fetch.inc',
|
|
'type' => MENU_CALLBACK,
|
|
);
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* Implementation of the hook_theme() registry.
|
|
*/
|
|
function update_theme() {
|
|
return array(
|
|
'update_settings' => array(
|
|
'arguments' => array('form' => NULL),
|
|
),
|
|
'update_report' => array(
|
|
'arguments' => array('data' => NULL),
|
|
),
|
|
'update_version' => array(
|
|
'arguments' => array('version' => NULL, 'tag' => NULL, 'class' => NULL),
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_requirements.
|
|
*
|
|
* @return
|
|
* An array describing the status of the site regarding available updates.
|
|
* If there is no update data, only one record will be returned, indicating
|
|
* that the status of core can't be determined. If data is available, there
|
|
* will be two records: one for core, and another for all of contrib
|
|
* (assuming there are any contributed modules or themes enabled on the
|
|
* site). In addition to the fields expected by hook_requirements ('value',
|
|
* 'severity', and optionally 'description'), this array will contain a
|
|
* 'reason' attribute, which is an integer constant to indicate why the
|
|
* given status is being returned (UPDATE_NOT_SECURE, UPDATE_NOT_CURRENT, or
|
|
* UPDATE_UNKNOWN). This is used for generating the appropriate e-mail
|
|
* notification messages during update_cron(), and might be useful for other
|
|
* modules that invoke update_requirements() to find out if the site is up
|
|
* to date or not.
|
|
*
|
|
* @see _update_message_text()
|
|
* @see _update_cron_notify()
|
|
*/
|
|
function update_requirements($phase) {
|
|
if ($phase == 'runtime') {
|
|
$requirements['update_core']['title'] = t('Drupal core update status');
|
|
$notification_level = variable_get('update_notification_threshold', 'all');
|
|
if ($available = update_get_available(FALSE)) {
|
|
include_once './modules/update/update.compare.inc';
|
|
$data = update_calculate_project_data($available);
|
|
switch ($data['drupal']['status']) {
|
|
case UPDATE_NOT_CURRENT:
|
|
$requirements['update_core']['value'] = t('Out of date (version @version available)', array('@version' => $data['drupal']['recommended']));
|
|
$requirements['update_core']['severity'] = $notification_level == 'all' ? REQUIREMENT_ERROR : REQUIREMENT_WARNING;
|
|
$requirements['update_core']['reason'] = UPDATE_NOT_CURRENT;
|
|
$requirements['update_core']['description'] = _update_message_text('core', UPDATE_NOT_CURRENT, TRUE);
|
|
break;
|
|
|
|
case UPDATE_NOT_SECURE:
|
|
$requirements['update_core']['value'] = t('Not secure! (version @version available)', array('@version' => $data['drupal']['recommended']));
|
|
$requirements['update_core']['severity'] = REQUIREMENT_ERROR;
|
|
$requirements['update_core']['reason'] = UPDATE_NOT_SECURE;
|
|
$requirements['update_core']['description'] = _update_message_text('core', UDPDATE_NOT_SECURE, TRUE);
|
|
break;
|
|
|
|
default:
|
|
$requirements['update_core']['value'] = t('Up to date');
|
|
break;
|
|
}
|
|
// We don't want to check drupal a second time.
|
|
unset($data['drupal']);
|
|
$not_current = FALSE;
|
|
if (!empty($data)) {
|
|
$requirements['update_contrib']['title'] = t('Module and theme update status');
|
|
// Default to being current until we see otherwise.
|
|
$requirements['update_contrib']['value'] = t('Up to date');
|
|
foreach (array_keys($data) as $project) {
|
|
if (isset($available[$project])) {
|
|
if ($data[$project]['status'] == UPDATE_NOT_SECURE) {
|
|
$requirements['update_contrib']['value'] = t('Not secure!');
|
|
$requirements['update_contrib']['severity'] = REQUIREMENT_ERROR;
|
|
$requirements['update_contrib']['reason'] = UPDATE_NOT_SECURE;
|
|
$requirements['update_contrib']['description'] = _update_message_text('contrib', UPDATE_NOT_SECURE, TRUE);
|
|
break;
|
|
}
|
|
elseif ($data[$project]['status'] == UPDATE_NOT_CURRENT) {
|
|
$not_current = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (!isset($requirements['update_contrib']['severity']) && $not_current) {
|
|
$requirements['update_contrib']['severity'] = $notification_level == 'all' ? REQUIREMENT_ERROR : REQUIREMENT_WARNING;
|
|
$requirements['update_contrib']['value'] = t('Out of date');
|
|
$requirements['update_contrib']['reason'] = UPDATE_NOT_CURRENT;
|
|
$requirements['update_contrib']['description'] = _update_message_text('contrib', UPDATE_NOT_CURRENT, TRUE);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$requirements['update_core']['value'] = t('No update data available');
|
|
$requirements['update_core']['severity'] = REQUIREMENT_WARNING;
|
|
$requirements['update_core']['reason'] = UPDATE_UNKNOWN;
|
|
$requirements['update_core']['description'] = _update_no_data();
|
|
}
|
|
return $requirements;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_cron().
|
|
*/
|
|
function update_cron() {
|
|
$frequency = variable_get('update_check_frequency', 1);
|
|
$interval = 60 * 60 * 24 * $frequency;
|
|
if (time() - variable_get('update_last_check', 0) > $interval) {
|
|
update_refresh();
|
|
_update_cron_notify();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_form_alter().
|
|
*
|
|
* Adds a submit handler to the system modules and themes forms, so that if a
|
|
* site admin saves either form, we invalidate the cache of available updates.
|
|
*
|
|
* @see update_invalidate_cache()
|
|
*/
|
|
function update_form_alter(&$form, $form_state, $form_id) {
|
|
if ($form_id == 'system_modules' || $form_id == 'system_themes' ) {
|
|
$form['#submit'][] = 'update_invalidate_cache';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prints a warning message when there is no data about available updates.
|
|
*/
|
|
function _update_no_data() {
|
|
$destination = drupal_get_destination();
|
|
return t('No information is available about potential new releases for currently installed modules and themes. To check for updates, you may need to <a href="@run_cron">run cron</a> or you can <a href="@check_manually">check manually</a>. Please note that checking for available updates can take a long time, so please be patient.', array(
|
|
'@run_cron' => url('admin/reports/status/run-cron', array('query' => $destination)),
|
|
'@check_manually' => url('admin/reports/updates/check', array('query' => $destination)),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Internal helper to try to get the update information from the cache
|
|
* if possible, and to refresh the cache when necessary.
|
|
*
|
|
* @param $refresh
|
|
* Boolean to indicate if this method should refresh the cache automatically
|
|
* if there's no data.
|
|
*/
|
|
function update_get_available($refresh = FALSE) {
|
|
$available = array();
|
|
if (($cache = cache_get('update_info', 'cache_update'))
|
|
&& $cache->expire > time()) {
|
|
$available = $cache->data;
|
|
}
|
|
elseif ($refresh) {
|
|
$available = update_refresh();
|
|
}
|
|
return $available;
|
|
}
|
|
|
|
/**
|
|
* Invalidates any cached data relating to update status.
|
|
*/
|
|
function update_invalidate_cache() {
|
|
cache_clear_all('update_info', 'cache_update');
|
|
}
|
|
|
|
/**
|
|
* Wrapper to load the include file and then refresh the release data.
|
|
*/
|
|
function update_refresh() {
|
|
include_once './modules/update/update.fetch.inc';
|
|
return _update_refresh();
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_mail().
|
|
*
|
|
* Constructs the email notification message when the site is out of date.
|
|
*
|
|
* @param $key
|
|
* Unique key to indicate what message to build, always 'status_notify'.
|
|
* @param $message
|
|
* Reference to the message array being built.
|
|
* @param $params
|
|
* Array of parameters to indicate what kind of text to include in the
|
|
* message body. This is a keyed array of message type ('core' or 'contrib')
|
|
* as the keys, and the status reason constant (UPDATE_NOT_SECURE, etc) for
|
|
* the values.
|
|
*
|
|
* @see drupal_mail();
|
|
* @see _update_cron_notify();
|
|
* @see _update_message_text();
|
|
*/
|
|
function update_mail($key, &$message, $params) {
|
|
$language = $message['language'];
|
|
$langcode = $language->language;
|
|
$message['subject'] .= t('New release(s) available for !site_name', array('!site_name' => variable_get('site_name', 'Drupal')), $langcode);
|
|
foreach ($params as $msg_type => $msg_reason) {
|
|
$message['body'][] = _update_message_text($msg_type, $msg_reason, FALSE, $language);
|
|
}
|
|
$message['body'][] = t('See the available updates page for more information:', array(), $langcode) ."\n". url('admin/reports/updates', array('absolute' => TRUE, 'language' => $language));
|
|
}
|
|
|
|
/**
|
|
* Helper function to return the appropriate message text when the site is out
|
|
* of date or missing a security update.
|
|
*
|
|
* These error messages are shared by both update_requirements() for the
|
|
* site-wide status report at admin/reports/status and in the body of the
|
|
* notification emails generated by update_cron().
|
|
*
|
|
* @param $msg_type
|
|
* String to indicate what kind of message to generate. Can be either
|
|
* 'core' or 'contrib'.
|
|
* @param $msg_reason
|
|
* Integer constant specifying why message is generated. Can be either
|
|
* UPDATE_NOT_CURRENT or UPDATE_NOT_SECURE.
|
|
* @param $report_link
|
|
* Boolean that controls if a link to the updates report should be added.
|
|
* @param $language
|
|
* An optional language object to use.
|
|
* @return
|
|
* The properly translated error message for the given key.
|
|
*/
|
|
function _update_message_text($msg_type, $msg_reason, $report_link = FALSE, $language = NULL) {
|
|
$langcode = isset($language) ? $language->language : NULL;
|
|
$text = '';
|
|
switch ($msg_reason) {
|
|
case UPDATE_NOT_CURRENT:
|
|
if ($msg_type == 'core') {
|
|
$text = t('There are updates available for your version of Drupal. To ensure the proper functioning of your site, you should update as soon as possible.', array(), $langcode);
|
|
}
|
|
else {
|
|
$text = t('There are updates available for one or more of your modules or themes. To ensure the proper functioning of your site, you should update as soon as possible.', array(), $langcode);
|
|
}
|
|
break;
|
|
|
|
case UPDATE_NOT_SECURE:
|
|
if ($msg_type == 'core') {
|
|
$text = t('There is a security update available for your version of Drupal. To ensure the security of your server, you should update immediately!', array(), $langcode);
|
|
}
|
|
else {
|
|
$text = t('There are security updates available for one or more of your modules or themes. To ensure the security of your server, you should update immediately!', array(), $langcode);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ($report_link) {
|
|
$text .= ' '. t('See the <a href="@available_updates">available updates</a> page for more information.', array('@available_updates' => url('admin/reports/updates', array('language' => $language))), $langcode);
|
|
}
|
|
|
|
return $text;
|
|
}
|