예제 #1
0
/** manufacture a theme object
 *
 * This loads (includes) a specific theme based on the parameter
 * $theme_id. Relevant data is read from the database.
 *
 * @param int $theme_id denotes which theme to retrieve from database via primary key
 * @param int $area_id the area we're working in
 * @param int $node_id the node that is to be displayed
 * @return bool|object FALSE on error, or an instance of the specified theme class
 * @todo should we massage the directory and file names of the included theme?
 * @todo what if the theme is not found? Currently no alternative is loaded but FALSE is returned.
 * @uses $CFG
 */
function theme_factory($theme_id, $area_id, $node_id)
{
    global $CFG;
    $o = FALSE;
    // assume failure
    $theme_record = db_select_single_record('themes', '*', array('theme_id' => intval($theme_id), 'is_active' => TRUE));
    if ($theme_record !== FALSE) {
        /* We have an existing and active theme. We know that we can find the
         * theme's files in $CFG->progdir.'/themes/'.$theme_name.
         * The file to include is called $theme_filename and the class
         * is $theme_class. We will now try to include the relevant file
         * and instantiate the theme.
         */
        $theme_name = $theme_record['name'];
        // e.g. 'frugal'
        $theme_class = $theme_record['class'];
        // e.g. 'ThemeFrugal'
        $theme_filename = $theme_record['class_file'];
        // e.g. 'frugal.class.php'
        $theme_directory = $CFG->progdir . '/themes/' . $theme_name;
        if (is_file($theme_directory . '/' . $theme_filename)) {
            include_once $theme_directory . '/' . $theme_filename;
            $o = new $theme_class($theme_record, $area_id, $node_id);
        }
    }
    return $o;
}
/** display the content of the htmlpage linked to node $node_id
 *
 * @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 htmlpage_view(&$theme, $area_id, $node_id, $module)
{
    $record = db_select_single_record('htmlpages', 'page_data', array('node_id' => intval($node_id)));
    if ($record === FALSE) {
        $msg = "Oops. Something went wrong with the htmlpage linked to node '{$node_id}'";
        $theme->add_message($msg);
        $theme->add_popup_top($msg);
        $retval = FALSE;
    } else {
        //        if ((10 <= $node_id) && ($node_id <= 11)) {
        //            $msg = 'STUB to demonstrate the message area and popup-function (only pages 10 and 11): <b>'.
        //                  __FUNCTION__."</b>(\$theme,$area_id,$node_id,{$module['module_id']})";
        //            $theme->add_message($msg);
        //            $theme->add_popup_bottom(strip_tags($msg));
        //        }
        $theme->add_content($record['page_data']);
        $retval = TRUE;
    }
    return $retval;
}
 /** construct the (possibly translated) name of the last directory in the path
  *
  * This examines $path and returns a string with the last directory component.
  * There are a few special cases:
  *
  *  - the empty string indicates the root directory
  *  - /users/<userpath> maps to a (translated) string t('filemanager_personal')
  *  - /areas maps to a (translated) string t('filemanager_areas')
  *  - /groups maps to a (translated) string t('filemanager_groups')
  *  - /users maps to a (translated) string t('filemanager_users')
  *
  * All other variations yield the last component in the list of components.
  *
  * @param string $path the path to examine
  * @return string the (possibly translated) name of the last directory component
  */
 function vname($path)
 {
     global $USER;
     $vname = '';
     // assume nothing
     $path_components = $this->explode_path($path);
     $n = count($path_components);
     if ($n <= 1) {
         switch ($path_components[0]) {
             case 'areas':
                 $vname = t('filemanager_areas', 'admin');
                 break;
             case 'users':
                 $vname = t('filemanager_users', 'admin');
                 break;
             case 'groups':
                 $vname = t('filemanager_groups', 'admin');
                 break;
             case '':
                 $vname = t('filemanager_root', 'admin');
                 break;
         }
     } elseif ($n == 2) {
         $name = strval($path_components[1]);
         switch ($path_components[0]) {
             case 'areas':
                 $vname = $name;
                 // fall back
                 foreach ($this->areas as $area_id => $area) {
                     if ($area['path'] == $name) {
                         $vname = $area['title'];
                         break;
                     }
                 }
                 break;
             case 'users':
                 if ($name == $USER->path) {
                     $vname = t('filemanager_personal', 'admin');
                 } else {
                     $table = 'users';
                     $fields = array('full_name');
                     $where = array('path' => $name);
                     if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
                         $vname = $name;
                         // fall back
                     } else {
                         $vname = $record['full_name'];
                     }
                 }
                 break;
             case 'groups':
                 $table = 'groups';
                 $fields = array('full_name');
                 $where = array('path' => $name);
                 if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
                     $vname = $name;
                     // fall back
                 } else {
                     $vname = $record['full_name'];
                 }
                 break;
         }
     } else {
         $vname = $path_components[$n - 1];
         // last component of the path
     }
     return $vname;
 }
/** construct a title, summary and readmore prompt for an htmlpage page
 *
 * this routine uses a heuristic approach to snip N paragraphs from
 * the actual text in the html-page. Because we cannot be sure that
 * stripos() is available we resort to first changing any '<P' to
 * '<p ' and subsequently searching the string until the N+1'th '<p '.
 * The offset we calculate this way should contain exactly N
 * paragraphs. Obviously this assumes (dangerous...) that the htmlpage
 * page_data actually contains paragraphs. However, if not enough '<p
 * strings are found, the complete page is used. Heuristics...
 *
 * @param int $counter is a sequential number identifying the aggregated nodes
 * @param object &$theme points to theme where the output goes
 * @param array &$node points to the node record of this htmlpage
 * @param array &$config points to the aggregator configuration
 * @return array ordered list of nodes to aggregate (could be empty)
 */
function aggregator_view_htmlpage($counter, &$theme, &$node, &$config)
{
    $id = strval($counter);
    // used to make all id's within this item unique
    // 1 -- outer div holds title+blurb+readmore...
    $attributes = array('class' => 'aggregator_htmlpage_outer', 'id' => 'aggregator_outer_' . $id);
    $theme->add_content(html_tag('div', $attributes));
    // 2A -- title
    $attributes = array('class' => 'aggregator_htmlpage_header', 'id' => 'aggregator_header_' . $id);
    $theme->add_content('  ' . html_tag('h3', $attributes, $node['title']));
    // 2B -- blurb (enclosed in inner div)
    $attributes = array('class' => 'aggregator_htmlpage_inner', 'id' => 'aggregator_inner_' . $id);
    $theme->add_content('  ' . html_tag('div', $attributes));
    // fetch actual content from database (requires knowledge of internals of htmlpage module/table)
    $table = 'htmlpages';
    $node_id = intval($node['node_id']);
    $where = array('node_id' => $node_id);
    $fields = array('page_data');
    if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
        logger(sprintf('%s: no pagedata (node=%d): %s', __FUNCTION__, $node_id, db_errormessage()));
        $pagedata = '';
    } else {
        // make SURE we only have lowercase <p followed by a space in the text (easy strpos'ing)
        $pattern = '/(<[pP])([^a-zA-Z0-9])/';
        $replace = '<p $2';
        $pagedata = preg_replace($pattern, $replace, $record['page_data']);
    }
    $offset = -1;
    $limit = $config['htmlpage_length'];
    for ($i = 0; $i <= $limit; ++$i) {
        if (($offset = strpos($pagedata, '<p ', $offset + 1)) === FALSE) {
            break;
        }
    }
    if ($offset === FALSE) {
        // not enough '<p ' seen => show everything
        $theme->add_content($pagedata . "\n");
    } else {
        $theme->add_content(substr($pagedata, 0, $offset) . "\n");
    }
    $theme->add_content('  </div>');
    // 2C -- readmore prompt
    $anchor = t('htmlpage_more', 'm_aggregator');
    $title = t('htmlpage_more_title', 'm_aggregator');
    $attr = array('title' => $title);
    $href = was_node_url($node, NULL, '', $theme->preview_mode);
    $attributes = array('class' => 'aggregator_htmlpage_more', 'id' => 'aggregator_more_' . $id);
    $theme->add_content('  ' . html_tag('div', $attributes, html_a($href, NULL, $attr, $anchor)));
    $theme->add_content('  <div style="clear:both;"></div>');
    // 3 -- close outer div
    $theme->add_content('</div>');
}
/** load the visitor/view interface of a module in core
 *
 * this includes the 'view'-part of a module via 'require_once()'. This routine
 * first figures out if the view-script file actually exists before the
 * file is included. Also, we look at a very specific location, namely:
 * /program/modules/<modulename>/<module_view_script> where <modulename> is retrieved
 * from the modules table in the database.
 *
 * Note that if modulename would somehow be something like "../../../../../../etc/passwd\x00",
 * we could be in trouble...
 *
 * @param int $module_id indicates which module to load
 * @return bool|array FALSE on error or an array with the module record from the modules table
 * @todo should we sanitise the modulename here? It is not user input, but it comes from the modules
 *       table in the database. However, if a module name would contain sequences of "../" we might
 *       be in trouble
 */
