views_send.module 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358
  1. <?php
  2. /**
  3. * @file
  4. * The Views Send module.
  5. *
  6. * Views Send allow mass mailing using Views.
  7. *
  8. * @ingroup views_send
  9. */
  10. /**
  11. * e-mail priorities.
  12. */
  13. define('VIEWS_SEND_PRIORITY_NONE', 0);
  14. define('VIEWS_SEND_PRIORITY_HIGHEST', 1);
  15. define('VIEWS_SEND_PRIORITY_HIGH', 2);
  16. define('VIEWS_SEND_PRIORITY_NORMAL', 3);
  17. define('VIEWS_SEND_PRIORITY_LOW', 4);
  18. define('VIEWS_SEND_PRIORITY_LOWEST', 5);
  19. /**
  20. * Capture PHP max_execution_time before drupal_cron_run().
  21. * Workaround for Drupal 6.14. See http://drupal.org/node/584334
  22. */
  23. define('VIEWS_SEND_MAX_EXECUTION_TIME', ini_get('max_execution_time'));
  24. /**
  25. * Token pattern.
  26. */
  27. define('VIEWS_SEND_TOKEN_PATTERN', 'views-send:%s');
  28. define('VIEWS_SEND_TOKEN_PREFIX', '[');
  29. define('VIEWS_SEND_TOKEN_POSTFIX', ']');
  30. /**
  31. * Detect if there is MIME support (thorough modules like Mime Mail or Mandrill).
  32. */
  33. define('VIEWS_SEND_MIMEMAIL', module_exists('mimemail') || module_exists('mandrill'));
  34. /**
  35. * Sets the mailsystem for Views Send (if not already set).
  36. */
  37. function _views_send_mailsystem_set($key) {
  38. $mailsystem = mailsystem_get();
  39. $mailsystem_key = "views_send_$key";
  40. if (empty($mailsystem[$mailsystem_key])) {
  41. mailsystem_set(array(
  42. $mailsystem_key => $mailsystem['default-system']
  43. ));
  44. }
  45. }
  46. /**
  47. * Gets the selector field if it exists on the passed-in view.
  48. *
  49. * @return
  50. * The field object if found. Otherwise, FALSE.
  51. */
  52. function _views_send_get_field_selector($view) {
  53. foreach ($view->field as $field_name => $field) {
  54. if ($field instanceof views_send_handler_field_selector) {
  55. // Add in the view object for convenience.
  56. $field->view = $view;
  57. return $field;
  58. }
  59. }
  60. return FALSE;
  61. }
  62. /**
  63. * Gets the field value from a result row in a view - rendered value (default),
  64. * plain text or array with mail addresses.
  65. *
  66. * @return
  67. * See description.
  68. */
  69. function _views_send_get_field_value_from_views_row($view, $row_id, $field_id, $type='') {
  70. if (strpos($field_id, 'custom_text') === 0) {
  71. // Handle the special case for custom text fields.
  72. $field_id = str_replace('custom_text', 'nothing', $field_id);
  73. }
  74. $rendered_field = $view->style_plugin->get_field($row_id, $field_id);
  75. if ($type == 'plain_text') {
  76. // Removing HTML tags. Used for names in headers, not body.
  77. $result = strip_tags($rendered_field);
  78. }
  79. elseif ($type == 'mail') {
  80. // Removing HTML tags and entities. Used for e-mail addresses in headers, not body.
  81. $result = explode(',', decode_entities(strip_tags($rendered_field)));
  82. $result = array_map('trim', $result);
  83. }
  84. else {
  85. $result = $rendered_field;
  86. }
  87. return $result;
  88. }
  89. /**
  90. * Implements hook_views_form_substitutions().
  91. */
  92. function views_send_views_form_substitutions() {
  93. // Views check_plains the column label, so do the same here in order for the
  94. // replace operation to succeed.
  95. $select_all_placeholder = check_plain('<!--views-send-select-all-->');
  96. $select_all = array(
  97. '#type' => 'checkbox',
  98. '#default_value' => FALSE,
  99. '#attributes' => array('class' => array('views-send-table-select-all')),
  100. );
  101. return array(
  102. $select_all_placeholder => drupal_render($select_all),
  103. );
  104. }
  105. /**
  106. * Returns the 'select all' div that gets inserted above the view results
  107. * for non-table style plugins.
  108. *
  109. * The actual insertion is done by JS, matching the degradation behavior
  110. * of Drupal core (no JS - no select all).
  111. */
  112. function theme_views_send_select_all($variables) {
  113. $form = array();
  114. $form['select_all'] = array(
  115. '#type' => 'fieldset',
  116. '#attributes' => array('class' => array('views-send-fieldset-select-all')),
  117. );
  118. $form['select_all']['this_page'] = array(
  119. '#type' => 'checkbox',
  120. '#title' => t('Select all items on this page'),
  121. '#default_value' => '',
  122. '#attributes' => array('class' => array('views-send-select-this-page')),
  123. );
  124. $output = '<div class="views-send-select-all-markup">';
  125. $output .= drupal_render($form);
  126. $output .= '</div>';
  127. return $output;
  128. }
  129. /**
  130. * Implements hook_form_alter().
  131. */
  132. function views_send_form_alter(&$form, &$form_state, $form_id) {
  133. if (strpos($form_id, 'views_form_') === 0) {
  134. $field = _views_send_get_field_selector($form_state['build_info']['args'][0]);
  135. }
  136. // This form isn't used by Views Send.
  137. if (empty($field)) {
  138. return;
  139. }
  140. // Allow Views Send to work when embedded using views_embed_view(), or in a block.
  141. if (empty($field->view->override_path)) {
  142. if (!empty($field->view->preview) || $field->view->display_handler instanceof views_plugin_display_block) {
  143. $field->view->override_path = $_GET['q'];
  144. }
  145. }
  146. $query = drupal_get_query_parameters($_GET, array('q'));
  147. $form['#action'] = url($field->view->get_url(), array('query' => $query));
  148. // Cache the built form to prevent it from being rebuilt prior to validation
  149. // and submission, which could lead to data being processed incorrectly,
  150. // because the views rows (and thus, the form elements as well) have changed
  151. // in the meantime. Matching views issue: http://drupal.org/node/1473276.
  152. $form_state['cache'] = TRUE;
  153. // Add the custom CSS for all steps of the form.
  154. $form['#attached']['css'][] = drupal_get_path('module', 'views_send') . '/views_send.css';
  155. if ($form_state['step'] == 'views_form_views_form') {
  156. $form['actions']['submit']['#value'] = t('Send e-mail');
  157. $form['actions']['submit']['#submit'] = array('views_send_form_submit');
  158. if (isset($form['#prefix']) && ($form['#prefix'] == '<div class="vbo-views-form">')) {
  159. $form['#prefix'] = '<div class="vbo-views-form views-send-selection-form">';
  160. }
  161. else {
  162. $form['#prefix'] = '<div class="views-send-selection-form">';
  163. }
  164. $form['#suffix'] = '</div>';
  165. // Add the custom JS for this step of the form.
  166. $form['#attached']['js'][] = drupal_get_path('module', 'views_send') . '/views_send.js';
  167. // Adds the "select all" functionality for non-table style plugins
  168. // if the view has results.
  169. if (!empty($field->view->result) && !($field->view->style_plugin instanceof views_plugin_style_table)) {
  170. $form['select_all_markup'] = array(
  171. '#type' => 'markup',
  172. '#markup' => theme('views_send_select_all'),
  173. );
  174. }
  175. }
  176. }
  177. /**
  178. * Multistep form callback for the "configure" step.
  179. *
  180. @TODO: Hide "Sender" (from) if Mandrill is used.
  181. */
  182. function views_send_config_form($form, &$form_state, $view, $output) {
  183. if (!empty($form_state['configuration'])) {
  184. // Values entered in the "config" step.
  185. $config = $form_state['configuration'];
  186. }
  187. $display = $view->name . ':' . $view->current_display;
  188. $form['display'] = array(
  189. '#type' => 'value',
  190. '#value' => $display,
  191. );
  192. $form['from'] = array(
  193. '#type' => 'fieldset',
  194. '#title' => t('Sender'),
  195. '#collapsible' => TRUE,
  196. '#collapsed' => FALSE,
  197. );
  198. $form['from']['views_send_from_name'] = array(
  199. '#type' => 'textfield',
  200. '#title' => t('Sender\'s name'),
  201. '#description' => t("Enter the sender's human readable name."),
  202. '#default_value' => isset($config['views_send_from_name']) ? $config['views_send_from_name'] : variable_get('views_send_from_name_' . $display, variable_get('site_name', '')),
  203. '#maxlen' => 255,
  204. );
  205. $form['from']['views_send_from_mail'] = array(
  206. '#type' => 'textfield',
  207. '#title' => t('Sender\'s e-mail'),
  208. '#description' => t("Enter the sender's e-mail address."),
  209. '#required' => TRUE,
  210. '#default_value' => isset($config['views_send_from_mail']) ? $config['views_send_from_mail'] : variable_get('views_send_from_mail_' . $display, variable_get('site_mail', ini_get('sendmail_from'))),
  211. '#maxlen' => 255,
  212. );
  213. $fields = _views_send_get_fields_and_tokens($view, 'fields');
  214. $tokens = _views_send_get_fields_and_tokens($view, 'tokens');
  215. $fields_name_text = _views_send_get_fields_and_tokens($view, 'fields_name_text');
  216. $fields_options = array_merge(array('' => '<' . t('select') . '>'), $fields);
  217. $form['views_send_tokens'] = array(
  218. '#type' => 'value',
  219. '#value' => $tokens,
  220. );
  221. $form['to'] = array(
  222. '#type' => 'fieldset',
  223. '#title' => t('Recipients'),
  224. '#collapsible' => TRUE,
  225. '#collapsed' => FALSE,
  226. );
  227. $form['to']['views_send_to_name'] = array(
  228. '#type' => 'select',
  229. '#title' => t('Field used for recipient\'s name'),
  230. '#description' => t('Select which field from the current view will be used as recipient\'s name.'),
  231. '#options' => $fields_options,
  232. '#default_value' => isset($config['views_send_to_name']) ? $config['views_send_to_name'] : variable_get('views_send_to_name_' . $display, ''),
  233. );
  234. $form['to']['views_send_to_mail'] = array(
  235. '#type' => 'select',
  236. '#title' => t('Field used for recipient\'s e-mail'),
  237. '#description' => t('Select which field from the current view will be used as recipient\'s e-mail.'),
  238. '#options' => $fields_options,
  239. '#default_value' => isset($config['views_send_to_mail']) ? $config['views_send_to_mail'] : variable_get('views_send_to_mail_' . $display, ''),
  240. '#required' => TRUE,
  241. );
  242. $form['mail'] = array(
  243. '#type' => 'fieldset',
  244. '#title' => t('E-mail content'),
  245. '#collapsible' => TRUE,
  246. '#collapsed' => FALSE,
  247. );
  248. $form['mail']['views_send_subject'] = array(
  249. '#type' => 'textfield',
  250. '#title' => t('Subject'),
  251. '#description' => t('Enter the e-mail\'s subject. You can use tokens in the subject.'),
  252. '#maxlen' => 255,
  253. '#required' => TRUE,
  254. '#default_value' => isset($config['views_send_subject']) ? $config['views_send_subject'] : variable_get('views_send_subject_' . $display, ''),
  255. );
  256. $form['mail']['views_send_message'] = array(
  257. '#type' => 'text_format',
  258. '#title' => t('Message'),
  259. '#description' => t('Enter the body of the message. You can use tokens in the message.'),
  260. '#required' => TRUE,
  261. '#rows' => 10,
  262. );
  263. if (isset($config['views_send_message']['value'])) {
  264. $form['mail']['views_send_message'] += array(
  265. '#format' => $config['views_send_message']['format'],
  266. '#default_value' => $config['views_send_message']['value'],
  267. );
  268. }
  269. else {
  270. $saved_message = variable_get('views_send_message_' . $display);
  271. $form['mail']['views_send_message'] += array(
  272. '#format' => isset($saved_message['format']) ? $saved_message['format'] : filter_fallback_format(),
  273. '#default_value' => isset($saved_message['value']) ? $saved_message['value'] : '',
  274. );
  275. }
  276. $form['mail']['token'] = array(
  277. '#type' => 'fieldset',
  278. '#title' => t('Tokens'),
  279. '#description' => t('You can use the following tokens in the subject or message.'),
  280. '#collapsible' => TRUE,
  281. '#collapsed' => TRUE,
  282. );
  283. if (!module_exists('token')) {
  284. $form['mail']['token']['tokens'] = array(
  285. '#markup' => theme('views_send_token_help', $fields_name_text)
  286. );
  287. }
  288. else {
  289. $form['mail']['token']['views_send'] = array(
  290. '#type' => 'fieldset',
  291. '#title' => t('Views Send specific tokens'),
  292. '#collapsible' => TRUE,
  293. '#collapsed' => TRUE,
  294. );
  295. $form['mail']['token']['views_send']['tokens'] = array(
  296. '#markup' => theme('views_send_token_help', $fields_name_text)
  297. );
  298. $form['mail']['token']['general'] = array(
  299. '#type' => 'fieldset',
  300. '#title' => t('General tokens'),
  301. '#collapsible' => TRUE,
  302. '#collapsed' => TRUE,
  303. );
  304. $token_types = array('site', 'user', 'node', 'current-date');
  305. $form['mail']['token']['general']['tokens'] = array(
  306. '#markup' => theme('token_tree', array('token_types' => $token_types))
  307. );
  308. }
  309. if (VIEWS_SEND_MIMEMAIL && user_access('attachments with views_send')) {
  310. // set the form encoding type
  311. $form['#attributes']['enctype'] = "multipart/form-data";
  312. // add a file upload file
  313. $form['mail']['views_send_attachments'] = array(
  314. '#type' => 'file',
  315. '#title' => t('Attachment'),
  316. '#description' => t('NB! The attached file is stored once per recipient in the database if you aren\'t sending the message directly.'),
  317. );
  318. }
  319. $form['additional'] = array(
  320. '#type' => 'fieldset',
  321. '#title' => t('Additional e-mail options'),
  322. '#collapsible' => TRUE,
  323. '#collapsed' => TRUE,
  324. );
  325. $form['additional']['views_send_priority'] = array(
  326. '#type' => 'select',
  327. '#title' => t('Priority'),
  328. '#options' => array(
  329. VIEWS_SEND_PRIORITY_NONE => t('none'),
  330. VIEWS_SEND_PRIORITY_HIGHEST => t('highest'),
  331. VIEWS_SEND_PRIORITY_HIGH => t('high'),
  332. VIEWS_SEND_PRIORITY_NORMAL => t('normal'),
  333. VIEWS_SEND_PRIORITY_LOW => t('low'),
  334. VIEWS_SEND_PRIORITY_LOWEST => t('lowest')
  335. ),
  336. '#description' => t('Note that e-mail priority is ignored by a lot of e-mail programs.'),
  337. '#default_value' => isset($config['views_send_priority']) ? $config['views_send_priority'] : variable_get('views_send_priority_' . $display, 0),
  338. );
  339. $form['additional']['views_send_receipt'] = array(
  340. '#type' => 'checkbox',
  341. '#title' => t('Request receipt'),
  342. '#default_value' => isset($config['views_send_receipt']) ? $config['views_send_receipt'] : variable_get('views_send_receipt_' . $display, 0),
  343. '#description' => t('Request a Read Receipt from your e-mails. A lot of e-mail programs ignore these so it is not a definitive indication of how many people have read your message.'),
  344. );
  345. $form['additional']['views_send_headers'] = array(
  346. '#type' => 'textarea',
  347. '#title' => t('Additional headers'),
  348. '#description' => t("Additional headers to be send with the message. You'll have to enter one per line. Example:<pre>Reply-To: noreply@example.com\nX-MyCustomHeader: Whatever</pre>"),
  349. '#rows' => 4,
  350. '#default_value' => isset($config['views_send_headers']) ? $config['views_send_headers'] : variable_get('views_send_headers_' . $display, ''),
  351. );
  352. $form['views_send_direct'] = array(
  353. '#type' => 'checkbox',
  354. '#title' => t('Send the message directly using the Batch API.'),
  355. '#default_value' => isset($config['views_send_direct']) ? $config['views_send_direct'] : variable_get('views_send_direct_'. $display, TRUE),
  356. );
  357. $form['views_send_carbon_copy'] = array(
  358. '#type' => 'checkbox',
  359. '#title' => t('Send a copy of the message to the sender.'),
  360. '#default_value' => isset($config['views_send_carbon_copy']) ? $config['views_send_carbon_copy'] : variable_get('views_send_carbon_copy_' . $display, TRUE),
  361. );
  362. $form['views_send_remember'] = array(
  363. '#type' => 'checkbox',
  364. '#title' => t('Remember these values for the next time a mass mail is sent. (The values are not stored per user.)'),
  365. '#default_value' => variable_get('views_send_remember_' . $display, FALSE),
  366. );
  367. $query = drupal_get_query_parameters($_GET, array('q'));
  368. $form['actions'] = array(
  369. '#type' => 'container',
  370. '#attributes' => array('class' => array('form-actions')),
  371. '#weight' => 999,
  372. );
  373. $form['actions']['submit'] = array(
  374. '#type' => 'submit',
  375. '#value' => t('Next'),
  376. '#validate' => array('views_send_config_form_validate'),
  377. '#submit' => array('views_send_form_submit'),
  378. '#suffix' => l(t('Cancel'), $view->get_url(), array('query' => $query)),
  379. );
  380. return $form;
  381. }
  382. /**
  383. * Validation callback for the "configure" step.
  384. */
  385. function views_send_config_form_validate($form, &$form_state) {
  386. $values =& $form_state['values'];
  387. $view = $form_state['build_info']['args'][0];
  388. $formats = filter_formats();
  389. if (!filter_access($formats[$values['views_send_message']['format']])) {
  390. form_set_error('views_send_message', t('Illegale format selected'));
  391. }
  392. // Check if sender's e-mail is a valid one.
  393. if (!valid_email_address(trim($values['views_send_from_mail']))) {
  394. form_set_error('views_send_from_mail',
  395. t('The sender\'s e-mail is not a valid e-mail address: %mail',
  396. array('%mail' => $values['views_send_from_mail'])
  397. )
  398. );
  399. }
  400. // Check in the column selected as e-mail contain valid e-mail values.
  401. if (!empty($values['views_send_to_mail'])) {
  402. $wrong_addresses = array();
  403. $to_mail_field = $values['views_send_tokens'][$values['views_send_to_mail']];
  404. foreach ($form_state['selection'] as $row_id) {
  405. $mail_addresses = _views_send_get_field_value_from_views_row($view, $row_id, $to_mail_field, 'mail');
  406. foreach ($mail_addresses as $mail_address) {
  407. if (!valid_email_address($mail_address)) {
  408. $wrong_addresses[$row_id] = $mail_address;
  409. break;
  410. }
  411. }
  412. }
  413. if (count($wrong_addresses) > 0) {
  414. if (count($wrong_addresses) == count($form_state['selection'])) {
  415. $error_message = t("The field used for recipient's e-mail contains an invalid e-mail address in all selected rows. Maybe choose another field to act as recipient's e-mail?");
  416. }
  417. else {
  418. $error_message = t("The field used for recipient's e-mail contains an invalid e-mail address in @wrong of @total selected rows. Choose another field to act as recipient's e-mail or return to the view and narrow the selection to a subset containing only valid addresses. Bad addresses:",
  419. array('@wrong' => count($wrong_addresses), '@total' => count($form_state['selection']))
  420. );
  421. $error_message .= sprintf('<table><tr><th>%s</th><th>%s</th></tr>',
  422. t('Row'), t('E-mail address'));
  423. foreach ($wrong_addresses as $rowid => $wrong_address) {
  424. $error_message .= sprintf('<tr><td>%s</td><td>%s</td></tr>',
  425. $rowid, check_plain($wrong_address));
  426. }
  427. $error_message .= '</table>';
  428. }
  429. form_set_error('views_send_to_mail', $error_message);
  430. }
  431. }
  432. }
  433. /**
  434. * Multistep form callback for the "confirm" step.
  435. * Allows the user to preview the whole message before sending it.
  436. */
  437. function views_send_confirm_form($form, &$form_state, $view, $output) {
  438. drupal_set_title(t('Review and confirm the message that is about to be sent'));
  439. // Values entered in the "config" step.
  440. $configuration = $form_state['configuration'];
  441. if (!VIEWS_SEND_MIMEMAIL && ($configuration['views_send_message']['format'] != 'plain_text')) {
  442. drupal_set_message(
  443. t("Only plain text is supported in the message. Any HTML will be converted to text. If you want to format the message with HTML, you'll have to install and enable the !mimemail or !mandrill module.",
  444. array(
  445. '!mimemail' => '<a href="http://drupal.org/project/mimemail">Mime Mail</a>',
  446. '!mandrill' => '<a href="http://drupal.org/project/mandrill">Mandrill</a>'
  447. )
  448. )
  449. );
  450. }
  451. // From: parts.
  452. $from_mail = trim($configuration['views_send_from_mail']);
  453. $from_name = trim($configuration['views_send_from_name']);
  454. $form['#attributes']['class'] = array('views-send-preview');
  455. $form['from'] = array(
  456. '#type' => 'item',
  457. '#title' => t('From'),
  458. '#markup' => '<div class="views-send-preview-value">' .
  459. (empty($from_name) ? $from_mail : $from_name . check_plain(' <' . $from_mail . '>')) .
  460. '</div>',
  461. );
  462. // To: parts. (Mail is mandatory, name is optional.)
  463. $recipients = array();
  464. if (!empty($configuration['views_send_to_name'])) {
  465. $to_name_field = $configuration['views_send_tokens'][$configuration['views_send_to_name']];
  466. }
  467. else {
  468. $to_name_field = false;
  469. $to_name = '';
  470. }
  471. $to_mail_field = $configuration['views_send_tokens'][$configuration['views_send_to_mail']];
  472. foreach ($form_state['selection'] as $row_id) {
  473. if ($to_name_field) {
  474. $to_name = _views_send_get_field_value_from_views_row($view, $row_id, $to_name_field, 'plain_text');
  475. }
  476. $mail_addresses = _views_send_get_field_value_from_views_row($view, $row_id, $to_mail_field, 'mail');
  477. foreach ($mail_addresses as $mail_address) {
  478. $recipients[] = check_plain(empty($to_name) ? $mail_address : trim($to_name) . ' <' . $mail_address . '>');
  479. }
  480. }
  481. $form['to'] = array(
  482. '#type' => 'item',
  483. '#title' => t('To'),
  484. '#markup' => '<div id="views-send-preview-to" class="views-send-preview-value">' . implode(', ', $recipients) . '</div>',
  485. );
  486. $form['subject'] = array(
  487. '#type' => 'item',
  488. '#title' => t('Subject'),
  489. '#markup' => '<div class="views-send-preview-value">' . $configuration['views_send_subject'] . '</div>',
  490. );
  491. $form['message'] = array(
  492. '#type' => 'item',
  493. '#title' => t('Message'),
  494. '#markup' => '<div id="views-send-preview-message" class="views-send-preview-value">' . check_markup($configuration['views_send_message']['value'], $configuration['views_send_message']['format']) . '</div>',
  495. );
  496. $headers = array();
  497. foreach (_views_send_headers($configuration['views_send_receipt'], $configuration['views_send_priority'], $configuration['views_send_from_mail'], $configuration['views_send_headers']) as $key => $value) {
  498. $headers[] = $key . ': ' . $value;
  499. }
  500. $form['headers'] = array(
  501. '#type' => 'item',
  502. '#title' => t('Headers'),
  503. '#markup' => '<div id="views-send-preview-headers" class="views-send-preview-value">' . implode('<br />', $headers) . '</div>',
  504. );
  505. if (VIEWS_SEND_MIMEMAIL && !empty($configuration['views_send_attachments']) && user_access('attachments with views_send')) {
  506. foreach ($configuration['views_send_attachments'] as $attachment) {
  507. $attachments[] = $attachment['filename'];
  508. }
  509. $form['attachments'] = array(
  510. '#type' => 'item',
  511. '#title' => t('Attachments'),
  512. '#markup' => '<div id="views-send-preview-attachments" class="views-send-preview-value">'. implode('<br />', $attachments) .'</div>',
  513. );
  514. }
  515. $query = drupal_get_query_parameters($_GET, array('q'));
  516. $form['actions'] = array(
  517. '#type' => 'container',
  518. '#attributes' => array('class' => array('form-actions')),
  519. '#weight' => 999,
  520. );
  521. $form['actions']['back'] = array(
  522. '#type' => 'submit',
  523. '#value' => t('Go back'),
  524. '#submit' => array('views_send_form_back_submit'),
  525. );
  526. $form['actions']['submit'] = array(
  527. '#type' => 'submit',
  528. '#value' => t('Send'),
  529. '#submit' => array('views_send_form_submit'),
  530. '#suffix' => l(t('Cancel'), $view->get_url(), array('query' => $query)),
  531. );
  532. return $form;
  533. }
  534. /**
  535. * Submit handler for all steps of the Views Send multistep form.
  536. */
  537. function views_send_form_submit($form, &$form_state) {
  538. $field = _views_send_get_field_selector($form_state['build_info']['args'][0]);
  539. switch ($form_state['step']) {
  540. case 'views_form_views_form':
  541. $field_name = $field->options['id'];
  542. $selection = array_filter($form_state['values'][$field_name]);
  543. $form_state['selection'] = array_keys($selection);
  544. $form_state['step'] = 'views_send_config_form';
  545. $form_state['rebuild'] = TRUE;
  546. break;
  547. case 'views_send_config_form':
  548. $display = $form['display']['#value'];
  549. foreach ($form_state['values'] as $key => $value) {
  550. $key = ($key == 'format') ? 'views_send_message_format' : $key;
  551. if (substr($key, 0, 11) == 'views_send_') {
  552. if ($form_state['values']['views_send_remember']) {
  553. variable_set($key . '_' . $display, $value);
  554. }
  555. else {
  556. variable_del($key . '_' . $display);
  557. }
  558. }
  559. }
  560. $form_state['configuration'] = $form_state['values'];
  561. // If a file was uploaded, process it.
  562. if (VIEWS_SEND_MIMEMAIL && user_access('attachments with views_send') && isset($_FILES['files']) && is_uploaded_file($_FILES['files']['tmp_name']['views_send_attachments'])) {
  563. // attempt to save the uploaded file
  564. $dir = file_default_scheme() . '://views_send_attachments';
  565. file_prepare_directory($dir, FILE_CREATE_DIRECTORY);
  566. $file_extensions = variable_get('views_send_attachment_valid_extensions', FALSE);
  567. if ($file_extensions) {
  568. $file_validators['file_validate_extensions'] = array();
  569. $file_validators['file_validate_extensions'][0] = $file_extensions;
  570. }
  571. else {
  572. $file_validators = array();
  573. }
  574. $file = file_save_upload('views_send_attachments', $file_validators, $dir);
  575. // set error if file was not uploaded
  576. if (!$file) {
  577. //form_set_error('views_send_attachment', 'Error uploading file.');
  578. }
  579. else {
  580. // set files to form_state, to process when form is submitted
  581. // @todo: when we add a multifile formfield then loop through to add each file to attachments array
  582. $form_state['configuration']['views_send_attachments'][] = (array)$file;
  583. }
  584. }
  585. $form_state['step'] = 'views_send_confirm_form';
  586. $form_state['rebuild'] = TRUE;
  587. break;
  588. case 'views_send_confirm_form':
  589. // Queue the email for sending.
  590. views_send_queue_mail($form_state['configuration'], $form_state['selection'], $field->view);
  591. // Redirect.
  592. $query = drupal_get_query_parameters($_GET, array('q'));
  593. $form_state['redirect'] = array($field->view->get_url(), array('query' => $query));
  594. break;
  595. }
  596. }
  597. /**
  598. * Submit handler that handles back buttons.
  599. */
  600. function views_send_form_back_submit($form, &$form_state) {
  601. switch ($form_state['step']) {
  602. case 'views_send_confirm_form':
  603. $form_state['step'] = 'views_send_config_form';
  604. $form_state['rebuild'] = TRUE;
  605. break;
  606. }
  607. }
  608. /**
  609. * Assembles the email and queues it for sending.
  610. *
  611. * @param $params
  612. * Data entered in the "config" step of the form.
  613. * @param $selected_rows
  614. * An array with the indexes of the selected views rows.
  615. * @param $view
  616. * The actual view object.
  617. */
  618. function views_send_queue_mail($params, $selected_rows, $view) {
  619. global $user;
  620. if (!user_access('mass mailing with views_send')) {
  621. drupal_set_message(
  622. t('No mails sent since you aren\'t allowed to send mass mail with Views. (<a href="@permurl">Edit the permission.</a>)',
  623. array('@permurl' => url('admin/people/permissions', array('fragment' => 'module-views_send')))),
  624. 'error'
  625. );
  626. return;
  627. }
  628. $formats = filter_formats();
  629. // From: parts.
  630. $from_mail = trim($params['views_send_from_mail']);
  631. $from_name = $params['views_send_from_name'];
  632. // To: parts. (Mail is mandatory, name is optional.)
  633. $to_mail_key = $params['views_send_tokens'][$params['views_send_to_mail']];
  634. if (!empty($params['views_send_to_name'])) {
  635. $to_name_key = $params['views_send_tokens'][$params['views_send_to_name']];
  636. }
  637. else {
  638. $to_name_key = false;
  639. $to_name = '';
  640. }
  641. foreach ($selected_rows as $selected_rows_key => $row_id) {
  642. // To: parts.
  643. $to_mail = implode(',', _views_send_get_field_value_from_views_row($view, $row_id, $to_mail_key, 'mail'));
  644. if ($to_name_key) {
  645. $to_name = _views_send_get_field_value_from_views_row($view, $row_id, $to_name_key, 'plain_text');
  646. }
  647. $subject = $params['views_send_subject'];
  648. $body = $params['views_send_message']['value'];
  649. $params['format'] = $params['views_send_message']['format'];
  650. // This shouldn't happen, but better be 100% sure.
  651. if (!filter_access($formats[$params['format']])) {
  652. drupal_set_message(t('No mails sent since an illegale format is selected for the message.'));
  653. return;
  654. }
  655. $body = check_markup($body, $params['format']);
  656. // Populate row/context tokens.
  657. $token_keys = $token_values = array();
  658. foreach ($params['views_send_tokens'] as $field_key => $field_name) {
  659. $token_keys[] = VIEWS_SEND_TOKEN_PREFIX . sprintf(VIEWS_SEND_TOKEN_PATTERN, $field_name) . VIEWS_SEND_TOKEN_POSTFIX;
  660. $token_values[] = _views_send_get_field_value_from_views_row($view, $row_id, $field_name);
  661. }
  662. // Views Send specific token replacements
  663. $subject = str_replace($token_keys, $token_values, $subject);
  664. $body = str_replace($token_keys, $token_values, $body);
  665. // Global token replacement, and node/user token replacements
  666. // if a nid/uid is found in the views result row.
  667. $data = array();
  668. if (property_exists($view->result[$row_id], 'uid')) {
  669. $data['user'] = user_load($view->result[$row_id]->uid);
  670. }
  671. if (property_exists($view->result[$row_id], 'nid')) {
  672. $data['node'] = node_load($view->result[$row_id]->nid);
  673. }
  674. $subject = token_replace($subject, $data);
  675. $body = token_replace($body, $data);
  676. if (!VIEWS_SEND_MIMEMAIL || (variable_get('mimemail_format', 'plain_text') == 'plain_text')) {
  677. $body = drupal_html_to_text($body);
  678. }
  679. if ($params['format'] == 'plain_text') {
  680. $plain_format = TRUE;
  681. }
  682. else {
  683. $plain_format = FALSE;
  684. }
  685. // We transform receipt, priority in headers,
  686. // merging them to the user defined headers.
  687. $headers = _views_send_headers($params['views_send_receipt'], $params['views_send_priority'], $from_mail, $params['views_send_headers']);
  688. $attachments = isset($params['views_send_attachments']) ? $params['views_send_attachments'] : array();
  689. $message = array(
  690. 'uid' => $user->uid,
  691. 'timestamp' => time(),
  692. 'from_name' => trim($from_name),
  693. 'from_mail' => trim($from_mail),
  694. 'to_name' => trim($to_name),
  695. 'to_mail' => trim($to_mail),
  696. 'subject' => strip_tags($subject),
  697. 'body' => $body,
  698. 'headers' => $headers,
  699. );
  700. // Enable other modules to alter the actual message before queueing it
  701. // by providing the hook 'views_send_mail_alter'.
  702. drupal_alter('views_send_mail', $message);
  703. if ($params['views_send_direct']) {
  704. $operations[] = array('views_send_batch_deliver', array($message, $plain_format, $attachments));
  705. }
  706. else {
  707. _views_send_prepare_mail($message, $plain_format, $attachments);
  708. // Only queue the message if it hasn't been cancelled by another module.
  709. if ($message['send']) {
  710. unset($message['send']);
  711. db_insert('views_send_spool')->fields($message)->execute();
  712. if (module_exists('rules')) {
  713. rules_invoke_event('views_send_email_added_to_spool', $message);
  714. }
  715. // Enabled other modules to act just after a message is queued
  716. // by providing the hook 'views_send_mail_queued'.
  717. module_invoke_all('views_send_mail_queued', $message, $view, $row_id);
  718. }
  719. else {
  720. unset($selected_rows[$selected_rows_key]);
  721. }
  722. }
  723. }
  724. if ($params['views_send_direct']) {
  725. if ($params['views_send_carbon_copy']) {
  726. $message['to_name'] = $from_name;
  727. $message['to_mail'] = $from_mail;
  728. $operations[] = array('views_send_batch_deliver', array($message, $plain_format, $attachments));
  729. }
  730. $batch = array(
  731. 'operations' => $operations,
  732. 'finished' => 'views_send_batch_deliver_finished',
  733. 'progress_message' => t('Sent @current of @total messages.'),
  734. );
  735. batch_set($batch);
  736. drupal_set_message(
  737. format_plural(count($selected_rows), '1 message processed.', '@count messages processed.')
  738. );
  739. }
  740. else {
  741. if ($params['views_send_carbon_copy']) {
  742. $message['to_name'] = $from_name;
  743. $message['to_mail'] = $from_mail;
  744. db_insert('views_send_spool')->fields($message)->execute();
  745. }
  746. drupal_set_message(
  747. format_plural(count($selected_rows), '1 message added to the spool.', '@count messages added to the spool.')
  748. );
  749. if (module_exists('rules')) {
  750. rules_invoke_event('views_send_all_email_added_to_spool', count($selected_rows));
  751. }
  752. }
  753. }
  754. // === Hook implementations ====================================================
  755. /**
  756. * Implements hook_menu().
  757. */
  758. function views_send_menu() {
  759. $items = array();
  760. $items['admin/config/system/views_send'] = array(
  761. 'type' => MENU_NORMAL_ITEM,
  762. 'title' => 'Views Send',
  763. 'description' => 'Configure Views Send general options.',
  764. 'page callback' => 'drupal_get_form',
  765. 'page arguments' => array('views_send_settings'),
  766. 'access arguments' => array('administer views_send'),
  767. 'file' => 'views_send.admin.inc',
  768. );
  769. return $items;
  770. }
  771. /**
  772. * Implements hook_permission().
  773. */
  774. function views_send_permission() {
  775. $perms = array(
  776. 'administer views_send' => array(
  777. 'title' => t('Administer mass mail with Views'),
  778. 'description' => t('Configure sending of e-mails to a list created with Views.'),
  779. ),
  780. 'mass mailing with views_send' => array(
  781. 'title' => t('Send mass mail with Views'),
  782. 'description' => t('Send e-mails to a list created with Views.'),
  783. ),
  784. );
  785. if (VIEWS_SEND_MIMEMAIL) {
  786. $perms['attachments with views_send'] = array(
  787. 'title' => t('Use attachments with Views Send'),
  788. 'description' => t('Attach files to e-mails sent with Views Send.'),
  789. );
  790. }
  791. return $perms;
  792. }
  793. /**
  794. * Implements hook_theme().
  795. */
  796. function views_send_theme($existing, $type, $theme, $path) {
  797. return array(
  798. 'views_send_select_all' => array(
  799. 'variables' => array(),
  800. ),
  801. 'views_send_token_help' => array(
  802. 'arguments' => array('tokens' => array()),
  803. ),
  804. );
  805. }
  806. /**
  807. * Implements hook_views_api().
  808. */
  809. function views_send_views_api() {
  810. return array(
  811. 'api' => 3,
  812. 'path' => drupal_get_path('module', 'views_send') . '/views',
  813. );
  814. }
  815. /**
  816. * Implements hook_cron().
  817. */
  818. function views_send_cron() {
  819. // Load cron functions.
  820. module_load_include('cron.inc', 'views_send');
  821. // Send pending messages from spool.
  822. views_send_send_from_spool();
  823. // Clear successful sent messages.
  824. views_send_clear_spool();
  825. }
  826. /**
  827. * Implements hook_mail().
  828. */
  829. function views_send_mail($key, &$message, $params) {
  830. // This is a simple message send. User inputs the content directly.
  831. if ($key == 'direct') {
  832. // Set the subject.
  833. $message['subject'] = $params['subject'];
  834. // Set the body.
  835. $message['body'][] = $params['body'];
  836. // Add additional headers.
  837. $message['headers'] += $params['headers'];
  838. }
  839. // TODO: Implement node message parsing.
  840. elseif ($key == 'node') {
  841. // Translations, theming, etc...
  842. }
  843. }
  844. // === Helper functions ========================================================
  845. /**
  846. * Build header array with priority and receipt confirmation settings.
  847. *
  848. * @param $receipt
  849. * Boolean: If a receipt is requested.
  850. * @param $priority
  851. * Integer: The message priority.
  852. * @param $from
  853. * String: The sender's e-mail address.
  854. *
  855. * @return Header array with priority and receipt confirmation info
  856. */
  857. function _views_send_headers($receipt, $priority, $from, $additional_headers) {
  858. $headers = array();
  859. // If receipt is requested, add headers.
  860. if ($receipt) {
  861. $headers['Disposition-Notification-To'] = $from;
  862. $headers['X-Confirm-Reading-To'] = $from;
  863. }
  864. // Add priority if set.
  865. switch ($priority) {
  866. case VIEWS_SEND_PRIORITY_HIGHEST:
  867. $headers['Priority'] = 'High';
  868. $headers['X-Priority'] = '1';
  869. $headers['X-MSMail-Priority'] = 'Highest';
  870. break;
  871. case VIEWS_SEND_PRIORITY_HIGH:
  872. $headers['Priority'] = 'urgent';
  873. $headers['X-Priority'] = '2';
  874. $headers['X-MSMail-Priority'] = 'High';
  875. break;
  876. case VIEWS_SEND_PRIORITY_NORMAL:
  877. $headers['Priority'] = 'normal';
  878. $headers['X-Priority'] = '3';
  879. $headers['X-MSMail-Priority'] = 'Normal';
  880. break;
  881. case VIEWS_SEND_PRIORITY_LOW:
  882. $headers['Priority'] = 'non-urgent';
  883. $headers['X-Priority'] = '4';
  884. $headers['X-MSMail-Priority'] = 'Low';
  885. break;
  886. case VIEWS_SEND_PRIORITY_LOWEST:
  887. $headers['Priority'] = 'non-urgent';
  888. $headers['X-Priority'] = '5';
  889. $headers['X-MSMail-Priority'] = 'Lowest';
  890. break;
  891. }
  892. // Add general headers.
  893. $headers['Precedence'] = 'bulk';
  894. // Add additional headers.
  895. $additional_headers = trim($additional_headers);
  896. $additional_headers = str_replace("\r", "\n", $additional_headers);
  897. $additional_headers = explode("\n", $additional_headers);
  898. foreach ($additional_headers as $header) {
  899. $header = trim($header);
  900. if (!empty($header)) {
  901. list($key, $value) = explode(': ', $header, 2);
  902. $headers[$key] = trim($value);
  903. }
  904. }
  905. return $headers;
  906. }
  907. /**
  908. * Build a formatted e-mail address.
  909. */
  910. function _views_send_format_address($mail, $name, $encode = TRUE) {
  911. // Do not format addres on Windows based PHP systems or when $name is empty.
  912. if ((substr(PHP_OS, 0, 3) == 'WIN') || empty($name)) {
  913. return $mail;
  914. }
  915. else {
  916. $name = ($encode ? _views_send_mime_header_encode($name) : $name);
  917. return sprintf('"%s" <%s>', $name, $mail);
  918. }
  919. }
  920. /**
  921. * Returns a mime-encoded string for strings that contain UTF-8.
  922. *
  923. * Simplified and correct version of mime_header_decode.
  924. */
  925. function _views_send_mime_header_encode($string) {
  926. if (preg_match('/[^\x20-\x7E]/', $string)) {
  927. $string = '=?UTF-8?B?' . base64_encode($string) . '?=';
  928. }
  929. return $string;
  930. }
  931. /**
  932. * Prepare the mail message before sending or spooling.
  933. *
  934. * @param array $message
  935. * which contains the following keys:
  936. * from_name
  937. * String holding the Sender's name.
  938. * from_mail
  939. * String holding the Sender's e-mail.
  940. * to_name
  941. * String holding the Recipient's name.
  942. * to_mail
  943. * String holding the Recipient's e-mail.
  944. * subject
  945. * String with the e-mail subject. This argument can be altered here.
  946. * body
  947. * Text with the e-mail body. This argument can be altered here.
  948. * headers
  949. * Associative array with e-mail headers. This argument can be altered here.
  950. * @param boolean $plain_format
  951. * Whether the e-mail should be sent in plain format.
  952. * @param array $attachments
  953. * An array with file information objects (as returned by file_save_upload).
  954. */
  955. function _views_send_prepare_mail(&$message, $plain_format=TRUE, $attachments=array()) {
  956. // Extract all variables/keys from the message.
  957. extract($message);
  958. /**
  959. * TODO: In the future, this module will be able to send an existing node.
  960. * $key will have to make the difference. A value when we pickup a node, other
  961. * when user inputs the subject & body of the message.
  962. */
  963. $key = 'direct';
  964. // Build message parameters.
  965. $params = array();
  966. $params['from_name'] = $from_name;
  967. $params['from_mail'] = $from_mail;
  968. $params['from_formatted'] = _views_send_format_address($from_mail, $from_name);
  969. $params['to_name'] = $to_name;
  970. $params['to_mail'] = $to_mail;
  971. $to_mail_formatted = array();
  972. foreach (explode(',', $to_mail) as $addr) {
  973. $to_mail_formatted[] = _views_send_format_address($addr, $to_name);
  974. }
  975. $params['to_formatted'] = implode(', ', $to_mail_formatted);
  976. $params['subject'] = $subject;
  977. $params['body'] = $body;
  978. $params['headers'] = $headers;
  979. if (VIEWS_SEND_MIMEMAIL) {
  980. _views_send_mailsystem_set($key);
  981. $params['attachments'] = $attachments;
  982. if ($plain_format) {
  983. $params['plain'] = TRUE;
  984. }
  985. }
  986. // Call Drupal standard mail function, but without sending.
  987. $mail = drupal_mail('views_send', $key, $params['to_formatted'], language_default(), $params, $params['from_formatted'], FALSE);
  988. // Add additional Mime Mail post processing.
  989. if (VIEWS_SEND_MIMEMAIL) {
  990. // We want to spool the Subject decoded.
  991. $mail['subject'] = mime_header_decode($mail['subject']);
  992. }
  993. // Updating message with data from generated mail
  994. $message['to_mail'] = $mail['to'];
  995. $message['from_mail'] = $mail['from'];
  996. $message['subject'] = $mail['subject'];
  997. $message['body'] = $mail['body'];
  998. $message['send'] = $mail['send'];
  999. $message['headers'] = serialize($mail['headers']);
  1000. }
  1001. /**
  1002. * Sending a prepared message.
  1003. *
  1004. * @return
  1005. * Boolean indicating if the message was sent successfully.
  1006. */
  1007. function views_send_deliver($message) {
  1008. if (is_array($message)) {
  1009. $message = (object) $message;
  1010. }
  1011. $key = 'direct';
  1012. $headers = unserialize($message->headers);
  1013. $mail = array(
  1014. 'id' => 'views_send_' . $key,
  1015. 'module' => 'views_send',
  1016. 'key' => $key,
  1017. 'to' => $message->to_mail,
  1018. 'from' => $message->from_mail,
  1019. 'subject' => $message->subject,
  1020. 'body' => $message->body,
  1021. 'headers' => $headers,
  1022. );
  1023. if (VIEWS_SEND_MIMEMAIL) {
  1024. _views_send_mailsystem_set($key);
  1025. }
  1026. // Mime encode the subject before passing to the mail function
  1027. // to work around a bug in Drupal's mime_header_encode.
  1028. $mail['subject'] = _views_send_mime_header_encode($message->subject);
  1029. $system = drupal_mail_system('views_send', $key);
  1030. return $system->mail($mail);
  1031. }
  1032. /**
  1033. * Preparing and sending a message (coming from a batch job).
  1034. */
  1035. function views_send_batch_deliver($message, $plain_format, $attachments, &$context) {
  1036. _views_send_prepare_mail($message, $plain_format, $attachments);
  1037. if (!$message['send']) {
  1038. $context['results'][] = t('Skipping sending message to %mail.',
  1039. array('%mail' => $message['to_mail']));
  1040. return;
  1041. }
  1042. else {
  1043. unset($message['send']);
  1044. }
  1045. $status = views_send_deliver($message);
  1046. if ($status) {
  1047. if (variable_get('views_send_debug', FALSE)) {
  1048. watchdog('views_send', 'Message sent to %mail.', array('%mail' => $message['to_mail']));
  1049. }
  1050. if (module_exists('rules')) {
  1051. rules_invoke_event('views_send_email_sent', $message);
  1052. }
  1053. }
  1054. else {
  1055. $context['results'][] = t('Failed sending message to %mail - spooling it.',
  1056. array('%mail' => $message['to_mail']));
  1057. // Queue the message to the spool table.
  1058. db_insert('views_send_spool')->fields($message)->execute();
  1059. if (module_exists('rules')) {
  1060. rules_invoke_event('views_send_email_added_to_spool', $message);
  1061. }
  1062. }
  1063. }
  1064. /**
  1065. * Displays status after sending messages as a batch job.
  1066. */
  1067. function views_send_batch_deliver_finished($success, $results, $operations) {
  1068. if ($success) {
  1069. foreach ($results as $result) {
  1070. drupal_set_message($result);
  1071. }
  1072. }
  1073. }
  1074. // === Theming functions =======================================================
  1075. /**
  1076. * Theme the replacement tokens.
  1077. *
  1078. * @param $tokens:
  1079. * Keyed array with tokens as keys and description as values.
  1080. *
  1081. * @return
  1082. * A themed table wirh all tokens.
  1083. *
  1084. * @todo: Add help for other tokens
  1085. */
  1086. function theme_views_send_token_help($fields) {
  1087. $header = array(t('Token'), t('Replacement value'));
  1088. $rows = array();
  1089. foreach ($fields as $field => $title) {
  1090. $rows[] = array(VIEWS_SEND_TOKEN_PREFIX . sprintf(VIEWS_SEND_TOKEN_PATTERN, $field) . VIEWS_SEND_TOKEN_POSTFIX, $title);
  1091. }
  1092. $output = theme('table', array('header' => $header, 'rows' => $rows));
  1093. return $output;
  1094. }
  1095. if (module_exists('token')) {
  1096. /**
  1097. * Implements hook_token_info().
  1098. *
  1099. * These token are used by Rules and not in the Views form.
  1100. */
  1101. function views_send_token_info() {
  1102. $data = array();
  1103. foreach (_views_send_email_message_property_info() as $key => $info) {
  1104. $data[$key] = array(
  1105. 'name' => $info['label'],
  1106. 'description' => ''
  1107. );
  1108. }
  1109. $type = array(
  1110. 'name' => t('Views Send e-mail message'),
  1111. 'description' => t('Tokens for Views Send e-mail message.'),
  1112. 'needs-data' => 'views_send_email_message',
  1113. );
  1114. return array(
  1115. 'types' => array('views_send_email_message' => $type),
  1116. 'tokens' => array('views_send_email_message' => $data),
  1117. );
  1118. }
  1119. /**
  1120. * Implementation hook_tokens().
  1121. *
  1122. * These token replacements are used by Rules and not in the Views form.
  1123. */
  1124. function views_send_tokens($type, $tokens, array $data = array(), array $options = array()) {
  1125. $replacements = array();
  1126. if ($type == 'views_send_email_message' && !empty($data['views_send_email_message'])) {
  1127. foreach ($tokens as $name => $original) {
  1128. $replacements[$original] = $data['views_send_email_message']->{$name};
  1129. }
  1130. }
  1131. return $replacements;
  1132. }
  1133. }
  1134. /**
  1135. * Generates and returns fields and tokens.
  1136. */
  1137. function _views_send_get_fields_and_tokens($view, $type) {
  1138. static $return;
  1139. if (isset($return[$type])) {
  1140. return $return[$type];
  1141. }
  1142. if (!in_array($type, array('fields', 'tokens', 'fields_name_text')) || !$view) {
  1143. return FALSE;
  1144. }
  1145. $fields = array();
  1146. $tokens = array();
  1147. $fields_name_text = array();
  1148. foreach ($view->field as $field_name => $field) {
  1149. // Ignore Views Form fields.
  1150. if (property_exists($field, 'views_form_callback') || method_exists($field, 'views_form')) {
  1151. continue;
  1152. }
  1153. if ($field instanceof views_handler_field_custom) {
  1154. $field_key = $field_name;
  1155. // Using a nice field name (for tokens) for custom text fields.
  1156. $field_name = str_replace('nothing', 'custom_text', $field_name);;
  1157. }
  1158. elseif (!empty($field->field_info)) {
  1159. $field_key = $field->field_info['field_name'];
  1160. }
  1161. elseif (property_exists($field, 'field_alias')) {
  1162. $field_key = $field->field_alias;
  1163. if ($field_key == 'unknown') {
  1164. $field_key = $field_name;
  1165. }
  1166. }
  1167. else {
  1168. $field_key = $field_name;
  1169. }
  1170. // Add field position to ensure unique keys.
  1171. $field_key .= '_pos_' . $field->position;
  1172. $field_text = $field->label() . ' (' . $field_name . ')';
  1173. $fields[$field_key] = $field_text;
  1174. $tokens[$field_key] = $field_name;
  1175. $fields_name_text[$field_name] = $field_text;
  1176. }
  1177. $return = array();
  1178. $return['fields'] = $fields;
  1179. $return['tokens'] = $tokens;
  1180. $return['fields_name_text'] = $fields_name_text;
  1181. return $return[$type];
  1182. }
  1183. /**
  1184. * Returns property info for Views Send Email Message
  1185. */
  1186. function _views_send_email_message_property_info() {
  1187. $propertyinfo = array(
  1188. 'uid' => array(
  1189. 'type' => 'integer',
  1190. 'label' => t('User ID'),
  1191. ),
  1192. 'timestamp' => array(
  1193. 'type' => 'integer',
  1194. 'label' => t('Timestamp'),
  1195. ),
  1196. 'from_name' => array(
  1197. 'type' => 'text',
  1198. 'label' => t('Sender\'s name'),
  1199. ),
  1200. 'from_mail' => array(
  1201. 'type' => 'text',
  1202. 'label' => t('Sender\'s e-mail'),
  1203. ),
  1204. 'to_name' => array(
  1205. 'type' => 'text',
  1206. 'label' => t('Recipient\'s name'),
  1207. ),
  1208. 'to_mail' => array(
  1209. 'type' => 'text',
  1210. 'label' => t('Recipient\'s e-mail'),
  1211. ),
  1212. 'subject' => array(
  1213. 'type' => 'text',
  1214. 'label' => t('E-mail subject'),
  1215. ),
  1216. 'body' => array(
  1217. 'type' => 'text',
  1218. 'label' => t('E-mail body'),
  1219. ),
  1220. 'headers' => array(
  1221. 'type' => 'text',
  1222. 'label' => t('E-mail headers (serialized)'),
  1223. ),
  1224. );
  1225. return $propertyinfo;
  1226. }