system.eval.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. /**
  3. * @file
  4. * Contains rules integration for the system module needed during evaluation.
  5. *
  6. * @addtogroup rules
  7. *
  8. * @{
  9. */
  10. /**
  11. * Action: Show a drupal message.
  12. */
  13. function rules_action_drupal_message($message, $status, $repeat) {
  14. drupal_set_message(filter_xss_admin($message), $status, $repeat);
  15. }
  16. /**
  17. * Action: Page redirect.
  18. *
  19. * @see rules_page_build()
  20. * @see rules_drupal_goto_alter()
  21. */
  22. function rules_action_drupal_goto($url, $force = FALSE, $destination = FALSE) {
  23. // Don't let administrators lock them out from Rules administration pages.
  24. if (isset($_GET['q']) && strpos($_GET['q'], 'admin/config/workflow/rules') === 0) {
  25. rules_log('Skipped page redirect on Rules administration page.', array(), RulesLog::WARN);
  26. return;
  27. }
  28. // Do not redirect during batch processing.
  29. if (($batch = batch_get()) && isset($batch['current_set'])) {
  30. rules_log('Skipped page redirect during batch processing.');
  31. return;
  32. }
  33. // Keep the current destination parameter if there is one set.
  34. if ($destination) {
  35. $url .= strpos($url, '?') === FALSE ? '?' : '&';
  36. $url .= drupal_http_build_query(drupal_get_destination());
  37. }
  38. // If force is enabled, remove any destination parameter.
  39. if ($force && isset($_GET['destination'])) {
  40. unset($_GET['destination']);
  41. }
  42. // We don't invoke drupal_goto() right now, as this would end the current
  43. // page execution unpredictably for modules. So we'll take over drupal_goto()
  44. // calls from somewhere else via hook_drupal_goto_alter() and make sure
  45. // a drupal_goto() is invoked before the page is output with
  46. // rules_page_build().
  47. $GLOBALS['_rules_action_drupal_goto_do'] = array($url, $force);
  48. }
  49. /**
  50. * Action: Set breadcrumb.
  51. */
  52. function rules_action_breadcrumb_set(array $titles, array $paths) {
  53. $trail = array(l(t('Home'), ''));
  54. foreach ($titles as $i => $title) {
  55. // Skip empty titles.
  56. if ($title = trim($title)) {
  57. // Output plaintext instead of a link if there is a title
  58. // without a path.
  59. $path = trim($paths[$i]);
  60. if (!empty($paths[$i]) && $paths[$i] != '<none>') {
  61. $trail[] = l($title, trim($paths[$i]));
  62. }
  63. else {
  64. $trail[] = check_plain($title);
  65. }
  66. }
  67. }
  68. drupal_set_breadcrumb($trail);
  69. }
  70. /**
  71. * Action Implementation: Send mail.
  72. */
  73. function rules_action_mail($to, $subject, $message, $from = NULL, $langcode, $settings, RulesState $state, RulesPlugin $element) {
  74. $to = str_replace(array("\r", "\n"), '', $to);
  75. $from = !empty($from) ? str_replace(array("\r", "\n"), '', $from) : NULL;
  76. $params = array(
  77. 'subject' => $subject,
  78. 'message' => $message,
  79. 'langcode' => $langcode,
  80. );
  81. // Set a unique key for this mail.
  82. $name = isset($element->root()->name) ? $element->root()->name : 'unnamed';
  83. $key = 'rules_action_mail_' . $name . '_' . $element->elementId();
  84. $languages = language_list();
  85. $language = isset($languages[$langcode]) ? $languages[$langcode] : language_default();
  86. $message = drupal_mail('rules', $key, $to, $language, $params, $from);
  87. if ($message['result']) {
  88. watchdog('rules', 'Successfully sent email to %recipient', array('%recipient' => $to));
  89. }
  90. }
  91. /**
  92. * Action: Send mail to all users of a specific role group(s).
  93. */
  94. function rules_action_mail_to_users_of_role($roles, $subject, $message, $from = NULL, $settings, RulesState $state, RulesPlugin $element) {
  95. $from = !empty($from) ? str_replace(array("\r", "\n"), '', $from) : NULL;
  96. // All authenticated users, which is everybody.
  97. if (in_array(DRUPAL_AUTHENTICATED_RID, $roles)) {
  98. $result = db_query('SELECT mail FROM {users} WHERE uid > 0');
  99. }
  100. else {
  101. // Avoid sending emails to members of two or more target role groups.
  102. $result = db_query('SELECT DISTINCT u.mail FROM {users} u INNER JOIN {users_roles} r ON u.uid = r.uid WHERE r.rid IN (:rids)', array(':rids' => $roles));
  103. }
  104. // Now, actually send the mails.
  105. $params = array(
  106. 'subject' => $subject,
  107. 'message' => $message,
  108. );
  109. // Set a unique key for this mail.
  110. $name = isset($element->root()->name) ? $element->root()->name : 'unnamed';
  111. $key = 'rules_action_mail_to_users_of_role_' . $name . '_' . $element->elementId(); $languages = language_list();
  112. $message = array('result' => TRUE);
  113. foreach ($result as $row) {
  114. $message = drupal_mail('rules', $key, $row->mail, language_default(), $params, $from);
  115. // If $message['result'] is FALSE, then it's likely that email sending is
  116. // failing at the moment, and we should just abort sending any more. If
  117. // however, $message['result'] is NULL, then it's likely that a module has
  118. // aborted sending this particular email to this particular user, and we
  119. // should just keep on sending emails to the other users.
  120. // For more information on the result value, see drupal_mail().
  121. if ($message['result'] === FALSE) {
  122. break;
  123. }
  124. }
  125. if ($message['result'] !== FALSE) {
  126. $role_names = array_intersect_key(user_roles(TRUE), array_flip($roles));
  127. watchdog('rules', 'Successfully sent email to the role(s) %roles.', array('%roles' => implode(', ', $role_names)));
  128. }
  129. }
  130. /**
  131. * Implements hook_mail().
  132. *
  133. * Sets the message subject and body as configured.
  134. */
  135. function rules_mail($key, &$message, $params) {
  136. $message['subject'] .= str_replace(array("\r", "\n"), '', $params['subject']);
  137. $message['body'][] = $params['message'];
  138. }
  139. /**
  140. * Action: Block an IP address.
  141. */
  142. function rules_action_block_ip($ip_address = NULL) {
  143. if (empty($ip_address)) {
  144. $ip_address = ip_address();
  145. }
  146. db_insert('blocked_ips')->fields(array('ip' => $ip_address))->execute();
  147. watchdog('rules', 'Banned IP address %ip', array('%ip' => $ip_address));
  148. }
  149. /**
  150. * A class implementing a rules input evaluator processing tokens.
  151. */
  152. class RulesTokenEvaluator extends RulesDataInputEvaluator {
  153. /**
  154. * Overrides RulesDataInputEvaluator::prepare().
  155. */
  156. public function prepare($text, $var_info) {
  157. $text = is_array($text) ? implode('', $text) : $text;
  158. // Skip this evaluator if there are no tokens.
  159. $this->setting = token_scan($text) ? TRUE : NULL;
  160. }
  161. /**
  162. * Evaluate tokens.
  163. *
  164. * We replace the tokens on our own as we cannot use token_replace(), because
  165. * token usually assumes that $data['node'] is a of type node, which doesn't
  166. * hold in general in our case.
  167. * So we properly map variable names to variable data types and then run the
  168. * replacement ourself.
  169. */
  170. public function evaluate($text, $options, RulesState $state) {
  171. $var_info = $state->varInfo();
  172. $options += array('sanitize' => FALSE);
  173. $replacements = array();
  174. $data = array();
  175. // We also support replacing tokens in a list of textual values.
  176. $whole_text = is_array($text) ? implode('', $text) : $text;
  177. foreach (token_scan($whole_text) as $var_name => $tokens) {
  178. $var_name = str_replace('-', '_', $var_name);
  179. if (isset($var_info[$var_name]) && ($token_type = _rules_system_token_map_type($var_info[$var_name]['type']))) {
  180. // We have to key $data with the type token uses for the variable.
  181. $data = rules_unwrap_data(array($token_type => $state->get($var_name)), array($token_type => $var_info[$var_name]));
  182. $replacements += token_generate($token_type, $tokens, $data, $options);
  183. }
  184. else {
  185. $replacements += token_generate($var_name, $tokens, array(), $options);
  186. }
  187. // Remove tokens if no replacement value is found. As token_replace() does
  188. // if 'clear' is set.
  189. $replacements += array_fill_keys($tokens, '');
  190. }
  191. // Optionally clean the list of replacement values.
  192. if (!empty($options['callback']) && function_exists($options['callback'])) {
  193. $function = $options['callback'];
  194. $function($replacements, $data, $options);
  195. }
  196. // Actually apply the replacements.
  197. $tokens = array_keys($replacements);
  198. $values = array_values($replacements);
  199. if (is_array($text)) {
  200. foreach ($text as $i => $text_item) {
  201. $text[$i] = str_replace($tokens, $values, $text_item);
  202. }
  203. return $text;
  204. }
  205. return str_replace($tokens, $values, $text);
  206. }
  207. /**
  208. * Create documentation about the available replacement patterns.
  209. *
  210. * @param array $var_info
  211. * Array with the available variables.
  212. *
  213. * @return array
  214. * Renderable array with the replacement pattern documentation.
  215. */
  216. public static function help($var_info) {
  217. $render = array(
  218. '#type' => 'fieldset',
  219. '#title' => t('Replacement patterns'),
  220. '#collapsible' => TRUE,
  221. '#collapsed' => TRUE,
  222. '#description' => t('Note that token replacements containing chained objects – such as [node:author:uid] – are not listed here, but are still available. The <em>data selection</em> input mode may help you find more complex replacement patterns. See <a href="@url">the online documentation</a> for more information about complex replacement patterns.',
  223. array('@url' => rules_external_help('chained-tokens'))),
  224. );
  225. $token_info = token_info();
  226. foreach ($var_info as $name => $info) {
  227. $token_types[$name] = _rules_system_token_map_type($info['type']);
  228. }
  229. foreach ($token_types as $name => $token_type) {
  230. if (isset($token_info['types'][$token_type])) {
  231. $render[$name] = array(
  232. '#theme' => 'table',
  233. '#header' => array(t('Token'), t('Label'), t('Description')),
  234. '#prefix' => '<h3>' . t('Replacement patterns for %label', array('%label' => $var_info[$name]['label'])) . '</h3>',
  235. );
  236. foreach ($token_info['tokens'][$token_type] as $token => $info) {
  237. $token = '[' . str_replace('_', '-', $name) . ':' . $token . ']';
  238. $render[$name]['#rows'][$token] = array(
  239. check_plain($token),
  240. check_plain($info['name']),
  241. check_plain($info['description']),
  242. );
  243. }
  244. }
  245. }
  246. return $render;
  247. }
  248. }
  249. /**
  250. * Looks for a token type mapping. Defaults to passing through the type.
  251. */
  252. function _rules_system_token_map_type($type) {
  253. $entity_info = entity_get_info();
  254. if (isset($entity_info[$type]['token type'])) {
  255. return $entity_info[$type]['token type'];
  256. }
  257. $cache = rules_get_cache();
  258. if (isset($cache['data_info'][$type]['token type'])) {
  259. return $cache['data_info'][$type]['token type'];
  260. }
  261. return $type;
  262. }
  263. /**
  264. * @}
  265. */