Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.659 diff -u -p -r1.659 common.inc --- includes/common.inc 22 Jun 2007 08:46:15 -0000 1.659 +++ includes/common.inc 24 Jun 2007 09:40:07 -0000 @@ -2253,6 +2253,7 @@ function _drupal_bootstrap_full() { require_once './includes/unicode.inc'; require_once './includes/image.inc'; require_once './includes/form.inc'; + require_once './includes/actions.inc'; // Set the Drupal custom error handler. set_error_handler('drupal_error_handler'); // Emit the correct charset HTTP header. @@ -2308,6 +2309,71 @@ function page_set_cache() { } } +function _drupal_mail_urls($url = 0) { + static $urls = array(); + if ($url) { + $urls[] = strpos($url, '://') ? $url : url($url, NULL, NULL, 1); + return count($urls); + } + return $urls; +} + +/** + * Transform a string into plain text, optionally preserving the + * structure of the text. Useful for preparing the body of a node + * to be sent by email. + * + * @param $string + * The string to be transformed. + * @param $allowed_tags + * Tags that will be transformed. + * @return + * The transformed string. + */ +function drupal_html_to_plain($string, $allowed_tags = array('a', 'em', 'strong', 'br', 'p', 'h1', 'h2', 'h3', 'h4', 'li', 'dd')) { + static $i = 0; + + // Make sure tags, entities and attributes are well-formed. + $string = filter_xss($string, $allowed_tags); + + // Replace inline a tags with the text of link and a footnote. + // 'See the Drupal site' becomes + // 'See the Drupal site [1]' with the URL included as a footnote. + $pattern = '@((.+?))@ei'; + $string = preg_replace($pattern, "'\\3 ['. _drupal_mail_urls('\\2') .']'", $string); + $urls = _drupal_mail_urls(); + if (count($urls)) { + $string .= "\n"; + for ($max = count($urls); $i < $max; $i++) { + $string .= '['. ($i + 1) .'] '. $urls[$i] ."\n"; + } + } + + // Replace HTML tags with semantically equivalent plain text. + $string = preg_replace('!!i', '"', $string); + $string = preg_replace('!!i', '/', $string); + $string = preg_replace('!!i', '*', $string); + $string = preg_replace("@
(?!\n)@i", "\n", $string); + $string = preg_replace("@(?!\n\n)@i", "\n\n", $string); + $string = preg_replace("@(?!\n\n)@i", " #\n", $string); + $string = preg_replace("@(?!\n\n)@i", " ##\n", $string); + $string = preg_replace("@(?!\n\n)@i", " ###\n", $string); + $string = preg_replace("@(?!\n\n)@i", " ####\n", $string); + $string = preg_replace("@\n?@i", "\n", $string); + $string = preg_replace("@@i", "\n\n# ", $string); + $string = preg_replace("@@i", "\n\n## ", $string); + $string = preg_replace("@@i", "\n\n### ", $string); + $string = preg_replace("@@i", "\n\n#### ", $string); + $string = preg_replace("@@i", "* ", $string); + + // strip_tags expects the allowable tags as a string. + $string = strip_tags($string, '<'. implode('><', $allowed_tags) . '>'); + $string = decode_entities($string); + $string = wordwrap($string, 72); + + return $string; +} + /** * Send an e-mail message, using Drupal variables and default settings. * More information in the @@ -3528,3 +3594,42 @@ function watchdog_severity_levels() { ); } +/** + * Explode a string of given tags into an array. + */ +function drupal_explode_tags($tags) { + // This regexp allows the following types of user input: + // this, "somecompany, llc", "and ""this"" w,o.rks", foo bar + $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; + preg_match_all($regexp, $tags, $matches); + $typed_tags = array_unique($matches[1]); + + $tags = array(); + foreach ($typed_tags as $tag) { + // If a user has escaped a term (to demonstrate that it is a group, + // or includes a comma or quote character), we remove the escape + // formatting so to save the term into the database as the user intends. + $tag = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $tag))); + if ($tag != "") { + $tags[] = $tag; + } + } + + return $tags; +} + +/** + * Implode an array of tags into a string. + */ +function drupal_implode_tags($tags) { + $encoded_tags = array(); + foreach ($tags as $tag) { + // Commas and quotes in tag names are special cases, so encode them. + if (strpos($tag, ',') !== FALSE || strpos($tag, '"') !== FALSE) { + $tag = '"'. str_replace('"', '""', $tag) .'"'; + } + + $encoded_tags[] = $tag; + } + return implode(', ', $encoded_tags); +} \ No newline at end of file Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.555 diff -u -p -r1.555 comment.module --- modules/comment/comment.module 24 Jun 2007 00:38:40 -0000 1.555 +++ modules/comment/comment.module 24 Jun 2007 09:40:11 -0000 @@ -2088,3 +2088,91 @@ function int2vancode($i = 0) { function vancode2int($c = '00') { return base_convert(substr($c, 1), 36, 10); } + +/** + * Implementation of hook_action_info(). + */ +function comment_action_info() { + return array( + 'comment_unpublish_action' => array( + 'description' => t('Unpublish comment'), + 'type' => 'comment', + 'configurable' => FALSE, + 'hooks' => array( + 'comment' => array('insert', 'update', 'view'), + ) + ), + 'comment_unpublish_by_keyword_action' => array( + 'description' => t('Unpublish comment containing keyword(s)'), + 'type' => 'comment', + 'configurable' => TRUE, + 'hooks' => array( + 'comment' => array('insert', 'update'), + ) + ) + ); +} + +/** + * Drupal action to unpublish a comment. + * + * @param $context + * Keyed array. Must contain the id of the comment if $comment is not passed. + * @param $comment + * An optional comment object. + */ +function comment_unpublish_action($comment, $context = array()) { + if (isset($comment->cid)) { + $cid = $comment->cid; + $subject = $comment->subject; + } + else { + $cid = $context['cid']; + $subject = db_result(db_query("SELECT subject FROM {comments} WHERE cid = %d", $cid)); + } + db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_NOT_PUBLISHED, $cid); + watchdog('action', 'Unpublished comment %subject.', array('%subject' => $subject)); +} + +function comment_unpublish_by_keyword_action_form($context) { + if (!module_exists('taxonomy')) { + $form['warning'] = array( + '#prefix' => "
", + '#suffix' => '
', + '#value' => t('This action requires the !taxonomy_module to be enabled.', array('!taxonomy_module' => l('taxonomy module', 'admin/build/modules'))), + '#weight' => -8, + ); + } + $form['keywords'] = array( + '#title' => t('Keywords'), + '#type' => 'textfield', + '#description' => t('The comment will be unpublished if it contains any of the character sequences above. Use a comma-separated list of character sequences. Example: funny, bungee jumping, "Company, Inc.".'), + '#default_value' => isset($context['keywords']) ? drupal_implode_tags($context['keywords']) : '', + ); + return $form; +} + +function comment_unpublish_by_keyword_action_submit($form, $form_state) { + return array('keywords' => drupal_explode_tags($form_state['values']['keywords'])); +} + +/** + * Implementation of a configurable Drupal action. + * Unpublish a comment if it contains a certain string. + * + * @param $context + * An array providing more information about the context of the call to this action. + * Unused here since this action currently only supports the insert and update ops of + * the comment hook, both of which provide a complete $comment object. + * @param $comment + * A comment object. + */ +function comment_unpublish_by_keyword_action($comment, $context) { + foreach ($context['keywords'] as $keyword) { + if (strstr($comment->comment, $keyword)) { + db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_NOT_PUBLISHED, $comment->cid); + watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject)); + break; + } + } +} \ No newline at end of file Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.837 diff -u -p -r1.837 node.module --- modules/node/node.module 24 Jun 2007 00:38:40 -0000 1.837 +++ modules/node/node.module 24 Jun 2007 09:40:13 -0000 @@ -616,6 +616,8 @@ function node_load($param = array(), $re * Save a node object into the database. */ function node_save(&$node) { + // Let modules modify the node before it is saved to the database. + node_invoke_nodeapi($node, 'presave'); global $user; $node->is_new = FALSE; @@ -3166,3 +3168,199 @@ function node_forms() { } return $forms; } + +/** + * Implementation of hook_action_info(). + */ +function node_action_info() { + return array( + 'node_publish_action' => array( + 'type' => 'node', + 'description' => t('Publish post'), + 'configurable' => FALSE, + 'hooks' => array( + 'nodeapi' => array('presave','insert','update', 'view'), + 'comment' => array('delete','insert','update', 'view'), + ), + ), + 'node_unpublish_action' => array( + 'type' => 'node', + 'description' => t('Unpublish post'), + 'configurable' => FALSE, + 'hooks' => array( + 'nodeapi' => array('presave','insert','update', 'view'), + 'comment' => array('delete','insert','update', 'view'), + ), + ), + 'node_make_sticky_action' => array( + 'type' => 'node', + 'description' => t('Make post sticky'), + 'configurable' => FALSE, + 'hooks' => array( + 'nodeapi' => array('presave','insert','update', 'view'), + 'comment' => array('delete','insert','update', 'view'), + ), + ), + 'node_make_unsticky_action' => array( + 'type' => 'node', + 'description' => t('Make post unsticky'), + 'configurable' => FALSE, + 'hooks' => array( + 'nodeapi' => array('presave','insert','update', 'view'), + 'comment' => array('delete','insert','update', 'view'), + ), + ), + 'node_promote_action' => array( + 'type' => 'node', + 'description' => t('Promote post to front page'), + 'configurable' => FALSE, + 'hooks' => array( + 'nodeapi' => array('presave','insert','update', 'view'), + 'comment' => array('delete','insert','update', 'view'), + ), + ), + 'node_unpromote_action' => array( + 'type' => 'node', + 'description' => t('Remove post from node page'), + 'configurable' => FALSE, + 'hooks' => array( + 'nodeapi' => array('presave','insert','update', 'view'), + 'comment' => array('delete','insert','update', 'view'), + ), + ), + 'node_assign_owner_action' => array( + 'type' => 'node', + 'description' => t('Change the author of a post'), + 'configurable' => TRUE, + 'hooks' => array( + 'nodeapi' => array('presave','insert','update', 'view'), + 'comment' => array('delete','insert','update', 'view'), + ), + ), + 'node_save_action' => array( + 'type' => 'node', + 'description' => t('Save post'), + 'configurable' => FALSE, + 'hooks' => array( + 'nodeapi' => array('delete','insert','update', 'view'), + 'comment' => array('delete','insert','update', 'view'), + ), + ), + ); +} + +/** + * Implementation of a Drupal action. + * Sets the status of a node to 1, meaning published. + */ +function node_publish_action(&$node, $context = array()) { + $node->status = 1; + watchdog('action', 'Set @type %title to published.', array('@type' => node_get_types('name', $node), '%title' => $node->title)); +} + +/** + * Implementation of a Drupal action. + * Sets the status of a node to 0, meaning unpublished. + */ +function node_unpublish_action(&$node, $context = array()) { + $node->status = 0; + watchdog('action', 'Set @type %title to unpublished.', array('@type' => node_get_types('name', $node), '%title' => $node->title)); +} + +/** + * Implementation of a Drupal action. + * Sets the sticky-at-top-of-list property of a node to 1. + */ +function node_make_sticky_action(&$node, $context = array()) { + $node->sticky = 1; + watchdog('action', 'Set @type %title to sticky.', array('@type' => node_get_types('name', $node), '%title' => $node->title)); +} + +/** + * Implementation of a Drupal action. + * Sets the sticky-at-top-of-list property of a node to 1. + */ +function node_make_unsticky_action(&$node, $context = array()) { + $node->sticky = 0; + watchdog('action', 'Set @type %title to unsticky.', array('@type' => node_get_types('name', $node), '%title' => $node->title)); +} + +/** + * Implementation of a Drupal action. + * Sets the promote property of a node to 1. + */ +function node_promote_action(&$node, $context = array()) { + $node->promote = 1; + watchdog('action', 'Promoted @type %title to front page.', array('@type' => node_get_types('type', $node), '%title' => $node->title)); +} + +/** + * Implementation of a Drupal action. + * Sets the promote property of a node to 0. + */ +function node_unpromote_action(&$node, $context = array()) { + $node->promote = 1; + watchdog('action', 'Removed @type %title from front page.', array('@type' => node_get_types('type', $node), '%title' => $node->title)); +} + +/** + * Implementation of a configurable Drupal action. + * Assigns ownership of a node to a user. + */ +function node_assign_owner_action(&$node, $context = array()) { + $uid = db_result(db_query("SELECT uid FROM {users} WHERE name = '%s'", $context['owner_name'])); + $node->uid = $uid; + watchdog('action', 'Changed owner of @type %title to uid %name.', array('@type' => node_get_types('type', $node), '%title' => $node->title, '%name' => $context['owner_name'])); +} + +function node_assign_owner_action_form($context) { + $description = t('The username of the user to which you would like to assign ownership.'); + $count = db_result(db_query("SELECT COUNT(*) FROM {users}")); + // Use dropdown for fewer than 200 users; textbox for more than that. + if (intval($count) < 200) { + $options = array(); + $result = db_query("SELECT uid, name FROM {users} WHERE uid > 0 ORDER BY name"); + while ($data = db_fetch_object($result)) { + $options[$data->name] = $data->name; + } + $form['owner_name'] = array( + '#type' => 'select', + '#title' => t('Username'), + '#default_value' => isset($context['owner_name']) ? $context['owner_name'] : '', + '#options' => $options, + '#description' => $description, + ); + } + else { + $form['owner_name'] = array( + '#type' => 'textfield', + '#title' => t('Username'), + '#default_value' => isset($context['owner_name']) ? $context['owner_name'] : '', + '#autocomplete_path' => 'user/autocomplete', + '#size' => '6', + '#maxlength' => '7', + '#description' => $description, + ); + } + return $form; +} + +function node_assign_owner_action_validate($form, $form_state) { + $count = db_result(db_query("SELECT COUNT(*) FROM {users} WHERE name = '%s'", $form_state['values']['owner_name'])); + if (intval($count) != 1) { + form_set_error('owner_name', t('Please enter a valid username.')); + } +} + +function node_assign_owner_action_submit($form, $form_state) { + return array('owner_name' => $form_state['values']['owner_name']); +} + +/** + * Implementation of a configurable Drupal action. + * Saves a node. + */ +function node_save_action($node) { + node_save($node); + watchdog('action', 'Saved @type %title', array('@type' => node_get_types('type', $node), '%title' => $node->title)); +} \ No newline at end of file Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.495 diff -u -p -r1.495 system.module --- modules/system/system.module 22 Jun 2007 08:32:28 -0000 1.495 +++ modules/system/system.module 24 Jun 2007 09:40:16 -0000 @@ -2744,3 +2744,262 @@ function system_batch_page() { print theme('page', $output, FALSE, FALSE); } } + +/** + * Implementation of hook_action_info(). + */ +function system_action_info() { + return array( + 'system_message_action' => array( + 'type' => 'system', + 'description' => t('Display a message to the user'), + 'configurable' => TRUE, + 'hooks' => array( + 'nodeapi' => array('view', 'insert', 'update', 'delete'), + 'comment' => array('view', 'insert', 'update', 'delete'), + 'user' => array('view', 'insert', 'update', 'delete', 'login'), + ), + ), + 'system_send_email_action' => array( + 'description' => t('Send e-mail'), + 'type' => 'system', + 'configurable' => TRUE, + 'hooks' => array( + 'nodeapi' => array('view', 'insert', 'update', 'delete'), + 'comment' => array('view', 'insert', 'update', 'delete'), + 'user' => array('view', 'insert', 'update', 'delete', 'login'), + ) + ), + 'system_goto_action' => array( + 'description' => t('Redirect to URL'), + 'type' => 'system', + 'configurable' => TRUE, + 'hooks' => array( + 'nodeapi' => array('view', 'insert', 'update', 'delete'), + 'comment' => array('view', 'insert', 'update', 'delete'), + 'user' => array('view', 'insert', 'update', 'delete', 'login'), + ) + ) + ); +} + +/** + * Return a form definition so the Send email action can be configured. + * + * @param $context + * Default values (if we are editing an existing action instance). + * @return + * Form definition. + */ +function system_send_email_action_form($context) { + // Set default values for form. + if (!isset($context['recipient'])) { + $context['recipient'] = ''; + } + if (!isset($context['subject'])) { + $context['subject'] = ''; + } + if (!isset($context['message'])) { + $context['message'] = ''; + } + + $form['recipient'] = array( + '#type' => 'textfield', + '#title' => t('Recipient'), + '#default_value' => $context['recipient'], + '#size' => '20', + '#maxlength' => '254', + '#description' => t('The email address to which the message should be sent OR enter %author if you would like to send an e-mail to the original author of the post.', array('%author' => '%author')), + ); + $form['subject'] = array( + '#type' => 'textfield', + '#title' => t('Subject'), + '#default_value' => $context['subject'], + '#size' => '20', + '#maxlength' => '254', + '#description' => t('The subject of the message.'), + ); + $form['message'] = array( + '#type' => 'textarea', + '#title' => t('Message'), + '#default_value' => $context['message'], + '#cols' => '80', + '#rows' => '20', + '#description' => t('The message that should be sent. You may include the following variables: %site_name, %username, %node_url, %node_type, %title, %teaser, %body. Not all variables will be available in all contexts.'), + ); + return $form; +} + +function system_send_email_action_validate($form, $form_state) { + $form_values = $form_state['values']; + // Validate the configuration form. + if (!valid_email_address($form_values['recipient']) && $form_values['recipient'] != '%author') { + // We want the literal %author placeholder to be emphasized in the error message. + form_set_error('recipient', t('Please enter a valid email address or %author.', array('%author' => '%author'))); + } +} + +function system_send_email_action_submit($form, $form_state) { + $form_values = $form_state['values']; + // Process the HTML form to store configuration. The keyed array that + // we return will be serialized to the database. + $params = array( + 'recipient' => $form_values['recipient'], + 'subject' => $form_values['subject'], + 'message' => $form_values['message'], + ); + return $params; +} + +/** + * Implementation of a configurable Drupal action. + * Sends an email. + */ +function system_send_email_action($object, $context) { + switch($context['hook']) { + case 'nodeapi': + // Because this is not an action of type 'node' the node + // will not be passed as $object, but it will still be available + // in $context. + $node = $context['node']; + break; + // The comment hook also provides the node, in context. + case 'comment': + $comment = $context['comment']; + $node = node_load($comment->nid); + case 'user': + $account = $context['account']; + break; + default: + // We are being called directly. + $node = $object; + } + + $from = variable_get('site_mail', ini_get('sendmail_from')); + if (isset($node)) { + if (!isset($account)) { + $account = user_load(array('uid' => $node->uid)); + } + if ($context['recipient'] == '%author') { + $recipient = $account->mail; + } + else { + $recipient = $context['recipient']; + } + } + $variables['%site_name'] = variable_get('site_name', 'Drupal'); + $variables['%username'] = $account->name; + + // Node-based variable translation is only available if we have a node. + if (isset($node) && is_object($node)) { + $variables = array_merge($variables, array( + '%username' => $account->name, + '%uid' => $node->uid, + '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)), + '%node_type' => $node->type, + '%title' => $node->title, + '%teaser' => $node->teaser, + '%body' => $node->body + ) + ); + + $subject = strtr($context['subject'], $variables); + $subject = str_replace(array("\r", "\n"), '', $subject); + $message = strtr($context['message'], $variables); + $body = drupal_html_to_plain($message); + } + if (drupal_mail('action_send_email', $recipient, $subject, $body, $from )) { + watchdog('action', 'Sent email to %recipient', array('%recipient' => $recipient)); + } + else { + watchdog('error', 'Unable to send email to %recipient', array('%recipient' => $recipient)); + } +} + +function system_message_action_form($context) { + $form['message'] = array( + '#type' => 'textarea', + '#title' => t('Message'), + '#default_value' => isset($context['message']) ? $context['message'] : '', + '#required' => TRUE, + '#rows' => '8', + '#description' => t('The message to be displayed to the current user. You may include the following variables: %site_name, %username, %node_url, %node_type, %title, %teaser, %body. Not all variables will be available in all contexts.'), + ); + return $form; +} + +function system_message_action_submit($form, $form_state) { + return array('message' => $form_state['values']['message']); +} + +/** + * Implementation of a configurable Drupal action. + * Sends a configurable message to the current user's screen. + */ +function system_message_action(&$object, $context = array()) { + global $user; + + // This action can be called in any context, but if placeholders + // are used a node object must be present to be the source + // of substituted text. + switch($context['hook']) { + case 'nodeapi': + // Because this is not an action of type 'node' the node + // will not be passed as $object, but it will still be available + // in $context. + $node = $context['node']; + break; + // The comment hook also provides the node, in context. + case 'comment': + $comment = $context['comment']; + $node = node_load($comment->nid); + break; + default: + // We are being called directly. + $node = $object; + } + + $variables = array( + '%site_name' => variable_get('site_name', 'Drupal'), + '%username' => $user->name ? $user->name : variable_get('anonymous', t('Anonymous')), + ); + + if (isset($node) && is_object($node)) { + $variables = array_merge($variables, array( + '%uid' => $node->uid, + '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)), + '%node_type' => check_plain($node->type), + '%title' => filter_xss($node->title), + '%teaser' => filter_xss($node->teaser), + '%body' => filter_xss($node->body), + ) + ); + } + $context['message'] = strtr($context['message'], $variables); + drupal_set_message($context['message']); +} + +/** + * Implementation of a configurable Drupal action. + * Redirect user to a URL. + */ +function system_goto_action_form($context) { + $form['url'] = array( + '#type' => 'textfield', + '#title' => t('URL'), + '#description' => t('The URL to which the user should be redirected. This can be an internal URL like node/1234 or an external URL like http://drupal.org.'), + '#default_value' => isset($context['url']) ? $context['url'] : '', + '#required' => TRUE, + ); + return $form; +} + +function system_goto_action_submit($form, $form_state) { + return array( + 'url' => $form_state['values']['url'] + ); +} + +function system_goto_action($object, $context) { + drupal_goto($context['url']); +} \ No newline at end of file Index: modules/taxonomy/taxonomy.module =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v retrieving revision 1.363 diff -u -p -r1.363 taxonomy.module --- modules/taxonomy/taxonomy.module 22 Jun 2007 08:46:15 -0000 1.363 +++ modules/taxonomy/taxonomy.module 24 Jun 2007 09:40:17 -0000 @@ -826,7 +826,7 @@ function taxonomy_node_save($node, $term unset($terms['tags']); foreach ($typed_input as $vid => $vid_value) { - $typed_terms = taxonomy_explode_tags($vid_value); + $typed_terms = drupal_explode_tags($vid_value); $inserted = array(); foreach ($typed_terms as $typed_term) { @@ -1502,7 +1502,7 @@ function _taxonomy_get_tid_from_term($te */ function taxonomy_autocomplete($vid, $string = '') { // The user enters a comma-separated list of tags. We only autocomplete the last tag. - $array = taxonomy_explode_tags($string); + $array = drupal_explode_tags($string); // Fetch last tag $last_string = trim(array_pop($array)); @@ -1526,30 +1526,6 @@ function taxonomy_autocomplete($vid, $st } /** - * Explode a string of given tags into an array. - */ -function taxonomy_explode_tags($tags) { - // This regexp allows the following types of user input: - // this, "somecompany, llc", "and ""this"" w,o.rks", foo bar - $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; - preg_match_all($regexp, $tags, $matches); - $typed_tags = array_unique($matches[1]); - - $tags = array(); - foreach ($typed_tags as $tag) { - // If a user has escaped a term (to demonstrate that it is a group, - // or includes a comma or quote character), we remove the escape - // formatting so to save the term into the database as the user intends. - $tag = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $tag))); - if ($tag != "") { - $tags[] = $tag; - } - } - - return $tags; -} - -/** * Implode a list of tags of a certain vocabulary into a string. */ function taxonomy_implode_tags($tags, $vid = NULL) { Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.806 diff -u -p -r1.806 user.module --- modules/user/user.module 24 Jun 2007 00:38:40 -0000 1.806 +++ modules/user/user.module 24 Jun 2007 09:40:19 -0000 @@ -3197,3 +3197,60 @@ function _user_password_dynamic_validati $complete = TRUE; } } + +/** + * Implementation of hook_action_info(). + */ +function user_action_info() { + return array( + 'user_block_user_action' => array( + 'description' => t('Block current user'), + 'type' => 'user', + 'configurable' => FALSE, + 'hooks' => array( + 'user' => array('view', 'insert', 'update', 'delete', 'login', 'logout'), + 'nodeapi' => array('presave', 'delete','insert','update','view'), + 'comment' => array('view', 'insert', 'update', 'delete'), + ), + ), + 'user_block_ip_action' => array( + 'description' => t('Ban IP address of current user'), + 'type' => 'user', + 'configurable' => FALSE, + 'hooks' => array( + 'user' => array('view', 'insert', 'update', 'delete', 'login', 'logout'), + 'nodeapi' => array('presave', 'delete','insert','update','view'), + 'comment' => array('view', 'insert', 'update', 'delete'), + ) + ) + ); +} + +/** + * Implementation of a Drupal action. + * Blocks the current user. + */ +function user_block_user_action(&$object, $context = array()) { + if (isset($object->uid)) { + $uid = $object->uid; + } + elseif (isset($context['uid'])) { + $uid = $context['uid']; + } + else { + global $user; + $uid = $user->uid; + } + db_query("UPDATE {users} SET status = 0 WHERE uid = %d", $uid); + sess_destroy_uid($uid); + watchdog('action', 'Blocked user %name.', array('%name' => check_plain($user->name))); +} + +/** + * Implementation of a Drupal action. + * Adds an access rule that blocks the user's IP address. + */ +function user_block_ip_action() { + db_query("INSERT INTO {access} (mask, type, status) VALUES ('%s', '%s', %d)", $_SERVER['REMOTE_ADDR'], 'host', 0); + watchdog('action', 'Banned IP address %ip', array('%ip' => $_SERVER['REMOTE_ADDR'])); +} \ No newline at end of file