function module_load_view($module_id)
{
    global $CFG;
    $module_record = db_select_single_record('modules', '*', array('module_id' => intval($module_id), 'is_active' => TRUE));
    if ($module_record === FALSE) {
        logger("module_load_view(): weird: module '{$module_id}' is not there. Is it de-activated?");
        return FALSE;
    }
    $module_view_script = $CFG->progdir . '/modules/' . $module_record['name'] . '/' . $module_record['view_script'];
    if (!file_exists($module_view_script)) {
        logger("module_load_view(): weird: file '{$module_view_script}' does not exist. Huh?");
        return FALSE;
    }
    require_once $module_view_script;
    return $module_record;
}
/** call the theme-specific upgrade routine
 *
 * this routine tries to execute the correct upgrade script/function for
 * theme $theme_id. If all goes well, a success message is written to $output
 * (and the update is performed), otherwise an error message is written to $output
 * Either way the event is logged via logger().
 *
 * Note that we take care not to load spurious files and execute non-existing functions.
 * However, at some point we do have to have some trust in the file system...
 *
 * @param object &$output collects the html output
 * @param string $theme_key unique secondary key for theme record in themes table in database
 * @return void results are returned as output in $output
 */
function update_theme(&$output, $theme_key)
{
    global $CFG;
    $theme_key = strval($theme_key);
    $params = array('{THEME}' => $theme_key);
    $progdir_themes = $CFG->progdir . '/themes';
    $manifests = get_manifests($progdir_themes);
    // 1 -- validate input: theme must exist in available manifests
    if (!isset($manifests[$theme_key])) {
        logger(sprintf('%s(): manifest for theme \'%s\' not found; nothing updated', __FUNCTION__, $theme_key));
        $output->add_message(t('update_subsystem_theme_error', 'admin', $params));
        return;
    }
    $manifest = $manifests[$theme_key];
    // 2 -- get the primary key of the installed theme (required for {$theme}_update() function)
    $table = 'themes';
    $fields = array('theme_id', 'name');
    $where = array('name' => $theme_key);
    $record = db_select_single_record($table, $fields, $where);
    if ($record === FALSE) {
        logger(sprintf('%s(): error retrieving theme_id for \'%s\': %s', __FUNCTION__, $theme_key, db_errormessage()));
        $output->add_message(t('update_subsystem_theme_error', 'admin', $params));
        return;
    }
    $name = $record['name'];
    $theme_id = intval($record['theme_id']);
    // 3 -- let the theme update code do its thing
    $retval = FALSE;
    // assume failure
    $messages = array();
    // collects error messages from call-back routine
    if (isset($manifest['install_script']) && !empty($manifest['install_script'])) {
        $filename = sprintf('%s/%s/%s', $progdir_themes, $name, $manifest['install_script']);
        if (file_exists($filename)) {
            @(include_once $filename);
            $theme_upgrade = $name . '_upgrade';
            if (function_exists($theme_upgrade)) {
                if ($theme_upgrade($messages, $theme_id)) {
                    logger(sprintf('%s(): %s(): success upgrading theme \'%s\' (new version is %d)', __FUNCTION__, $theme_upgrade, $name, $manifest['version']));
                    $retval = TRUE;
                    // so far so good; remember this success
                } else {
                    logger(sprintf('%s(): %s() returned an error', __FUNCTION__, $theme_upgrade));
                }
                foreach ($messages as $message) {
                    // remember messages, either good or bad
                    logger($message);
                }
            } else {
                logger(sprintf('%s(): function %s() does not exist?', __FUNCTION__, $theme_upgrade));
            }
        } else {
            logger(sprintf('%s(): file %s does not exist?', __FUNCTION__, $filename));
        }
    } else {
        logger(sprintf('%s(): no install script specified in manifest for theme \'%s\'', __FUNCTION__, $name));
    }
    // 4 -- on success update the relevant record in the database (and make it active)
    if ($retval) {
        $table = 'themes';
        $fields = array('version' => intval($manifest['version']), 'manifest' => $manifest['manifest'], 'is_active' => TRUE, 'class' => isset($manifest['class']) ? $manifest['class'] : NULL, 'class_file' => isset($manifest['class_file']) ? $manifest['class_file'] : NULL);
        $where = array('theme_id' => $theme_id);
        if (db_update($table, $fields, $where) === FALSE) {
            logger(sprintf('%s(): cannot update theme data for \'%s\': %s', __FUNCTION__, $theme_key, db_errormessage()));
            $retval = FALSE;
        }
    }
    // 5 -- inform the user about the final outcome
    if ($retval) {
        $output->add_message(t('update_subsystem_theme_success', 'admin', $params));
    } else {
        $output->add_message(t('update_subsystem_theme_error', 'admin', $params));
    }
}
/** validate and massage the user-supplied data path
 *
 * this checks the directory path the user entered,
 * returns TRUE if the tests are passed.
 * 
 * There three places from which snapshots can be retrieved:
 *  - /areas/aaa
 *  - /users/uuu
 *  - /groups/ggg
 * 
 * That is: the path should at least contain 2 levels (and possibly more).
 * In other words: a bare '/' is not enough and neither are bare '/areas',
 * '/users' or '/groups'. And of course the directory should already exist
 * in the file systen under $CFG->datadir.
 * 
 * Various tests are done:
 * - the selected area directory must be active
 * - if the selected area is private,
 *     $USER must have intranet access for this area, OR
 *     the selected area must be the same as the area in which $node_id resides
 * - the selected user directory must be the $USER's, OR
 *   the $USER has access to the account manager (able to manipulate ALL users' directories)
 * - the selected group directory must be from a group the $USER is a member of, OR
 *   the $USER has access to the account manager (able to manipulate ALL groups' directories)
 * 
 * If all tests succeed, we may want to warn the user in the case that the
 * file location is in a different (and public) area than the node holding the snapshots module.
 * However, this is a warning only.
 *
 * Finally, we reconstruct the path in such a way that it starts with a slash
 * and does NOT end with a slash. This is done by changing the content of the $item parameter.
 * 
 * @param array &$item holds the field definition from the $dialogdef for the snapshots_path
 * @param int $area_id the area in which we are editing a snapshot module configuration
 * @param int $node_id the node to which the snapshot module is connected (unused)
 * @return bool TRUE if valid path, otherwise FALSE + messages in dialogdef
 * @todo should the user / group really be active here? If not, the images will fail in file.php
 *       but that may leak information about inactive users. Hmmm...
 * @todo we should use a different error message as soon as it is available in was.php,
 *       eg. 'validate_bad_directory' (much like 'validate_bad_filename').
 */
