ManageMail.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. <?php
  2. /**
  3. * This file is all about mail, how we love it so. In particular it handles the admin side of
  4. * mail configuration, as well as reviewing the mail queue - if enabled.
  5. * @todo refactor as controller-model.
  6. *
  7. * Simple Machines Forum (SMF)
  8. *
  9. * @package SMF
  10. * @author Simple Machines http://www.simplemachines.org
  11. * @copyright 2013 Simple Machines and individual contributors
  12. * @license http://www.simplemachines.org/about/smf/license.php BSD
  13. *
  14. * @version 2.1 Alpha 1
  15. */
  16. if (!defined('SMF'))
  17. die('No direct access...');
  18. /**
  19. * Main dispatcher. This function checks permissions and passes control through to the relevant section.
  20. */
  21. function ManageMail()
  22. {
  23. global $context, $txt, $scripturl, $modSettings, $sourcedir;
  24. // You need to be an admin to edit settings!
  25. isAllowedTo('admin_forum');
  26. loadLanguage('Help');
  27. loadLanguage('ManageMail');
  28. // We'll need the utility functions from here.
  29. require_once($sourcedir . '/ManageServer.php');
  30. $context['page_title'] = $txt['mailqueue_title'];
  31. $context['sub_template'] = 'show_settings';
  32. $subActions = array(
  33. 'browse' => 'BrowseMailQueue',
  34. 'clear' => 'ClearMailQueue',
  35. 'settings' => 'ModifyMailSettings',
  36. );
  37. call_integration_hook('integrate_manage_mail', array(&$subActions));
  38. // By default we want to browse
  39. $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'browse';
  40. $context['sub_action'] = $_REQUEST['sa'];
  41. // Load up all the tabs...
  42. $context[$context['admin_menu_name']]['tab_data'] = array(
  43. 'title' => $txt['mailqueue_title'],
  44. 'help' => '',
  45. 'description' => $txt['mailqueue_desc'],
  46. );
  47. // Call the right function for this sub-action.
  48. $subActions[$_REQUEST['sa']]();
  49. }
  50. /**
  51. * Display the mail queue...
  52. */
  53. function BrowseMailQueue()
  54. {
  55. global $scripturl, $context, $modSettings, $txt, $smcFunc;
  56. global $sourcedir;
  57. // First, are we deleting something from the queue?
  58. if (isset($_REQUEST['delete']))
  59. {
  60. checkSession('post');
  61. $smcFunc['db_query']('', '
  62. DELETE FROM {db_prefix}mail_queue
  63. WHERE id_mail IN ({array_int:mail_ids})',
  64. array(
  65. 'mail_ids' => $_REQUEST['delete'],
  66. )
  67. );
  68. }
  69. // How many items do we have?
  70. $request = $smcFunc['db_query']('', '
  71. SELECT COUNT(*) AS queue_size, MIN(time_sent) AS oldest
  72. FROM {db_prefix}mail_queue',
  73. array(
  74. )
  75. );
  76. list ($mailQueueSize, $mailOldest) = $smcFunc['db_fetch_row']($request);
  77. $smcFunc['db_free_result']($request);
  78. $context['oldest_mail'] = empty($mailOldest) ? $txt['mailqueue_oldest_not_available'] : time_since(time() - $mailOldest);
  79. $context['mail_queue_size'] = comma_format($mailQueueSize);
  80. $listOptions = array(
  81. 'id' => 'mail_queue',
  82. 'title' => $txt['mailqueue_browse'],
  83. 'items_per_page' => 20,
  84. 'base_href' => $scripturl . '?action=admin;area=mailqueue',
  85. 'default_sort_col' => 'age',
  86. 'no_items_label' => $txt['mailqueue_no_items'],
  87. 'get_items' => array(
  88. 'function' => 'list_getMailQueue',
  89. ),
  90. 'get_count' => array(
  91. 'function' => 'list_getMailQueueSize',
  92. ),
  93. 'columns' => array(
  94. 'subject' => array(
  95. 'header' => array(
  96. 'value' => $txt['mailqueue_subject'],
  97. ),
  98. 'data' => array(
  99. 'function' => create_function('$rowData', '
  100. global $smcFunc;
  101. return $smcFunc[\'strlen\']($rowData[\'subject\']) > 50 ? sprintf(\'%1$s...\', $smcFunc[\'htmlspecialchars\']($smcFunc[\'substr\']($rowData[\'subject\'], 0, 47))) : $smcFunc[\'htmlspecialchars\']($rowData[\'subject\']);
  102. '),
  103. 'class' => 'smalltext',
  104. ),
  105. 'sort' => array(
  106. 'default' => 'subject',
  107. 'reverse' => 'subject DESC',
  108. ),
  109. ),
  110. 'recipient' => array(
  111. 'header' => array(
  112. 'value' => $txt['mailqueue_recipient'],
  113. ),
  114. 'data' => array(
  115. 'sprintf' => array(
  116. 'format' => '<a href="mailto:%1$s">%1$s</a>',
  117. 'params' => array(
  118. 'recipient' => true,
  119. ),
  120. ),
  121. 'class' => 'smalltext',
  122. ),
  123. 'sort' => array(
  124. 'default' => 'recipient',
  125. 'reverse' => 'recipient DESC',
  126. ),
  127. ),
  128. 'priority' => array(
  129. 'header' => array(
  130. 'value' => $txt['mailqueue_priority'],
  131. ),
  132. 'data' => array(
  133. 'function' => create_function('$rowData', '
  134. global $txt;
  135. // We probably have a text label with your priority.
  136. $txtKey = sprintf(\'mq_mpriority_%1$s\', $rowData[\'priority\']);
  137. // But if not, revert to priority 0.
  138. return isset($txt[$txtKey]) ? $txt[$txtKey] : $txt[\'mq_mpriority_1\'];
  139. '),
  140. 'class' => 'smalltext',
  141. ),
  142. 'sort' => array(
  143. 'default' => 'priority',
  144. 'reverse' => 'priority DESC',
  145. ),
  146. ),
  147. 'age' => array(
  148. 'header' => array(
  149. 'value' => $txt['mailqueue_age'],
  150. ),
  151. 'data' => array(
  152. 'function' => create_function('$rowData', '
  153. return time_since(time() - $rowData[\'time_sent\']);
  154. '),
  155. 'class' => 'smalltext',
  156. ),
  157. 'sort' => array(
  158. 'default' => 'time_sent',
  159. 'reverse' => 'time_sent DESC',
  160. ),
  161. ),
  162. 'check' => array(
  163. 'header' => array(
  164. 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
  165. ),
  166. 'data' => array(
  167. 'function' => create_function('$rowData', '
  168. return \'<input type="checkbox" name="delete[]" value="\' . $rowData[\'id_mail\'] . \'" class="input_check" />\';
  169. '),
  170. 'class' => 'smalltext',
  171. ),
  172. ),
  173. ),
  174. 'form' => array(
  175. 'href' => $scripturl . '?action=admin;area=mailqueue',
  176. 'include_start' => true,
  177. 'include_sort' => true,
  178. ),
  179. 'additional_rows' => array(
  180. array(
  181. 'position' => 'bottom_of_list',
  182. 'value' => '<input type="submit" name="delete_redirects" value="' . $txt['quickmod_delete_selected'] . '" onclick="return confirm(\'' . $txt['quickmod_confirm'] . '\');" class="button_submit" /><a class="button_link" href="' . $scripturl . '?action=admin;area=mailqueue;sa=clear;' . $context['session_var'] . '=' . $context['session_id'] . '" onclick="return confirm(\'' . $txt['mailqueue_clear_list_warning'] . '\');">' . $txt['mailqueue_clear_list'] . '</a> ',
  183. ),
  184. ),
  185. );
  186. require_once($sourcedir . '/Subs-List.php');
  187. createList($listOptions);
  188. loadTemplate('ManageMail');
  189. $context['sub_template'] = 'browse';
  190. }
  191. /**
  192. * This function grabs the mail queue items from the database, according to the params given.
  193. *
  194. * @param int $start
  195. * @param int $items_per_page
  196. * @param string $sort
  197. * @return array
  198. */
  199. function list_getMailQueue($start, $items_per_page, $sort)
  200. {
  201. global $smcFunc, $txt;
  202. $request = $smcFunc['db_query']('', '
  203. SELECT
  204. id_mail, time_sent, recipient, priority, private, subject
  205. FROM {db_prefix}mail_queue
  206. ORDER BY {raw:sort}
  207. LIMIT {int:start}, {int:items_per_page}',
  208. array(
  209. 'start' => $start,
  210. 'sort' => $sort,
  211. 'items_per_page' => $items_per_page,
  212. )
  213. );
  214. $mails = array();
  215. while ($row = $smcFunc['db_fetch_assoc']($request))
  216. {
  217. // Private PM/email subjects and similar shouldn't be shown in the mailbox area.
  218. if (!empty($row['private']))
  219. $row['subject'] = $txt['personal_message'];
  220. $mails[] = $row;
  221. }
  222. $smcFunc['db_free_result']($request);
  223. return $mails;
  224. }
  225. /**
  226. * Returns the total count of items in the mail queue.
  227. * @return int
  228. */
  229. function list_getMailQueueSize()
  230. {
  231. global $smcFunc;
  232. // How many items do we have?
  233. $request = $smcFunc['db_query']('', '
  234. SELECT COUNT(*) AS queue_size
  235. FROM {db_prefix}mail_queue',
  236. array(
  237. )
  238. );
  239. list ($mailQueueSize) = $smcFunc['db_fetch_row']($request);
  240. $smcFunc['db_free_result']($request);
  241. return $mailQueueSize;
  242. }
  243. /**
  244. * Allows to view and modify the mail settings.
  245. *
  246. * @param bool $return_config = false
  247. * @return array
  248. */
  249. function ModifyMailSettings($return_config = false)
  250. {
  251. global $txt, $scripturl, $context, $settings, $modSettings, $txtBirthdayEmails;
  252. loadLanguage('EmailTemplates');
  253. $body = $txtBirthdayEmails[(empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']) . '_body'];
  254. $subject = $txtBirthdayEmails[(empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']) . '_subject'];
  255. $emails = array();
  256. $processedBirthdayEmails = array();
  257. foreach ($txtBirthdayEmails as $key => $value)
  258. {
  259. $index = substr($key, 0, strrpos($key, '_'));
  260. $element = substr($key, strrpos($key, '_') + 1);
  261. $processedBirthdayEmails[$index][$element] = $value;
  262. }
  263. foreach ($processedBirthdayEmails as $index => $dummy)
  264. $emails[$index] = $index;
  265. $config_vars = array(
  266. // Mail queue stuff, this rocks ;)
  267. array('check', 'mail_queue'),
  268. array('int', 'mail_limit'),
  269. array('int', 'mail_quantity'),
  270. '',
  271. // SMTP stuff.
  272. array('select', 'mail_type', array($txt['mail_type_default'], 'SMTP')),
  273. array('text', 'smtp_host'),
  274. array('text', 'smtp_port'),
  275. array('text', 'smtp_username'),
  276. array('password', 'smtp_password'),
  277. '',
  278. array('select', 'birthday_email', $emails, 'value' => array('subject' => $subject, 'body' => $body), 'javascript' => 'onchange="fetch_birthday_preview()"'),
  279. 'birthday_subject' => array('var_message', 'birthday_subject', 'var_message' => $processedBirthdayEmails[empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']]['subject'], 'disabled' => true, 'size' => strlen($subject) + 3),
  280. 'birthday_body' => array('var_message', 'birthday_body', 'var_message' => nl2br($body), 'disabled' => true, 'size' => ceil(strlen($body) / 25)),
  281. );
  282. call_integration_hook('integrate_modify_mail_settings', array(&$config_vars));
  283. if ($return_config)
  284. return $config_vars;
  285. // Saving?
  286. if (isset($_GET['save']))
  287. {
  288. // Make the SMTP password a little harder to see in a backup etc.
  289. if (!empty($_POST['smtp_password'][1]))
  290. {
  291. $_POST['smtp_password'][0] = base64_encode($_POST['smtp_password'][0]);
  292. $_POST['smtp_password'][1] = base64_encode($_POST['smtp_password'][1]);
  293. }
  294. checkSession();
  295. // We don't want to save the subject and body previews.
  296. unset($config_vars['birthday_subject'], $config_vars['birthday_body']);
  297. call_integration_hook('integrate_save_mail_settings');
  298. saveDBSettings($config_vars);
  299. redirectexit('action=admin;area=mailqueue;sa=settings');
  300. }
  301. $context['post_url'] = $scripturl . '?action=admin;area=mailqueue;save;sa=settings';
  302. $context['settings_title'] = $txt['mailqueue_settings'];
  303. prepareDBSettingContext($config_vars);
  304. $context['settings_insert_above'] = '
  305. <script type="text/javascript"><!-- // --><![CDATA[
  306. var bDay = {';
  307. $i = 0;
  308. foreach ($processedBirthdayEmails as $index => $email)
  309. {
  310. $is_last = ++$i == count($processedBirthdayEmails);
  311. $context['settings_insert_above'] .= '
  312. ' . $index . ': {
  313. subject: ' . JavaScriptEscape($email['subject']) . ',
  314. body: ' . JavaScriptEscape(nl2br($email['body'])) . '
  315. }' . (!$is_last ? ',' : '');
  316. }
  317. $context['settings_insert_above'] .= '
  318. };
  319. function fetch_birthday_preview()
  320. {
  321. var index = document.getElementById(\'birthday_email\').value;
  322. document.getElementById(\'birthday_subject\').innerHTML = bDay[index].subject;
  323. document.getElementById(\'birthday_body\').innerHTML = bDay[index].body;
  324. }
  325. // ]]></script>';
  326. }
  327. /**
  328. * This function clears the mail queue of all emails, and at the end redirects to browse.
  329. */
  330. function ClearMailQueue()
  331. {
  332. global $sourcedir, $smcFunc;
  333. checkSession('get');
  334. // This is certainly needed!
  335. require_once($sourcedir . '/ScheduledTasks.php');
  336. // If we don't yet have the total to clear, find it.
  337. if (!isset($_GET['te']))
  338. {
  339. // How many items do we have?
  340. $request = $smcFunc['db_query']('', '
  341. SELECT COUNT(*) AS queue_size
  342. FROM {db_prefix}mail_queue',
  343. array(
  344. )
  345. );
  346. list ($_GET['te']) = $smcFunc['db_fetch_row']($request);
  347. $smcFunc['db_free_result']($request);
  348. }
  349. else
  350. $_GET['te'] = (int) $_GET['te'];
  351. $_GET['sent'] = isset($_GET['sent']) ? (int) $_GET['sent'] : 0;
  352. // Send 50 at a time, then go for a break...
  353. while (ReduceMailQueue(50, true, true) === true)
  354. {
  355. // Sent another 50.
  356. $_GET['sent'] += 50;
  357. pauseMailQueueClear();
  358. }
  359. return BrowseMailQueue();
  360. }
  361. /**
  362. * Used for pausing the mail queue.
  363. */
  364. function pauseMailQueueClear()
  365. {
  366. global $context, $txt, $time_start;
  367. // Try get more time...
  368. @set_time_limit(600);
  369. if (function_exists('apache_reset_timeout'))
  370. @apache_reset_timeout();
  371. // Have we already used our maximum time?
  372. if (time() - array_sum(explode(' ', $time_start)) < 5)
  373. return;
  374. $context['continue_get_data'] = '?action=admin;area=mailqueue;sa=clear;te=' . $_GET['te'] . ';sent=' . $_GET['sent'] . ';' . $context['session_var'] . '=' . $context['session_id'];
  375. $context['page_title'] = $txt['not_done_title'];
  376. $context['continue_post_data'] = '';
  377. $context['continue_countdown'] = '2';
  378. $context['sub_template'] = 'not_done';
  379. // Keep browse selected.
  380. $context['selected'] = 'browse';
  381. // What percent through are we?
  382. $context['continue_percent'] = round(($_GET['sent'] / $_GET['te']) * 100, 1);
  383. // Never more than 100%!
  384. $context['continue_percent'] = min($context['continue_percent'], 100);
  385. obExit();
  386. }
  387. /**
  388. * Little utility function to calculate how long ago a time was.
  389. *
  390. * @param long $time_diff
  391. * @return string
  392. */
  393. function time_since($time_diff)
  394. {
  395. global $txt;
  396. if ($time_diff < 0)
  397. $time_diff = 0;
  398. // Just do a bit of an if fest...
  399. if ($time_diff > 86400)
  400. {
  401. $days = round($time_diff / 86400, 1);
  402. return sprintf($days == 1 ? $txt['mq_day'] : $txt['mq_days'], $time_diff / 86400);
  403. }
  404. // Hours?
  405. elseif ($time_diff > 3600)
  406. {
  407. $hours = round($time_diff / 3600, 1);
  408. return sprintf($hours == 1 ? $txt['mq_hour'] : $txt['mq_hours'], $hours);
  409. }
  410. // Minutes?
  411. elseif ($time_diff > 60)
  412. {
  413. $minutes = (int) ($time_diff / 60);
  414. return sprintf($minutes == 1 ? $txt['mq_minute'] : $txt['mq_minutes'], $minutes);
  415. }
  416. // Otherwise must be second
  417. else
  418. return sprintf($time_diff == 1 ? $txt['mq_second'] : $txt['mq_seconds'], $time_diff);
  419. }
  420. ?>