? 170583.patch
? includes/table.inc
Index: commands/core/watchdog.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/core/watchdog.drush.inc,v
retrieving revision 1.4
diff -u -r1.4 watchdog.drush.inc
--- commands/core/watchdog.drush.inc	16 Feb 2010 16:01:40 -0000	1.4
+++ commands/core/watchdog.drush.inc	22 Feb 2010 12:18:13 -0000
@@ -1,130 +1,335 @@
 <?php
 
+/**
+ * Implementation of hook_drush_help().
+ */
 function watchdog_drush_help($section) {
   switch ($section)  {
+    case 'drush:watchdog-list':
+      return dt('Show watchdog severity levels and message types.');
     case 'drush:watchdog-show':
-      return dt("Show recent watchdog messages.");
+      return dt('Show watchdog messages. Arguments and options can be combined to configure which messages to show.');
     case 'drush:watchdog-delete':
-      return dt("Delete watchdog messages.");
+      return dt('Delete watchdog messages. Arguments or options must be provided to specify which messages to delete.');
   }
 }
 
+/**
+ * Implementation of hook_drush_command().
+ */
 function watchdog_drush_command() {
+  $items['watchdog-list'] = array(
+    'description' => 'Show watchdog severity levels and message types.',
+    'drupal dependencies' => drush_drupal_major_version() >= 6 ? array('dblog') : array('watchdog'),
+    'aliases' => array('wd-list'),
+  );
   $items['watchdog-show'] = array(
-    'description' => 'Shows recent watchdog log messages. Optionally filter for a specific type.',
+    'description' => 'Show watchdog messages.',
     'drupal dependencies' => drush_drupal_major_version() >= 6 ? array('dblog') : array('watchdog'),
     'arguments' => array(
-      'type' => 'The type of messages to show. Defaults to all.',
+      'wid' => 'Optional id of a watchdog message to show in detail. If not provided, a listing of most recent 10 messages will be displayed. Alternatively if a string is provided, watchdog messages will be filtered by it.',
     ),
     'options' => array(
-      '--limit' => 'The number of messages to show. Defaults to 10.',
-      '--severity' => 'Restrict to a given severity level. Error=0, Warning=4.',
+      '--count' => 'The number of messages to show. Defaults to 10.',
+      '--severity' => 'Restrict to messages of a given severity level.',
+      '--type' => 'Restrict to messages of a given type.',
     ),
     'examples' => array(
-      'watchdog-show cron' => 'Show recent cron watchdog messages.',
-      'watchdog-show --severity=0' => 'Show recent error messages.',
-      'watchdog-show --limit=50' => 'Show 50 recent watchdog messages.',
+      'watchdog-show' => 'Show a listing of most recent 10 messages.',
+      'watchdog-show 64' => 'Show in detail message with id 64.',
+      'watchdog-show "cron run succesful"' => 'Show a listing of most recent 10 messages containing the string "cron run succesful".',
+      'watchdog-show --count=46' => 'Show a listing of most recent 46 messages.',
+      'watchdog-show --severity=notice' => 'Show a listing of most recent 10 messages with a severity of notice.',
+      'watchdog-show --type=php' => 'Show a listing of most recent 10 messages of type php.',
     ),
-    'aliases' => array('ws'),
+    'aliases' => array('wd-show'),
+    'deprecated-aliases' => array('ws'),
   );
   $items['watchdog-delete'] = array(
-    'description' => 'Delete all messages or only those of a specified type.',
+    'description' => 'Delete watchdog messages.',
+    'drupal dependencies' => drush_drupal_major_version() >= 6 ? array('dblog') : array('watchdog'),
     'arguments' => array(
-      'type' => 'The type of messages to delete. Use \'all.\' to do a complete wipe.',
+      '--count' => 'The number of messages to *not* delete.',
+      '--severity' => 'Delete messages of a given severity level.',
+      '--type' => 'Delete messages of a given type.',
     ),
-    'drupal dependencies' => drush_drupal_major_version() >= 6 ? array('dblog') : array('watchdog'),
     'examples' => array(
-      'watchdog-delete all' => 'Delete all watchdog messages.',
-      'watchdog-delete cron' => 'Delete all cron watchdog messages.',
+      'watchdog-delete all' => 'Delete all messages.',
+      'watchdog-delete 64' => 'Delete message with id 64.',
+      'watchdog-delete "cron run succesful"' => 'Delete messages containing the string "cron run succesful".',
+      'watchdog-delete --count=46' => 'Delete all messages but 46 recent ones.',
+      'watchdog-delete --severity=notice' => 'Delete all messages with a severity of notice.',
+      'watchdog-delete --type=cron' => 'Delete all messages of type cron.',
     ),
-    'aliases' => array('wd'),
+    'aliases' => array('wd-del', 'wd-delete'),
+    'deprecated-aliases' => array('wd'),
   );
   return $items;
 }
 
 /**
- * Displays the most recent watchdog log messages (default: 10 messages).
+ * Command callback.
  */