function snapshots_check_path(&$item, $area_id, $node_id)
{
    global $USER, $CFG;
    $warning = '';
    $invalid = FALSE;
    $path_components = explode('/', trim(strtr($item['value'], '\\', '/'), '/'));
    if (sizeof($path_components) < 2 || in_array('..', $path_components)) {
        $invalid = TRUE;
    } else {
        switch ($path_components[0]) {
            case 'areas':
                $fields = array('area_id', 'is_private', 'title');
                $where = array('is_active' => TRUE, 'path' => $path_components[1]);
                $table = 'areas';
                if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
                    // area doesn't exist or is inactive
                    $invalid = TRUE;
                } elseif (db_bool_is(TRUE, $record['is_private'])) {
                    // specified area is private
                    if (intval($record['area_id']) != $area_id || !$USER->has_intranet_permissions(ACL_ROLE_INTRANET_ACCESS, $record['area_id'])) {
                        // this private area is NOT the one where $node_id resides OR this user is denied access
                        $invalid = TRUE;
                    }
                } else {
                    // specified area is public
                    if (intval($record['area_id']) != $area_id) {
                        // but it is not the same as the one where $node_id resides: go warn user eventually!
                        $params = array('{AREANAME}' => htmlspecialchars($record['title']));
                        $warning = t('warning_different_area', 'm_snapshots', $params);
                    }
                }
                break;
            case 'users':
                if (!$USER->has_job_permissions(JOB_PERMISSION_ACCOUNTMANAGER) && $path_components[1] != $USER->path) {
                    $invalid = TRUE;
                }
                if ($path_components[1] == $USER->path) {
                    $warning = t('warning_personal_directory', 'm_snapshots');
                }
                break;
            case 'groups':
                if (!$USER->has_job_permissions(JOB_PERMISSION_ACCOUNTMANAGER)) {
                    $usergroups = get_user_groups($USER->user_id);
                    $is_member = FALSE;
                    foreach ($usergroups as $group_id => $usergroup) {
                        if ($usergroup['path'] == $path_components[1]) {
                            $is_member = TRUE;
                            break;
                        }
                    }
                    if (!$is_member) {
                        $invalid = TRUE;
                    }
                }
                break;
            default:
                $invalid = TRUE;
                break;
        }
    }
    if (!$invalid) {
        $path = '/' . implode('/', $path_components);
        if (!is_dir($CFG->datadir . $path)) {
            $invalid = TRUE;
        }
    }
    if ($invalid) {
        $fname = str_replace('~', '', $item['label']);
        $params = array('{PATH}' => htmlspecialchars($item['value']));
        $error_message = sprintf('%s: %s', $fname, t('invalid_path', 'admin', $params));
        ++$item['errors'];
        $item['error_messages'][] = $error_message;
        return FALSE;
    }
    if ($warning != '') {
        $item['warnings'] = 0;
        ++$item['warnings'];
        $item['warning_messages'] = $warning;
    }
    $item['value'] = $path;
    return TRUE;
}
/** retrieve all configuration data for this mailpage
 *
 * this retrieves all configuration data for this mailpage,
 * i.e. both the general parameters (header/intro/etc.) and
 * the full list of configured destination addresses.
 * Here is a quick reminder of the structure in $config.
 * <pre>
 * 'node_id'
 * 'header'
 * 'introduction'
 * 'message'
 * 'addresses' = ['mailpage_address_id','node_id','sort_order','name','email','description','thankyou'],...
 * </pre>
 *
 * @param int $node_id identifies the page
 * @return bool|array configuration data in a (nested) array or FALSE on error
 */
function mailpage_view_get_config($node_id)
{
    // 1 -- generic configuration
    $table = 'mailpages';
    $fields = array('node_id', 'header', 'introduction', 'message');
    $where = array('node_id' => intval($node_id));
    if (($config = db_select_single_record($table, $fields, $where)) === FALSE) {
        logger(sprintf('%s(): error retrieving configuration: %s', __FUNCTION__, db_errormessage()));
        return FALSE;
    }
    // 2 -- fetch all configured destinations
    $table = 'mailpages_addresses';
    $fields = '*';
    $where = array('node_id' => intval($node_id));
    $order = array('sort_order', 'mailpage_address_id');
    if (($records = db_select_all_records($table, $fields, $where, $order)) === FALSE) {
        logger(sprintf('%s(): error retrieving addresses: %s', __FUNCTION__, db_errormessage()));
        return FALSE;
    }
    $config['addresses'] = $records;
    // simple 0-based array, not keyed by mailpage_address_id
    return $config;
}
/** construct a dialog definition for a mailpage address configuration
 *
 * @param object &$output collects the html output (if any)
 * @param int $viewonly if TRUE the Save button is not displayed and config values cannot be changed
 * @param int $node_id identifies the current mailpage
 * @param int $address_id identifies the address, 0 for a new one
 * @param int $sort_order is the next available sort order for new records
 * @return array dialog definition
 */
function mailpage_get_dialogdef_address(&$output, $viewonly, $node_id, $address_id, $sort_order = 10)
{
    //
    // 1 -- maybe get existing data from table
    //
    $values = array('name' => '', 'sort_order' => $sort_order, 'email' => '', 'description' => '', 'thankyou' => '', 'message' => '');
    if ($address_id > 0) {
        $table = 'mailpages_addresses';
        $keyfield = 'mailpage_address_id';
        $fields = '*';
        $where = array('node_id' => intval($node_id), $keyfield => intval($address_id));
        if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
            logger(sprintf('%s(): error retrieving configuration: %s', __FUNCTION__, db_errormessage()));
            $output->add_message(t('error_retrieving_data', 'admin'));
        } else {
            $values = $record;
        }
    }
    //
    // 2 -- define the dialog
    //
    $dialogdef = array('name' => array('type' => F_ALPHANUMERIC, 'name' => 'name', 'minlength' => 0, 'maxlength' => 80, 'columns' => 40, 'label' => t('address_name_label', 'm_mailpage'), 'title' => t('address_name_title', 'm_mailpage'), 'viewonly' => $viewonly, 'value' => $values['name'], 'old_value' => $values['name']), 'email' => array('type' => F_ALPHANUMERIC, 'name' => 'email', 'minlength' => 0, 'maxlength' => 255, 'columns' => 40, 'label' => t('address_email_label', 'm_mailpage'), 'title' => t('address_email_title', 'm_mailpage'), 'viewonly' => $viewonly, 'value' => $values['email'], 'old_value' => $values['email']), 'description' => array('type' => F_ALPHANUMERIC, 'name' => 'description', 'minlength' => 0, 'maxlength' => 240, 'columns' => 40, 'label' => t('address_description_label', 'm_mailpage'), 'title' => t('address_description_title', 'm_mailpage'), 'viewonly' => $viewonly, 'value' => $values['description'], 'old_value' => $values['description']), 'thankyou' => array('type' => F_ALPHANUMERIC, 'name' => 'thankyou', 'minlength' => 0, 'maxlength' => 240, 'columns' => 40, 'label' => t('address_thankyou_label', 'm_mailpage'), 'title' => t('address_thankyou_title', 'm_mailpage'), 'viewonly' => $viewonly, 'value' => $values['thankyou'], 'old_value' => $values['thankyou']), 'sort_order' => array('type' => F_INTEGER, 'name' => 'sort_order', 'minlength' => 0, 'maxlength' => 10, 'columns' => 7, 'minvalue' => 0, 'label' => t('address_sort_order_label', 'm_mailpage'), 'title' => t('address_sort_order_title', 'm_mailpage'), 'viewonly' => $viewonly, 'value' => $values['sort_order'], 'old_value' => $values['sort_order']));
    //
    // 3 -- add buttons: save (mabye), cancel and delete (maybe)
    //
    if (!$viewonly) {
        $dialogdef['button_save'] = dialog_buttondef(BUTTON_SAVE);
    }
    $dialogdef['button_cancel'] = dialog_buttondef(BUTTON_CANCEL);
    if (!$viewonly && $address_id != 0) {
        $dialogdef['button_delete'] = dialog_buttondef(BUTTON_DELETE);
    }
    return $dialogdef;
}
예제 #10
0
/** create three areas + themes
 *
 * @param array &$messages used to return (error) messages to caller
 * @param array &$config pertinent information about the site
 * @param array &$tr translations of demodata texts
 * @return bool TRUE on success + data entered into database, FALSE on error
 */
