/** display the content of the sitemap linked to node $node_id * * there are three different variations (depends on configuration parameter 'scope'): * * - 0 (small): only show a map of the tree in the current area $area_id * - 1 (medium): show a list of available areas followed by the map of the current area $area_id * - 2 (large): show the maps of all available areas * * The default is 0 (small). * * @param object &$theme collects the (html) output * @param int $area_id identifies the area where $node_id lives * @param int $node_id the node to which this module is connected * @param array $module the module record straight from the database * @return bool TRUE on success + output via $theme, FALSE otherwise */ function sitemap_view(&$theme, $area_id, $node_id, $module) { global $USER; // // 1 -- determine scope of sitemap: 0=small, 1=medium, 2=large // $table = 'sitemaps'; $fields = array('header', 'introduction', 'scope'); $where = array('node_id' => intval($node_id)); $record = db_select_single_record($table, $fields, $where); if ($record === FALSE) { logger(sprintf('%s(): error retrieving configuration: %s', __FUNCTION__, db_errormessage())); $scope = 0; $header = ''; $introduction = ''; } else { $scope = intval($record['scope']); $header = trim($record['header']); $introduction = trim($record['introduction']); } // // 2 -- compute a list of areas to process (could be just 1) // // 2A -- retrieve all areas, including those out of bounds for this user if (($all_areas = get_area_records()) === FALSE) { logger(sprintf('%s(): huh? cannot get area records: %s', __FUNCTION__, db_errormessage())); return FALSE; // shouldn't happen } // 2B -- narrow down the selection (active, (private) access allowed, within scope) $areas = array(); foreach ($all_areas as $id => $area) { if (db_bool_is(TRUE, $area['is_active']) && (db_bool_is(FALSE, $area['is_private']) || $USER->has_intranet_permissions(ACL_ROLE_INTRANET_ACCESS, $id))) { if ($scope == 2 || $scope == 1 || $scope == 0 && $id == $area_id) { $title = $area['title']; $params = array('area' => $id); $href = was_node_url(NULL, $params, $title, $theme->preview_mode); $areas[$id] = html_a($href, NULL, NULL, $title); } } } unset($all_areas); // $areas now holds all areas that we should to process if (sizeof($areas) <= 0) { logger(sprintf('%s(): weird, no areas to process; bailing out', __FUNCTION__)); return FALSE; // shouldn't happen } // // 3 -- maybe output a header and an introduction // if (!empty($header)) { $theme->add_content('<h2>' . $header . '</h2>'); } if (!empty($introduction)) { $theme->add_content($introduction); } // // 4 - Actually output a sitemap by walking the tree once for every elegible area // foreach ($areas as $id => $area_anchor) { if ($scope == 1 && $area_id != $id) { // 1=medium only shows area map of $area_id (and an area list lateron) continue; } // 4A -- output a clickable area title $theme->add_content('<h2>' . $area_anchor . '</h2>'); // 4B -- fetch the tree for this area... $tree = tree_build($id); tree_visibility($tree[0]['first_child_id'], $tree); // 4C -- ...and walk the tree sitemap_tree_walk($theme, $tree[0]['first_child_id'], $tree); unset($tree); } if ($scope == 1) { $theme->add_content('<h2>' . t('sitemap_available_areas', 'm_sitemap') . '</h2>'); $theme->add_content('<ul>'); foreach ($areas as $id => $area_anchor) { $theme->add_content('<li>' . $area_anchor); } $theme->add_content('</ul>'); } return TRUE; // indicate success }
/** main program for visitors * * this routine is called from /index.php. It is the main program for visitors. * * @return void page sent to the browser * @todo cleanup login/logout-code */ function main_index() { global $USER; global $CFG; global $LANGUAGE; /** initialise the program, setup database, read configuration, etc. */ require_once $CFG->progdir . '/init.php'; initialise(); was_version_check(); // this never returns if versions don't match // TODO: cleanup like in main_admin() // handle login/logout/continuation so we quickly find out which user is calling if (isset($_GET['logout'])) { /** loginlib.php contains both login- and logout-routines */ require_once $CFG->progdir . '/lib/loginlib.php'; was_logout(); // may or may not return here } elseif (isset($_GET['login'])) { /** loginlib.php contains both login- and logout-routines */ require_once $CFG->progdir . '/lib/loginlib.php'; was_login(magic_unquote($_GET['login'])); // may or may not return here } elseif (isset($_COOKIE[$CFG->session_name])) { /** dbsessionlib.php contains our own database based session handler */ require_once $CFG->progdir . '/lib/dbsessionlib.php'; dbsession_setup($CFG->session_name); if (dbsession_exists(magic_unquote($_COOKIE[$CFG->session_name]))) { session_start(); } } // At this point we either have a valid session with a logged-in user // (indicated via existence of $_SESSION) or we are dealing with an anonymous // visitor with non-existing $_SESSION. Keep track of the number of calls // this user makes (may be logged lateron on logout). if (isset($_SESSION)) { if (!isset($_SESSION['session_counter'])) { // first time after login, record start time of session $_SESSION['session_counter'] = 1; $_SESSION['session_start'] = strftime("%Y-%m-%d %T"); } else { $_SESSION['session_counter']++; } } // Now is the time to create a USER object, even when the visitor is just a passerby // because we can then determine easily if a visitor is allowed certain things, e.g. // view a protected area or something /** useraccount.class.php is used to define the USER object */ require_once $CFG->progdir . '/lib/useraccount.class.php'; if (isset($_SESSION) && isset($_SESSION['user_id'])) { $USER = new Useraccount($_SESSION['user_id']); $USER->is_logged_in = TRUE; $_SESSION['language_key'] = $LANGUAGE->get_current_language(); // remember language set via _GET or otherwise } else { $USER = new Useraccount(); $USER->is_logged_in = FALSE; } // Check for the special preview-mode // This allows a webmaster to preview a page in the correct environment (theme) // even when the page is under embargo. Note that the node_id and area_id are // retrieved from the session; the user only has a cryptic preview-code. // See pagemanagerlib.php for more information (function task_page_preview()). $in_preview_mode = FALSE; if ($USER->is_logged_in) { $preview_code_from_url = get_parameter_string('preview'); if (!is_null($preview_code_from_url) && isset($_SESSION['preview_salt']) && isset($_SESSION['preview_node'])) { $hash = md5($_SESSION['preview_salt'] . $_SESSION['preview_node']); if ($hash === $preview_code_from_url) { $node_id = intval($_SESSION['preview_node']); $area_id = intval($_SESSION['preview_area']); $area = db_select_single_record('areas', '*', array('area_id' => $area_id)); if ($area === FALSE) { logger("Fatal error 070: cannot preview node '{$node_id}' in area '{$area_id}'"); error_exit('070'); } else { $tree = tree_build($area_id); $in_preview_mode = TRUE; } } } } if ($in_preview_mode == FALSE) { $requested_area = get_requested_area(); $requested_node = get_requested_node(); $req_area_str = is_null($requested_area) ? "NULL" : strval($requested_area); $req_node_str = is_null($requested_node) ? "NULL" : strval($requested_node); if (($area = calculate_area($requested_area, $requested_node)) === FALSE) { logger("Fatal error 080: no valid area (request: area='{$req_area_str}', node='{$req_node_str}')"); error_exit('080'); // no such area } $area_id = intval($area['area_id']); // If $USER has no permission to view area $area_id, we simply bail out. // Rationale: if the user is genuine, she knows about logging in first. // If the user is NOT logged in and tries to view a protected area, I'd consider // it malicious, and in that case I won't even confirm the existence of // the requested area. (If a cracker simply tries areas 0,1,.. and sometimes is greeted // with 'please enter credentials' and sometimes with 'area does not exist', this // provides information to the cracker. I don't want that). Note that the error code // is the same as the one for non-existing area. In other words: for an unauthorised // visitor an existing private area is just as non-existent as a non-existing public area. if (db_bool_is(TRUE, $area['is_private']) && !$USER->has_intranet_permissions(ACL_ROLE_INTRANET_ACCESS, $area_id)) { logger(sprintf("Fatal error 080: no view permissions for area '%d' (request: area='%s', node='%s')", $area_id, $req_area_str, $req_node_str)); error_exit('080'); // no such area } // still here? // then we've got a valid $area_id and corresponding $area record. // now we need to figure out which $node_id to use $tree = tree_build($area_id); if (($node_id = calculate_node_id($tree, $area_id, $requested_node)) === FALSE) { logger(sprintf("Fatal error 080: no valid node within area '%d' (request: area='%s', node='%s')", $area_id, $req_area_str, $req_node_str)); error_exit('080'); // no such area } } // At this point we have the following in our hands // - a valid $area_id // - a valid $node_id // - the complete tree from area $area_id in $tree // - the area record from database in $area // - the node record from database in $tree[$node_id]['record'] // - a flag that signals preview mode in $in_preview_mode // We are on our way to generate a full page with content and all, // but otoh we MIGHT be in the middle of a redirect, so we may have to // leave without showing anything at all... if (!empty($tree[$node_id]['record']['link_href'])) { update_statistics($node_id); if (isset($_SESSION)) { session_write_close(); } redirect_and_exit(htmlspecialchars($tree[$node_id]['record']['link_href'])); // exit; redirect_and_exit() never returns } /** themelib contains the theme factory */ require_once $CFG->progdir . '/lib/themelib.php'; // And now we know about the $area, we can carry on determining which $theme to use. // $theme = theme_factory($area['theme_id'], $area_id, $node_id); if ($theme === FALSE) { logger("Fatal error 090: cannot setup theme '{$area['theme_id']}' in area '{$area_id}'"); error_exit('090'); } // Tell the theme about the preview mode $theme->set_preview_mode($in_preview_mode); // Now all we need to do is let the module connected to node $node_id generate output $module_id = $tree[$node_id]['record']['module_id']; module_view($theme, $area_id, $node_id, $module_id); // Remember this visitor update_statistics($node_id); // Finally, send output to user $theme->send_output(); if (isset($_SESSION)) { session_write_close(); } // done! exit; }
/** construct $this->tree for future reference * * this constructs the tree of the area $area_id so all other * routines can simply use that tree instead of passing it around * again and again via function arguments. * * @param int $area_id indicates which area to buffer if not already buffered * @param bool $force re-read of the tree for area $area_id * @return void a copy of the area tree is cached in $this->tree */ function build_cached_tree($area_id, $force = FALSE) { if ($this->area_id !== $area_id || $force) { $this->area_id = $area_id; $this->tree = tree_build($this->area_id, $force); } }
/** construct a dialog definition for the workshop configuration * * this generates an array which defines the dialog for workshop configuration. * There are a few plain fields that simply go into the appropriate workshops * record and the save and cancel button. However, there may also be items * related to ACLs. These fields are used to define the user's roles and the * should be presented in a table. We abuse the field names for this purpose: * if the first 3 characters are 'acl' we put the widgets in an HTML-table, otherwise * it is just an ordinary widget. * * Note that in the case of a single simple user without any aquaintances (ie. a user * that is not member of any group) the user is not able to add herself to the list * of authorised users. What is the point of having a _collaborative_ workshop when * you are the only one to collaborate with? (However, it would be fairly easy to force/add * an entry for this user if the tmp table would turn out empty. Maybe later....?) * @param object &$output collects the html output (if any) * @param int $viewonly if TRUE the Save button is not displayed and values cannot be changed * @param int $module_id indicates the id of the crew module in the database (needed for ACL) * @param int $area_id indicates the area where node_id lives (needed for ACL) * @param int $node_id indicates which page we are loooking at (needed for ACL) * @param int $user_id indicates the current user (needed for ACL) * @return array dialog definition */ function crew_get_dialogdef(&$output, $viewonly, $module_id, $area_id, $node_id, $user_id) { global $DB, $USER; static $dialogdef = NULL; if (!is_null($dialogdef)) { // recycle return $dialogdef; } $visibilities = array('2' => array('option' => t('visibility_world_label', 'm_crew'), 'title' => t('visibility_world_title', 'm_crew')), '1' => array('option' => t('visibility_all_label', 'm_crew'), 'title' => t('visibility_all_title', 'm_crew')), '0' => array('option' => t('visibility_workers_label', 'm_crew'), 'title' => t('visibility_workers_title', 'm_crew'))); $roles = array(ACL_ROLE_NONE => array('option' => t('acl_role_none_option', 'admin'), 'title' => t('acl_role_none_title', 'admin')), CREW_ACL_ROLE_READONLY => array('option' => t('crew_acl_role_readonly_option', 'm_crew'), 'title' => t('crew_acl_role_readonly_title', 'm_crew')), CREW_ACL_ROLE_READWRITE => array('option' => t('crew_acl_role_readwrite_option', 'm_crew'), 'title' => t('crew_acl_role_readwrite_title', 'm_crew')), ACL_ROLE_GURU => array('option' => t('acl_role_guru_option', 'admin'), 'title' => t('acl_role_guru_title', 'admin'))); // 1 -- plain & simple fields // make a fresh start with data from the database $dialogdef = array('header' => array('type' => F_ALPHANUMERIC, 'name' => 'header', 'minlength' => 0, 'maxlength' => 240, 'columns' => 30, 'label' => t('header_label', 'm_crew'), 'title' => t('header_title', 'm_crew'), 'viewonly' => $viewonly, 'value' => '', 'old_value' => ''), 'introduction' => array('type' => F_ALPHANUMERIC, 'name' => 'introduction', 'minlength' => 0, 'maxlength' => 32768, 'columns' => 50, 'rows' => 10, 'label' => t('introduction_label', 'm_crew'), 'title' => t('introduction_title', 'm_crew'), 'viewonly' => $viewonly, 'value' => '', 'old_value' => ''), 'visibility' => array('type' => F_RADIO, 'name' => 'visibility', 'value' => 0, 'old_value' => 0, 'options' => $visibilities, 'viewonly' => $viewonly, 'title' => t('visibility_title', 'm_crew'), 'label' => t('visibility_label', 'm_crew'))); $table = 'workshops'; $fields = array('header', 'introduction', 'visibility'); $where = array('node_id' => intval($node_id)); if (($record = db_select_single_record($table, $fields, $where)) === FALSE) { logger(sprintf('%s(): error retrieving CREW configuration: %s', __FUNCTION__, db_errormessage())); $output->add_message(t('error_retrieving_data', 'admin')); } else { foreach ($record as $name => $value) { $dialogdef[$name]['value'] = $dialogdef[$name]['old_value'] = $value; } } $sql = sprintf('DROP TEMPORARY TABLE IF EXISTS %screw_tmp', $DB->prefix); $retval = $DB->exec($sql); if ($USER->has_job_permissions(JOB_PERMISSION_ACCOUNTMANAGER)) { // Allow $USER to set/edit any user's permission because she is already able // to manipulate useraccounts, _all_ useraccounts. We are sure that $USER is a // valid user with at least JOB_PERMISSION_STARTCENTER or else we would not be here. $sql = sprintf('CREATE TEMPORARY TABLE %screw_tmp ' . 'SELECT u.acl_id, u.username, u.full_name, amn.permissions_modules ' . 'FROM %susers u ' . 'LEFT JOIN %sacls_modules_nodes amn ' . 'ON amn.acl_id = u.acl_id AND amn.module_id = %d AND amn.node_id = %d ' . 'ORDER BY u.full_name', $DB->prefix, $DB->prefix, $DB->prefix, $module_id, $node_id); } else { // Only allow $USER to set permissions for all her acquaintances, ie. all users // that are members of the group(s) that $USER is a also member of. $sql = sprintf('CREATE TEMPORARY TABLE %screw_tmp ' . 'SELECT DISTINCT u.acl_id, u.username, u.full_name, amn.permissions_modules ' . 'FROM %susers u ' . 'INNER JOIN %susers_groups_capacities ugc1 USING(user_id) ' . 'INNER JOIN %susers_groups_capacities ugc2 USING(group_id) ' . 'LEFT JOIN %sacls_modules_nodes amn ' . 'ON amn.acl_id = u.acl_id AND amn.module_id = %d AND amn.node_id = %d ' . 'WHERE ugc2.user_id = %d ' . 'ORDER BY u.full_name', $DB->prefix, $DB->prefix, $DB->prefix, $DB->prefix, $DB->prefix, $module_id, $node_id, $user_id); } $retval = $DB->exec($sql); // at this point we have a temporary table with all 'editable' accounts // we first add those to the dialogdef. $table = 'crew_tmp'; $fields = '*'; $where = ''; $order = array('full_name', 'username'); if (($records = db_select_all_records($table, $fields, $where, $order)) === FALSE) { logger(sprintf('%s(): error retrieving elegible CREW-members: %s', __FUNCTION__, db_errormessage())); $output->add_message(t('error_retrieving_data', 'admin')); } else { foreach ($records as $record) { $acl_id = intval($record['acl_id']); $name = 'acl_rw_' . $acl_id; $dialogdef[$name] = array('type' => F_LISTBOX, 'name' => $name, 'value' => is_null($record['permissions_modules']) ? 0 : $record['permissions_modules'], 'old_value' => $record['permissions_modules'], 'acl_id' => $acl_id, 'options' => $roles, 'viewonly' => $viewonly, 'title' => $record['username'], 'label' => $record['full_name']); } } // the next step is to generate a list of any OTHER accounts that happen to have // permissions for this module on this node other than ACL_ROLE_NONE. // This list consists of a few UNIONs that effectively yields all accounts that // somehow have a non-0 permissions_modules, either global (acls), any node for // this module (acls_modules), any node within this area (acls_modules_area), // any node that is an ancestor of node_id (acls_modules_nodes) OR this specific // node for a user that is NOT an acquaintance (ie. who is not in the temp table). // Note that we don't check the ancestors (parents) when node happens to be at // the top level within the area, ie. when parent is 0. We also peek inside // 'acls_areas' and 'acls_nodes'. Pfew, complicated... // All these OTHER accounts cannot be manipulated by $USER because all accounts // would then be in the temp table, so there. // Since there may be more records for the same user (or rather acl_id), we need // to drill down the results. As all permissions are additive we can simply OR // these together per acl_id/user which yields a single combined role for that user. $tree = tree_build($area_id); $ancestors = array(); for ($next_id = $tree[$node_id]['parent_id']; $next_id; $next_id = $tree[$next_id]['parent_id']) { $ancestors[] = $next_id; } unset($tree); $sql = empty($ancestors) ? '' : sprintf('SELECT u.acl_id, u.username, u.full_name, amn.permissions_modules ' . 'FROM %susers u INNER JOIN %sacls_modules_nodes amn USING (acl_id) ' . 'WHERE amn.permissions_modules <> 0 AND amn.module_id = %d AND amn.node_id IN (%s)', $DB->prefix, $DB->prefix, $module_id, join(',', $ancestors)) . ' UNION ' . sprintf('SELECT u.acl_id, u.username, u.full_name, an.permissions_modules ' . 'FROM %susers u INNER JOIN %sacls_nodes an USING (acl_id) ' . 'WHERE an.permissions_modules <> 0 AND an.node_id IN (%s)', $DB->prefix, $DB->prefix, join(',', $ancestors)) . ' UNION '; $sql .= sprintf('SELECT u.acl_id, u.username, u.full_name, a.permissions_modules ' . 'FROM %susers u INNER JOIN %sacls a USING (acl_id) ' . 'WHERE a.permissions_modules <> 0', $DB->prefix, $DB->prefix) . ' UNION ' . sprintf('SELECT u.acl_id, u.username, u.full_name, am.permissions_modules ' . 'FROM %susers u INNER JOIN %sacls_modules am USING (acl_id) ' . 'WHERE am.permissions_modules <> 0 AND am.module_id = %d', $DB->prefix, $DB->prefix, $module_id) . ' UNION ' . sprintf('SELECT u.acl_id, u.username, u.full_name, aa.permissions_modules ' . 'FROM %susers u INNER JOIN %sacls_areas aa USING (acl_id) ' . 'WHERE aa.permissions_modules <> 0 AND aa.area_id = %d', $DB->prefix, $DB->prefix, $area_id) . ' UNION ' . sprintf('SELECT u.acl_id, u.username, u.full_name, ama.permissions_modules ' . 'FROM %susers u INNER JOIN %sacls_modules_areas ama USING (acl_id) ' . 'WHERE ama.permissions_modules <> 0 AND ama.module_id = %d AND ama.area_id = %d', $DB->prefix, $DB->prefix, $module_id, $area_id) . ' UNION ' . sprintf('SELECT u.acl_id, u.username, u.full_name, an.permissions_modules ' . 'FROM %susers u INNER JOIN %sacls_nodes an USING (acl_id) ' . 'WHERE an.permissions_modules <> 0 AND an.node_id = %d', $DB->prefix, $DB->prefix, $node_id) . ' UNION ' . sprintf('SELECT u.acl_id, u.username, u.full_name, amn.permissions_modules ' . 'FROM %susers u INNER JOIN %sacls_modules_nodes amn USING (acl_id) ' . 'LEFT JOIN %screw_tmp tmp USING(acl_id) ' . 'WHERE amn.permissions_modules <> 0 AND amn.module_id = %d AND amn.node_id = %d ' . 'AND tmp.acl_id IS NULL ', $DB->prefix, $DB->prefix, $DB->prefix, $module_id, $node_id) . 'ORDER BY full_name, acl_id'; if (($result = $DB->query($sql)) === FALSE) { logger(sprintf('%s(): error retrieving other account names: %s', __FUNCTION__, db_errormessage())); $output->add_message(t('error_retrieving_data', 'admin')); } else { if ($result->num_rows > 0) { $records = array(); while (($record = $result->fetch_row_assoc()) !== FALSE) { $acl_id = intval($record['acl_id']); if (isset($records[$acl_id])) { $records[$acl_id]['permissions_modules'] |= intval($record['permissions_modules']); } else { $records[$acl_id] = $record; } } $result->close(); foreach ($records as $acl_id => $record) { $name = 'acl_ro_' . $acl_id; $dialogdef[$name] = array('type' => F_LISTBOX, 'name' => $name, 'value' => is_null($record['permissions_modules']) ? 0 : $record['permissions_modules'], 'options' => $roles, 'viewonly' => TRUE, 'title' => $record['username'], 'label' => $record['full_name']); } } } if (!$viewonly) { $dialogdef['button_save'] = dialog_buttondef(BUTTON_SAVE); } $dialogdef['button_cancel'] = dialog_buttondef(BUTTON_CANCEL); return $dialogdef; }
/** read all nodes from table for this area and construct a tree * * this constructs the tree for this area, and makes sure that only * non-hidden pages and non-empty sections are visible * * @param int $area_id the tree is built from nodes within this area * @return array an array with the node-records linked as a tree */ function construct_tree($area_id) { $tree = tree_build($area_id); foreach ($tree as $node_id => $item) { $tree[$node_id]['is_visible'] = FALSE; $tree[$node_id]['is_breadcrumb'] = FALSE; } tree_visibility($tree[0]['first_child_id'], $tree); return $tree; }