? sites/all/modules
? sites/default/settings.php
Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.46
diff -u -p -r1.46 system.admin.inc
--- modules/system/system.admin.inc	28 Dec 2007 22:23:57 -0000	1.46
+++ modules/system/system.admin.inc	28 Dec 2007 22:53:33 -0000
@@ -2084,7 +2084,40 @@ function theme_system_modules($form) {
       }
 
       $row[] = drupal_render($form['version'][$key]);
+
+      $class = '';
+      if (module_exists('update')) {
+        // Check if status is available.
+        if (isset($module['update_status'])) {
+          if ($module['update_status'] == UPDATE_NOT_SECURE
+              || $module['update_status'] == UPDATE_NOT_CURRENT) {
+            $class = 'update';
+            if (variable_get('update_notification_threshold', 'all') == 'all'
+                || $status == UPDATE_NOT_SECURE) {
+              $class .= ' error';
+            }
+            else {
+              $class .= ' warning';
+            }
+            // Try to get the project name if it's not in the .info file.
+            if (!isset($module['project'])) {
+              $file = $form['validation_modules']['#value'][$key];
+              $project_name = update_get_project_name($file);
+              $options = array();
+              if ($project_name != '') {
+                $options = array('url_fragment' => $project_name);
+              }
+            }
+            else {
+              $options = array('url_fragment' => $module['project']);
+            }
+            $status = theme('update_version_status', $module['update_status'], $options);
+            $description = $status . $description;
+          }
+        }
+      }
       $row[] = array('data' => $description, 'class' => 'description');