function demodata_areas(&$messages, &$config, &$tr)
{
    global $wizard;
    // This is a kludge to get to the sanitise_filename() code. There must be a better way...
    global $DB;
    static $seq = 0;
    // circumvent file/directory name clashes by appending a 'unique' sequence number
    $retval = TRUE;
    // assume success
    // 0 -- setup essential information
    if (($record = db_select_single_record('themes', 'theme_id', array('name' => 'frugal'))) === FALSE) {
        $messages[] = $tr['error'] . ' ' . db_errormessage();
        $retval = FALSE;
        $theme_id = 1;
        // lucky guess
    } else {
        $theme_id = intval($record['theme_id']);
    }
    $user_id = $config['user_id'];
    $metadata = "<meta name=\"keywords\" content=\"school website, websiteatschool, primary education, " . "secondary education, freire, freinet, habermas, learing tool, it learning tool, " . "ict learing tool, ict, bazaar style sheet, bss, screen reader, braille reader, " . "braille terminal, learning html, learning css, free software, exemplum, site@school, " . "siteatschool, websiteatschool.eu\">\n" . "<meta name=\"description\" content=\"Website@School is a website content management system " . "(CMS) for schools\">\n";
    $now = strftime('%Y-%m-%d %T');
    // 1 -- construct area records
    $areas = array('public' => array('title' => $tr['public_area_title'], 'is_private' => FALSE, 'is_active' => TRUE, 'is_default' => TRUE, 'path' => utf8_strtolower($wizard->sanitise_filename($tr['public_area_path'])), 'metadata' => $metadata, 'sort_order' => 10, 'theme_id' => $theme_id, 'ctime' => $now, 'cuser_id' => $user_id, 'mtime' => $now, 'muser_id' => $user_id), 'private' => array('title' => $tr['private_area_title'], 'is_private' => TRUE, 'is_active' => TRUE, 'is_default' => FALSE, 'path' => utf8_strtolower($wizard->sanitise_filename($tr['private_area_path'])), 'sort_order' => 20, 'theme_id' => $theme_id, 'ctime' => $now, 'cuser_id' => $user_id, 'mtime' => $now, 'muser_id' => $user_id), 'extra' => array('title' => $tr['extra_area_title'], 'is_private' => FALSE, 'is_active' => FALSE, 'is_default' => FALSE, 'path' => utf8_strtolower($wizard->sanitise_filename($tr['extra_area_path'])), 'sort_order' => 30, 'theme_id' => $theme_id, 'ctime' => $now, 'cuser_id' => $user_id, 'mtime' => $now, 'muser_id' => $user_id));
    // 1A -- make sure the area directories so not exist yet; maybe change name
    $datadir_areas = $config['datadir'] . '/areas/';
    foreach ($areas as $area => $fields) {
        $path = $fields['path'];
        if ($path == '_') {
            $path .= strval(++$seq);
        }
        $ext = '';
        while (is_dir($datadir_areas . $path . $ext)) {
            $ext = strval(++$seq);
        }
        $areas[$area]['path'] = $path . $ext;
    }
    // 1B -- actually make the directories and store the name and other data in table
    foreach ($areas as $area => $fields) {
        $fullpath = $datadir_areas . $fields['path'];
        if (@mkdir($fullpath, 0700)) {
            @touch($fullpath . '/index.html');
            // try to "protect" directory
        } else {
            $messages[] = $tr['error'] . " mkdir('{$fullpath}')";
            $retval = FALSE;
        }
        if (($area_id = db_insert_into_and_get_id('areas', $fields, 'area_id')) === FALSE) {
            $messages[] = $tr['error'] . ' ' . db_errormessage();
            $retval = FALSE;
        }
        $area_id = intval($area_id);
        // remember the area_id
        $areas[$area]['area_id'] = intval($area_id);
        // copy the theme's default setting to this area
        $sql = sprintf('INSERT INTO %s%s(area_id,theme_id,name,type,value,extra,sort_order,description) ' . 'SELECT %d AS area_id,theme_id,name,type,value,extra,sort_order,description ' . 'FROM %s%s ' . 'WHERE theme_id = %d', $DB->prefix, 'themes_areas_properties', intval($area_id), $DB->prefix, 'themes_properties', intval($theme_id));
        if ($DB->exec($sql) === FALSE) {
            $messages[] = $tr['error'] . ' ' . db_errormessage();
            $retval = FALSE;
        }
    }
    $config['demo_areas'] = $areas;
    return $retval;
}
/** present the user with a dialog to modify the aggregator that are connected to node $node_id
 *
 * this prepares a dialog for the user filled with existing data (if any), possibly allowing
 * the user to modify the content. If the flag $viewonly is TRUE, this routine should only
 * display the content rather than let the user edit it. If the flag $edit_again is TRUE,
 * the routine should use the data available in the $_POST array, otherwise it should read
 * the data from the database (or wherever the data comes from). The parameter $href is the
 * place where the form should be POST'ed.
 *
 * The dialog should be added to the $output object. Useful routines are:
 * <code>
 * $output->add_content($content): add $content to the content area
 * $output->add_message($message): add $message to the message area (feedback to the user)
 * $output->add_popup_bottom($message): make $message popup in the browser after loading the page (uses javascript)
 * $output->add_popup_top($message): make $message popup in the browser before loading the page (uses javascript)
 * </code>
 * 
 * @param object &$output collects the html output (if any)
 * @param int $area_id the area in which $node_id resides
 * @param int $node_id the node to which this module is connected
 * @param array $module the module record straight from the database
 * @param bool $viewonly if TRUE, editing is not allowed (but simply showing the content is allowed)
 * @param bool $edit_again if TRUE start with data from $_POST, else use data from database
 * @param string $href the action property of the HTML-form, the place where data will be POST'ed
 * @return bool TRUE on success + output stored via $output, FALSE otherwise
 */
function aggregator_show_edit(&$output, $area_id, $node_id, $module, $viewonly, $edit_again, $href)
{
    $dialogdef = aggregator_get_dialogdef($viewonly);
    if ($edit_again) {
        // retrieve and validate the POSTed values
        dialog_validate($dialogdef);
        // no need to show messages; we did that alread in aggregator_save()
        aggregator_check_node_list($dialogdef['node_list'], $area_id, $node_id);
    } else {
        // make a fresh start with data from the database
        $where = array('node_id' => intval($node_id));
        if (($record = db_select_single_record('aggregator', '*', $where)) === FALSE) {
            logger(sprintf('%s(): error retrieving aggregator configuration: %s', __FUNCTION__, db_errormessage()));
        } else {
            foreach ($dialogdef as $name => $item) {
                switch ($name) {
                    case 'header':
                    case 'introduction':
                    case 'node_list':
                    case 'items':
                    case 'reverse_order':
                    case 'htmlpage_length':
                    case 'snapshots_width':
                    case 'snapshots_height':
                    case 'snapshots_visible':
                    case 'snapshots_showtime':
                        $dialogdef[$name]['value'] = strval($record[$name]);
                        break;
                }
            }
        }
    }
    $output->add_content('<h2>' . t('aggregator_content_header', 'm_aggregator') . '</h2>');
    $output->add_content(t('aggregator_content_explanation', 'm_aggregator'));
    $output->add_content(dialog_quickform($href, $dialogdef));
    return TRUE;
}
 /** retrieve a single group's record possibly from the cache
  *
  * @param int $group_id identifies the group record
  * @param bool $forced if TRUE unconditionally fetch the record from the database 
  * @return bool|array FALSE if there were errors, the group record otherwise
  */
 function get_group_record($group_id, $forced = FALSE)
 {
     $group_id = intval($group_id);
     if (!isset($this->groups[$group_id]) || $forced) {
         $table = 'groups';
         $fields = '*';
         $where = array('group_id' => $group_id);
         if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
             logger(sprintf("%s.%s(): cannot retrieve record for group '%d': %s", __CLASS__, __FUNCTION__, $group_id, db_errormessage()));
             $this->output->add_message(t('error_retrieving_data', 'admin'));
             return FALSE;
         } else {
             $this->groups[$group_id] = $record;
         }
     }
     return isset($this->groups[$group_id]) ? $this->groups[$group_id] : FALSE;
 }
예제 #13
0
/** put a (co-operative) lock on a record
 *
 * this tries to set the co-operative) lock on the record with serial (pkey) $id
 * in table $tablename by setting the $locked_by field to our own session_id. This
 * is the companion routine of {@link lock_release()}.
 *
 * The mechanism of co-operative locking works as follows. Some tables (such as
 * the 'nodes' table) have an int field, e.g. 'locked_by_session_id'. This field
 * can either be NULL (indicating that the record is not locked) or hold the primary
 * key of a session (indicating that the record is locked and also by which session).
 * 
 * Obtaining a lock boils down to updating the table and setting that field to the
 * session_id. As long as the underlying database system guarantees that execution
 * of an UPDATE statement is not interrupted, we can use UPDATE as a 
 * 'Test-And-Set'-function. According to the docentation MySQL does this.
 * 
 * The procedure is as follows.
 *
 * 1. we try to set the locked_by-field to our session_id on the condition that
 *    the previous value of that field is NULL. If this succeeds, we have effectively
 *    locked the record.
 *
 * 2. If this fails, we retrieve the current value of the field to see which session has
 *    locked it. If this happens to be us, we had already locked the record before and
 *    we're done.
 *
 * 3. If another session_id holds the lock, we check for that session's existence. If it
 *    still exists, we're out of luck: we can't obtain the lock.
 *
 * 4. If that other session does no longer exist, we try to replace that other session's
 *    session_id with our own session_id, once again using a single UPDATE (avoiding another
 *    race condition). If that succeeds we're done and we have the lock; if it failes
 *    we're also done but without lock.
 *
 * If locking the record fails because the record is already locked by another session,
 * this routine returns information about that other session in $lockinfo. It is up to
 * the caller to use this information or not.
 *
 * Note.
 * A record can stay locked if the webbrowser of the locking session has crashed. Eventually
 * this will be resolved if the crashed session is removed from the sessions table. However,
 * the user may have restarted her browser while the record was locked. From the new session
 * it appears that the record is still locked. This may take a while. Mmmmm...
 * The other option is to lock on a per-user basis rather than per-session basis. Mmmm...
 * Should we ask the user to override the session if it happens to be the same user?
 * Mmm. put it on the todo list. (A small improvement might be to call the garbage collection
 * between step 2 and 3. Oh well).
 *
 * @todo perhaps we can save 1 trip to the database by checking for something like
 *       UPDATE SET locked_by = $session_id WHERE (id = $id) AND ((locked_by IS NULL) OR (locked_by = $session_id))
 *       but I don't know how many affected rows that would yield if we already had the lock and
 *       effectively nothing changes in the record. (Perhaps always update atime to force 1 affected row?)
 * @todo do we need a 'force lock' option to forcefully take over spurious locks?
 * @todo we need to resolve the problem of crashing browsers and locked records
 * @param int $id the primary key of the record to lock
 * @param array &$lockinfo returns information about the session that already locked this record
 * @param string $tablename the name of the table
 * @param string $pkey name of the field holding the serial (pkey)
 * @param string $locked_by name of the field to hold our session_id indicating we locked the record
 * @param string $locked_since name of the field holding the datetime when the lock was obtained
 * @return bool TRUE if locked succesfully, FALSE on error or already locked ; extra info returned in $lockinfo
 */
