sms_actions.module in SMS Framework 7
Same filename and directory in other branches
Provides a "Send SMS" action and the ability to define custom triggers for incoming messages.
File
modules/sms_actions/sms_actions.moduleView source
<?php
/**
* @file
* Provides a "Send SMS" action and the ability to define custom triggers for
* incoming messages.
*/
/**
* Implementation of hook_sms_incoming().
*/
function sms_actions_sms_incoming($op, $number, $message) {
if ($op == 'process') {
$commands = sms_actions_get_commands();
foreach ($commands as $command) {
if (stripos($message, $command->discriminator) === 0) {
$active_command = $command;
}
}
if (!empty($active_command)) {
sms_actions_do($active_command, $number, $message);
}
else {
// Try to figure out what command was actually given
// preg_match('!^([A-z0-9\-]+).+$!', $message, $matches);
// if (variable_get('sms_actions_invalid_cmd_reply', 0)) {
// sms_send($number, t('The command @command was not found.', array('@command' => $matches[1])));
// }
// watchdog('sms', t('Command %command not found from @number.', array('%command' => $matches[1], '@number' => $number)), WATCHDOG_WARNING);
}
}
}
/**
* Executes actions defined by sms_actions module.
*
* @param string $command
* The command to execute.
* @param string $number
* The phone number associated with the action.
* @param string $message
* The message to be sent.
*/
function sms_actions_do($command, $number, $message) {
// Keep objects for reuse so that changes actions make to objects can persist.
static $objects;
$aids = trigger_get_assigned_actions($command->hook) + trigger_get_assigned_actions('sms_actions');
if (!$aids) {
return;
}
$context = array(
'group' => 'sms_actions',
'hook' => $command->hook,
'from' => $number,
);
foreach ($aids as $aid => $action_info) {
$type = $action_info['type'];
if ($type != 'sms') {
if ($type == 'node') {
preg_match('!^[A-z0-9\\-]+\\s(.+)$!', $message, $matches);
$params = explode(' ', $matches[1]);
if ($node = node_load($params[0])) {
if (!isset($objects[$type])) {
$objects[$type] = _trigger_normalize_comment_context($type, $node);
}
$context['group'] = 'node';
actions_do($aid, $objects[$type], $context);
}
}
}
else {
if (!isset($objects[$type])) {
$objects[$type] = (object) array(
'message' => $message,
'from' => $number,
);
}
actions_do($aid, $objects[$type], $context);
}
}
}
/**
* Implementation of hook_menu().
*/
function sms_actions_menu() {
$items['admin/smsframework/actions'] = array(
'title' => 'SMS Actions',
'description' => 'Define custom triggers for incoming SMS messages.',
'access arguments' => array(
'administer smsframework',
),
'page callback' => 'sms_actions_command_list',
);
$items['admin/smsframework/actions/list'] = array(
'title' => 'List',
'access arguments' => array(
'administer smsframework',
),
'weight' => -5,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/smsframework/actions/add'] = array(
'title' => 'Add command',
'access arguments' => array(
'administer smsframework',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'sms_actions_edit_command_form',
),
'type' => MENU_LOCAL_TASK,
);
$items['admin/smsframework/actions/edit'] = array(
'title' => 'Edit command',
'access arguments' => array(
'administer smsframework',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'sms_actions_edit_command_form',
4,
),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implementation of hook_action_info().
*/
function sms_actions_action_info() {
return array(
'sms_actions_send_action' => array(
'type' => 'sms',
'label' => t('Send SMS'),
'configurable' => TRUE,
'triggers' => array(
'sms_actions',
'node_presave',
'comment_insert',
'comment_update',
'comment_delete',
),
),
);
}
/**
* Form constructor for sms_actions_send_action form
*
* @param array $context
* Array containing contextual information for the action.
*
* @return array
* Drupal form array.
*/
function sms_actions_send_action_form($context) {
// Set default values for form.
if (!isset($context['number'])) {
$context['number'] = '';
}
if (!isset($context['author'])) {
$context['author'] = FALSE;
}
if (!isset($context['message'])) {
$context['message'] = '';
}
$form = sms_send_form(NULL, NULL, FALSE);
$form['number']['#default_value'] = $context['number'];
$form['author'] = array(
'#type' => 'checkbox',
'#title' => t('Send to author of original post'),
'#description' => t('If checked, the message will be sent to author of the orginal post and the number field will be ignored.'),
'#default_value' => $context['author'],
);
$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, %uid, %node_url, %node_alias, %node_type, %title, %teaser, %body. Not all variables will be available in all contexts.'),
);
return $form;
}
/**
* Submit handler for sms_actions_send_action form.
*/
function sms_actions_send_action_submit($form, &$form_state) {
// Process the HTML form to store configuration. The keyed array that
// we return will be serialized to the database.
$params = array(
'number' => $form_state['values']['number'],
'author' => $form_state['values']['author'],
'message' => $form_state['values']['message'],
);
return $params;
}
/**
* Implements the action sms_action_send_action.
*
* @param object $object
* The object initiating the action.
* @param array $context
* The context in which the action is being triggered.
*/
function sms_actions_send_action($object, $context) {
global $user;
switch ($context['group']) {
case 'node':
$node = $context['node'];
break;
case 'comment':
$comment = $context['comment'];
$node = node_load($comment->nid);
break;
case 'user':
// Because this is not an action of type 'user' the user
// object is not passed as $object, but it will still be available
// in $context. Use user_load() to pull in ->sms_user data as well.
$account = user_load($context['account']->uid);
$variables['%username'] = $account->name;
if (isset($context['node'])) {
$node = $context['node'];
}
elseif ($context['recipient'] == '%author') {
// If we don't have a node, we don't have a node author.
watchdog('error', t("Cannot use '%author' token in this context."));
return;
}
break;
case 'sms_actions':
// We don't want the default: action for 'sms_actions' group.
break;
default:
// Check context for node.
if (!isset($object) && isset($context['node'])) {
$node = $context['node'];
}
else {
// We are being called directly.
$node = $object;
}
break;
}
$number = $context['number'];
$variables['%site_name'] = variable_get('site_name', 'Drupal');
if (isset($node)) {
if (!isset($account)) {
$account = user_load($node->uid);
$variables['%username'] = $account->name;
}
if ($context['author'] && $account->sms_user['status'] == 2) {
$number = $account->sms_user['number'];
}
$names = node_type_get_names();
$variables = array_merge($variables, array(
'%uid' => $node->uid,
'%node_url' => url('node/' . $node->nid, array(
'absolute' => TRUE,
)),
'%node_type' => check_plain($names[$node->type]),
'%title' => filter_xss($node->title),
'%teaser' => filter_xss($node->teaser),
'%body' => filter_xss($node->body),
));
}
$message = strtr($context['message'], $variables);
if (isset($node) && $context['author'] && isset($account)) {
sms_user_send($account->uid, $message);
}
else {
sms_send($number, $message);
}
}
/**
* Implements hook_action_info_alter().
*/
function sms_actions_action_info_alter(&$actions) {
// Actions to alter.
$hooks = array(
'node_publish_action',
'node_unpublish_action',
'node_make_sticky_action',
'node_make_unsticky_action',
'node_promote_action',
'node_unpromote_action',
'node_assign_owner_action',
'node_save_action',
'node_unpublish_by_keyword_action',
'system_send_email_action',
'sms_actions_send_action',
);
$commands = sms_actions_get_commands();
$command_triggers[] = 'sms_actions';
foreach ($commands as $command) {
$command_triggers[] = $command->hook;
}
foreach ($actions as $hook => $action) {
if (in_array($hook, $hooks) && $action['triggers'] != array(
'any',
)) {
$actions[$hook]['triggers'] = array_unique(array_merge($actions[$hook]['triggers'], $command_triggers));
}
}
}
/**
* Implementation of hook_trigger_info().
*/
function sms_actions_trigger_info() {
$hooks = array();
$hooks['sms_actions']['sms_actions'] = array(
'label' => t('When an SMS message with any discriminator is received.'),
);
$commands = sms_actions_get_commands();
foreach ($commands as $command) {
$hooks['sms_actions'][$command->hook] = array(
'label' => t('When an SMS message with the %discriminator discriminator is received.', array(
'%discriminator' => $command->discriminator,
)),
);
}
return $hooks;
}
/**
* Returns an array of commands objects.
*
* Command objects are created in the UI.
*
* @return array
* The list of commands that have been created by the UI.
*/
function sms_actions_get_commands() {
$commands = variable_get('sms_actions_commands', array());
foreach ($commands as $key => $command) {
$commands[$key]->hook = 'sms_actions_' . $command->discriminator;
}
return $commands;
}
/**
* Loads a specific command.
*
* @param string $discriminator
* The given name for the command.
*
* @return object
* The command object with the associated discriminator (machine_name).
*/
function sms_actions_command_load($discriminator) {
$commands = variable_get('sms_actions_commands', array());
return $commands[$discriminator];
}
/**
* Saves a command.
*/
function sms_actions_command_save($command) {
$commands = sms_actions_get_commands();
$existing_discriminator = !empty($command->old_discriminator) ? $command->old_discriminator : $command->discriminator;
if (array_key_exists($existing_discriminator, $commands)) {
db_query("UPDATE {trigger_assignments} SET hook = '%s' WHERE hook = '%s'", array(
'sms_actions_' . $command->discriminator,
'sms_actions_' . $existing_discriminator,
));
}
unset($commands[$existing_discriminator]);
$commands[$command->discriminator] = $command;
variable_set('sms_actions_commands', $commands);
}
/**
* Deletes a command.
*/
function sms_actions_command_delete($discriminator) {
$commands = variable_get('sms_actions_commands', array());
unset($commands[$discriminator]);
variable_set('sms_actions_commands', $commands);
db_query("DELETE FROM {trigger_assignments} WHERE hook = '%s'", array(
'sms_actions_' . $discriminator,
));
}
/**
* Menu callback: command listing.
*
* @return string
* HTML string for the table of commands.
*/
function sms_actions_command_list() {
$header = array(
t('Discriminator'),
t('Operations'),
);
$commands = sms_actions_get_commands();
$rows = array();
foreach ($commands as $command) {
$row = array(
$command->discriminator,
l(t('edit'), 'admin/smsframework/actions/edit/' . $command->discriminator),
);
$rows[] = $row;
}
return theme('table', array(
'header' => $header,
'rows' => $rows,
'empty' => t('No commands available.'),
));
}
/**
* Form constructor for the sms_actions command editing form.
*
* @param string $discriminator
* Determines which command is to be edited. Null for an empty form.
*
* @return array
* Drupal form array.
*
* @see sms_actions_edit_command_form_validate()
* @see sms_actions_edit_command_form_submit()
*/
function sms_actions_edit_command_form($form, &$form_state, $discriminator = NULL) {
// Check for confirmation forms.
if (isset($form_state['confirm_delete'])) {
return sms_actions_command_confirm_delete($form_state, $discriminator);
}
if (isset($discriminator)) {
$command = sms_actions_command_load($discriminator);
}
$form['discriminator'] = array(
'#type' => 'textfield',
'#title' => t('Discriminator'),
'#description' => t('A keyword that will be used to identify incoming messages. The discriminator may consist only of lowercase letters, numbers, and dashes.'),
'#size' => 40,
'#maxlength' => 16,
'#required' => TRUE,
'#default_value' => isset($command) ? $command->discriminator : '',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save command'),
);
if (isset($command)) {
$form['old_discriminator'] = array(
'#type' => 'value',
'#value' => $command->discriminator,
);
$form['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
'#weight' => 45,
);
}
return $form;
}
/**
* Validate handler for sms_actions_edit_command_form().
*
* @see sms_actions_edit_command_form()
*/
function sms_actions_edit_command_form_validate($form, &$form_state) {
if (!preg_match('!^[a-z0-9\\-]+$!', $form_state['values']['discriminator'])) {
form_set_error('discriminator', t('The discriminator may only consist of lowercase letters, dashes, and numbers.'));
}
}
/**
* Submit handler for sms_actions_edit_command_form().
*
* @see sms_actions_edit_command_form()
*/
function sms_actions_edit_command_form_submit($form, &$form_state) {
// $commands = sms_actions_get_commands();
$command = new stdClass();
$command->discriminator = $form_state['values']['discriminator'];
$command->old_discriminator = isset($form_state['values']['old_discriminator']) ? $form_state['values']['old_discriminator'] : '';
if ($form_state['clicked_button']['#value'] == t('Delete')) {
if ($form_state['values']['delete'] === TRUE) {
return sms_actions_command_confirm_delete_submit($form, $form_state);
}
$form_state['rebuild'] = TRUE;
$form_state['confirm_delete'] = TRUE;
return;
}
sms_actions_command_save($command);
// $commands[$command->discriminator] = $command;
// variable_set('sms_actions_commands', $commands);
$form_state['redirect'] = 'admin/smsframework/actions';
}
/**
* Form constructor for command delete confirmation form.
*
* @param string $discriminator
* Determines which command to confirm deletion.
*
* @return array
* Drupal form array.
*
* @see sms_actions_command_confirm_delete_submit()
*/
function sms_actions_command_confirm_delete($form_state, $discriminator) {
$command = sms_actions_command_load($discriminator);
$form['discriminator'] = array(
'#type' => 'value',
'#value' => $command->discriminator,
);
$form['delete'] = array(
'#type' => 'value',
'#value' => TRUE,
);
return confirm_form($form, t('Are you sure you want to delete the command %discriminator?', array(
'%discriminator' => $command->discriminator,
)), 'admin/smsframework/actions', t('Deleting a command will remove any action assignments. This action cannot be undone.'), t('Delete'), t('Cancel'));
}
/**
* Submit handler for sms_actions_command_confirm_delete().
*
* @see sms_actions_command_confirm_delete()
*/
function sms_actions_command_confirm_delete_submit($form, &$form_state) {
sms_actions_command_delete($form_state['values']['discriminator']);
drupal_set_message(t('The command %command has been deleted.', array(
'%command' => $form_state['values']['discriminator'],
)));
$form_state['redirect'] = 'admin/smsframework/actions';
}