PageRenderTime 1116ms CodeModel.GetById 15ms RepoModel.GetById 11ms app.codeStats 0ms

/sources/ScheduledTasks.php

https://github.com/Arantor/Elkarte
PHP | 1735 lines | 1270 code | 216 blank | 249 comment | 192 complexity | 330b27f4f2d492343bac51b703fadc78 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. /**
  3. * @name ElkArte Forum
  4. * @copyright ElkArte Forum contributors
  5. * @license BSD http://opensource.org/licenses/BSD-3-Clause
  6. *
  7. * This software is a derived product, based on:
  8. *
  9. * Simple Machines Forum (SMF)
  10. * copyright: 2011 Simple Machines (http://www.simplemachines.org)
  11. * license: BSD, See included LICENSE.TXT for terms and conditions.
  12. *
  13. * @version 1.0 Alpha
  14. *
  15. * This file is automatically called and handles all manner of scheduled things.
  16. *
  17. */
  18. if (!defined('ELKARTE'))
  19. die('No access...');
  20. /**
  21. * This function works out what to do!
  22. */
  23. function AutoTask()
  24. {
  25. global $time_start, $modSettings, $smcFunc;
  26. // Special case for doing the mail queue.
  27. if (isset($_GET['scheduled']) && $_GET['scheduled'] == 'mailq')
  28. ReduceMailQueue();
  29. else
  30. {
  31. call_integration_hook('integrate_autotask_include');
  32. // Select the next task to do.
  33. $request = $smcFunc['db_query']('', '
  34. SELECT id_task, task, next_time, time_offset, time_regularity, time_unit
  35. FROM {db_prefix}scheduled_tasks
  36. WHERE disabled = {int:not_disabled}
  37. AND next_time <= {int:current_time}
  38. ORDER BY next_time ASC
  39. LIMIT 1',
  40. array(
  41. 'not_disabled' => 0,
  42. 'current_time' => time(),
  43. )
  44. );
  45. if ($smcFunc['db_num_rows']($request) != 0)
  46. {
  47. // The two important things really...
  48. $row = $smcFunc['db_fetch_assoc']($request);
  49. // When should this next be run?
  50. $next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']);
  51. // How long in seconds it the gap?
  52. $duration = $row['time_regularity'];
  53. if ($row['time_unit'] == 'm')
  54. $duration *= 60;
  55. elseif ($row['time_unit'] == 'h')
  56. $duration *= 3600;
  57. elseif ($row['time_unit'] == 'd')
  58. $duration *= 86400;
  59. elseif ($row['time_unit'] == 'w')
  60. $duration *= 604800;
  61. // If we were really late running this task actually skip the next one.
  62. if (time() + ($duration / 2) > $next_time)
  63. $next_time += $duration;
  64. // Update it now, so no others run this!
  65. $smcFunc['db_query']('', '
  66. UPDATE {db_prefix}scheduled_tasks
  67. SET next_time = {int:next_time}
  68. WHERE id_task = {int:id_task}
  69. AND next_time = {int:current_next_time}',
  70. array(
  71. 'next_time' => $next_time,
  72. 'id_task' => $row['id_task'],
  73. 'current_next_time' => $row['next_time'],
  74. )
  75. );
  76. $affected_rows = $smcFunc['db_affected_rows']();
  77. // The function must exist or we are wasting our time, plus do some timestamp checking, and database check!
  78. if (function_exists('scheduled_' . $row['task']) && (!isset($_GET['ts']) || $_GET['ts'] == $row['next_time']) && $affected_rows)
  79. {
  80. ignore_user_abort(true);
  81. // Do the task...
  82. $completed = call_user_func('scheduled_' . $row['task']);
  83. // Log that we did it ;)
  84. if ($completed)
  85. {
  86. $total_time = round(microtime(true) - $time_start, 3);
  87. $smcFunc['db_insert']('',
  88. '{db_prefix}log_scheduled_tasks',
  89. array(
  90. 'id_task' => 'int', 'time_run' => 'int', 'time_taken' => 'float',
  91. ),
  92. array(
  93. $row['id_task'], time(), (int) $total_time,
  94. ),
  95. array()
  96. );
  97. }
  98. }
  99. }
  100. $smcFunc['db_free_result']($request);
  101. // Get the next timestamp right.
  102. $request = $smcFunc['db_query']('', '
  103. SELECT next_time
  104. FROM {db_prefix}scheduled_tasks
  105. WHERE disabled = {int:not_disabled}
  106. ORDER BY next_time ASC
  107. LIMIT 1',
  108. array(
  109. 'not_disabled' => 0,
  110. )
  111. );
  112. // No new task scheduled yet?
  113. if ($smcFunc['db_num_rows']($request) === 0)
  114. $nextEvent = time() + 86400;
  115. else
  116. list ($nextEvent) = $smcFunc['db_fetch_row']($request);
  117. $smcFunc['db_free_result']($request);
  118. updateSettings(array('next_task_time' => $nextEvent));
  119. }
  120. // Shall we return?
  121. if (!isset($_GET['scheduled']))
  122. return true;
  123. // Finally, send some stuff...
  124. header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
  125. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  126. header('Content-Type: image/gif');
  127. die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B");
  128. }
  129. /**
  130. * Function to sending out approval notices to moderators etc.
  131. */
  132. function scheduled_approval_notification()
  133. {
  134. global $scripturl, $modSettings, $mbname, $txt, $smcFunc;
  135. // Grab all the items awaiting approval and sort type then board - clear up any things that are no longer relevant.
  136. $request = $smcFunc['db_query']('', '
  137. SELECT aq.id_msg, aq.id_attach, aq.id_event, m.id_topic, m.id_board, m.subject, t.id_first_msg,
  138. b.id_profile
  139. FROM {db_prefix}approval_queue AS aq
  140. INNER JOIN {db_prefix}messages AS m ON (m.id_msg = aq.id_msg)
  141. INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
  142. INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)',
  143. array(
  144. )
  145. );
  146. $notices = array();
  147. $profiles = array();
  148. while ($row = $smcFunc['db_fetch_assoc']($request))
  149. {
  150. // If this is no longer around we'll ignore it.
  151. if (empty($row['id_topic']))
  152. continue;
  153. // What type is it?
  154. if ($row['id_first_msg'] && $row['id_first_msg'] == $row['id_msg'])
  155. $type = 'topic';
  156. elseif ($row['id_attach'])
  157. $type = 'attach';
  158. else
  159. $type = 'msg';
  160. // Add it to the array otherwise.
  161. $notices[$row['id_board']][$type][] = array(
  162. 'subject' => $row['subject'],
  163. 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
  164. );
  165. // Store the profile for a bit later.
  166. $profiles[$row['id_board']] = $row['id_profile'];
  167. }
  168. $smcFunc['db_free_result']($request);
  169. // Delete it all!
  170. $smcFunc['db_query']('', '
  171. DELETE FROM {db_prefix}approval_queue',
  172. array(
  173. )
  174. );
  175. // If nothing quit now.
  176. if (empty($notices))
  177. return true;
  178. // Now we need to think about finding out *who* can approve - this is hard!
  179. // First off, get all the groups with this permission and sort by board.
  180. $request = $smcFunc['db_query']('', '
  181. SELECT id_group, id_profile, add_deny
  182. FROM {db_prefix}board_permissions
  183. WHERE permission = {string:approve_posts}
  184. AND id_profile IN ({array_int:profile_list})',
  185. array(
  186. 'profile_list' => $profiles,
  187. 'approve_posts' => 'approve_posts',
  188. )
  189. );
  190. $perms = array();
  191. $addGroups = array(1);
  192. while ($row = $smcFunc['db_fetch_assoc']($request))
  193. {
  194. // Sorry guys, but we have to ignore guests AND members - it would be too many otherwise.
  195. if ($row['id_group'] < 2)
  196. continue;
  197. $perms[$row['id_profile']][$row['add_deny'] ? 'add' : 'deny'][] = $row['id_group'];
  198. // Anyone who can access has to be considered.
  199. if ($row['add_deny'])
  200. $addGroups[] = $row['id_group'];
  201. }
  202. $smcFunc['db_free_result']($request);
  203. // Grab the moderators if they have permission!
  204. $mods = array();
  205. $members = array();
  206. if (in_array(2, $addGroups))
  207. {
  208. $request = $smcFunc['db_query']('', '
  209. SELECT id_member, id_board
  210. FROM {db_prefix}moderators',
  211. array(
  212. )
  213. );
  214. while ($row = $smcFunc['db_fetch_assoc']($request))
  215. {
  216. $mods[$row['id_member']][$row['id_board']] = true;
  217. // Make sure they get included in the big loop.
  218. $members[] = $row['id_member'];
  219. }
  220. $smcFunc['db_free_result']($request);
  221. }
  222. // Come along one and all... until we reject you ;)
  223. $request = $smcFunc['db_query']('', '
  224. SELECT id_member, real_name, email_address, lngfile, id_group, additional_groups, mod_prefs
  225. FROM {db_prefix}members
  226. WHERE id_group IN ({array_int:additional_group_list})
  227. OR FIND_IN_SET({raw:additional_group_list_implode}, additional_groups) != 0' . (empty($members) ? '' : '
  228. OR id_member IN ({array_int:member_list})') . '
  229. ORDER BY lngfile',
  230. array(
  231. 'additional_group_list' => $addGroups,
  232. 'member_list' => $members,
  233. 'additional_group_list_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $addGroups),
  234. )
  235. );
  236. $members = array();
  237. while ($row = $smcFunc['db_fetch_assoc']($request))
  238. {
  239. // Check whether they are interested.
  240. if (!empty($row['mod_prefs']))
  241. {
  242. list(,, $pref_binary) = explode('|', $row['mod_prefs']);
  243. if (!($pref_binary & 4))
  244. continue;
  245. }
  246. $members[$row['id_member']] = array(
  247. 'id' => $row['id_member'],
  248. 'groups' => array_merge(explode(',', $row['additional_groups']), array($row['id_group'])),
  249. 'language' => $row['lngfile'],
  250. 'email' => $row['email_address'],
  251. 'name' => $row['real_name'],
  252. );
  253. }
  254. $smcFunc['db_free_result']($request);
  255. // Get the mailing stuff.
  256. require_once(SUBSDIR . '/Mail.subs.php');
  257. // Need the below for loadLanguage to work!
  258. loadEssentialThemeData();
  259. $current_language = '';
  260. // Finally, loop through each member, work out what they can do, and send it.
  261. foreach ($members as $id => $member)
  262. {
  263. $emailbody = '';
  264. // Load the language file as required.
  265. if (empty($current_language) || $current_language != $member['language'])
  266. $current_language = loadLanguage('EmailTemplates', $member['language'], false);
  267. // Loop through each notice...
  268. foreach ($notices as $board => $notice)
  269. {
  270. $access = false;
  271. // Can they mod in this board?
  272. if (isset($mods[$id][$board]))
  273. $access = true;
  274. // Do the group check...
  275. if (!$access && isset($perms[$profiles[$board]]['add']))
  276. {
  277. // They can access?!
  278. if (array_intersect($perms[$profiles[$board]]['add'], $member['groups']))
  279. $access = true;
  280. // If they have deny rights don't consider them!
  281. if (isset($perms[$profiles[$board]]['deny']))
  282. if (array_intersect($perms[$profiles[$board]]['deny'], $member['groups']))
  283. $access = false;
  284. }
  285. // Finally, fix it for admins!
  286. if (in_array(1, $member['groups']))
  287. $access = true;
  288. // If they can't access it then give it a break!
  289. if (!$access)
  290. continue;
  291. foreach ($notice as $type => $items)
  292. {
  293. // Build up the top of this section.
  294. $emailbody .= $txt['scheduled_approval_email_' . $type] . "\n" .
  295. '------------------------------------------------------' . "\n";
  296. foreach ($items as $item)
  297. $emailbody .= $item['subject'] . ' - ' . $item['href'] . "\n";
  298. $emailbody .= "\n";
  299. }
  300. }
  301. if ($emailbody == '')
  302. continue;
  303. $replacements = array(
  304. 'REALNAME' => $member['name'],
  305. 'BODY' => $emailbody,
  306. );
  307. $emaildata = loadEmailTemplate('scheduled_approval', $replacements, $current_language);
  308. // Send the actual email.
  309. sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
  310. }
  311. // All went well!
  312. return true;
  313. }
  314. /**
  315. * Do some daily cleaning up.
  316. */
  317. function scheduled_daily_maintenance()
  318. {
  319. global $smcFunc, $modSettings, $db_type;
  320. // First clean out the cache.
  321. clean_cache();
  322. // If warning decrement is enabled and we have people who have not had a new warning in 24 hours, lower their warning level.
  323. list (, , $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']);
  324. if ($modSettings['warning_decrement'])
  325. {
  326. // Find every member who has a warning level...
  327. $request = $smcFunc['db_query']('', '
  328. SELECT id_member, warning
  329. FROM {db_prefix}members
  330. WHERE warning > {int:no_warning}',
  331. array(
  332. 'no_warning' => 0,
  333. )
  334. );
  335. $members = array();
  336. while ($row = $smcFunc['db_fetch_assoc']($request))
  337. $members[$row['id_member']] = $row['warning'];
  338. $smcFunc['db_free_result']($request);
  339. // Have some members to check?
  340. if (!empty($members))
  341. {
  342. // Find out when they were last warned.
  343. $request = $smcFunc['db_query']('', '
  344. SELECT id_recipient, MAX(log_time) AS last_warning
  345. FROM {db_prefix}log_comments
  346. WHERE id_recipient IN ({array_int:member_list})
  347. AND comment_type = {string:warning}
  348. GROUP BY id_recipient',
  349. array(
  350. 'member_list' => array_keys($members),
  351. 'warning' => 'warning',
  352. )
  353. );
  354. $member_changes = array();
  355. while ($row = $smcFunc['db_fetch_assoc']($request))
  356. {
  357. // More than 24 hours ago?
  358. if ($row['last_warning'] <= time() - 86400)
  359. $member_changes[] = array(
  360. 'id' => $row['id_recipient'],
  361. 'warning' => $members[$row['id_recipient']] >= $modSettings['warning_decrement'] ? $members[$row['id_recipient']] - $modSettings['warning_decrement'] : 0,
  362. );
  363. }
  364. $smcFunc['db_free_result']($request);
  365. // Have some members to change?
  366. if (!empty($member_changes))
  367. foreach ($member_changes as $change)
  368. $smcFunc['db_query']('', '
  369. UPDATE {db_prefix}members
  370. SET warning = {int:warning}
  371. WHERE id_member = {int:id_member}',
  372. array(
  373. 'warning' => $change['warning'],
  374. 'id_member' => $change['id'],
  375. )
  376. );
  377. }
  378. }
  379. // Do any spider stuff.
  380. if (!empty($modSettings['spider_mode']) && $modSettings['spider_mode'] > 1)
  381. {
  382. // We'll need this.
  383. require_once(SUBSDIR . '/SearchEngines.subs.php');
  384. consolidateSpiderStats();
  385. }
  386. // Check the database version - for some buggy MySQL version.
  387. $server_version = $smcFunc['db_server_info']();
  388. if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
  389. updateSettings(array('db_mysql_group_by_fix' => '1'));
  390. elseif (!empty($modSettings['db_mysql_group_by_fix']))
  391. $smcFunc['db_query']('', '
  392. DELETE FROM {db_prefix}settings
  393. WHERE variable = {string:mysql_fix}',
  394. array(
  395. 'mysql_fix' => 'db_mysql_group_by_fix',
  396. )
  397. );
  398. // Regenerate the Diffie-Hellman keys if OpenID is enabled.
  399. if (!empty($modSettings['enableOpenID']))
  400. {
  401. require_once(SUBSDIR . '/OpenID.subs.php');
  402. openID_setup_DH(true);
  403. }
  404. elseif (!empty($modSettings['dh_keys']))
  405. $smcFunc['db_query']('', '
  406. DELETE FROM {db_prefix}settings
  407. WHERE variable = {string:dh_keys}',
  408. array(
  409. 'dh_keys' => 'dh_keys',
  410. )
  411. );
  412. // Clean up some old login history information.
  413. $smcFunc['db_query']('', '
  414. DELETE FROM {db_prefix}member_logins
  415. WHERE time > {int:oldLogins}',
  416. array(
  417. 'oldLogins' => !empty($modSettings['loginHistoryDays']) ? 60 * 60 * $modSettings['loginHistoryDays'] : 108000,
  418. ));
  419. // Log we've done it...
  420. return true;
  421. }
  422. /**
  423. * Auto optimize the database?
  424. */
  425. function scheduled_auto_optimize()
  426. {
  427. global $modSettings, $smcFunc, $db_prefix, $db_type;
  428. // By default do it now!
  429. $delay = false;
  430. // As a kind of hack, if the server load is too great delay, but only by a bit!
  431. if (!empty($modSettings['load_average']) && !empty($modSettings['loadavg_auto_opt']) && $modSettings['load_average'] >= $modSettings['loadavg_auto_opt'])
  432. $delay = true;
  433. // Otherwise are we restricting the number of people online for this?
  434. if (!empty($modSettings['autoOptMaxOnline']))
  435. {
  436. $request = $smcFunc['db_query']('', '
  437. SELECT COUNT(*)
  438. FROM {db_prefix}log_online',
  439. array(
  440. )
  441. );
  442. list ($dont_do_it) = $smcFunc['db_fetch_row']($request);
  443. $smcFunc['db_free_result']($request);
  444. if ($dont_do_it > $modSettings['autoOptMaxOnline'])
  445. $delay = true;
  446. }
  447. // If we are gonna delay, do so now!
  448. if ($delay)
  449. return false;
  450. db_extend();
  451. // Get all the tables.
  452. $tables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
  453. // Actually do the optimisation.
  454. if ($db_type == 'sqlite')
  455. $smcFunc['db_optimize_table']($tables[0]);
  456. else
  457. foreach ($tables as $table)
  458. $smcFunc['db_optimize_table']($table);
  459. // Return for the log...
  460. return true;
  461. }
  462. /**
  463. * Send out a daily email of all subscribed topics.
  464. */
  465. function scheduled_daily_digest()
  466. {
  467. global $is_weekly, $txt, $mbname, $scripturl, $smcFunc, $context, $modSettings;
  468. // We'll want this...
  469. require_once(SUBSDIR . '/Mail.subs.php');
  470. loadEssentialThemeData();
  471. $is_weekly = !empty($is_weekly) ? 1 : 0;
  472. // Right - get all the notification data FIRST.
  473. $request = $smcFunc['db_query']('', '
  474. SELECT ln.id_topic, COALESCE(t.id_board, ln.id_board) AS id_board, mem.email_address, mem.member_name, mem.notify_types,
  475. mem.lngfile, mem.id_member
  476. FROM {db_prefix}log_notify AS ln
  477. INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
  478. LEFT JOIN {db_prefix}topics AS t ON (ln.id_topic != {int:empty_topic} AND t.id_topic = ln.id_topic)
  479. WHERE mem.notify_regularity = {int:notify_regularity}
  480. AND mem.is_activated = {int:is_activated}',
  481. array(
  482. 'empty_topic' => 0,
  483. 'notify_regularity' => $is_weekly ? '3' : '2',
  484. 'is_activated' => 1,
  485. )
  486. );
  487. $members = array();
  488. $langs = array();
  489. $notify = array();
  490. while ($row = $smcFunc['db_fetch_assoc']($request))
  491. {
  492. if (!isset($members[$row['id_member']]))
  493. {
  494. $members[$row['id_member']] = array(
  495. 'email' => $row['email_address'],
  496. 'name' => $row['member_name'],
  497. 'id' => $row['id_member'],
  498. 'notifyMod' => $row['notify_types'] < 3 ? true : false,
  499. 'lang' => $row['lngfile'],
  500. );
  501. $langs[$row['lngfile']] = $row['lngfile'];
  502. }
  503. // Store this useful data!
  504. $boards[$row['id_board']] = $row['id_board'];
  505. if ($row['id_topic'])
  506. $notify['topics'][$row['id_topic']][] = $row['id_member'];
  507. else
  508. $notify['boards'][$row['id_board']][] = $row['id_member'];
  509. }
  510. $smcFunc['db_free_result']($request);
  511. if (empty($boards))
  512. return true;
  513. // Just get the board names.
  514. $request = $smcFunc['db_query']('', '
  515. SELECT id_board, name
  516. FROM {db_prefix}boards
  517. WHERE id_board IN ({array_int:board_list})',
  518. array(
  519. 'board_list' => $boards,
  520. )
  521. );
  522. $boards = array();
  523. while ($row = $smcFunc['db_fetch_assoc']($request))
  524. $boards[$row['id_board']] = $row['name'];
  525. $smcFunc['db_free_result']($request);
  526. if (empty($boards))
  527. return true;
  528. // Get the actual topics...
  529. $request = $smcFunc['db_query']('', '
  530. SELECT ld.note_type, t.id_topic, t.id_board, t.id_member_started, m.id_msg, m.subject,
  531. b.name AS board_name
  532. FROM {db_prefix}log_digest AS ld
  533. INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ld.id_topic
  534. AND t.id_board IN ({array_int:board_list}))
  535. INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
  536. INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
  537. WHERE ' . ($is_weekly ? 'ld.daily != {int:daily_value}' : 'ld.daily IN (0, 2)'),
  538. array(
  539. 'board_list' => array_keys($boards),
  540. 'daily_value' => 2,
  541. )
  542. );
  543. $types = array();
  544. while ($row = $smcFunc['db_fetch_assoc']($request))
  545. {
  546. if (!isset($types[$row['note_type']][$row['id_board']]))
  547. $types[$row['note_type']][$row['id_board']] = array(
  548. 'lines' => array(),
  549. 'name' => $row['board_name'],
  550. 'id' => $row['id_board'],
  551. );
  552. if ($row['note_type'] == 'reply')
  553. {
  554. if (isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
  555. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['count']++;
  556. else
  557. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
  558. 'id' => $row['id_topic'],
  559. 'subject' => un_htmlspecialchars($row['subject']),
  560. 'count' => 1,
  561. );
  562. }
  563. elseif ($row['note_type'] == 'topic')
  564. {
  565. if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
  566. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
  567. 'id' => $row['id_topic'],
  568. 'subject' => un_htmlspecialchars($row['subject']),
  569. );
  570. }
  571. else
  572. {
  573. if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
  574. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
  575. 'id' => $row['id_topic'],
  576. 'subject' => un_htmlspecialchars($row['subject']),
  577. 'starter' => $row['id_member_started'],
  578. );
  579. }
  580. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array();
  581. if (!empty($notify['topics'][$row['id_topic']]))
  582. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['topics'][$row['id_topic']]);
  583. if (!empty($notify['boards'][$row['id_board']]))
  584. $types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['boards'][$row['id_board']]);
  585. }
  586. $smcFunc['db_free_result']($request);
  587. if (empty($types))
  588. return true;
  589. // Let's load all the languages into a cache thingy.
  590. $langtxt = array();
  591. foreach ($langs as $lang)
  592. {
  593. loadLanguage('Post', $lang);
  594. loadLanguage('index', $lang);
  595. loadLanguage('EmailTemplates', $lang);
  596. $langtxt[$lang] = array(
  597. 'subject' => $txt['digest_subject_' . ($is_weekly ? 'weekly' : 'daily')],
  598. 'char_set' => 'UTF-8',
  599. 'intro' => sprintf($txt['digest_intro_' . ($is_weekly ? 'weekly' : 'daily')], $mbname),
  600. 'new_topics' => $txt['digest_new_topics'],
  601. 'topic_lines' => $txt['digest_new_topics_line'],
  602. 'new_replies' => $txt['digest_new_replies'],
  603. 'mod_actions' => $txt['digest_mod_actions'],
  604. 'replies_one' => $txt['digest_new_replies_one'],
  605. 'replies_many' => $txt['digest_new_replies_many'],
  606. 'sticky' => $txt['digest_mod_act_sticky'],
  607. 'lock' => $txt['digest_mod_act_lock'],
  608. 'unlock' => $txt['digest_mod_act_unlock'],
  609. 'remove' => $txt['digest_mod_act_remove'],
  610. 'move' => $txt['digest_mod_act_move'],
  611. 'merge' => $txt['digest_mod_act_merge'],
  612. 'split' => $txt['digest_mod_act_split'],
  613. 'bye' => $txt['regards_team'],
  614. );
  615. }
  616. // Right - send out the silly things - this will take quite some space!
  617. $emails = array();
  618. foreach ($members as $mid => $member)
  619. {
  620. // Right character set!
  621. $context['character_set'] = 'UTF-8';
  622. // Do the start stuff!
  623. $email = array(
  624. 'subject' => $mbname . ' - ' . $langtxt[$lang]['subject'],
  625. 'body' => $member['name'] . ',' . "\n\n" . $langtxt[$lang]['intro'] . "\n" . $scripturl . '?action=profile;area=notification;u=' . $member['id'] . "\n",
  626. 'email' => $member['email'],
  627. );
  628. // All new topics?
  629. if (isset($types['topic']))
  630. {
  631. $titled = false;
  632. foreach ($types['topic'] as $id => $board)
  633. foreach ($board['lines'] as $topic)
  634. if (in_array($mid, $topic['members']))
  635. {
  636. if (!$titled)
  637. {
  638. $email['body'] .= "\n" . $langtxt[$lang]['new_topics'] . ':' . "\n" . '-----------------------------------------------';
  639. $titled = true;
  640. }
  641. $email['body'] .= "\n" . sprintf($langtxt[$lang]['topic_lines'], $topic['subject'], $board['name']);
  642. }
  643. if ($titled)
  644. $email['body'] .= "\n";
  645. }
  646. // What about replies?
  647. if (isset($types['reply']))
  648. {
  649. $titled = false;
  650. foreach ($types['reply'] as $id => $board)
  651. foreach ($board['lines'] as $topic)
  652. if (in_array($mid, $topic['members']))
  653. {
  654. if (!$titled)
  655. {
  656. $email['body'] .= "\n" . $langtxt[$lang]['new_replies'] . ':' . "\n" . '-----------------------------------------------';
  657. $titled = true;
  658. }
  659. $email['body'] .= "\n" . ($topic['count'] == 1 ? sprintf($langtxt[$lang]['replies_one'], $topic['subject']) : sprintf($langtxt[$lang]['replies_many'], $topic['count'], $topic['subject']));
  660. }
  661. if ($titled)
  662. $email['body'] .= "\n";
  663. }
  664. // Finally, moderation actions!
  665. $titled = false;
  666. foreach ($types as $note_type => $type)
  667. {
  668. if ($note_type == 'topic' || $note_type == 'reply')
  669. continue;
  670. foreach ($type as $id => $board)
  671. foreach ($board['lines'] as $topic)
  672. if (in_array($mid, $topic['members']))
  673. {
  674. if (!$titled)
  675. {
  676. $email['body'] .= "\n" . $langtxt[$lang]['mod_actions'] . ':' . "\n" . '-----------------------------------------------';
  677. $titled = true;
  678. }
  679. $email['body'] .= "\n" . sprintf($langtxt[$lang][$note_type], $topic['subject']);
  680. }
  681. }
  682. if ($titled)
  683. $email['body'] .= "\n";
  684. // Then just say our goodbyes!
  685. $email['body'] .= "\n\n" . $txt['regards_team'];
  686. // Send it - low priority!
  687. sendmail($email['email'], $email['subject'], $email['body'], null, null, false, 4);
  688. }
  689. // Clean up...
  690. if ($is_weekly)
  691. {
  692. $smcFunc['db_query']('', '
  693. DELETE FROM {db_prefix}log_digest
  694. WHERE daily != {int:not_daily}',
  695. array(
  696. 'not_daily' => 0,
  697. )
  698. );
  699. $smcFunc['db_query']('', '
  700. UPDATE {db_prefix}log_digest
  701. SET daily = {int:daily_value}
  702. WHERE daily = {int:not_daily}',
  703. array(
  704. 'daily_value' => 2,
  705. 'not_daily' => 0,
  706. )
  707. );
  708. }
  709. else
  710. {
  711. // Clear any only weekly ones, and stop us from sending daily again.
  712. $smcFunc['db_query']('', '
  713. DELETE FROM {db_prefix}log_digest
  714. WHERE daily = {int:daily_value}',
  715. array(
  716. 'daily_value' => 2,
  717. )
  718. );
  719. $smcFunc['db_query']('', '
  720. UPDATE {db_prefix}log_digest
  721. SET daily = {int:both_value}
  722. WHERE daily = {int:no_value}',
  723. array(
  724. 'both_value' => 1,
  725. 'no_value' => 0,
  726. )
  727. );
  728. }
  729. // Just in case the member changes their settings mark this as sent.
  730. $members = array_keys($members);
  731. $smcFunc['db_query']('', '
  732. UPDATE {db_prefix}log_notify
  733. SET sent = {int:is_sent}
  734. WHERE id_member IN ({array_int:member_list})',
  735. array(
  736. 'member_list' => $members,
  737. 'is_sent' => 1,
  738. )
  739. );
  740. // Log we've done it...
  741. return true;
  742. }
  743. /**
  744. * Like the daily stuff - just seven times less regular ;)
  745. */
  746. function scheduled_weekly_digest()
  747. {
  748. global $is_weekly;
  749. // We just pass through to the daily function - avoid duplication!
  750. $is_weekly = true;
  751. return scheduled_daily_digest();
  752. }
  753. /**
  754. * Send a group of emails from the mail queue.
  755. *
  756. * @param type $number the number to send each loop through
  757. * @param type $override_limit bypassing our limit flaf
  758. * @param type $force_send
  759. * @return boolean
  760. */
  761. function ReduceMailQueue($number = false, $override_limit = false, $force_send = false)
  762. {
  763. global $modSettings, $smcFunc;
  764. // Are we intending another script to be sending out the queue?
  765. if (!empty($modSettings['mail_queue_use_cron']) && empty($force_send))
  766. return false;
  767. // By default send 5 at once.
  768. if (!$number)
  769. $number = empty($modSettings['mail_quantity']) ? 5 : $modSettings['mail_quantity'];
  770. // If we came with a timestamp, and that doesn't match the next event, then someone else has beaten us.
  771. if (isset($_GET['ts']) && $_GET['ts'] != $modSettings['mail_next_send'] && empty($force_send))
  772. return false;
  773. // By default move the next sending on by 10 seconds, and require an affected row.
  774. if (!$override_limit)
  775. {
  776. $delay = !empty($modSettings['mail_queue_delay']) ? $modSettings['mail_queue_delay'] : (!empty($modSettings['mail_limit']) && $modSettings['mail_limit'] < 5 ? 10 : 5);
  777. $smcFunc['db_query']('', '
  778. UPDATE {db_prefix}settings
  779. SET value = {string:next_mail_send}
  780. WHERE variable = {string:mail_next_send}
  781. AND value = {string:last_send}',
  782. array(
  783. 'next_mail_send' => time() + $delay,
  784. 'mail_next_send' => 'mail_next_send',
  785. 'last_send' => $modSettings['mail_next_send'],
  786. )
  787. );
  788. if ($smcFunc['db_affected_rows']() == 0)
  789. return false;
  790. $modSettings['mail_next_send'] = time() + $delay;
  791. }
  792. // If we're not overriding how many are we allow to send?
  793. if (!$override_limit && !empty($modSettings['mail_limit']))
  794. {
  795. list ($mt, $mn) = @explode('|', $modSettings['mail_recent']);
  796. // Nothing worth noting...
  797. if (empty($mn) || $mt < time() - 60)
  798. {
  799. $mt = time();
  800. $mn = $number;
  801. }
  802. // Otherwise we have a few more we can spend?
  803. elseif ($mn < $modSettings['mail_limit'])
  804. {
  805. $mn += $number;
  806. }
  807. // No more I'm afraid, return!
  808. else
  809. return false;
  810. // Reflect that we're about to send some, do it now to be safe.
  811. updateSettings(array('mail_recent' => $mt . '|' . $mn));
  812. }
  813. // Now we know how many we're sending, let's send them.
  814. $request = $smcFunc['db_query']('', '
  815. SELECT /*!40001 SQL_NO_CACHE */ id_mail, recipient, body, subject, headers, send_html, time_sent
  816. FROM {db_prefix}mail_queue
  817. ORDER BY priority ASC, id_mail ASC
  818. LIMIT ' . $number,
  819. array(
  820. )
  821. );
  822. $ids = array();
  823. $emails = array();
  824. while ($row = $smcFunc['db_fetch_assoc']($request))
  825. {
  826. // We want to delete these from the database ASAP, so just get the data and go.
  827. $ids[] = $row['id_mail'];
  828. $emails[] = array(
  829. 'to' => $row['recipient'],
  830. 'body' => $row['body'],
  831. 'subject' => $row['subject'],
  832. 'headers' => $row['headers'],
  833. 'send_html' => $row['send_html'],
  834. 'time_sent' => $row['time_sent'],
  835. );
  836. }
  837. $smcFunc['db_free_result']($request);
  838. // Delete, delete, delete!!!
  839. if (!empty($ids))
  840. $smcFunc['db_query']('', '
  841. DELETE FROM {db_prefix}mail_queue
  842. WHERE id_mail IN ({array_int:mail_list})',
  843. array(
  844. 'mail_list' => $ids,
  845. )
  846. );
  847. // Don't believe we have any left?
  848. if (count($ids) < $number)
  849. {
  850. // Only update the setting if no-one else has beaten us to it.
  851. $smcFunc['db_query']('', '
  852. UPDATE {db_prefix}settings
  853. SET value = {string:no_send}
  854. WHERE variable = {string:mail_next_send}
  855. AND value = {string:last_mail_send}',
  856. array(
  857. 'no_send' => '0',
  858. 'mail_next_send' => 'mail_next_send',
  859. 'last_mail_send' => $modSettings['mail_next_send'],
  860. )
  861. );
  862. }
  863. if (empty($ids))
  864. return false;
  865. if (!empty($modSettings['mail_type']) && $modSettings['smtp_host'] != '')
  866. require_once(SUBSDIR . '/Post.subs.php');
  867. // Send each email, yea!
  868. $failed_emails = array();
  869. foreach ($emails as $key => $email)
  870. {
  871. if (empty($modSettings['mail_type']) || $modSettings['smtp_host'] == '')
  872. {
  873. $email['subject'] = strtr($email['subject'], array("\r" => '', "\n" => ''));
  874. if (!empty($modSettings['mail_strip_carriage']))
  875. {
  876. $email['body'] = strtr($email['body'], array("\r" => ''));
  877. $email['headers'] = strtr($email['headers'], array("\r" => ''));
  878. }
  879. // No point logging a specific error here, as we have no language. PHP error is helpful anyway...
  880. $result = mail(strtr($email['to'], array("\r" => '', "\n" => '')), $email['subject'], $email['body'], $email['headers']);
  881. // Try to stop a timeout, this would be bad...
  882. @set_time_limit(300);
  883. if (function_exists('apache_reset_timeout'))
  884. @apache_reset_timeout();
  885. }
  886. else
  887. $result = smtp_mail(array($email['to']), $email['subject'], $email['body'], $email['send_html'] ? $email['headers'] : 'Mime-Version: 1.0' . "\r\n" . $email['headers']);
  888. // Hopefully it sent?
  889. if (!$result)
  890. $failed_emails[] = array($email['to'], $email['body'], $email['subject'], $email['headers'], $email['send_html'], $email['time_sent']);
  891. }
  892. // Any emails that didn't send?
  893. if (!empty($failed_emails))
  894. {
  895. // Update the failed attempts check.
  896. $smcFunc['db_insert']('replace',
  897. '{db_prefix}settings',
  898. array('variable' => 'string', 'value' => 'string'),
  899. array('mail_failed_attempts', empty($modSettings['mail_failed_attempts']) ? 1 : ++$modSettings['mail_failed_attempts']),
  900. array('variable')
  901. );
  902. // If we have failed to many times, tell mail to wait a bit and try again.
  903. if ($modSettings['mail_failed_attempts'] > 5)
  904. $smcFunc['db_query']('', '
  905. UPDATE {db_prefix}settings
  906. SET value = {string:mail_next_send}
  907. WHERE variable = {string:next_mail_send}
  908. AND value = {string:last_send}',
  909. array(
  910. 'next_mail_send' => time() + 60,
  911. 'mail_next_send' => 'mail_next_send',
  912. 'last_send' => $modSettings['mail_next_send'],
  913. ));
  914. // Add our email back to the queue, manually.
  915. $smcFunc['db_insert']('insert',
  916. '{db_prefix}mail_queue',
  917. array('recipient' => 'string', 'body' => 'string', 'subject' => 'string', 'headers' => 'string', 'send_html' => 'string', 'time_sent' => 'string'),
  918. $failed_emails,
  919. array('id_mail')
  920. );
  921. return false;
  922. }
  923. // We where unable to send the email, clear our failed attempts.
  924. elseif (!empty($modSettings['mail_failed_attempts']))
  925. $smcFunc['db_query']('', '
  926. UPDATE {db_prefix}settings
  927. SET value = {string:zero}
  928. WHERE variable = {string:mail_failed_attempts}',
  929. array(
  930. 'zero' => '0',
  931. 'mail_failed_attempts' => 'mail_failed_attempts',
  932. ));
  933. // Had something to send...
  934. return true;
  935. }
  936. /**
  937. * Calculate the next time the passed tasks should be triggered.
  938. *
  939. * @param type $tasks
  940. * @param type $forceUpdate
  941. */
  942. function CalculateNextTrigger($tasks = array(), $forceUpdate = false)
  943. {
  944. global $modSettings, $smcFunc;
  945. $task_query = '';
  946. if (!is_array($tasks))
  947. $tasks = array($tasks);
  948. // Actually have something passed?
  949. if (!empty($tasks))
  950. {
  951. if (!isset($tasks[0]) || is_numeric($tasks[0]))
  952. $task_query = ' AND id_task IN ({array_int:tasks})';
  953. else
  954. $task_query = ' AND task IN ({array_string:tasks})';
  955. }
  956. $nextTaskTime = empty($tasks) ? time() + 86400 : $modSettings['next_task_time'];
  957. // Get the critical info for the tasks.
  958. $request = $smcFunc['db_query']('', '
  959. SELECT id_task, next_time, time_offset, time_regularity, time_unit
  960. FROM {db_prefix}scheduled_tasks
  961. WHERE disabled = {int:no_disabled}
  962. ' . $task_query,
  963. array(
  964. 'no_disabled' => 0,
  965. 'tasks' => $tasks,
  966. )
  967. );
  968. $tasks = array();
  969. while ($row = $smcFunc['db_fetch_assoc']($request))
  970. {
  971. $next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']);
  972. // Only bother moving the task if it's out of place or we're forcing it!
  973. if ($forceUpdate || $next_time < $row['next_time'] || $row['next_time'] < time())
  974. $tasks[$row['id_task']] = $next_time;
  975. else
  976. $next_time = $row['next_time'];
  977. // If this is sooner than the current next task, make this the next task.
  978. if ($next_time < $nextTaskTime)
  979. $nextTaskTime = $next_time;
  980. }
  981. $smcFunc['db_free_result']($request);
  982. // Now make the changes!
  983. foreach ($tasks as $id => $time)
  984. $smcFunc['db_query']('', '
  985. UPDATE {db_prefix}scheduled_tasks
  986. SET next_time = {int:next_time}
  987. WHERE id_task = {int:id_task}',
  988. array(
  989. 'next_time' => $time,
  990. 'id_task' => $id,
  991. )
  992. );
  993. // If the next task is now different update.
  994. if ($modSettings['next_task_time'] != $nextTaskTime)
  995. updateSettings(array('next_task_time' => $nextTaskTime));
  996. }
  997. /**
  998. * Simply returns a time stamp of the next instance of these time parameters.
  999. *
  1000. * @param int $regularity
  1001. * @param type $unit
  1002. * @param type $offset
  1003. * @return int
  1004. */
  1005. function next_time($regularity, $unit, $offset)
  1006. {
  1007. // Just in case!
  1008. if ($regularity == 0)
  1009. $regularity = 2;
  1010. $curHour = date('H', time());
  1011. $curMin = date('i', time());
  1012. $next_time = 9999999999;
  1013. // If the unit is minutes only check regularity in minutes.
  1014. if ($unit == 'm')
  1015. {
  1016. $off = date('i', $offset);
  1017. // If it's now just pretend it ain't,
  1018. if ($off == $curMin)
  1019. $next_time = time() + $regularity;
  1020. else
  1021. {
  1022. // Make sure that the offset is always in the past.
  1023. $off = $off > $curMin ? $off - 60 : $off;
  1024. while ($off <= $curMin)
  1025. $off += $regularity;
  1026. // Now we know when the time should be!
  1027. $next_time = time() + 60 * ($off - $curMin);
  1028. }
  1029. }
  1030. // Otherwise, work out what the offset would be with todays date.
  1031. else
  1032. {
  1033. $next_time = mktime(date('H', $offset), date('i', $offset), 0, date('m'), date('d'), date('Y'));
  1034. // Make the time offset in the past!
  1035. if ($next_time > time())
  1036. {
  1037. $next_time -= 86400;
  1038. }
  1039. // Default we'll jump in hours.
  1040. $applyOffset = 3600;
  1041. // 24 hours = 1 day.
  1042. if ($unit == 'd')
  1043. $applyOffset = 86400;
  1044. // Otherwise a week.
  1045. if ($unit == 'w')
  1046. $applyOffset = 604800;
  1047. $applyOffset *= $regularity;
  1048. // Just add on the offset.
  1049. while ($next_time <= time())
  1050. {
  1051. $next_time += $applyOffset;
  1052. }
  1053. }
  1054. return $next_time;
  1055. }
  1056. /**
  1057. * This retrieves data (e.g. last version of ELKARTE)
  1058. */
  1059. function scheduled_fetchFiles()
  1060. {
  1061. global $txt, $language, $settings, $forum_version, $modSettings, $smcFunc;
  1062. // What files do we want to get
  1063. $request = $smcFunc['db_query']('', '
  1064. SELECT id_file, filename, path, parameters
  1065. FROM {db_prefix}admin_info_files',
  1066. array(
  1067. )
  1068. );
  1069. $js_files = array();
  1070. $errors = 0;
  1071. while ($row = $smcFunc['db_fetch_assoc']($request))
  1072. {
  1073. $js_files[$row['id_file']] = array(
  1074. 'filename' => $row['filename'],
  1075. 'path' => $row['path'],
  1076. 'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
  1077. );
  1078. }
  1079. $smcFunc['db_free_result']($request);
  1080. // We're gonna need fetch_web_data() to pull this off.
  1081. require_once(SUBSDIR . '/Package.subs.php');
  1082. // Just in case we run into a problem.
  1083. loadEssentialThemeData();
  1084. loadLanguage('Errors', $language, false);
  1085. foreach ($js_files as $ID_FILE => $file)
  1086. {
  1087. // Create the url
  1088. $server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.elkarte.net' : '';
  1089. $url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
  1090. // Get the file
  1091. $file_data = fetch_web_data($url);
  1092. // If we are tossing errors - give up - the site might be down.
  1093. if ($file_data === false && $errors++ > 2)
  1094. {
  1095. log_error(sprintf($txt['st_cannot_retrieve_file'], $url));
  1096. return false;
  1097. }
  1098. elseif ($file_data !== false)
  1099. {
  1100. // Save the update to the database
  1101. $smcFunc['db_query']('substring', '
  1102. UPDATE {db_prefix}admin_info_files
  1103. SET data = SUBSTRING({string:file_data}, 1, 65534)
  1104. WHERE id_file = {int:id_file}',
  1105. array(
  1106. 'id_file' => $ID_FILE,
  1107. 'file_data' => $file_data,
  1108. )
  1109. );
  1110. }
  1111. }
  1112. return true;
  1113. }
  1114. /**
  1115. * Schedule birthday emails.
  1116. * (aka "Happy birthday!!")
  1117. */
  1118. function scheduled_birthdayemails()
  1119. {
  1120. global $modSettings, $mbname, $txt, $smcFunc, $birthdayEmails;
  1121. // Need this in order to load the language files.
  1122. loadEssentialThemeData();
  1123. // Going to need this to send the emails.
  1124. require_once(SUBSDIR . '/Mail.subs.php');
  1125. $greeting = isset($modSettings['birthday_email']) ? $modSettings['birthday_email'] : 'happy_birthday';
  1126. // Get the month and day of today.
  1127. $month = date('n'); // Month without leading zeros.
  1128. $day = date('j'); // Day without leading zeros.
  1129. // So who are the lucky ones? Don't include those who are banned and those who don't want them.
  1130. $result = $smcFunc['db_query']('', '
  1131. SELECT id_member, real_name, lngfile, email_address
  1132. FROM {db_prefix}members
  1133. WHERE is_activated < 10
  1134. AND MONTH(birthdate) = {int:month}
  1135. AND DAYOFMONTH(birthdate) = {int:day}
  1136. AND notify_announcements = {int:notify_announcements}
  1137. AND YEAR(birthdate) > {int:year}',
  1138. array(
  1139. 'notify_announcements' => 1,
  1140. 'year' => 1,
  1141. 'month' => $month,
  1142. 'day' => $day,
  1143. )
  1144. );
  1145. // Group them by languages.
  1146. $birthdays = array();
  1147. while ($row = $smcFunc['db_fetch_assoc']($result))
  1148. {
  1149. if (!isset($birthdays[$row['lngfile']]))
  1150. $birthdays[$row['lngfile']] = array();
  1151. $birthdays[$row['lngfile']][$row['id_member']] = array(
  1152. 'name' => $row['real_name'],
  1153. 'email' => $row['email_address']
  1154. );
  1155. }
  1156. $smcFunc['db_free_result']($result);
  1157. // Send out the greetings!
  1158. foreach ($birthdays as $lang => $recps)
  1159. {
  1160. // We need to do some shuffling to make this work properly.
  1161. loadLanguage('EmailTemplates', $lang);
  1162. $txt['emails']['happy_birthday']['subject'] = $txtBirthdayEmails[$greeting . '_subject'];
  1163. $txt['emails']['happy_birthday']['body'] = $txtBirthdayEmails[$greeting . '_body'];
  1164. foreach ($recps as $recp)
  1165. {
  1166. $replacements = array(
  1167. 'REALNAME' => $recp['name'],
  1168. );
  1169. $emaildata = loadEmailTemplate('happy_birthday', $replacements, $lang, false);
  1170. sendmail($recp['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 4);
  1171. // Try to stop a timeout, this would be bad...
  1172. @set_time_limit(300);
  1173. if (function_exists('apache_reset_timeout'))
  1174. @apache_reset_timeout();
  1175. }
  1176. }
  1177. // Flush the mail queue, just in case.
  1178. AddMailQueue(true);
  1179. return true;
  1180. }
  1181. /**
  1182. * Weekly maintenance
  1183. */
  1184. function scheduled_weekly_maintenance()
  1185. {
  1186. global $modSettings, $smcFunc;
  1187. // Delete some settings that needn't be set if they are otherwise empty.
  1188. $emptySettings = array(
  1189. 'warning_mute', 'warning_moderate', 'warning_watch', 'warning_show', 'disableCustomPerPage', 'spider_mode', 'spider_group',
  1190. 'paid_currency_code', 'paid_currency_symbol', 'paid_email_to', 'paid_email', 'paid_enabled', 'paypal_email',
  1191. 'search_enable_captcha', 'search_floodcontrol_time', 'show_spider_online',
  1192. );
  1193. $smcFunc['db_query']('', '
  1194. DELETE FROM {db_prefix}settings
  1195. WHERE variable IN ({array_string:setting_list})
  1196. AND (value = {string:zero_value} OR value = {string:blank_value})',
  1197. array(
  1198. 'zero_value' => '0',
  1199. 'blank_value' => '',
  1200. 'setting_list' => $emptySettings,
  1201. )
  1202. );
  1203. // Some settings we never want to keep - they are just there for temporary purposes.
  1204. $deleteAnywaySettings = array(
  1205. 'attachment_full_notified',
  1206. );
  1207. $smcFunc['db_query']('', '
  1208. DELETE FROM {db_prefix}settings
  1209. WHERE variable IN ({array_string:setting_list})',
  1210. array(
  1211. 'setting_list' => $deleteAnywaySettings,
  1212. )
  1213. );
  1214. // Ok should we prune the logs?
  1215. if (!empty($modSettings['pruningOptions']))
  1216. {
  1217. if (!empty($modSettings['pruningOptions']) && strpos($modSettings['pruningOptions'], ',') !== false)
  1218. list ($modSettings['pruneErrorLog'], $modSettings['pruneModLog'], $modSettings['pruneBanLog'], $modSettings['pruneReportLog'], $modSettings['pruneScheduledTaskLog'], $modSettings['pruneSpiderHitLog']) = explode(',', $modSettings['pruningOptions']);
  1219. if (!empty($modSettings['pruneErrorLog']))
  1220. {
  1221. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1222. $t = time() - $modSettings['pruneErrorLog'] * 86400;
  1223. $smcFunc['db_query']('', '
  1224. DELETE FROM {db_prefix}log_errors
  1225. WHERE log_time < {int:log_time}',
  1226. array(
  1227. 'log_time' => $t,
  1228. )
  1229. );
  1230. }
  1231. if (!empty($modSettings['pruneModLog']))
  1232. {
  1233. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1234. $t = time() - $modSettings['pruneModLog'] * 86400;
  1235. $smcFunc['db_query']('', '
  1236. DELETE FROM {db_prefix}log_actions
  1237. WHERE log_time < {int:log_time}
  1238. AND id_log = {int:moderation_log}',
  1239. array(
  1240. 'log_time' => $t,
  1241. 'moderation_log' => 1,
  1242. )
  1243. );
  1244. }
  1245. if (!empty($modSettings['pruneBanLog']))
  1246. {
  1247. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1248. $t = time() - $modSettings['pruneBanLog'] * 86400;
  1249. $smcFunc['db_query']('', '
  1250. DELETE FROM {db_prefix}log_banned
  1251. WHERE log_time < {int:log_time}',
  1252. array(
  1253. 'log_time' => $t,
  1254. )
  1255. );
  1256. }
  1257. if (!empty($modSettings['pruneBadbehaviorLog']))
  1258. {
  1259. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1260. $t = time() - $modSettings['pruneBadbehaviorLog'] * 86400;
  1261. $smcFunc['db_query']('', '
  1262. DELETE FROM {db_prefix}log_badbehavior
  1263. WHERE log_time < {int:log_time}',
  1264. array(
  1265. 'log_time' => $t,
  1266. )
  1267. );
  1268. }
  1269. if (!empty($modSettings['pruneReportLog']))
  1270. {
  1271. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1272. $t = time() - $modSettings['pruneReportLog'] * 86400;
  1273. // This one is more complex then the other logs. First we need to figure out which reports are too old.
  1274. $reports = array();
  1275. $result = $smcFunc['db_query']('', '
  1276. SELECT id_report
  1277. FROM {db_prefix}log_reported
  1278. WHERE time_started < {int:time_started}',
  1279. array(
  1280. 'time_started' => $t,
  1281. )
  1282. );
  1283. while ($row = $smcFunc['db_fetch_row']($result))
  1284. $reports[] = $row[0];
  1285. $smcFunc['db_free_result']($result);
  1286. if (!empty($reports))
  1287. {
  1288. // Now delete the reports...
  1289. $smcFunc['db_query']('', '
  1290. DELETE FROM {db_prefix}log_reported
  1291. WHERE id_report IN ({array_int:report_list})',
  1292. array(
  1293. 'report_list' => $reports,
  1294. )
  1295. );
  1296. // And delete the comments for those reports...
  1297. $smcFunc['db_query']('', '
  1298. DELETE FROM {db_prefix}log_reported_comments
  1299. WHERE id_report IN ({array_int:report_list})',
  1300. array(
  1301. 'report_list' => $reports,
  1302. )
  1303. );
  1304. }
  1305. }
  1306. if (!empty($modSettings['pruneScheduledTaskLog']))
  1307. {
  1308. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1309. $t = time() - $modSettings['pruneScheduledTaskLog'] * 86400;
  1310. $smcFunc['db_query']('', '
  1311. DELETE FROM {db_prefix}log_scheduled_tasks
  1312. WHERE time_run < {int:time_run}',
  1313. array(
  1314. 'time_run' => $t,
  1315. )
  1316. );
  1317. }
  1318. if (!empty($modSettings['pruneSpiderHitLog']))
  1319. {
  1320. // Figure out when our cutoff time is. 1 day = 86400 seconds.
  1321. $t = time() - $modSettings['pruneSpiderHitLog'] * 86400;
  1322. $smcFunc['db_query']('', '
  1323. DELETE FROM {db_prefix}log_spider_hits
  1324. WHERE log_time < {int:log_time}',
  1325. array(
  1326. 'log_time' => $t,
  1327. )
  1328. );
  1329. }
  1330. }
  1331. // Get rid of any paid subscriptions that were never actioned.
  1332. $smcFunc['db_query']('', '
  1333. DELETE FROM {db_prefix}log_subscribed
  1334. WHERE end_time = {int:no_end_time}
  1335. AND status = {int:not_active}
  1336. AND start_time < {int:start_time}
  1337. AND payments_pending < {int:payments_pending}',
  1338. array(
  1339. 'no_end_time' => 0,
  1340. 'not_active' => 0,
  1341. 'start_time' => time() - 60,
  1342. 'payments_pending' => 1,
  1343. )
  1344. );
  1345. // Some OS's don't seem to clean out their sessions.
  1346. $smcFunc['db_query']('', '
  1347. DELETE FROM {db_prefix}sessions
  1348. WHERE last_update < {int:last_update}',
  1349. array(
  1350. 'last_update' => time() - 86400,
  1351. )
  1352. );
  1353. return true;
  1354. }
  1355. /**
  1356. * Perform the standard checks on expiring/near expiring subscriptions.
  1357. */
  1358. function scheduled_paid_subscriptions()
  1359. {
  1360. global $txt, $scripturl, $smcFunc, $modSettings, $language;
  1361. // Start off by checking for removed subscriptions.
  1362. $request = $smcFunc['db_query']('', '
  1363. SELECT id_subscribe, id_member
  1364. FROM {db_prefix}log_subscribed
  1365. WHERE status = {int:is_active}
  1366. AND end_time < {int:time_now}',
  1367. array(
  1368. 'is_active' => 1,
  1369. 'time_now' => time(),
  1370. )
  1371. );
  1372. while ($row = $smcFunc['db_fetch_assoc']($request))
  1373. {
  1374. require_once(ADMINDIR . '/ManagePaid.php');
  1375. removeSubscription($row['id_subscribe'], $row['id_member']);
  1376. }
  1377. $smcFunc['db_free_result']($request);
  1378. // Get all those about to expire that have not had a reminder sent.
  1379. $request = $smcFunc['db_query']('', '
  1380. SELECT ls.id_sublog, m.id_member, m.member_name, m.email_address, m.lngfile, s.name, ls.end_time
  1381. FROM {db_prefix}log_subscribed AS ls
  1382. INNER JOIN {db_prefix}subscriptions AS s ON (s.id_subscribe = ls.id_subscribe)
  1383. INNER JOIN {db_prefix}members AS m ON (m.id_member = ls.id_member)
  1384. WHERE ls.status = {int:is_active}
  1385. AND ls.reminder_sent = {int:reminder_sent}
  1386. AND s.reminder > {int:reminder_wanted}
  1387. AND ls.end_time < ({int:time_now} + s.reminder * 86400)',
  1388. array(
  1389. 'is_active' => 1,
  1390. 'reminder_sent' => 0,
  1391. 'reminder_wanted' => 0,
  1392. 'time_now' => time(),
  1393. )
  1394. );
  1395. $subs_reminded = array();
  1396. while ($row = $smcFunc['db_fetch_assoc']($request))
  1397. {
  1398. // If this is the first one load the important bits.
  1399. if (empty($subs_reminded))
  1400. {
  1401. require_once(SUBSDIR . '/Mail.subs.php');
  1402. // Need the below for loadLanguage to work!
  1403. loadEssentialThemeData();
  1404. }
  1405. $subs_reminded[] = $row['id_sublog'];
  1406. $replacements = array(
  1407. 'PROFILE_LINK' => $scripturl . '?action=profile;area=subscriptions;u=' . $row['id_member'],
  1408. 'REALNAME' => $row['member_name'],
  1409. 'SUBSCRIPTION' => $row['name'],
  1410. 'END_DATE' => strip_tags(timeformat($row['end_time'])),
  1411. );
  1412. $emaildata = loadEmailTemplate('paid_subscription_reminder', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
  1413. // Send the actual email.
  1414. sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
  1415. }
  1416. $smcFunc['db_free_result']($request);
  1417. // Mark the reminder as sent.
  1418. if (!empty($subs_reminded))
  1419. $smcFunc['db_query']('', '
  1420. UPDATE {db_prefix}log_subscribed
  1421. SET reminder_sent = {int:reminder_sent}
  1422. WHERE id_sublog IN ({array_int:subscription_list})',
  1423. array(
  1424. 'subscription_list' => $subs_reminded,
  1425. 'reminder_sent' => 1,
  1426. )
  1427. );
  1428. return true;
  1429. }
  1430. /**
  1431. * Check for un-posted attachments is something we can do once in a while :P
  1432. * This function uses opendir cycling through all the attachments
  1433. */
  1434. function scheduled_remove_temp_attachments()
  1435. {
  1436. global $modSettings;
  1437. // We need to know where this thing is going.
  1438. require_once(SUBSDIR . '/Attachment.subs.php');
  1439. $attach_dirs = attachmentPaths();
  1440. foreach ($attach_dirs as $attach_dir)
  1441. {
  1442. $dir = @opendir($attach_dir) or fatal_lang_error('cant_access_upload_path', 'critical');
  1443. while ($file = readdir($dir))
  1444. {
  1445. if ($file == '.' || $file == '..')
  1446. continue;
  1447. if (strpos($file, 'post_tmp_') !== false)
  1448. {
  1449. // Temp file is more than 5 hours old!
  1450. if (filemtime($attach_dir . '/' . $file) < time() - 18000)
  1451. @unlink($attach_dir . '/' . $file);
  1452. }
  1453. }
  1454. closedir($dir);
  1455. }
  1456. }
  1457. /**
  1458. * Check for move topic notices that have past their best by date
  1459. */
  1460. function scheduled_remove_topic_redirect()
  1461. {
  1462. global $smcFunc;
  1463. // init
  1464. $topics = array();
  1465. // We will need this for lanaguage files
  1466. loadEssentialThemeData();
  1467. // Find all of the old MOVE topic notices that were set to expire
  1468. $request = $smcFunc['db_query']('', '
  1469. SELECT id_topic
  1470. FROM {db_prefix}topics
  1471. WHERE redirect_expires <= {int:redirect_expires}
  1472. AND redirect_expires <> 0',
  1473. array(
  1474. 'redirect_expires' => time(),
  1475. )
  1476. );
  1477. while ($row = $smcFunc['db_fetch_row']($request))
  1478. $topics[] = $row[0];
  1479. $smcFunc['db_free_result']($request);
  1480. // Zap, your gone
  1481. if (count($topics) > 0)
  1482. {
  1483. require_once(SUBSDIR . '/Topic.subs.php');
  1484. removeTopics($topics, false, true);
  1485. }
  1486. return true;
  1487. }
  1488. /**
  1489. * Check for old drafts and remove them
  1490. */
  1491. function scheduled_remove_old_drafts()
  1492. {
  1493. global $smcFunc, $modSettings;
  1494. if (empty($modSettings['drafts_keep_days']))
  1495. return true;
  1496. // init
  1497. $drafts= array();
  1498. // We need this for language items
  1499. loadEssentialThemeData();
  1500. // Find all of the old drafts
  1501. $request = $smcFunc['db_query']('', '
  1502. SELECT id_draft
  1503. FROM {db_prefix}user_drafts
  1504. WHERE poster_time <= {int:poster_time_old}',
  1505. array(
  1506. 'poster_time_old' => time() - (86400 * $modSettings['drafts_keep_days']),
  1507. )
  1508. );
  1509. while ($row = $smcFunc['db_fetch_row']($request))
  1510. $drafts[] = (int) $row[0];
  1511. $smcFunc['db_free_result']($request);
  1512. // If we have old one, remove them
  1513. if (count($drafts) > 0)
  1514. {
  1515. require_once(SUBSDIR . '/Drafts.subs.php');
  1516. deleteDrafts($drafts, -1, false);
  1517. }
  1518. return true;
  1519. }