function lock_record($id, &$lockinfo, $tablename, $pkey, $locked_by, $locked_since)
{
    global $DB;
    $now = strftime("%Y-%m-%d %T");
    $lockinfo = array();
    if (!isset($_SESSION['session_id'])) {
        // weird, we SHOULD have a session id
        logger('weird: no session_id in lock_record()', WLOG_DEBUG);
        return FALSE;
    } else {
        $session_id = $_SESSION['session_id'];
        if (!is_int($session_id)) {
            logger('weird: session_id in lock_record() is not an int', WLOG_DEBUG);
            return FALSE;
        }
    }
    //
    // 1 -- try to get a lock from scratch (ie: record is currently not locked by anyone)
    //
    $fields = array($locked_by => $session_id, $locked_since => $now);
    $where = array($pkey => intval($id), $locked_by => NULL);
    $retval = db_update($tablename, $fields, $where);
    if ($retval === FALSE) {
        // error
        return FALSE;
    } elseif ($retval == 1) {
        // exactly 1 row was updated, hurray, success!
        return TRUE;
    }
    // else record was probably locked by another session or by our session; go check it out
    //
    // 2 -- 1 didn't work, find out who holds the lock
    //
    $retval = db_select_single_record($tablename, $locked_by, array($pkey => $id));
    if ($retval === FALSE) {
        return FALSE;
        // error
    } elseif ($retval[$locked_by] == $session_id) {
        return TRUE;
        // we had the lock ourselves all along
    }
    // else record was definately locked by session $retval[$locked_by]
    //
    // 3 -- whoever holds the lock, it is not us. check out if 'their' session is still active
    //
    $locked_by_session_id = $retval[$locked_by];
    $prefixed_tablename = $DB->prefix . $tablename;
    $sessions_table = $DB->prefix . 'sessions';
    $users_table = $DB->prefix . 'users';
    $sql = "SELECT s.user_id, u.username, u.full_name, s.user_information, s.ctime, s.atime, x.{$locked_since} AS ltime " . "FROM {$prefixed_tablename} AS x INNER JOIN {$sessions_table} AS s ON x.{$locked_by} = s.session_id " . "LEFT JOIN {$users_table} AS u ON s.user_id = u.user_id " . "WHERE s.session_id = {$locked_by_session_id}";
    $DBResult = $DB->query($sql, 1);
    if ($DBResult === FALSE) {
        // error
        return FALSE;
    } elseif ($DBResult->num_rows == 1) {
        // alas, already locked by another
        $lockinfo = $DBResult->fetch_row_assoc();
        $DBResult->close();
        return FALSE;
    } else {
        $DBResult->close();
    }
    //
    // 4 -- We still have a chance to obtain the lock because the current 'lock' appears
    //      to belong to a session that no longer exists
    //
    $fields = array($locked_by => $session_id, $locked_since => $now);
    $where = array($pkey => intval($id), $locked_by => $locked_by_session_id);
    $retval = db_update($tablename, $fields, $where);
    if ($retval === FALSE) {
        // error
        return FALSE;
    } elseif ($retval == 1) {
        // exactly 1 row was updated, hurray, success!
        return TRUE;
    } else {
        return FALSE;
        // no joy, give up
    }
}
/** save the modified content data of this module linked to node $node_id
 *
 * this validates and saves the data that was submitted by the user.
 * If validation fails, or storing the data doesn't work, the flag $edit_again
 * is set to TRUE and the return value is FALSE.
 *
 * If the user has cancelled the operation, the flag $edit_again is set to FALSE
 * and the return value is also FALSE.
 *
 * If the modified data is stored successfully, the return value is TRUE (and
 * the value of $edit_again is a don't care).
 *
 * Here is a summary of return values.
 *
 *  - retval = TRUE ==> data saved successfully
 *  - retval = FALSE && edit_again = TRUE ==> re-edit the data, show the edit dialog again
 *  - retval = FALSE && edit_again = FALSE ==> cancelled, do nothing
 *
 * @param object &$output collects the html output (if any)
 * @param int $area_id the area in which $node_id resides
 * @param int $node_id the node to which the content is connected
 * @param array $module the module record straight from the database
 * @param bool $viewonly if TRUE, editing and hence saving is not allowed
 * @param bool &$edit_again set to TRUE if we need to edit the content again, FALSE otherwise
 * @return bool TRUE on success + output stored via $output, FALSE otherwise
 */
function htmlpage_save(&$output, $area_id, $node_id, $module, $viewonly, &$edit_again)
{
    global $USER;
    if (isset($_POST['button_cancel']) || $viewonly) {
        $edit_again = FALSE;
        $retval = FALSE;
    } else {
        $edit_again = TRUE;
        if (!$viewonly) {
            $where = array('node_id' => intval($node_id));
            $order = array('version DESC');
            $record = db_select_single_record('htmlpages', '*', $where, $order);
            if ($record !== FALSE) {
                $now = strftime('%Y-%m-%d %T');
                $fields = array('version' => intval($record['version']) + 1, 'page_data' => magic_unquote($_POST['htmlpage_content']), 'mtime' => $now, 'muser_id' => $USER->user_id);
                $where = array('htmlpage_id' => $record['htmlpage_id']);
                $retval = db_update('htmlpages', $fields, $where);
            } else {
                $retval = FALSE;
            }
        } else {
            $retval = FALSE;
        }
    }
    return $retval;
}
예제 #15
0
/** display the content of the workshop linked to node $node_id
 *
 *
 * @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
 * @todo FixMe: we need to take parent node permissions into account
 *       as soon as we can assign crew permissions to sections.
 *       We now specificly look at permissions in table acls_modules_nodes
 *       OR at global (guru) permissions for modules in table acls. (June 2013).
 */
function crew_view(&$theme, $area_id, $node_id, $module)
{
    global $USER;
    //
    // 1 -- initialise: check out the visibility of this page
    //
    $table = 'workshops';
    $fields = array('visibility');
    $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()));
        $visibility = 0;
    } else {
        $visibility = intval($record['visibility']);
    }
    //
    // 2 -- compute permissions for the current user: 1=READ, 2=WRITE
    //
    $module_id = intval($module['module_id']);
    if (!($visibility == 2 || $visibility == 1 && $USER->is_logged_in || $visibility == 0 && $USER->has_module_node_permissions(1, $module_id, $area_id, $node_id))) {
        $theme->add_content(t('crew_view_access_denied', 'm_crew'));
        return TRUE;
    }
    // OK. Reading is permitted. How about writing?
    $writer = $USER->has_module_node_permissions(2, $module_id, $area_id, $node_id);
    //
    // 3 -- what do we need to do here?
    //
    // If we are still here, we have at least read access.
    // That means that we always can show the current version of the document
    // via crew_view_show_view(). Anything else requires the $writer permissions.
    // If, for some reason, someone attempts to save the document without
    // having write permissions, we simply can/will discard the POST'ed
    // document and pretend that the visitor just wants to read the current
    // version of the document.
    //
    // Note that we have a total of 5 variations here.
    // 1. Initial visit: a simple GET for the current version of the page (for starters)
    // 2. The user pressed [Edit] from 1. and now sees the popup JS Workshop
    // 3. The user submitted via [Save] which saves the document and shows 1. again
    // 4. The user submitted via [SaveEdit] which saves the document and shows 2. again without popup
    // 5. The user submitted via [Cancel] and now sees 1. again.
    // Here we go.
    if ($writer) {
        if (isset($_POST['button_edit'])) {
            $retval = crew_view_show_edit($theme, $module_id, TRUE);
            // TRUE means 1st time; generate pop-up
        } elseif (isset($_POST['button_saveedit'])) {
            crew_view_save_document($theme, $node_id);
            $retval = crew_view_show_edit($theme, $module_id, FALSE);
            // FALSE means do not generate pop-up
        } else {
            if (isset($_POST['button_save'])) {
                crew_view_save_document($theme, $node_id);
            } elseif (isset($_POST['button_cancel'])) {
                $theme->add_message(t('cancelled', 'admin'));
            }
            $retval = crew_view_show_view($theme, $writer, $node_id);
        }
    } else {
        $retval = crew_view_show_view($theme, $writer, $node_id);
    }
    return $retval;
}
예제 #16
0
/** add 1 point to score for a particular IP-address and failed procedure, return the new score
 *
 * This records a login failure in a table and returns the the number
 * of failures for the specified procedure in the past T1 minutes.
 *
 * @param string $remote_addr the remote IP-address that is the origin of the failure
 * @param int $procedure indicates in which procedure the user failed
 * @param string $username extra information, could be useful for troubleshooting afterwards
 * @return int the current score
 */