-function drush_core_watchdog_show($type = NULL) {
-  $limit = drush_get_option('limit') ? drush_get_option('limit') : 10;
-  $severity = drush_get_option('severity');
+function drush_core_watchdog_list() {
+  drush_print('Severity levels:');
+  foreach (core_watchdog_severity_levels() as $key => $value) {
+     drush_print("$key: $value", 2);
+  }
+  drush_print('Message types:');
+  foreach (core_watchdog_message_types() as $type) {
+     drush_print($type, 2);
+  }
+}
 
-  switch (drush_drupal_major_version()) {
-    case 5:
-    case 6:
-      if ($type) {
-        $where[] = "w.type = '%s'";
-        $placeholders[] = $type;
-      }
-      if ($severity) {
-        $where[] = "w.severity = %d";
-        $placeholders[] = $severity;
-      }
-      $criteria = empty($where) ? '' : ' WHERE ' . implode(' AND ', $where);
-      $sql = 'SELECT w.*, u.name, u.uid FROM {watchdog} w INNER JOIN {users} u ON w.uid = u.uid  ';
-      $sort = ' ORDER BY w.wid DESC';
-      $result = db_query_range($sql . $criteria . $sort, $placeholders, 0, $limit);
-      while ($watchdog = db_fetch_object($result)) {
-        $rows[] = core_watchdog_format_row($watchdog);
-      }
-      break;
-    default:
-      $query = db_select('watchdog', 'w')
-                ->fields('w')
-                ->fields('u', array('name', 'uid'))
-                ->orderBy('w.wid', 'DESC')
-                ->range(0, $limit);
-      $query->join('users', 'u', 'w.uid = u.uid');
-      if ($type) {
-        $query->condition('w.type', $type);
-      }
-      if ($severity) {
-        $query->condition('w.severity', $severity);
-      }
-      $results = $query->execute();
-      $watchdogs = $results->fetchAllAssoc('wid');
-      foreach ($watchdogs as $watchdog) {
-        $rows[] = core_watchdog_format_row($watchdog);
-      }
+/**
+ * Helper function to obtain the severity levels based on drupal version.
+ *
+ * This is a copy of watchdog_severity_levels() without t().
+ *
+ * Severity levels, as defined in RFC 3164: http://www.ietf.org/rfc/rfc3164.txt.
+ *
+ * @return
+ *   Array of watchdog severity levels.
+ */
+function core_watchdog_severity_levels() {
+  if (drush_drupal_major_version() == 5) {
+    return array(
+      WATCHDOG_NOTICE => 'notice',
+      WATCHDOG_WARNING => 'warning',
+      WATCHDOG_ERROR => 'error'
+    );
+  }
+  else {
+    return array(
+      WATCHDOG_EMERG    => 'emergency',
+      WATCHDOG_ALERT    => 'alert',
+      WATCHDOG_CRITICAL => 'critical',
+      WATCHDOG_ERROR    => 'error',
+      WATCHDOG_WARNING  => 'warning',
+      WATCHDOG_NOTICE   => 'notice',
+      WATCHDOG_INFO     => 'info',
+      WATCHDOG_DEBUG    => 'debug',
+    );
   }
+}
 
-  if (empty($rows)) {
-    return drush_log('No log messages available.', 'ok');
+/**
+ * Helper function to obtain the message types based on drupal version.
+ *
+ * @return
+ *   Array of watchdog message types.
+ */
+function core_watchdog_message_types() {
+  if (drush_drupal_major_version() == 5) {
+    return _watchdog_get_message_types();
   }
   else {
-    drush_log(dt('Last !count watchdog log messages:', array('!count' => $limit)));
+    return _dblog_get_message_types();
+  }
+}
 
-    array_unshift($rows, array(dt('Date'), dt('Severity'), dt('Type'), dt('Message'), dt('User')));
-    drush_print_table($rows, TRUE);
+/**
+ * Command callback.
+ */
+function drush_core_watchdog_show($arg = NULL) {
+  if (is_numeric($arg)) {
+    drush_core_watchdog_show_one($arg);
+  }
+  else {
+    drush_core_watchdog_show_many($arg);
   }
 }
 
-function core_watchdog_format_row($watchdog) {
-  $severities = watchdog_severity_levels();
-  return array(
-    format_date($watchdog->timestamp, 'custom', 'Y.n.j G.i.s'),
-    $severities[$watchdog->severity],
-    dt($watchdog->type),
-    truncate_utf8(drush_watchdog_format_message($watchdog), 188, FALSE, FALSE),
-    $watchdog->uid ? strip_tags(theme('username', (array)$watchdog)) : dt('Anon'),
-  );
+/**
+ * Print a watchdog message.
+ *
+ * @param $wid
+ *    The id of the message to show.
+ */
+function drush_core_watchdog_show_one($wid) {
+  $rsc = drush_db_select('watchdog', '*', 'wid = :wid', array(':wid' => $wid), 0, 1);
+  $result = drush_db_fetch_object($rsc);
+  if (!$result) {
+    return drush_set_error(dt('Watchdog event #!wid not found.', array('!wid' => $wid)));
+  }
+  $result = core_watchdog_format_result($result);
+  drush_print_table(drush_key_value_to_array_table($result));
+  print "\n";
+}
+
+/**
+ * Print a table of watchdog messages.
+ *
+ * @param $filter
+ *   String to filter the message's text by.
+ */
+function drush_core_watchdog_show_many($filter = NULL) {
+  $count = drush_get_option('count', 10);
+  $type = drush_get_option('type');
+  $severity = drush_get_option('severity');
+  
+  $where = core_watchdog_query($type, $severity, $filter);
+  if ($where === FALSE) {
+    return drush_log(dt('Aborting.'));
+  }
+  $rsc = drush_db_select('watchdog', '*', $where['where'], $where['args'], 0, $count, 'wid', 'DESC');
+  if ($rsc === FALSE) {
+    return drush_log(dt('Aborting.'));
+  }
+  $table[] = array(dt('Id'), dt('Date'), dt('Severity'), dt('Type'), dt('Message'));
+  while ($result = drush_db_fetch_object($rsc)) {
+    $row = core_watchdog_format_result($result);
+    $table[] = array($row->wid, $row->date, $row->severity, $row->type, $row->message);
+  }
+  if (count($table) == 1) {
+    return drush_log(dt('No log messages available.'), 'ok');
+  }
+  else {
+    drush_log(dt('Most recent !count watchdog log messages:', array('!count' => $count)));
+    drush_print_table($table, TRUE);
+    print "\n";
+  }
+}
+
+
+/**
+ * Format a watchdog database row.
+ *
+ * @param $result
+ *   Array. A database result object.
+ * @return
+ *   Array. The result object with some attributes themed.
+ */
+function core_watchdog_format_result($result) {
+  // severity
+  $severities = core_watchdog_severity_levels();
+  $result->severity = $severities[$result->severity];
+  // date
+  $result->date = format_date($result->timestamp, 'custom', 'd/M H:i');
+  unset($result->timestamp);
+  // message
+  if (drush_drupal_major_version() > 5) {
+    $variables = unserialize($result->variables);
+    $result->message = strtr($result->message, $variables);
+    unset($result->variables); 
+  }
+  $result->message = truncate_utf8(strip_tags(decode_entities($result->message)), 188, FALSE, FALSE);
+  // user
+  $result->uid = $result->uid ? strip_tags(theme('username', (array)$result)) : dt('Anonymous');
+  // possible empty values
+  if (empty($result->link)) {
+    unset($result->link);
+  }
+  if (empty($result->referer)) {
+    unset($result->referer);
+  }
+  
+  return $result;
 }
 
 /**
- * Deletes all log messages of a certain type from the watchdog log
- * (default: all).
+ * Command callback.
+ * 
+ * @param $arg
+ *   The id of the message to delete or 'all'.
  */
-function drush_core_watchdog_delete($type = NULL) {
-  if ($type == "all") {
-    // D7: ought to be a dynamic query.
-    drush_op('db_query', 'DELETE FROM {watchdog}'); // Indiscriminately delete all
-    drush_log(dt('Deleted all rows.'), 'ok');
-  }
-  elseif (!empty($type)) {
-    drush_op('db_query', 'DELETE FROM {watchdog} WHERE type = \'%s\'', $type);
-    drush_log(dt('Deleted all rows.'), 'ok');
+function drush_core_watchdog_delete($arg = NULL) {
+  if ($arg == 'all') {
+    drush_print(dt('All watchdog messages will be deleted.'));
+    if (!drush_confirm(dt('Do you really want to continue?'))) {
+      return drush_log(dt('Aborting.'));
+    }
+    $affected_rows = drush_db_delete('watchdog');
+    drush_log(dt('!affected watchdog messages have been deleted.', array('!affected' => $affected_rows)), 'ok');
+  }
+  else if (is_numeric($arg)) {
+    drush_print(dt('Watchdog message #!wid will be deleted.', array('!wid' => $arg)));
+    if(!drush_confirm(dt('Do you really want to continue?'))) {
+      return drush_log(dt('Aborting.'));
+    }
+    $affected_rows = drush_db_delete('watchdog', 'wid=:wid', array(':wid' => $arg));
+    if ($affected_rows == 1) {
+      drush_log(dt('Watchdog message #!wid has been deleted.', array('!wid' => $arg)), 'ok');
+    }
+    else {
+      return drush_set_error(dt('Watchdog message #!wid does not exist.', array('!wid' => $arg)));
+    }
   }
   else {
-    drush_set_error(dt('Please specify a message type, or "all" to delete all messages.'));
+    $type = drush_get_option('type');
+    $severity = drush_get_option('severity');
+    if ((is_null($arg))&&(is_null($type))&&(is_null($severity))) {
+      return drush_set_error(dt('No options provided.'));
+    }
+    $where = core_watchdog_query($type, $severity, $filter, 'OR');
+    if ($where === FALSE) {
+      return drush_log(dt('Aborting.'), 'error');
+    }
+    drush_print(dt('All messages matching "!where" will be deleted.', array('!where' => strtr($where['where'], $where['args']))));
+    if(!drush_confirm(dt('Do you really want to continue?'))) {
+      return drush_log(dt('Aborting.'));
+    }
+    $affected_rows = drush_db_delete('watchdog', $where['where'], $where['args']);
+    drush_log(dt('!affected_rows watchdog messages have been deleted.', array('!affected_rows' => $affected_rows)), 'ok');
+  }
+}
+
+/**
+ * Build a WHERE snippet based on given parameters.
+ *
+ * @param $type
+ *   String. Valid watchdog type.
+ * @param $severity
+ *   Int or String for a valid watchdog severity message.
+ * @param $filter
+ *   String. Value to filter watchdog messages by.
+ * @param $criteria
+ *   ('AND', 'OR'). Criteria for the WHERE snippet.
+ * @return
+ *   False or array with structure ('where' => string, 'args' => array())
+ */
+function core_watchdog_query($type = NULL, $severity = NULL, $filter = NULL, $criteria = 'AND') {
+  $args = array();
+  $conditions = array();
+  if ($type) {
+    $types = core_watchdog_message_types();
+    if (array_search($type, $types) === FALSE) {
+      drush_log(dt('Unknown message type: !type.', array('!type' => $type)), 'error');
+      drush_log(dt('Valid types are: !types.', array('!types' => implode(', ', $types))), 'error');
+      return FALSE;
+    }
+    $conditions[] = "type = :type";
+    $args[':type'] = $type;
+  }
+  if (!is_null($severity)) {
+    $severities = core_watchdog_severity_levels();
+    if (isset($severities[$severity])) {
+      $level = $severity;
+    }
+    elseif (($key = array_search($severity, $severities)) !== FALSE) {
+        $level = $key;
+    }
+    else {
+      $level = FALSE;
+    }
+    if ($level === FALSE) {
+      foreach ($severities as $key => $value) {
+        $levels[] = "$value($key)";
+      }
+      drush_log(dt('Unknown severity level: !severity.', array('!severity' => $severity)), 'error');
+      drush_log(dt('Valid severity levels are: !levels', array('!levels' => implode(', ', $levels))), 'error');
+      return FALSE;
+    }
+    $conditions[] = 'severity = :severity';
+    $args[':severity'] = $level;
+  }
+  if ($filter) {
+    $conditions[] = "message LIKE :filter";
+    $args[':filter'] = '%'.$filter.'%';
   }
+
+  $where = implode(" $criteria ", $conditions);
+  
+  return array('where' => $where, 'args' => $args);
 }
+
Index: includes/drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/drush.inc,v
retrieving revision 1.86
diff -u -r1.86 drush.inc
--- includes/drush.inc	21 Feb 2010 05:31:27 -0000	1.86
+++ includes/drush.inc	22 Feb 2010 12:18:14 -0000
@@ -243,6 +243,141 @@
 }
 
 /**
+ * Replace named placeholders in a WHERE snippet. 
+ *
+ * Helper function to allow the usage of Drupal 7 WHERE snippets
+ * with named placeholders in code for Drupal 5 and 6.
+ *
+ * @param $where
+ *   Stringwith a WHERE snippet using named placeholders.
+ * @param $args
+ *   Array of placeholder values.
+ * @return
+ *   String. $where filled with literals from $args.
+ */
+function _drush_replace_query_placeholders($where, $args) {
+  foreach ($args as $key => $data) {
+    if (!is_numeric($data)) {
+      $args[$key] = '"'.$data.'"';
+    }
+    else if (is_array($data)) {
+      $new_keys = array();
+      foreach ($data as $i => $value) {
+        $new_keys[$key . '_' . $i] = $value;
+      }
+      $where = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $query);
+      unset($args[$key]);
+      $args += $new_keys;
+    }
+  }
+
+  foreach ($args as $key => $data) {
+    $where = str_replace($key, $data, $where);
+  }
+
+  return $where;
+}
+
+/**
+ * A db_select() that works for any version of Drupal.
+ *
+ * @param $table
+ *   String. The table to operate on.
+ * @param $fields
+ *   Array or '*'. Fields affected in this operation.
+ * @param $where
+ *   String. WHERE snippet for the operation. It uses named placeholders. see @_drush_replace_query_placeholders()
+ * @param $args
+ *   Array. Arguments for the WHERE snippet.
+ * @param $start
+ *   Int. Value for OFFSET.
+ * @param $length
+ *   Int. Value for LIMIT.
+ * @param $order_by_field
+ *   String. Database column to order by.
+ * @param $order_by_direction
+ *   ('ASC', 'DESC'). Ordering direction.
+ * @return
+ *   A database resource.
+ */
+function drush_db_select($table, $fields = '*', $where = NULL, $args = NULL, $start = NULL, $length = NULL, $order_by_field = NULL, $order_by_direction = 'ASC') {
+  if (drush_drupal_major_version() >= 7) {
+    if ($fields == '*') {
+      $fields = array();
+    }
+    $query = db_select($table, $table)
+      ->fields($table, $fields);
+    if (!empty($where)) {
+      $query = $query->where($where, $args);
+    }
+    if (!is_null($order_by_field)) {
+      $query = $query->orderBy($order_by_field, $order_by_direction);
+    }
+    if (!is_null($length)) {
+      $query = $query->range($start, $length);
+    }
+    return $query->execute();
+  }
+  else {
+    if ($fields != '*') {
+      $fields = implode('","', $fields);
+    }
+    $query = "SELECT $fields FROM {$table}";
+    if (!empty($where)) {
+      $where = _drush_replace_query_placeholders($where, $args);
+      $query .= " WHERE ".$where;
+    }
+    if (!is_null($order_by_field)) {
+      $query .= " ORDER BY $order_by_field $order_by_direction";
+    }
+    if (!is_null($length)) {
+      $limit = " LIMIT $length";
+      if (!is_null($start)) {
+        $limit .= " OFFSET $start";
+      }
+      $query .= $limit;
+    }
+
+    return db_query($query, $args);
+  }
+}
+
+/**
+ * A db_delete() that works for any version of Drupal.
+ *
+ * @param $table
+ *   String. The table to operate on.
+ * @param $where
+ *   String. WHERE snippet for the operation. It uses named placeholders. see @_drush_replace_query_placeholders()
+ * @param $args
+ *   Array. Arguments for the WHERE snippet.
+ * @return
+ *   Affected rows or FALSE.
+ */
+function drush_db_delete($table, $where = NULL, $args = NULL) {
+  if (drush_drupal_major_version() >= 7) {
+    if (!empty($where)) {
+      $query = db_delete($table)->where($where, $args);
+      return $query->execute();
+    }
+    else {
+      return db_truncate($table)->execute();
+    }
+  }
+  else {
+    $query = "DELETE FROM {$table}";
+    if (!empty($where)) {
+      $where = _drush_replace_query_placeholders($where, $args);
+      $query .= ' WHERE '.$where;
+    }
+    if (!db_query($query, $args)) {
+      return FALSE;
+    }
+    return db_affected_rows();
+  }
+}
+
+/**
  * A db_result() that works for any version of Drupal.
  *
  * @param
@@ -1271,43 +1406,32 @@
  * A sneaky implementation of hook_watchdog().
  */
 function system_watchdog($log_entry) {
-  $levels = drush_watchdog_severity_levels();
-  drush_log('WD '. $log_entry['type'] . ': ' . drush_watchdog_format_message((object)$log_entry), $levels[$log_entry['severity']]);
-}
-
-/**
- * Copy of watchdog_severity_levels() without t() and tuned for _drush_print_log.
- *
- * Severity levels, as defined in RFC 3164: http://www.ietf.org/rfc/rfc3164.txt.
- *
- * @return
- *   Array of the possible severity levels for log messages.
- *
- * @see watchdog()
- */
-function drush_watchdog_severity_levels() {
-  return array(
-    WATCHDOG_EMERG    => 'error', // emergency
-    WATCHDOG_ALERT    => 'error', // alert
-    WATCHDOG_CRITICAL => 'error', // critical
-    WATCHDOG_ERROR    => 'error',
-    WATCHDOG_WARNING  => 'warning',
-    WATCHDOG_NOTICE   => 'notice',
-    WATCHDOG_INFO     => 'info',
-    WATCHDOG_DEBUG    => 'debug',
+  static $errors = array(
+    WATCHDOG_EMERG, 
+    WATCHDOG_ALERT, 
+    WATCHDOG_CRITICAL,
+    WATCHDOG_ERROR
   );
-}
 
-function drush_watchdog_format_message($watchdog) {
-  if (drush_drupal_major_version() == 5) {
-    $message = $watchdog->message;
+  // Transform non informative severity levels to 'error' for compatibility with _drush_print_log.
+  // Other severity levels are coincident with the ones we use in drush.  
+  if (in_array($log_entry['severity'], $errors)) {
+    $severity = 'error';
+  }
+  else {
+    $levels = core_watchdog_severity_levels();
+    $severity = $levels[$log_entry['severity']];
+  }
+  // Format the message.
+  if (is_array($log_entry['variables'])) {
+    $message = strtr($log_entry['message'], $log_entry['variables']);
   }
   else {
-    // variables is an array during logging but is a string when retrieving from DB.
-    $variables = is_string($watchdog->variables) ? unserialize($watchdog->variables) : $watchdog->variables;
-    $message = is_array($variables) ? strtr($watchdog->message, $variables) : $watchdog->message;
+    $message = $log_entry['message'];
   }
-  return strip_tags(decode_entities($message));
+  $message = strip_tags(decode_entities($message));
+  // Log it.
+  drush_log('WD '. $log_entry['type'] . ': '.$message, $severity);
 }
 
 /**
