/** * Re-syncs if a user can access a mention, * * - for example if they loose or gain access to a board, this will correct * the viewing of the mention table. Since this can be a large job it is run * as a scheduled immediate task */ public function user_access_mentions() { global $modSettings; $db = database(); $user_access_mentions = @unserialize($modSettings['user_access_mentions']); // This should be set only because of an immediate scheduled task, so higher priority if (!empty($user_access_mentions)) { foreach ($user_access_mentions as $member => $begin) { // Just to stay on the safe side... if (empty($member)) { continue; } require_once SUBSDIR . '/Boards.subs.php'; require_once SUBSDIR . '/Mentions.subs.php'; require_once SUBSDIR . '/Members.subs.php'; $user_see_board = memberQuerySeeBoard($member); $limit = 100; // We need to repeat this twice: once to find the boards the user can access, // once for those he cannot access foreach (array('can', 'cannot') as $can) { // Let's always start from the begin $start = $begin; while (true) { // Find all the mentions that this user can or cannot see $request = $db->query('', ' SELECT mnt.id_mention, m.id_board FROM {db_prefix}log_mentions as mnt LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = mnt.id_msg) LEFT JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) WHERE mnt.id_member = {int:current_member} AND mnt.mention_type IN ({array_string:mention_types}) AND {raw:user_see_board} LIMIT {int:start}, {int:limit}', array('current_member' => $member, 'mention_types' => array('men', 'like', 'rlike'), 'user_see_board' => ($can == 'can' ? '' : 'NOT ') . $user_see_board, 'start' => $start, 'limit' => $limit)); $mentions = array(); $remove = array(); while ($row = $db->fetch_assoc($request)) { if (empty($row['id_board'])) { $remove[] = $row['id_mention']; } else { $mentions[] = $row['id_mention']; } } $db->free_result($request); if (!empty($remove)) { removeMentions($remove); } // If we found something toggle them and increment the start for the next round if (!empty($mentions)) { toggleMentionsAccessibility($mentions, $can == 'can'); } else { break; } // Next batch $start += $limit; } } // Drop the member unset($user_access_mentions[$member]); // And save everything for the next run updateSettings(array('user_access_mentions' => serialize($user_access_mentions))); // Count helps keep things correct countUserMentions(false, '', $member); // Run this only once for each user, it may be quite heavy, let's split up the load break; } // If there are no more users, scheduleTaskImmediate can be stopped if (empty($user_access_mentions)) { removeScheduleTaskImmediate('user_access_mentions', false); } return true; } else { // Checks 10 users at a time, the scheduled task is set to run once per hour, so 240 users a day // @todo <= I know you like it Spuds! :P It may be necessary to set it to something higher. $limit = 10; $current_check = !empty($modSettings['mentions_member_check']) ? $modSettings['mentions_member_check'] : 0; require_once SUBSDIR . '/Members.subs.php'; require_once SUBSDIR . '/Mentions.subs.php'; // Grab users with mentions $request = $db->query('', ' SELECT COUNT(DISTINCT(id_member)) FROM {db_prefix}log_mentions WHERE id_member > {int:last_id_member} AND mention_type IN ({array_string:mention_types})', array('last_id_member' => $current_check, 'mention_types' => array('men', 'like', 'rlike'))); list($remaining) = $db->fetch_row($request); $db->free_result($request); if ($remaining == 0) { $current_check = 0; } // Grab users with mentions $request = $db->query('', ' SELECT DISTINCT(id_member) as id_member FROM {db_prefix}log_mentions WHERE id_member > {int:last_id_member} AND mention_type IN ({array_string:mention_types}) LIMIT {int:limit}', array('last_id_member' => $current_check, 'mention_types' => array('men', 'like', 'rlike'), 'limit' => $limit)); // Remember where we are updateSettings(array('mentions_member_check' => $current_check + $limit)); while ($row = $db->fetch_assoc($request)) { // Rebuild 'query_see_board', a lot of code duplication... :( $user_see_board = memberQuerySeeBoard($row['id_member']); // Find out if this user cannot see something that was supposed to be able to see $request2 = $db->query('', ' SELECT mnt.id_mention FROM {db_prefix}log_mentions as mnt LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = mnt.id_msg) LEFT JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) WHERE mnt.id_member = {int:current_member} AND mnt.mention_type IN ({array_string:mention_types}) AND {raw:user_see_board} AND status < 0 LIMIT 1', array('current_member' => $row['id_member'], 'mention_types' => array('men', 'like', 'rlike'), 'user_see_board' => 'NOT ' . $user_see_board)); // One row of results is enough: scheduleTaskImmediate! if ($db->num_rows($request2) == 1) { if (!empty($modSettings['user_access_mentions'])) { $modSettings['user_access_mentions'] = @unserialize($modSettings['user_access_mentions']); } else { $modSettings['user_access_mentions'] = array(); } // But if the member is already on the list, let's skip it if (!isset($modSettings['user_access_mentions'][$row['id_member']])) { $modSettings['user_access_mentions'][$row['id_member']] = 0; updateSettings(array('user_access_mentions' => serialize(array_unique($modSettings['user_access_mentions'])))); scheduleTaskImmediate('user_access_mentions'); } } $db->free_result($request2); } $db->free_result($request); return true; } }
/** * Calls the supplied task_name so that it is executed. * * - Logs that the task was executed with success or if it failed * * @package ScheduledTasks * @param int $id_task specific id of the task to run, used for logging * @param string $task_name name of the task, class name, function name, method in ScheduledTask.class */ function run_this_task($id_task, $task_name) { global $time_start, $modSettings; // Let's start logging the task and saying we failed it $log_task_id = logTask(0, $id_task); // The method must exist in ScheduledTask class, or we are wasting our time. // Actually for extendability sake, we need to have other ways, so: // A simple procedural function? if (strpos($task_name, '::') === false && function_exists($task_name)) { $method = $task_name; // Do the task... $completed = $method(); } else { // It may be a custom one if (strpos($task_name, '::') !== false) { $call = explode('::', $task_name); $task_object = new $call[0](); $method = $call[1]; } else { $task_object = new Scheduled_Task(); $method = $task_name; } if (method_exists($task_object, $method)) { // Try to stop a timeout, this would be bad... @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Do the task... $completed = $task_object->{$method}(); } } // Log that we did it ;) if ($completed) { // Taking care of scheduleTaskImmediate having a maximum of 10 "fast" executions $scheduleTaskImmediate = @unserialize($modSettings['scheduleTaskImmediate']); if (!empty($scheduleTaskImmediate) && isset($scheduleTaskImmediate[$task_name])) { $scheduleTaskImmediate[$task_name]++; if ($scheduleTaskImmediate[$task_name] > 9) { removeScheduleTaskImmediate($task_name, false); } else { updateSettings(array('scheduleTaskImmediate' => serialize($scheduleTaskImmediate))); } } $total_time = round(microtime(true) - $time_start, 3); // If the task ended successfully, then log the proper time taken to complete logTask($log_task_id, $id_task, $total_time); } }