function login_failure_increment($remote_addr, $procedure, $username = '')
{
    global $CFG;
    // this array used to validate $procedure _and_ to make a human readable description with logger()
    static $procedure_names = array(LOGIN_PROCEDURE_NORMAL => 'normal login', LOGIN_PROCEDURE_CHANGE_PASSWORD => 'change password', LOGIN_PROCEDURE_SEND_LAISSEZ_PASSER => 'send laissez passer', LOGIN_PROCEDURE_SEND_BYPASS => 'send bypass');
    $retval = 0;
    $procedure = intval($procedure);
    if (isset($procedure_names[$procedure])) {
        $now = strftime('%Y-%m-%d %T');
        $retval = db_insert_into('login_failures', array('remote_addr' => $remote_addr, 'datim' => $now, 'failed_procedure' => $procedure, 'points' => 1, 'username' => $username));
        if ($retval !== FALSE) {
            $minutes = intval($CFG->login_failures_interval);
            $interval_begin = strftime('%Y-%m-%d %T', time() - $minutes * 60);
            $where = 'remote_addr = ' . db_escape_and_quote($remote_addr) . ' AND failed_procedure = ' . $procedure . ' AND ' . db_escape_and_quote($interval_begin) . ' < datim';
            $record = db_select_single_record('login_failures', 'SUM(points) AS score', $where);
            if ($record !== FALSE) {
                $retval = intval($record['score']);
            } else {
                logger('could not calculate failure score', WLOG_DEBUG);
            }
        } else {
            logger('could not increment failure count', WLOG_DEBUG);
        }
        logger('login: failed; procedure=' . $procedure_names[$procedure] . ', count=' . $retval . ', username=\'' . $username . '\'');
    } else {
        logger('internal error: unknown procedure', WLOG_DEBUG);
    }
    return $retval;
}
 /** determine the number of existing properties for a theme in an area
  *
  * @param int $area_id
  * @param int $theme_id
  * @return int number of existing properties
  */
 function count_existing_theme_properties($area_id, $theme_id)
 {
     $where = array('area_id' => intval($area_id), 'theme_id' => intval($theme_id));
     $record = db_select_single_record('themes_areas_properties', 'count(*) as properties', $where);
     if ($record === FALSE) {
         logger("areamanager: error counting properties in theme '{$theme_id} and area '{$area_id}': " . db_errormessage());
         $num = 0;
     } else {
         $num = intval($record['properties']);
     }
     return $num;
 }
 /** get pertinent user information in core
  *
  * Note:
  * We used to have a bool named 'high_visibility' in both the users table
  * and this class. That changed with version 0.90.4 (April 2012) and we now
  * have a field and variable 'skin' which is a varchar(20). The values were
  * mapped as follows: high_availability=FALSE -> skin='base' and 
  * high_availability=TRUE -> skin='textonly'. The extra test for the
  * existence of $record['skin'] was necessary for the case where the user
  * wanted to upgrade from 0.90.3 to 0.90.4 where 'skin' replaced 'high_visibility'.
  * 
  * @param int $user_id identifies data from which user to load, 0 means no user/a passerby
  * @return void
  */
 function Useraccount($user_id = 0)
 {
     $user_id = intval($user_id);
     $this->user_id = $user_id;
     if ($this->user_id == 0) {
         // just a passerby gets no privileges
         return FALSE;
     }
     // Now try to fetch data for this user from database
     $fields = '*';
     $record = db_select_single_record('users', $fields, array('user_id' => $user_id, 'is_active' => TRUE));
     if ($record === FALSE) {
         logger('useraccount: cannot find record for user_id \'' . $user_id . '\'', WLOG_INFO, $user_id);
         return FALSE;
     }
     $this->username = $record['username'];
     $this->full_name = $record['full_name'];
     $this->email = $record['email'];
     $this->language_key = $record['language_key'];
     $this->path = $record['path'];
     $this->editor = $record['editor'];
     $this->skin = isset($record['skin']) ? $record['skin'] : 'base';
     // see note above
     // Prepare for retrieval of acls/permissions
     $this->acl_id = intval($record['acl_id']);
     $this->related_acls = calc_user_related_acls($user_id);
     // Always fetch the site-wide permissions from acls table (others are cached on demand)
     $fields = array_keys($this->acls);
     $where = $this->where_acl_id();
     $records = db_select_all_records('acls', $fields, $where);
     if ($records === FALSE) {
         logger('useraccount: cannot find acls records for user_id \'' . $user_id . '\'', WLOG_INFO, $user_id);
     } else {
         foreach ($records as $record) {
             foreach ($fields as $field) {
                 $this->acls[$field] |= $record[$field];
             }
         }
     }
     // get all properties for this user in 2D array (sections, entries)
     $tablename = 'users_properties';
     $fields = array('section', 'name', 'type', 'value');
     $where = array('user_id' => $user_id);
     $order = array('section', 'name');
     $records = db_select_all_records($tablename, $fields, $where, $order);
     if ($records !== FALSE) {
         $properties = array();
         foreach ($records as $rec) {
             $properties[$rec['section']][$rec['name']] = convert_to_type($rec['type'], $rec['value']);
         }
         $this->properties = $properties;
     }
     return TRUE;
 }
 /** retrieve page data for htmlpage module on a node
  *
  * this retrieves data from the page $node_id (requires knowledge of
  * internals of htmlpage module/table) in order to show that in a box
  * in the 3rd column.
  * The content is wrapped in a div which has a unique ID of the form
  * sidebar_blockN where N counts from 1 upward and a common class
  * sidebar_htmlpage. This allows for connecting style information to
  * a particular sidebar block in a flexible way.
  *
  * @param in $index block number (1-based)
  * @param int $node_id identifies the pagedata to retrieve 
  * @param string $m increased readbility
  * @return string ready-to-use HTML-code
  */
 function cornelia_get_sidebar_htmlpage($index, $node_id, $m = '')
 {
     $attributes = array('id' => 'sidebar_block' . strval($index), 'class' => 'sidebar_htmlpage');
     $s = $m . html_tag('div', $attributes) . "\n";
     // fetch actual content from database (requires knowledge of internals of htmlpage module/table)
     $table = 'htmlpages';
     $node_id = intval($node_id);
     $where = array('node_id' => $node_id);
     $fields = array('page_data');
     if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
         logger(sprintf('%s: no pagedata (node=%d): %s', __FUNCTION__, $node_id, db_errormessage()));
     } else {
         $s .= $record['page_data'] . "\n";
     }
     $s .= $m . "</div>\n";
     return $s;
 }
/** present the user with a dialog to modify the redirect that is connected to node $node_id
 *
 * this prepares a dialog for the user filled with existing data (if any), possibly allowing
 * the user to modify the content. If the flag $viewonly is TRUE, this routine should only
 * display the content rather than let the user edit it. If the flag $edit_again is TRUE,
 * the routine should use the data available in the $_POST array, otherwise it should read
 * the data from the database (or wherever the data comes from). The parameter $href is the
 * place where the form should be POST'ed.
 *
 * The dialog should be added to the $output object. Useful routines are:
 * <code>
 * $output->add_content($content): add $content to the content area
 * $output->add_message($message): add $message to the message area (feedback to the user)
 * $output->add_popup_bottom($message): make $message popup in the browser after loading the page (uses javascript)
 * $output->add_popup_top($message): make $message popup in the browser before loading the page (uses javascript)
 * </code>
 * 
 * @param object &$output collects the html output (if any)
 * @param int $area_id the area in which $node_id resides
 * @param int $node_id the node to which this module is connected
 * @param array $module the module record straight from the database
 * @param bool $viewonly if TRUE, editing is not allowed (but simply showing the content is allowed)
 * @param bool $edit_again if TRUE start with data from $_POST, else use data from database
 * @param string $href the action property of the HTML-form, the place where data will be POST'ed
 * @return bool TRUE on success + output stored via $output, FALSE otherwise
 */
