View source
<?php
define('UPDATE_STATUS_CURRENT', 1);
define('UPDATE_STATUS_NOT_CURRENT', 2);
define('UPDATE_STATUS_UNKNOWN', 3);
define('UPDATE_STATUS_NOT_CHECKED', 4);
define('UPDATE_STATUS_CANT_CHECK', 5);
function update_status_help($section) {
switch ($section) {
case 'admin/logs/updates':
return '<p>' . t('Here you can find information on the update status of your installed modules. Note that each module is part of a "project", which may have the same name as the module or may have a different name. Also note that this can only check the status for "official releases", and will not be able to check update status for development snapshots and modules updated directly from CVS.') . '</p>';
case 'admin/build/modules':
$status = update_status_requirements('runtime');
if ($status['update_status']['severity'] == REQUIREMENT_ERROR) {
drupal_set_message(t('There are updates available for one or more of your modules. To ensure the security of your server, you should update immediately. See the !status_page for more information', array(
'!status_page' => l('update status page', 'admin/logs/updates'),
)), 'error');
}
return '<p>' . t('See the <a href="!updates">updates log page</a> for information on available updates.', array(
'!updates' => url('admin/logs/updates'),
)) . '</p>';
}
}
function update_status_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'admin/logs/updates',
'title' => t('Available updates'),
'description' => t('Get a status report on your installed modules and available updates.'),
'callback' => 'update_status_status',
'weight' => 10,
'access' => user_access('administer site configuration'),
);
$items[] = array(
'path' => 'admin/logs/updates/list',
'title' => t('List'),
'callback' => 'update_status_status',
'access' => user_access('administer site configuration'),
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/logs/updates/settings',
'title' => t('Settings'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'update_status_settings',
),
'access' => user_access('administer site configuration'),
'type' => MENU_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/logs/updates/force-check',
'title' => t('Manual update check'),
'callback' => 'update_status_force_status',
'access' => user_access('administer site configuration'),
'type' => MENU_CALLBACK,
);
}
return $items;
}
function update_status_calculate_project_data($info) {
$data = update_status_get_projects();
$settings = variable_get('update_status_settings', array());
foreach (array_keys($data) as $project) {
if (array_key_exists($project, $info)) {
$info[$project]['title'] = $info[$project]['name'];
$data[$project] += $info[$project];
if (isset($settings[$project]) && isset($settings[$project]['check']) && ($settings[$project]['check'] == 'never' || $settings[$project]['check'] == $info[$project]['version'])) {
$data[$project]['check'] = FALSE;
$data[$project]['status'] = UPDATE_STATUS_NOT_CHECKED;
}
else {
if (isset($data[$project]['check']) && empty($data[$project]['check'])) {
$data[$project]['status'] = UPDATE_STATUS_CANT_CHECK;
}
else {
$data[$project]['status'] = $data[$project]['existing_version'] == $data[$project]['version'] ? UPDATE_STATUS_CURRENT : UPDATE_STATUS_NOT_CURRENT;
}
}
}
else {
$data[$project]['status'] = UPDATE_STATUS_UNKNOWN;
}
}
return $data;
}
function update_status_status() {
if (!function_exists('gzinflate')) {
drupal_set_message(t('Your system needs the zlib extension for this module to work. See !link for more information.', array(
'!link' => l('http://us.php.net/manual/en/ref.zlib.php', 'http://us.php.net/manual/en/ref.zlib.php', NULL, NULL, NULL, TRUE),
)), 'error');
return FALSE;
}
if ($info = variable_get('update_status', FALSE)) {
$data = update_status_calculate_project_data($info);
return theme('update_status_report', $data);
}
else {
return theme('update_status_report', t('No update data is available. To fetch data, you may need to !run_cron.', array(
'!run_cron' => l(t('run cron'), 'admin/logs/status/run-cron', NULL, 'destination=' . url('admin/logs/updates')),
)));
}
}
function update_status_settings() {
$form = array();
if ($info = variable_get('update_status', FALSE)) {
$values = variable_get('update_status_settings', array());
$form['projects'] = array(
'#tree' => TRUE,
);
$data = update_status_get_projects();
$form['data'] = array(
'#type' => 'value',
'#value' => $data,
);
$form['info'] = array(
'#type' => 'value',
'#value' => $info,
);
foreach ($data as $key => $project) {
if (array_key_exists($key, $info)) {
if (!isset($values[$key])) {
$values[$key] = array(
'check' => 'always',
'notes' => '',
);
}
$options = array(
'always' => t('Always'),
$info[$key]['version'] => t('Ignore @version', array(
'@version' => $info[$key]['version'],
)),
'never' => t('Never'),
);
$form['projects'][$key]['check'] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => $values[$key]['check'],
);
$form['projects'][$key]['notes'] = array(
'#type' => 'textfield',
'#size' => 50,
'#default_value' => $values[$key]['notes'],
);
}
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit changes'),
);
}
else {
$form['error'] = array(
'#value' => theme('update_status_report', t('No update data is available. To fetch data, you may need to !run_cron.', array(
'!run_cron' => l(t('run cron'), 'admin/logs/status/run-cron', NULL, 'destination=' . url('admin/logs/updates')),
))),
);
}
return $form;
}
function theme_update_status_settings($form) {
if (isset($form['error'])) {
return drupal_render($form);
}
$header = array(
array(
'data' => t('Project'),
'class' => 'project',
),
array(
'data' => t('Warn if out of date'),
'class' => 'status',
),
array(
'data' => t('Notes'),
'class' => 'current-version',
),
);
$data = $form['data']['#value'];
$info = $form['info']['#value'];
$rows = array();
foreach ($data as $key => $project) {
if (array_key_exists($key, $info)) {
$row = array();
$row['data'][] = array(
'class' => 'project',
'data' => check_plain($info[$key]['name']),
);
$row['data'][] = drupal_render($form['projects'][$key]['check']);
$row['data'][] = drupal_render($form['projects'][$key]['notes']);
$rows[] = $row;
}
}
return theme('table', $header, $rows) . drupal_render($form);
}
function update_status_settings_submit($form_id, $form_values) {
variable_set('update_status_settings', $form_values['projects']);
drupal_set_message(t('Your changes have been saved.'));
}
function update_status_info($projects = 'all') {
$server = variable_get('update_status_server', 'http://updates.drupal.org/xmlrpc.php');
$result = xmlrpc($server, 'project.release.data', $projects, '5.x');
if ($result === FALSE) {
watchdog('update_status', t('Update Status: Error %code: %message', array(
'%code' => xmlrpc_errno(),
'%message' => xmlrpc_error_msg(),
)), WATCHDOG_ERROR);
return FALSE;
}
return unserialize(gzinflate(substr(substr(base64_decode($result), 10), 0, -8)));
}
function update_status_cron() {
if (time() - variable_get('update_status_last', 0) > 86400) {
update_status_refresh();
variable_set('update_status_last', time());
}
}
function update_status_force_status() {
update_status_refresh();
variable_set('update_status_last', time());
drupal_goto('admin/logs/updates');
}
function update_status_refresh() {
$projects = array_keys(update_status_get_projects());
$info = update_status_info($projects);
variable_set('update_status', $info);
}
function update_status_make_nice_version($version, &$check) {
if (!$version) {
$version = t('Unknown');
}
elseif (preg_match('/\\$' . 'Name: (.*?)\\$/', $version, $matches)) {
$version = trim($matches[1]);
if (!$version) {
$version = 'HEAD';
}
}
$check = FALSE;
return $version;
}
function update_status_get_projects() {
$projects = array();
$files = drupal_system_listing('\\.module$', 'modules', 'name', 0);
system_get_files_database($files, 'module');
foreach ($files as $filename => $file) {
if (empty($file->status)) {
continue;
}
$info = _module_parse_info_file(dirname($file->filename) . '/' . $file->name . '.info');
if (empty($info)) {
continue;
}
$info['check'] = TRUE;
if (!array_key_exists('project', $info)) {
$last = '';
foreach (array_reverse(explode('/', $file->filename)) as $dir) {
if ($dir == 'modules') {
break;
}
$last = $dir;
}
if ($last) {
$info['project'] = $last;
}
else {
continue;
}
}
if (!array_key_exists($info['project'], $projects)) {
if (!array_key_exists('version', $info)) {
$info['check'] = FALSE;
$info['version'] = t('Unknown');
}
if (strpos($info['version'], '$Name') !== FALSE) {
$info['version'] = update_status_make_nice_version($info['version'], $info['check']);
}
$projects[$info['project']] = array(
'name' => $info['project'],
'existing_version' => $info['version'],
'check' => $info['check'],
'modules' => array(
$file->name => $info['name'],
),
);
}
else {
$projects[$info['project']]['modules'][$file->name] = $info['name'];
}
}
asort($projects);
return $projects;
}
function theme_update_status_report($data) {
$i = 0;
$last = variable_get('update_status_last', 0);
$output = '<p>' . t('Last checked: ') . ($last ? format_date($last) : t('Never'));
$output .= ' ' . l(t('Check manually'), 'admin/logs/updates/force-check') . '</p>';
if (!is_array($data)) {
$output .= '<p>' . $data . '</p>';
return $output;
}
$data = array(
'drupal' => $data['drupal'],
) + $data;
$header = array(
array(
'data' => t('Project'),
'class' => 'project',
),
array(
'data' => t('Status'),
'class' => 'status',
),
array(
'data' => t('Current version'),
'class' => 'current-version',
),
array(
'data' => t('Available version'),
'class' => 'available-version',
),
array(
'data' => t('Download latest version'),
'class' => 'links',
),
);
foreach ($data as $project) {
if (!$project['title']) {
continue;
}
switch ($project['status']) {
case UPDATE_STATUS_NOT_CURRENT:
$class = 'error';
break;
case UPDATE_STATUS_CURRENT:
$class = 'ok';
break;
default:
$class = 'unknown';
break;
}
$row1 = array(
'class' => 'top-row ' . $class,
'data' => array(),
);
$row2 = array(
'class' => 'bottom-row ' . $class,
'data' => array(),
);
$row1['data'][] = array(
'class' => 'project',
'data' => l($project['title'], $project['link']),
);
switch ($project['status']) {
case UPDATE_STATUS_CURRENT:
$row1['data'][] = t('Up to date');
break;
case UPDATE_STATUS_NOT_CURRENT:
$row1['data'][] = t('Update available');
break;
case UPDATE_STATUS_NOT_CHECKED:
$row1['data'][] = t('Ignored');
break;
case UPDATE_STATUS_CANT_CHECK:
$row1['data'][] = t("Ignored (CVS)");
break;
default:
$row1['data'][] = t('Unknown');
}
$row1['data'][] = array(
'class' => 'current-version',
'data' => $project['existing_version'],
);
$row1['data'][] = array(
'class' => 'new-version',
'data' => l($project['version'], $project['release']) . ' (' . format_date($project['date'], 'custom', 'Y-M-d') . ')',
);
$links = array();
$links[] = l($project['download']['title'], $project['download']['href']);
$row1['data'][] = array(
'class' => 'links',
'data' => implode(' | ', $links),
);
$row2['data'][] = array(
'class' => 'info',
'colspan' => 5,
'data' => t('Includes: %modules', array(
'%modules' => implode(', ', $project['modules']),
)),
);
$rows[] = $row1;
$rows[] = $row2;
}
$output .= theme('table', $header, $rows, array(
'class' => 'update-status',
));
drupal_add_css(drupal_get_path('module', 'update_status') . '/' . 'update_status.css');
return $output;
}
function update_status_requirements($phase) {
if ($phase == 'runtime') {
$requirements['update_status']['title'] = t('Module update status');
$requirements['update_status_drupal']['title'] = t('Drupal core update status');
if ($info = variable_get('update_status', FALSE)) {
$data = update_status_calculate_project_data($info);
if ($data['drupal']['status'] == UPDATE_STATUS_NOT_CURRENT) {
$requirements['update_status_drupal']['value'] = t('Out of date. Version @version available.', array(
'@version' => $info['drupal']['version'],
));
$requirements['update_status_drupal']['severity'] = REQUIREMENT_ERROR;
$requirements['update_status_drupal']['description'] = t('There are updates available for your version of Drupal. To ensure the security of your server, you should update immediately.See the !status_page for more information', array(
'!status_page' => l('update status page', 'admin/logs/updates'),
));
}
else {
$requirements['update_status_drupal']['value'] = t('Up to date');
}
unset($data['drupal']);
$requirements['update_status']['value'] = t('Up to date');
foreach (array_keys($data) as $project) {
if (array_key_exists($project, $info) && $data[$project]['status'] == UPDATE_STATUS_NOT_CURRENT) {
$requirements['update_status']['value'] = t('Out of date');
$requirements['update_status']['severity'] = REQUIREMENT_ERROR;
$requirements['update_status']['description'] = t('There are updates available for one or more of your modules. To ensure the security of your server, you should update immediately. See the !status_page for more information', array(
'!status_page' => l('update status page', 'admin/logs/updates'),
));
break;
}
}
}
else {
$requirements['update_status']['value'] = t('Update status is unavailable. cron may need to be run.');
$requirements['update_status_drupal']['value'] = t('Update status is unavailable. cron may need to be run.');
}
return $requirements;
}
}