+      $row = array('data' => $row, 'class' => $class);
       $rows[] = $row;
     }
     $fieldset = array(
Index: modules/update/update.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/update/update.module,v
retrieving revision 1.7
diff -u -p -r1.7 update.module
--- modules/update/update.module	14 Dec 2007 18:08:49 -0000	1.7
+++ modules/update/update.module	28 Dec 2007 22:53:34 -0000
@@ -57,11 +57,20 @@ function update_help($path, $arg) {
       foreach (array('core', 'contrib') as $report_type) {
         $type = 'update_'. $report_type;
         if (isset($status[$type]['severity'])) {
+          $description = $status[$type]['description'];
+          if ($report_type == 'contrib') {
+            if ($available = update_get_available(TRUE)) {
+              include_once './modules/update/update.compare.inc';
+              $data = update_calculate_project_data($available);
+              unset($data['drupal']);
+              $description .= theme('update_module_list', $data);
+            }
+          }
           if ($status[$type]['severity'] == REQUIREMENT_ERROR) {
-            drupal_set_message($status[$type]['description'], 'error');
+            drupal_set_message($description, 'error');
           }
           elseif ($status[$type]['severity'] == REQUIREMENT_WARNING) {
-            drupal_set_message($status[$type]['description'], 'warning');
+            drupal_set_message($description, 'warning');
           }
         }
       }
@@ -153,6 +162,12 @@ function update_theme() {
     'update_version' => array(
       'arguments' => array('version' => NULL, 'tag' => NULL, 'class' => NULL),
     ),
+    'update_module_list' => array(
+      'arguments' => array('data' => NULL),
+    ),
+    'update_version_status' => array(
+      'arguments' => array('status' => UPDATE_CURRENT, 'options' => NULL),
+    ),
   );
 }
 
@@ -266,6 +281,25 @@ function update_form_alter(&$form, $form
   if ($form_id == 'system_modules' || $form_id == 'system_themes' ) {
     $form['#submit'][] = 'update_invalidate_cache';
   }
+  if ($form_id == 'system_modules') {
+    if ($available = update_get_available(TRUE)) {
+      include_once './modules/update/update.compare.inc';
+      $data = update_calculate_project_data($available);
+      if (isset($data)) {
+        foreach ($data as $project) {
+          foreach ($project['includes'] as $module => $title) {
+            // Check if it's a module.
+            if (isset($form['validation_modules']['#value'][$module]->name)) {
+              // Check if status is available.
+              if (isset($project['status'])) {
+                $form['validation_modules']['#value'][$module]->info['update_status'] = $project['status'];
+              }
+            }
+          }
+        }
+      }
+    }
+  }
 }
 
 /**
@@ -393,3 +427,91 @@ function _update_message_text($msg_type,
 
   return $text;
 }
+
+/**
+ * Themes a list of modules that have update into a list.
+ * 
+ * @ingroup themeable
+ * @param $data
+ *   The data array obtained from update_calculate_project_data().
+ * @return
+ *   An HTML string of the list.
+ */
+function theme_update_module_list($data) {
+  $projects = array();
+  foreach ($data as $project) {
+    if ($project['status'] == UPDATE_NOT_SECURE
+        || $project['status'] == UPDATE_NOT_CURRENT) {
+      $title = isset($project['title']) ? $project['title'] : $project['name'];
+      $projects[] = l($title, 'admin/reports/updates', array('fragment' => $project['name']));
+    }
+  }
+  return theme('item_list', $projects);
+}
+
+/**
+ * Themes a piece of text indicating update status.
+ * 
+ * @ingroup themeable
+ * @param $status
+ *   A status constant that is defined by update.module.
+ * @param $options
+ *   An associative array of additional options, with the following keys:
+ *     'reason'
+ *       A reason not defined as a status constant. Not frequently used.
+ *     'url_fragment'
+ *       An anchor on admin/reports/updates to link to. Usually the name of the project (e.g. 'devel' or 'themesettingsapi').
+ * @return
+ *   An HTML string of the status markup.
+ */
+function theme_update_version_status($status = UPDATE_CURRENT, $options = NULL) {
+  $notification_level = variable_get('update_notification_threshold', 'all');
+  switch ($status) {
+    case UPDATE_CURRENT:
+      $icon = theme('image', 'misc/watchdog-ok.png');
+      break;
+    case UPDATE_NOT_SECURE:
+    case UPDATE_NOT_CURRENT:
+      if ($notification_level == 'all'
+          || $status == UPDATE_NOT_SECURE) {
+        $icon = theme('image', 'misc/watchdog-error.png');
+        break;
+      }
+      // Otherwise, deliberate no break and use the warning icon.
+    default:
+      $icon = theme('image', 'misc/watchdog-warning.png');
+      break;
+  }
+
+  $output = '<div class="version-status">';
+  $text = '';
+  switch ($status) {
+    case UPDATE_CURRENT:
+      $text .= t('Up to date');
+      break;
+    case UPDATE_NOT_SECURE:
+      $text .= '<span class="security-error">';
+      $text .= t('Security update required!');
+      $text .= '</span>';
+      break;
+    case UPDATE_NOT_CURRENT:
+      $text .= t('Update available');
+      break;
+    default:
+      if (isset($options['reason'])) {
+        $text .= check_plain($options['reason']);
+      }
+      break;
+  }
+  if (isset($options['url_fragment'])) {
+    $output .= l($text, 'admin/reports/updates', array('html' => TRUE, 'fragment' => $options['url_fragment']));
+  }
+  else {
+    $output .= $text;
+  }
+  $output .= '<span class="icon">'. $icon .'</span>';
+  $output .= "</div>\n";
+
+  drupal_add_css(drupal_get_path('module', 'update') .'/update.css');
+  return $output;
+}
Index: modules/update/update.report.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/update/update.report.inc,v
retrieving revision 1.6
diff -u -p -r1.6 update.report.inc
--- modules/update/update.report.inc	20 Dec 2007 08:42:05 -0000	1.6
+++ modules/update/update.report.inc	28 Dec 2007 22:53:34 -0000
@@ -45,44 +45,25 @@ function theme_update_report($data) {
     switch ($project['status']) {
       case UPDATE_CURRENT:
         $class = 'ok';
-        $icon = theme('image', 'misc/watchdog-ok.png');
         break;
       case UPDATE_NOT_SECURE:
       case UPDATE_NOT_CURRENT:
         if ($notification_level == 'all'
             || $project['status'] == UPDATE_NOT_SECURE) {
           $class = 'error';
-          $icon = theme('image', 'misc/watchdog-error.png');
           break;
         }
-        // Otherwise, deliberate no break and use the warning class/icon.
+        // Otherwise, deliberate no break and use the warning class.
       default:
         $class = 'warning';
-        $icon = theme('image', 'misc/watchdog-warning.png');
         break;
     }
 
-    $row = '<div class="version-status">';
-    switch ($project['status']) {
-      case UPDATE_CURRENT:
-        $row .= t('Up to date');
-        break;
-      case UPDATE_NOT_SECURE:
-        $row .= '<span class="security-error">';
-        $row .= t('Security update required!');
-        $row .= '</span>';
-        break;
-      case UPDATE_NOT_CURRENT:
-        $row .= t('Update available');
-        break;
-      default:
-        $row .= check_plain($project['reason']);
-        break;
-    }
-    $row .= '<span class="icon">'. $icon .'</span>';
-    $row .= "</div>\n";
+    $reason = (isset($project['reason'])) ? $project['reason'] : NULL;
+    $row = theme('update_version_status', $project['status'], array('reason' => $reason));
 
     $row .= '<div class="project">';
+    $row .= '<a name="'. check_plain($project['name']) .'"></a>';
     if (isset($project['title'])) {
       if (isset($project['link'])) {
         $row .= l($project['title'], $project['link']);