function redirect_show_edit(&$output, $area_id, $node_id, $module, $viewonly, $edit_again, $href)
{
    $dialogdef = redirect_get_dialogdef($viewonly);
    if ($edit_again) {
        // retrieve and validate the POSTed values
        dialog_validate($dialogdef);
        // no need to show messages; we did that alread in redirect_save() bel0w
    } else {
        // make a fresh start with data from the database
        $table = 'nodes';
        $fields = array('link_href', 'link_target');
        $where = array('node_id' => intval($node_id));
        if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
            logger(sprintf('%s(): error retrieving node record for redirect configuration: %s', __FUNCTION__, db_errormessage()));
        } else {
            foreach ($dialogdef as $name => $item) {
                switch ($name) {
                    case 'link_href':
                    case 'link_target':
                        $dialogdef[$name]['value'] = strval($record[$name]);
                        break;
                }
            }
        }
    }
    $output->add_content('<h2>' . t('redirect_content_header', 'm_redirect') . '</h2>');
    $output->add_content(t('redirect_content_explanation', 'm_redirect'));
    $output->add_content(dialog_quickform($href, $dialogdef));
    return TRUE;
}
/** 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 the (serialised) session data from the database
 *
 * @param string the unique session_key that identifies the session
 * @return string empty string on failure, existing session data otherwise
 */
function dbsession_read($session_key)
{
    $retval = db_select_single_record('sessions', 'session_data', array('session_key' => $session_key));
    if ($retval === FALSE) {
        return '';
    }
    // updating the atime of the session extends the session lifetime,
    // but a failure while updating will be ignored (it's not _that_ important)
    $current_time = strftime('%Y-%m-%d %T');
    db_update('sessions', array('atime' => $current_time), array('session_key' => $session_key));
    return (string) $retval['session_data'];
}
예제 #23
0
/** quick and dirty logfile viewer
 *
 * this constructs a table of the HTML-variety with the contents of the logtable.
 * fields displayed are: datim, IP-address, username, logpriority and message
 * we use a LEFT JOIN in order to get to a meaningful username rather than a numeric user_id
 * an attempt is made to start with the last page of the logs because that would probably
 * be the most interesting part. We paginate the log in order to keep it manageable.
 *
 * &lt;Rant&gt;<br>
 * I used to use the built-in constants like LOG_INFO and LOG_DEBUG to allow for different levels
 * of logging (see {@link logger()}). To my complete surprise logging didn't work at all on
 * Windows (it did on Linux). The reason was that LOG_DEBUG and LOG_INFO and LOG_NOTICE are all
 * defined to be the same value. WTF? Any test based on LOG_DEBUG and LOG_INFO being different
 * would fail, hence no logging at all. The mind boggles! So, instead of using built-in constants
 * I had to define my own and do a global search&replace. Aaarghhhhhh!!!!<br>
 * &lt;/Rant&gt;<br>
 *
 * @param object &$output collects output to show to user
 * @return void output displayed via $output
 * @todo should we allow for fancy selection mechanisms on the logfile or is that over the top?
 */
function task_logview(&$output)
{
    global $CFG, $WAS_SCRIPT_NAME, $DB;
    static $priorities = array(WLOG_EMERG => 'LOG_EMERG', WLOG_ALERT => 'LOG_ALERT', WLOG_CRIT => 'LOG_CRIT', WLOG_ERR => 'LOG_ERR', WLOG_WARNING => 'LOG_WARNING', WLOG_NOTICE => 'LOG_NOTICE', WLOG_INFO => 'LOG_INFO', WLOG_DEBUG => 'LOG_DEBUG');
    // 0 -- at least we allow the user to navigate away if something goes wrong
    $output->set_helptopic('logview');
    show_tools_menu($output, TASK_LOGVIEW);
    // 1A -- how many messages are there anyway?
    $table = 'log_messages';
    $where = '';
    // could be used to select per user, per priority, etc. For now: always select everything
    if (($record = db_select_single_record($table, 'COUNT(log_message_id) AS messages', $where)) === FALSE) {
        $output->add_content('<h2>' . t('menu_logview', 'admin') . '</h2>');
        $output->add_content(t('logview_error', 'admin'));
        $output->add_message(t('logview_error', 'admin'));
        logger(sprintf('%s(): cannot retrieve log message count: %s', __FUNCTION__, db_errormessage()));
        return;
    }
    // 1B -- if there are no message we leave
    if (($num_messages = intval($record['messages'])) < 1) {
        $output->add_content('<h2>' . t('menu_logview', 'admin') . '</h2>');
        $output->add_content(t('logview_no_messages', 'admin'));
        $output->add_message(t('logview_no_messages', 'admin'));
        logger(sprintf('%s(): no messages to show', __FUNCTION__), WLOG_DEBUG);
        return;
    }
    // 2 -- which part of the logs do they want to see? (calculate/retrieve offset and limit)
    $limit = get_parameter_int('limit', $CFG->pagination_height);
    $limit = max(1, $limit);
    // make sure 1 <= $limit
    $offset = intval(floor($num_messages / $limit)) * $limit;
    // attempt to start at begin of LAST page
    $offset = get_parameter_int('offset', max($offset, 0));
    $offset = max(min($num_messages - 1, $offset), 0);
    // make sure 0 <= $offset < $num_messages
    // 3 -- show the pagination in the page header (if necessary)
    if ($num_messages <= $limit && $offset == 0) {
        // listing fits on a single screen
        $header = '<h2>' . t('menu_logview', 'admin') . '</h2>';
    } else {
        // pagination necessary, tell user where we are
        $param = array('{FIRST}' => strval($offset + 1), '{LAST}' => strval(min($num_messages, $offset + $limit)), '{TOTAL}' => strval($num_messages));
        $header = '<h2>' . t('menu_logview', 'admin') . ' ' . t('pagination_count_of_total', 'admin', $param) . '</h2>';
        $parameters = array('job' => JOB_TOOLS, 'task' => TASK_LOGVIEW);
        $output->add_pagination($WAS_SCRIPT_NAME, $parameters, $num_messages, $limit, $offset, $CFG->pagination_width);
    }
    // 4 -- retrieve the selected messages (including optional username via LEFT JOIN)
    $sql = sprintf('SELECT l.datim, l.remote_addr, l.priority, l.user_id, u.username, l.message ' . 'FROM %slog_messages l LEFT JOIN %susers u USING (user_id) ' . 'ORDER BY l.datim, l.log_message_id', $DB->prefix, $DB->prefix);
    if (($DBResult = $DB->query($sql, $limit, $offset)) === FALSE) {
        $output->add_message(t('logview_error', 'admin'));
        logger(sprintf('%s(): cannot retrieve log messages: %s', __FUNCTION__, db_errormessage()));
        return;
    }
    $records = $DBResult->fetch_all_assoc();
    $DBResult->close();
    // 5A -- setup a table with a header
    $index = $offset + 1;
    $output->add_content($header);
    $class = 'header';
    $attributes = array('class' => $class, 'align' => 'right');
    $output->add_content('<p>');
    $output->add_content(html_table(array('cellpadding' => '3')));
    $output->add_content('  ' . html_table_row($attributes));
    $output->add_content('    ' . html_table_head($attributes, t('logview_nr', 'admin')));
    $attributes['align'] = 'left';
    $output->add_content('    ' . html_table_head($attributes, t('logview_datim', 'admin')));
    $output->add_content('    ' . html_table_head($attributes, t('logview_remote_addr', 'admin')));
    $output->add_content('    ' . html_table_head($attributes, t('logview_user_id', 'admin')));
    $output->add_content('    ' . html_table_head($attributes, t('logview_priority', 'admin')));
    $output->add_content('    ' . html_table_head($attributes, t('logview_message', 'admin')));
    $output->add_content('  ' . html_table_row_close());
    // 5B -- step through the recordset and dump into the table
    foreach ($records as $record) {
        $class = $class == 'odd' ? 'even' : 'odd';
        $priority = isset($priorities[$record['priority']]) ? $priorities[$record['priority']] : strval(intval($record['priority']));
        $attributes = array('class' => $class);
        $output->add_content('  ' . html_table_row($attributes));
        $attributes['align'] = 'right';
        $output->add_content('    ' . html_table_cell($attributes, strval($index++)));
        $attributes['align'] = 'left';
        $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($record['datim'])));
        $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($record['remote_addr'])));
        $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($record['username'])));
        $output->add_content('    ' . html_table_cell($attributes, $priority));
        $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($record['message'])));
        $output->add_content('  ' . html_table_row_close());
    }
    // 5C -- all done
    $output->add_content(html_table_close());
    $output->add_content('<p>');
}
예제 #24
0
/** main program for serving files
 *
 * this routine is called from /file.php.
 *
 * This routine is responsible for serving files to the visitor.
 * These files are stored in a (virtual) file hierarchy that looks
 * like this.
 *
 * <pre>
 * /areas/areaname
 *       /another
 *       /stillmore
 *       ...
 * /users/username
 *       /another
 *       /stillmore
 *       ...
 * /groups/groupname
 *        /another
 *        /stillmore
 *        ...
 * /websiteatschool/program
 *                 /manual
 *                 /languages
 * </pre>
 *
 * This structure maps to the real file system as follows.  The (virtual)
 * directories /areas, /users and /groups correspond to the fysical
 * directories {$CFG->datadir}/areas, {$CFG->datadir}/users and
 * {$CFG->datadir}/groups respectively. The subdirectories correspond to
 * a (unique) area, user or group and serve as a file repository for that
 * area, user or group.
 *
 * The (virtual) top-level directory /websiteatschool is a special case.
 * It is used to serve the currently running website program code and the
 * user-defined translations of active languages.
 *
 * Before any file is transmitted to the visitor the access privileges
 * are checked.  The following rules apply.
 *
 * Access control for the /areas subdirectory
 *
 *  - an area must be active before any files are served
 *  - the visitor must have access to the private area if files are to be served
 *  - non-existing files yield a 404 Not Found error
 *  - non-existing areas also yield a 404 Not Found error
 *  - if the visitor has no access to the private area, also a 404 Not Found error is returned
 *
 * Access control for /users and /groups
 *
 *  - a user/group must be active before any files are served
 *  - non-existing users/groups yield 404 Not Found
 *  - non-existing files in existing directories also yield 404 Not Found
 *
 * Access control for /websiteatschool
 *
 *  - there is no limit on downloading the currently active program code or user-defined translations of active languages
 *
 * Note:
 * The check on '..' in the requested filename would be inconclusive if the $path
 * is encoded in invalid UTF-8: the overlong sequence 2F C0 AE 2E 2F eventually
 * yields 2F 2E 2E 2F or '/../'. Reference: RFC3629 section 10. However, we use
 * the filename processed with get_requested_filename() which already checks for
 * utf8 validity, which rules out the trick with overlong sequences.
 *
 * @return void file sent to the browser OR 404 not found on error
 */
