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('!?blockquote>!i', '"', $string);
+ $string = preg_replace('!?(em|i)>!i', '/', $string);
+ $string = preg_replace('!?(b|strong)>!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("@(li|dd)>\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