/** add a message to message queue of 0 or more alerts * * this adds $alert_message to the message buffers of 0 or more alert accounts * The alerts that qualify to receive this addition via the alerts_areas_nodes table. * The logic in that table is as follows: * - the area_id must match the area_id(s) (specified in $areas) OR * it must be 0 which acts as a wildcard for ALL areas * - the node_id must match the node_id(s) specified in $nodes) OR * it must be 0 which acts as a wildcard for ALL nodes * Also the account must be active and the flag for the area/node-combination * must be TRUE. * * As a rule this routine is called with a single area_id in $areas and * a collection of node_id's in $nodes. The nodes follow the path up through * the tree, in order to alert accounts that are only watching a section at * a higher level. * * Example: * If user 'webmaster' adds new page, say 34, to subsection 8 in * section 4 in area 1, you get something like this: * * queue_area_node_alert(1,array(8,4,34),'node 34 added','webmaster'); * * The effect will be that all accounts with the following combinations of * area A and node N have the message added to their buffers: * A=0, N=1 - qualifies for all nodes in all areas * A=1, N=0 - qualifies for all nodes in area 1 * A=1, N=4 - qualifies for node 4 in area 1 * A=1, N=8 - qualifies for node 8 in area 1 * * It is very well possible that no message is added at all if there is no * alert account watching the specified area and node (using wildcards or * otherwise). * * Near the end of this routine, we check the queue with pending messages, * and perhaps send out a few alerts. The number of messages that can be * sent from here is limited; we don't want to steal too much time from an * unsuspecting user. It is the task of cron.php to take care of eventually * sending the queued messages. However, sending only a few messages won't * be noticed. I hope. * * Note that this routine adds a timestamp to the message and, if it is * specified, the name of the user. * * Also note that the messages are added to the buffer with the last message * at the top, it means that the receiver will travel back in time reading * the collection of messages. This is based on the assumption that the latest * messages sometimes override a previous message and therefore should be * read first. * * @param mixed $areas an array or a single int identifying the area(s) of interest * @param mixed $nodes an array or a single int identifying the node(s) of interest * @param string $message the message to add to the buffer of qualifying alert accounts * @param string $username (optional) the name of the user that initiated the action * @return void * @uses $DB; */ function queue_area_node_alert($areas, $nodes, $alert_message, $username = '') { global $DB; // 0 -- massage the message, add a timestamp, optional username and an extra empty line $message = sprintf("%s%s\n%s\n\n", strftime('%Y-%m-%d %T'), empty($username) ? '' : ' (' . $username . ')', $alert_message); // 1 -- construct the area part of the statement // example where-clause (part 1/3): ((area_id = 0) OR (area_id = 1)) $where_clause = ''; if (!empty($areas)) { $where_clause_area = '(n.area_id = 0)'; // area_id = 0 means 'any area' in this context if (is_array($areas)) { foreach ($areas as $area_id) { $where_clause_area .= sprintf(' OR (n.area_id = %d)', intval($area_id)); } } else { $where_clause_area .= sprintf(' OR (n.area_id = %d)', intval($areas)); } $where_clause .= '(' . $where_clause_area . ') AND '; } // 2 -- construct the node part of the statement // example where-clause (part 2/3): ((node_id = 0) OR (node_id = 4) OR (node_id = 8) OR (node_id = 34)) if (!empty($nodes)) { $where_clause_node = '(n.node_id = 0)'; // node_id = 0 means 'any node' in this context if (is_array($nodes)) { foreach ($nodes as $node_id) { $where_clause_node .= sprintf(' OR (n.node_id = %d)', intval($node_id)); } } else { $where_clause_node .= sprintf(' OR (n.node_id = %d)', intval($nodes)); } $where_clause .= '(' . $where_clause_node . ') AND '; } // 3 -- only send msgs to active alerts // example where-clause (part 3/3): combine previous two parts with check for active alert accounts/flags $where_clause .= '(a.is_active) AND (n.flag)'; // 4 -- construct complete statement // Note that we also are constructing the update of the message field manually, so // we MUST take care of proper escaping and quoting. Also, the trick with $DB->concat() // complicates this SQL-statement, but alas this is necessary due to the non-standard way // MySQL interprets the string concatenation operator '||'. Aaarrrghhhhhh // (see http://troels.arvin.dk/db/rdbms/#functions-concat) $sql = sprintf('UPDATE %salerts AS a INNER JOIN %salerts_areas_nodes AS n ON a.alert_id = n.alert_id ' . 'SET a.message_buffer = %s, a.messages = a.messages + 1 ' . 'WHERE %s', $DB->prefix, $DB->prefix, $DB->concat(db_escape_and_quote($message), 'a.message_buffer'), $where_clause); $retval = $DB->exec($sql); if ($retval === FALSE) { logger(__CLASS__ . ": error queueing alerts: '" . db_errormessage() . "' with sql = {$sql}"); } else { logger(__CLASS__ . ": number of alerts queued: {$retval}"); logger(__FUNCTION__ . "(): sql = " . $sql, WLOG_DEBUG); } // Even if we did not add a message ourselves, we can 'steal' some time // of the current user and see if there are mails that need to be sent out. cron_send_queued_alerts(2); // limit processing to 2 messages at this time }
/** main program for site maintenance * * This is the main administrator program. * First step is to deal with users logging in or out. * If a user is not logged in, a login dialog is displayed. * If a user is logged in but has no admin privileges, she * is redirected to the public site (ie. index.php). * * Once we have established that the user is an administrator, * we setup an output collecting object and see what the user * wants us to do by interpreting the parameter 'job'. * If the user has access to the specified job, the corresponding * code is included and the main routine of that handler is called. * It is then the responsability of that handler to further decide * what needs to be done. * After the handler returns, the collected output is sent to the user. * This includes the main navigation (i.e. links to the various * 'managers') and also the menu and the content generated by the * handler. * * If the user has no privilege to access a particular manager, * an error messate is displayed in both the message area and the content * area. This makes it clear to the user that access is denied. * Note that the inaccessible items are displayed in the main navigation * via 'dimmed' (light-grey) links or black/white images. * By showing these 'dimmed' links, the user will be aware that there * is more that just what she is allowed to see. This is more transparent * than suppressing items and keeping them secret. * * @return void generated page sent to user's browser * @uses $CFG; * @uses $LANGUAGE; * @uses $USER; * @todo should we cater for a special 'print' button + * support for a special style sheet for media="print"? */ function main_admin() { global $CFG; global $LANGUAGE; global $USER; /** initialise, setup database, read configuration, etc. */ require_once $CFG->progdir . '/init.php'; initialise(); // user must be logged in to perform any admin tasks at all if (isset($_GET['logout'])) { admin_logout_and_exit(); } elseif (isset($_GET['login'])) { $user_id = admin_login(magic_unquote($_GET['login'])); } elseif (isset($_COOKIE[$CFG->session_name])) { $user_id = admin_continue_session(); } else { admin_show_login_and_exit(); } /** useraccount.class.php is used to define the USER object */ require_once $CFG->progdir . '/lib/useraccount.class.php'; $USER = new Useraccount($user_id); $USER->is_logged_in = TRUE; $_SESSION['language_key'] = $LANGUAGE->get_current_language(); // remember language set via _GET or otherwise // Only admins are allowed, others are redirected to index.php if (!$USER->is_admin()) { logger("admin.php: '{$USER->username}' ({$USER->user_id}) is no admin and was redirected to index.php or login"); session_write_close(); non_admin_redirect_and_exit(); } // We now know that this user is an admin, but // is she allowed to perform upgrades if any? Check it out in 2 steps // 1--we do NOT want exit on error if the user has enough privileges // 2--we check the version and stay here if the user has enough privileges $exit_on_error = $USER->has_job_permissions(JOB_PERMISSION_UPDATE) ? FALSE : TRUE; $need_to_update = was_version_check($exit_on_error) ? FALSE : TRUE; // We are still here if versions are OK _or_ versions mismatch but user has UPDATE privilege. // Now we know we _will_ be generating output => setup output object // using the specified skin OR the user's prefererred skin OR the one // stored before in $_SESSION $_SESSION['skin'] = get_current_skin(); // echo "DDD: {$_SESSION['skin']}"; $output = new AdminOutput($_SESSION['skin'], $CFG->title); // Display a 'welcome message' if this is the first page after logging in. if ($_SESSION['session_counter'] == 1) { $output->add_message(t('login_user_success', 'admin', array('{USERNAME}' => $USER->username))); } // Let's see what what job needs to be done $job = $need_to_update ? JOB_UPDATE : get_parameter_string('job', JOB_STARTCENTER); // main dispatcher switch ($job) { case JOB_STARTCENTER: job_start($output); break; case JOB_PAGEMANAGER: add_javascript_popup_function($output, ' '); if ($USER->has_job_permissions(JOB_PERMISSION_PAGEMANAGER)) { include $CFG->progdir . '/lib/pagemanager.class.php'; $manager = new PageManager($output); } else { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('job_access_denied', 'admin')); $output->add_message(t('job_access_denied', 'admin')); } break; case JOB_FILEMANAGER: case JOB_FILEBROWSER: case JOB_IMAGEBROWSER: case JOB_FLASHBROWSER: add_javascript_popup_function($output, ' '); add_javascript_select_url_function($output, ' '); if ($USER->has_job_permissions(JOB_PERMISSION_FILEMANAGER)) { include $CFG->progdir . '/lib/filemanager.class.php'; $manager = new FileManager($output, $job); } else { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('job_access_denied', 'admin')); $output->add_message(t('job_access_denied', 'admin')); } break; case JOB_MODULEMANAGER: if ($USER->has_job_permissions(JOB_PERMISSION_MODULEMANAGER)) { include $CFG->progdir . '/lib/modulemanagerlib.php'; job_modulemanager($output); } else { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('job_access_denied', 'admin')); $output->add_message(t('job_access_denied', 'admin')); } break; case JOB_ACCOUNTMANAGER: if ($USER->has_job_permissions(JOB_PERMISSION_ACCOUNTMANAGER)) { include $CFG->progdir . '/lib/accountmanagerlib.php'; job_accountmanager($output); } else { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('job_access_denied', 'admin')); $output->add_message(t('job_access_denied', 'admin')); } break; case JOB_CONFIGURATIONMANAGER: if ($USER->has_job_permissions(JOB_PERMISSION_CONFIGURATIONMANAGER)) { include $CFG->progdir . '/lib/configurationmanagerlib.php'; job_configurationmanager($output); } else { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('job_access_denied', 'admin')); $output->add_message(t('job_access_denied', 'admin')); } break; case JOB_STATISTICS: if ($USER->has_job_permissions(JOB_PERMISSION_STATISTICS)) { include $CFG->progdir . '/lib/statisticslib.php'; job_statistics($output); } else { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('job_access_denied', 'admin')); $output->add_message(t('job_access_denied', 'admin')); } break; case JOB_TOOLS: if ($USER->has_job_permissions(JOB_PERMISSION_TOOLS)) { // user has permission to access at least one of the tools include $CFG->progdir . '/lib/toolslib.php'; job_tools($output); } else { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('job_access_denied', 'admin')); $output->add_message(t('job_access_denied', 'admin')); } break; case JOB_UPDATE: if ($USER->has_job_permissions(JOB_PERMISSION_UPDATE)) { // user has permission to access the update routine(s) include $CFG->progdir . '/lib/updatelib.php'; job_update($output); } else { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('job_access_denied', 'admin')); $output->add_message(t('job_access_denied', 'admin')); } break; default: if (!empty($job)) { $output->add_content("<h2>" . t('access_denied', 'admin') . "</h2>"); $output->add_content(t('unknown_job', 'admin', array('{JOB}' => htmlspecialchars($job)))); $output->add_message(t('unknown_job', 'admin', array('{JOB}' => htmlspecialchars($job)))); logger("'" . $USER->username . "': unknown job '" . htmlspecialchars($job) . "'"); } else { job_start($output); } break; } // the various functions job_*() will have put their output in $output // Now it is time to actually output the output to the user's browser. $output->send_output(); // make sure that any changes in $_SESSION are properly stored // note that we close the session only after all processing is done, // allowing the various job_*()'s to manipulate the session variables session_write_close(); // at this point we have sent the page to the user, // we can now use the remaining time in this run to process // a few alerts (if any). cron_send_queued_alerts(25); // if there are more than 25, do them later or let cron do it. return; }