function main_file()
{
    global $USER;
    global $CFG;
    global $WAS_SCRIPT_NAME;
    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
    /** utility routines for manipulating files */
    require_once $CFG->progdir . '/lib/filelib.php';
    $filename = get_requested_filename();
    if (is_null($filename)) {
        error_exit404();
    }
    // 0 -- is the visitor logged in
    if (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();
            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']++;
            }
        }
    }
    /** 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
        session_write_close();
        // we no longer need this here, everything relevant is now in $USER
    } else {
        $USER = new Useraccount();
        $USER->is_logged_in = FALSE;
    }
    //
    // 1 -- does the visitor want to download the source code
    //
    $path_components = explode('/', trim(strtr($filename, '\\', '/'), '/'));
    if (strtolower($path_components[0]) == 'websiteatschool') {
        $source = isset($path_components[1]) ? strtolower($path_components[1]) : 'program';
        download_source($source);
        exit;
    }
    //
    // 2 -- no source code requested. check out regular files
    //
    $path = '/' . implode('/', $path_components);
    // 2A -- always disallow attempts to escape from tree via parent directory tricks
    if (in_array('..', $path_components)) {
        logger(sprintf("%s(): access denied for file '%s': no tricks with '/../': return 404 Not Found", __FUNCTION__, $path), WLOG_DEBUG);
        error_exit404($path);
    }
    // 2B -- check the 1st and 2nd component of the requested file
    switch ($path_components[0]) {
        case 'areas':
            $area_path = isset($path_components[1]) ? $path_components[1] : '';
            $fields = array('area_id', 'is_private');
            $where = array('is_active' => TRUE, 'path' => $area_path);
            $table = 'areas';
            if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
                logger(sprintf("%s(): access denied for file '%s': non-existing or inactive area: return 404 Not Found", __FUNCTION__, $path), WLOG_DEBUG);
                error_exit404($path);
            }
            $area_id = intval($record['area_id']);
            if (db_bool_is(TRUE, $record['is_private']) && !$USER->has_intranet_permissions(ACL_ROLE_INTRANET_ACCESS, $area_id)) {
                logger(sprintf("%s(): access denied for file '%s' in private area '%d': return 404 Not Found", __FUNCTION__, $path, $area_id), WLOG_DEBUG);
                error_exit404($path);
            }
            break;
        case 'users':
            $user_path = isset($path_components[1]) ? $path_components[1] : '';
            $fields = array('user_id');
            $where = array('path' => $user_path, 'is_active' => TRUE);
            $table = 'users';
            if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
                logger(sprintf("%s(): access denied for file '%s': non-existing or inactive user: return 404 Not Found", __FUNCTION__, $path), WLOG_DEBUG);
                error_exit404($path);
            }
            break;
        case 'groups':
            $group_path = isset($path_components[1]) ? $path_components[1] : '';
            $fields = array('group_id');
            $where = array('path' => $group_path, 'is_active' => TRUE);
            $table = 'groups';
            if (($record = db_select_single_record($table, $fields, $where)) === FALSE) {
                logger(sprintf("%s(): access denied for file '%s': non-existing or inactive group: return 404 Not Found", __FUNCTION__, $path), WLOG_DEBUG);
                error_exit404($path);
            }
            break;
        default:
            logger(sprintf("%s(): access denied for file '%s': subdirectory '%s' not recognised: return 404 Not Found", __FUNCTION__, $path, $path_components[0]), WLOG_DEBUG);
            error_exit404($path);
            break;
    }
    // 2C -- still here? 1st and 2nd components are good but does the file exist?
    if (!is_file($CFG->datadir . $path)) {
        logger(sprintf("%s(): access denied for file '%s': file does not exist: return 404 Not Found", __FUNCTION__, $path), WLOG_DEBUG);
        error_exit404($path);
    }
    //
    // At this point we confident that the file exists within the data directory and also that
    // the visitor is allowed access to the file. Now send the file to the visitor.
    //
    $name = basename($path);
    if (($bytes_sent = send_file_from_datadir($path, $name)) === FALSE) {
        logger(sprintf("Failed to send '%s' using filename '%s'", $path, $name));
        $retval = FALSE;
    } else {
        logger(sprintf("Success sending '%s' using filename '%s', size = %d bytes", $path, $name, $bytes_sent), WLOG_DEBUG);
        $retval = TRUE;
    }
    exit;
}
/** 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
}
 /** retrieve configuration data for this set of snapshots
  *
  * this routine fetches the configuration from the snapshots table and stores
  * the sanitised information from the various fields in the object variables.
  *
  * @param int $node_id this key identifies the snapshots series
  * @return void and information stored in object variables
  * @todo check for information leaks (private path) here?
  */
 function get_snapshots_configuration($node_id)
 {
     //
     // 1 -- retrieve the relevant data for this series of snapshots from database
     //
     $table = 'snapshots';
     $fields = array('header', 'introduction', 'snapshots_path', 'variant', 'dimension');
     $where = array('node_id' => intval($node_id));
     $record = db_select_single_record($table, $fields, $where);
     if ($record === FALSE) {
         logger(sprintf('%s.%s(): error retrieving configuration: %s', __CLASS__, __FUNCTION__, db_errormessage()));
         $record = array('header' => '', 'introduction' => '', 'snapshots_path' => '', 'variant' => 1, 'dimension' => 512);
     }
     $this->header = trim($record['header']);
     $this->introduction = trim($record['introduction']);
     $this->variant = intval($record['variant']);
     $this->dimension = intval($record['dimension']);
     //
     // 2 -- sanity checks (just in case); massage pathname
     //
     $path = trim($record['snapshots_path']);
     if (!utf8_validate($path) || strpos('/' . $path . '/', '/../') !== FALSE) {
         logger(sprintf("%s.%s(): invalid path '%s'; using root path", __CLASS__, __FUNCTION__, $path));
         $path = '/';
         // shouldn't happen
     }
     if (substr($path, 0, 1) != '/') {
         $path = '/' . $path;
     }
     if (substr($path, -1) == '/') {
         $path = substr($path, 0, -1);
     }
     $this->snapshots_path = $path;
     // FIXME: check permissions here to prevent leaking a private area path to anonymous visitors?
     return;
 }