/** construct an AreaManager object
  *
  * This initialises the AreaManager and also dispatches the chore to do.
  *
  * @param object &$output collects the html output
  * @uses $CFG
  */
 function AreaManager(&$output)
 {
     global $CFG;
     $this->output =& $output;
     $this->output->set_helptopic('areamanager');
     $chore = get_parameter_string('chore', AREAMANAGER_CHORE_VIEW);
     switch ($chore) {
         case AREAMANAGER_CHORE_VIEW:
             $this->area_overview();
             break;
         case AREAMANAGER_CHORE_ADD:
             $this->area_add();
             break;
         case AREAMANAGER_CHORE_SET_DEFAULT:
             $this->area_setdefault();
             break;
         case AREAMANAGER_CHORE_DELETE:
             $this->area_delete();
             break;
         case AREAMANAGER_CHORE_EDIT:
             $this->area_edit();
             break;
         case AREAMANAGER_CHORE_EDIT_THEME:
             $this->area_edittheme();
             break;
         case AREAMANAGER_CHORE_RESET_THEME:
             $this->area_resettheme();
             break;
         case AREAMANAGER_CHORE_SAVE_NEW:
             $this->area_savenew();
             break;
         case AREAMANAGER_CHORE_SAVE:
             $this->area_save();
             break;
         default:
             $s = utf8_strlen($chore) <= 50 ? $chore : utf8_substr($chore, 0, 44) . ' (...)';
             $message = t('chore_unknown', 'admin', array('{CHORE}' => htmlspecialchars($s)));
             $output->add_message($message);
             logger('areamanager: unknown chore: ' . htmlspecialchars($s));
             $this->area_overview();
             break;
     }
 }
/** main entry point for modulemanager (called from /program/main_admin.php)
 *
 * this routine dispatches the tasks, If the specified task
 * is not recognised, the default task TASK_MODULEMANAGER_INTRO
 * is executed.
 *
 * @param object &$output collects the html output
 * @return void results are returned as output in $output
 */
function job_modulemanager(&$output)
{
    $output->set_helptopic('modulemanager');
    $task = get_parameter_string('task', TASK_MODULEMANAGER_INTRO);
    switch ($task) {
        case TASK_MODULEMANAGER_INTRO:
            modulemanager_show_intro($output);
            modulemanager_show_menu($output);
            break;
        case TASK_MODULEMANAGER_EDIT:
        case TASK_MODULEMANAGER_SAVE:
            modulemanager_process($output, $task);
            break;
        default:
            $s = utf8_strlen($task) <= 50 ? $task : utf8_substr($task, 0, 44) . ' (...)';
            $message = t('task_unknown', 'admin', array('{TASK}' => htmlspecialchars($s)));
            $output->add_message($message);
            logger('modulemanager: unknown task: ' . htmlspecialchars($s));
            modulemanager_show_intro($output);
            modulemanager_show_menu($output);
            break;
    }
}
/** main program for visitors
 *
 * this routine is called from /index.php. It is the main program for visitors.
 *
 * @return void page sent to the browser
 * @todo cleanup login/logout-code
 */
function main_index()
{
    global $USER;
    global $CFG;
    global $LANGUAGE;
    /** initialise the program, setup database, read configuration, etc. */
    require_once $CFG->progdir . '/init.php';
    initialise();
    was_version_check();
    // this never returns if versions don't match
    // TODO: cleanup like in main_admin()
    // handle login/logout/continuation so we quickly find out which user is calling
    if (isset($_GET['logout'])) {
        /** loginlib.php contains both login- and logout-routines */
        require_once $CFG->progdir . '/lib/loginlib.php';
        was_logout();
        // may or may not return here
    } elseif (isset($_GET['login'])) {
        /** loginlib.php contains both login- and logout-routines */
        require_once $CFG->progdir . '/lib/loginlib.php';
        was_login(magic_unquote($_GET['login']));
        // may or may not return here
    } elseif (isset($_COOKIE[$CFG->session_name])) {
        /** dbsessionlib.php contains our own database based session handler */
        require_once $CFG->progdir . '/lib/dbsessionlib.php';
        dbsession_setup($CFG->session_name);
        if (dbsession_exists(magic_unquote($_COOKIE[$CFG->session_name]))) {
            session_start();
        }
    }
    // At this point we either have a valid session with a logged-in user
    // (indicated via existence of $_SESSION) or we are dealing with an anonymous
    // visitor with non-existing $_SESSION. Keep track of the number of calls
    // this user makes (may be logged lateron on logout).
    if (isset($_SESSION)) {
        if (!isset($_SESSION['session_counter'])) {
            // first time after login, record start time of session
            $_SESSION['session_counter'] = 1;
            $_SESSION['session_start'] = strftime("%Y-%m-%d %T");
        } else {
            $_SESSION['session_counter']++;
        }
    }
    // Now is the time to create a USER object, even when the visitor is just a passerby
    // because we can then determine easily if a visitor is allowed certain things, e.g.
    // view a protected area or something
    /** useraccount.class.php is used to define the USER object */
    require_once $CFG->progdir . '/lib/useraccount.class.php';
    if (isset($_SESSION) && isset($_SESSION['user_id'])) {
        $USER = new Useraccount($_SESSION['user_id']);
        $USER->is_logged_in = TRUE;
        $_SESSION['language_key'] = $LANGUAGE->get_current_language();
        // remember language set via _GET or otherwise
    } else {
        $USER = new Useraccount();
        $USER->is_logged_in = FALSE;
    }
    // Check for the special preview-mode
    // This allows a webmaster to preview a page in the correct environment (theme)
    // even when the page is under embargo. Note that the node_id and area_id are
    // retrieved from the session; the user only has a cryptic preview-code.
    // See pagemanagerlib.php for more information (function task_page_preview()).
    $in_preview_mode = FALSE;
    if ($USER->is_logged_in) {
        $preview_code_from_url = get_parameter_string('preview');
        if (!is_null($preview_code_from_url) && isset($_SESSION['preview_salt']) && isset($_SESSION['preview_node'])) {
            $hash = md5($_SESSION['preview_salt'] . $_SESSION['preview_node']);
            if ($hash === $preview_code_from_url) {
                $node_id = intval($_SESSION['preview_node']);
                $area_id = intval($_SESSION['preview_area']);
                $area = db_select_single_record('areas', '*', array('area_id' => $area_id));
                if ($area === FALSE) {
                    logger("Fatal error 070: cannot preview node '{$node_id}' in area '{$area_id}'");
                    error_exit('070');
                } else {
                    $tree = tree_build($area_id);
                    $in_preview_mode = TRUE;
                }
            }
        }
    }
    if ($in_preview_mode == FALSE) {
        $requested_area = get_requested_area();
        $requested_node = get_requested_node();
        $req_area_str = is_null($requested_area) ? "NULL" : strval($requested_area);
        $req_node_str = is_null($requested_node) ? "NULL" : strval($requested_node);
        if (($area = calculate_area($requested_area, $requested_node)) === FALSE) {
            logger("Fatal error 080: no valid area (request: area='{$req_area_str}', node='{$req_node_str}')");
            error_exit('080');
            // no such area
        }
        $area_id = intval($area['area_id']);
        // If $USER has no permission to view area $area_id, we simply bail out.
        // Rationale: if the user is genuine, she knows about logging in first.
        // If the user is NOT logged in and tries to view a protected area, I'd consider
        // it malicious, and in that case I won't even confirm the existence of
        // the requested area. (If a cracker simply tries areas 0,1,.. and sometimes is greeted
        // with 'please enter credentials' and sometimes with 'area does not exist', this
        // provides information to the cracker. I don't want that). Note that the error code
        // is the same as the one for non-existing area. In other words: for an unauthorised
        // visitor an existing private area is just as non-existent as a non-existing public area.
        if (db_bool_is(TRUE, $area['is_private']) && !$USER->has_intranet_permissions(ACL_ROLE_INTRANET_ACCESS, $area_id)) {
            logger(sprintf("Fatal error 080: no view permissions for area '%d' (request: area='%s', node='%s')", $area_id, $req_area_str, $req_node_str));
            error_exit('080');
            // no such area
        }
        // still here?
        // then we've got a valid $area_id and corresponding $area record.
        // now we need to figure out which $node_id to use
        $tree = tree_build($area_id);
        if (($node_id = calculate_node_id($tree, $area_id, $requested_node)) === FALSE) {
            logger(sprintf("Fatal error 080: no valid node within area '%d' (request: area='%s', node='%s')", $area_id, $req_area_str, $req_node_str));
            error_exit('080');
            // no such area
        }
    }
    // At this point we have the following in our hands
    // - a valid $area_id
    // - a valid $node_id
    // - the complete tree from area $area_id in $tree
    // - the area record from database in $area
    // - the node record from database in $tree[$node_id]['record']
    // - a flag that signals preview mode in $in_preview_mode
    // We are on our way to generate a full page with content and all,
    // but otoh we MIGHT be in the middle of a redirect, so we may have to
    // leave without showing anything at all...
    if (!empty($tree[$node_id]['record']['link_href'])) {
        update_statistics($node_id);
        if (isset($_SESSION)) {
            session_write_close();
        }
        redirect_and_exit(htmlspecialchars($tree[$node_id]['record']['link_href']));
        // exit; redirect_and_exit() never returns
    }
    /** themelib contains the theme factory */
    require_once $CFG->progdir . '/lib/themelib.php';
    // And now we know about the $area, we can carry on determining which $theme to use.
    //
    $theme = theme_factory($area['theme_id'], $area_id, $node_id);
    if ($theme === FALSE) {
        logger("Fatal error 090: cannot setup theme '{$area['theme_id']}' in area '{$area_id}'");
        error_exit('090');
    }
    // Tell the theme about the preview mode
    $theme->set_preview_mode($in_preview_mode);
    // Now all we need to do is let the module connected to node $node_id generate output
    $module_id = $tree[$node_id]['record']['module_id'];
    module_view($theme, $area_id, $node_id, $module_id);
    // Remember this visitor
    update_statistics($node_id);
    // Finally, send output to user
    $theme->send_output();
    if (isset($_SESSION)) {
        session_write_close();
    }
    // done!
    exit;
}
Пример #4
0
/** show an introductory text for backup tool OR stream a ZIP-file to the browser
 *
 * If we arrive here via the tools menu, the parameter download is not set.
 * In that case we show an introductory text with a link that yields a ZIP-file
 * with the backup.
 *
 * If the user follows the download link, we arrive here too but with the download
 * parameter set. We then dump the database in a variable and subsequently 
 * compress it in a ZIP-file which we stream to the browser. We do code some
 * things in the basename of the backup:
 *  - the hostname
 *  - the database name
 *  - the database prefix
 *  - the date and the time
 * which should be enough to distinguish nearly all backups if you happen to have a
 * lot of different ones. Note that the URL is also encoded as a comment in the .ZIP.
 *
 * The parameter download is currently set to 'zip'. However, we do attempt to send the
 * plain uncompressed data if that parameter is set to 'sql' (quick and dirty). Oh well.
 * hopefully there is enough memory to accomodate backups of moderate sized sites.
 *
 * Note that we need space to compress the data; a informal test yielded that we need
 * about 160% of the uncompressed size of the backup (tested with a small testset).
 * Rule of the thumb for memory: the more the merrier but at least twice the size of the
 * uncompressed backup.
 *
 * @param object &$output collects output to show to user
 * @return output displayed via $output OR ZIP-file streamed to the browser
 */
function task_backuptool(&$output)
{
    global $DB, $CFG, $WAS_SCRIPT_NAME;
    $download = get_parameter_string('download', NULL);
    if (is_null($download)) {
        $output->set_helptopic('backuptool');
        $output->add_content("<h2>" . t('backuptool_header', 'admin') . "</h2>");
        $params = array('{DATADIRECTORY}' => htmlspecialchars($CFG->datadir));
        $output->add_content(t('backuptool_intro', 'admin', $params));
        $output->add_content('<p>');
        $parameters = array('job' => JOB_TOOLS, 'task' => TASK_BACKUPTOOL, 'download' => 'zip');
        $attributes = array('title' => t('backuptool_download_title', 'admin'));
        $anchor = t('backuptool_download', 'admin');
        $output->add_content(html_a($WAS_SCRIPT_NAME, $parameters, $attributes, $anchor));
        show_tools_menu($output, TASK_BACKUPTOOL);
        return;
    }
    // Try to construct a suitable hostname as part of the filename.
    $host = '';
    if (($url = parse_url($CFG->www)) !== FALSE) {
        $host = isset($url['host']) ? trim(ereg_replace('[^a-zA-Z0-9.-]', '', $url['host']), '.') : '';
    }
    if (empty($host) && isset($_SERVER['SERVER_NAME'])) {
        $host = trim(ereg_replace('[^a-zA-Z0-9.-]', '', $_SERVER['SERVER_NAME']), '.');
    }
    if (empty($host)) {
        $host = 'localhost';
        // last resort
    }
    // basename = host "-" database "-" prefix "-" timestamp (only acceptable chars for filename; strip fancy chars)
    $basename = $host . '-' . trim(ereg_replace('[^a-zA-Z0-9._-]', '', $CFG->db_name), '-_.') . '-' . trim(ereg_replace('[^a-zA-Z0-9._-]', '', $CFG->prefix), '-_.') . '-' . strftime('%Y%m%d-%H%M%S');
    $archive = $basename . '.zip';
    $backup = $basename . '.sql';
    $data = '';
    logger(sprintf('%s(): start creating/streaming backup %s', __FUNCTION__, $basename), WLOG_DEBUG);
    if ($DB->dump($data)) {
        $data_length = strlen($data);
        logger(sprintf('%s(): backup file %s is %d bytes', __FUNCTION__, $backup, $data_length), WLOG_DEBUG);
        if ($download == 'sql') {
            header('Content-Type: text/x-sql');
            header(sprintf('Content-Disposition: attachment; filename="%s"', $backup));
            logger(sprintf('%s(): about to stream %d bytes (backup = %s)', __FUNCTION__, $data_length, $backup), WLOG_DEBUG);
            echo $data;
            logger(sprintf('%s(): success streaming data %s (%d bytes uncompressed)', __FUNCTION__, $backup, $data_length));
            exit;
        } else {
            include_once $CFG->progdir . '/lib/zip.class.php';
            $zip = new Zip();
            $comment = $CFG->www;
            if ($zip->OpenZipstream($archive, $comment)) {
                if ($zip->AddData($data, $backup)) {
                    if ($zip->CloseZip()) {
                        logger(sprintf('%s(): success streaming backup %s (%d bytes uncompressed)', __FUNCTION__, $archive, $data_length));
                    } else {
                        logger(sprintf('%s(): cannot close zipstream %s: %s', __FUNCTION__, $archive, $zip->Error));
                    }
                } else {
                    logger(sprintf('%s(): cannot add backup data to zipstream %s: %s', __FUNCTION__, $archive, $zip->Error));
                    $zip->CloseZip();
                }
                // ZipStream is done, no point in adding garbage at the end of that file, quit completely
                exit;
            } else {
                logger(sprintf('%s(): cannot open zipstream %s for backup: %s', __FUNCTION__, $archive, $zip->Error));
            }
        }
    } else {
        logger(sprintf('%s(): cannot open dump data for backup: %s', __FUNCTION__, db_errormessage()));
    }
    $output->add_message(t('backuptool_error', 'admin'));
    show_tools_menu($output);
    show_tools_intro($output);
}
/** main entry point for update wizard (called from /program/main_admin.php)
 *
 * This routine takes care of executing update routines for both the core
 * program and modules, themes, etc. It is called automagically whenever
 * the core program version in the database is different from the version
 * in the file {@lnk version.php} (see also {@link main_admin()}).
 *
 * It can also be called manually via 'job=update'. When no specific
 * task is specified, this routine shows the overview of versions for
 * core, modules, themes, etc. Whenever a component is NOT up to date,
 * an [Update] button is displayed. If a component IS up to date, we
 * simply display the word 'OK'. This implies that when everything is up
 * to date, the overview simply displays a list of OK's and the user
 * is 'free to go'.
 *
 * The actual updates for modules, themes, etc. is done via the various
 * subsystems themselves, e.g. by calling htmlpage_upgrade() in the file
 * /program/modules/htmlpage/htmlpage_install.php. The updates for the
 * core program are actually performed from this file right here, see
 * {@link update_core_2010120800()} below for an example.
 *
 * Note that we give a core update high priority: if the core
 * is not up to date, nothing will work, except updating the core.
 *
 * @param object &$output collects the html output
 * @return void results are returned as output in $output
 */
function job_update(&$output)
{
    global $CFG, $WAS_SCRIPT_NAME, $USER;
    $output->set_helptopic('update');
    $task = get_parameter_string('task', TASK_UPDATE_OVERVIEW);
    if ($task == TASK_UPDATE_OVERVIEW) {
        update_show_overview($output);
    } elseif ($task == TASK_UPDATE_CORE) {
        update_core($output);
        update_show_overview($output);
    } elseif (intval($CFG->version) != intval(WAS_VERSION)) {
        $output->add_message(t('update_core_warnning_core_goes_first', 'admin'));
        update_show_overview($output);
    } else {
        $key = get_parameter_string('key', '');
        switch ($task) {
            case TASK_INSTALL_LANGUAGE:
                install_language($output, $key);
                update_show_overview($output);
                break;
            case TASK_UPDATE_LANGUAGE:
                update_language($output, $key);
                update_show_overview($output);
                break;
            case TASK_INSTALL_MODULE:
                install_module($output, $key);
                update_show_overview($output);
                break;
            case TASK_UPDATE_MODULE:
                update_module($output, $key);
                update_show_overview($output);
                break;
            case TASK_INSTALL_THEME:
                install_theme($output, $key);
                update_show_overview($output);
                break;
            case TASK_UPDATE_THEME:
                update_theme($output, $key);
                update_show_overview($output);
                break;
            default:
                $s = utf8_strlen($task) <= 50 ? $task : utf8_substr($task, 0, 44) . ' (...)';
                $message = t('task_unknown', 'admin', array('{TASK}' => htmlspecialchars($s)));
                $output->add_message($message);
                logger('tools: unknown task: ' . htmlspecialchars($s));
                update_show_overview($output);
                break;
        }
    }
}
 /** create a new subdirectory
  *
  * This routine either shows a dialog where the user can specify the name of a new
  * directory to add OR processes the dialog.
  *
  * In case of directory name too short, already exists, etc. the user is
  * returned to the dialog to try again. If all goes well the new directory
  * is created and at the same time the empty file 'index.html' is created
  * to "protect" the directory from prying eyes.
  *
  * @return void output returned via $this->output
  */
 function task_add_directory()
 {
     global $WAS_SCRIPT_NAME, $CFG;
     // 1A -- bail out if user pressed cancel button
     if (isset($_POST['button_cancel'])) {
         $this->output->add_message(t('cancelled', 'admin'));
         $this->task_list_directory();
         return;
     }
     // 1B -- Check validity of working directory, maybe bail out
     $newdir = get_parameter_string(PARAM_PATH, $this->current_directory);
     if (($path = $this->valid_path($newdir)) === FALSE) {
         $this->output->add_message(t('invalid_path', 'admin', array('{PATH}' => htmlspecialchars($newdir))));
         $this->task_list_directory();
         return;
     }
     $this->current_directory = $path;
     // this is where we will create the new subdirectory
     // 2 -- prepare dialog (either to show it or to validate it)
     $dialogdef = array('subdirectory' => array('type' => F_ALPHANUMERIC, 'name' => 'subdirectory', 'minlength' => 1, 'maxlength' => 240, 'columns' => 30, 'label' => t('filemanager_add_subdirectory_label', 'admin'), 'title' => t('filemanager_add_subdirectory_title', 'admin'), 'value' => ''), 'button_save' => dialog_buttondef(BUTTON_SAVE), 'button_cancel' => dialog_buttondef(BUTTON_CANCEL));
     $a_params = array('job' => $this->job, 'task' => TASK_ADD_DIRECTORY, PARAM_PATH => $path);
     $href = href($WAS_SCRIPT_NAME, $a_params);
     // 3 -- show dialog or validate + process?
     if (!isset($_POST['subdirectory'])) {
         $this->output->add_content('<h2>' . t('filemanager_add_subdirectory_header', 'admin') . '</h2>');
         $this->output->add_content(t('filemanager_add_subdirectory_explanation', 'admin'));
         $this->output->add_content(dialog_quickform($href, $dialogdef));
         $this->show_breadcrumbs($path);
         $this->show_menu($path);
         return;
     }
     // 4 -- validate user input and maybe create directory
     // 4A -- check for generic errors
     $invalid = FALSE;
     if (!dialog_validate($dialogdef)) {
         $invalid = TRUE;
     }
     // 4B -- check for additional errors: sane filename
     $subdirectory = $dialogdef['subdirectory']['value'];
     $sanitised_subdirectory = sanitise_filename($subdirectory);
     if ($subdirectory != $sanitised_subdirectory || substr($sanitised_subdirectory, 0, strlen(THUMBNAIL_PREFIX)) == THUMBNAIL_PREFIX) {
         ++$dialogdef['subdirectory']['errors'];
         $params = array('{FIELD}' => str_replace('~', '', $dialogdef['subdirectory']['label']), '{VALUE}' => htmlspecialchars($subdirectory));
         $dialogdef['subdirectory']['error_messages'][] = t('validate_bad_filename', '', $params);
         $invalid = TRUE;
     }
     // 4C -- check for additional errors: directory should not exist already
     $subdirectory_full_path = $CFG->datadir . $path . '/' . $sanitised_subdirectory;
     if (file_exists($subdirectory_full_path)) {
         ++$dialogdef['subdirectory']['errors'];
         $params = array('{FIELD}' => str_replace('~', '', $dialogdef['subdirectory']['label']), '{VALUE}' => $this->vpath($path) . '/' . $sanitised_subdirectory);
         $dialogdef['subdirectory']['error_messages'][] = t('validate_already_exists', '', $params);
         $invalid = TRUE;
     }
     // 4D -- redo dialog if there were errors
     if ($invalid) {
         // shortcut: only the subdirectory field can (and does) yield errors
         $this->output->add_message($dialogdef['subdirectory']['error_messages']);
         $dialogdef['subdirectory']['value'] = $sanitised_subdirectory;
         $this->output->add_content('<h2>' . t('filemanager_add_subdirectory_header', 'admin') . '</h2>');
         $this->output->add_content(t('filemanager_add_subdirectory_explanation', 'admin'));
         $this->output->add_content(dialog_quickform($href, $dialogdef));
         $this->show_breadcrumbs($path);
         // note that we do NOT display the menu here; let the user concentrate (or press Cancel) this time
         return;
     }
     // 5 -- all set, go create subdir
     $params = array('{PATH}' => htmlspecialchars($this->vpath($path)), '{DIRECTORY}' => htmlspecialchars($sanitised_subdirectory));
     if (@mkdir($subdirectory_full_path, 0700)) {
         @touch($subdirectory_full_path . '/index.html');
         // "protect" the newly created directory from prying eyes
         $this->output->add_message(t('filemanager_add_subdirectory_success', 'admin', $params));
         logger(sprintf('%s.%s(): success with mkdir %s', __CLASS__, __FUNCTION__, $path . '/' . $subdirectory), WLOG_DEBUG);
     } else {
         $this->output->add_message(t('filemanager_add_subdirectory_failure', 'admin', $params));
         logger(sprintf('%s.%s(): cannot mkdir %s', __CLASS__, __FUNCTION__, $path . '/' . $subdirectory));
     }
     $this->task_list_directory();
 }
/** handle the editing/saving of the main configuration information
 *
 * this routine handles editing of the main configuration parameters.
 * It either displays the edit dialog or saves the modified data and
 * shows the configuration manager introduction screen.
 *
 * Note that we do NOT try to redirect the user via a header() after
 * a succesful save. It would be handy because this particular
 * save action may have had impact on the global configuration,
 * which is already read at this point. By redirecting we would
 * make a fresh start, with the new parameters.
 * However, we lose the easy ability to tell the user that the data
 * was saved (via $output->add_message()). So, either no feedback
 * or obsolete global config in core. Hmmmm. I settle for the feedback
 * and the 'wrong' settings.
 *
 * @param object &$output collects the html output
 * @return void results are returned as output in $output
 * @uses ConfigAssistant()
 */
function process_task_site(&$output)
{
    global $CFG, $WAS_SCRIPT_NAME;
    // 1 -- prepare
    include_once $CFG->progdir . '/lib/configassistant.class.php';
    $table = 'config';
    $keyfield = 'name';
    $prefix = 'site_config_';
    $domain = 'admin';
    $where = '';
    $assistant = new ConfigAssistant($table, $keyfield, $prefix, $domain, $where);
    $href = href($WAS_SCRIPT_NAME, array('job' => JOB_CONFIGURATIONMANAGER, 'task' => TASK_SITE, 'chore' => CHORE_SAVE));
    // 2 -- what do we need to do?
    $chore = get_parameter_string('chore');
    if ($chore == CHORE_SAVE) {
        // save data (or cancel if they want to cancel)
        if (isset($_POST['button_save'])) {
            if ($assistant->save_data($output)) {
                //if (!headers_sent()) {
                //    header('Location: '.href($WAS_SCRIPT_NAME,array('job' => JOB_CONFIGURATIONMANAGER)));
                //    exit;
                //} else {
                show_configuration_intro($output);
                show_configuration_menu($output);
                //}
            } else {
                $output->add_content('<h2>' . t($prefix . 'header', 'admin') . '</h2>');
                $output->add_content(t($prefix . 'explanation', 'admin'));
                $assistant->show_dialog($output, $href);
            }
        } else {
            $output->add_message(t('cancelled', 'admin'));
            show_configuration_intro($output);
            show_configuration_menu($output);
        }
    } else {
        // no save yet, simply show dialog
        $output->add_content('<h2>' . t($prefix . 'header', 'admin') . '</h2>');
        $output->add_content(t($prefix . 'explanation', 'admin'));
        $assistant->show_dialog($output, $href);
        show_configuration_menu($output, TASK_SITE);
    }
}
/** determine the default skin to use
 *
 * This routine determines which skin to use in AdminOutput.
 * It always returns a valid skin (using 'base' in case of error).
 *
 * @uses $USER;
 * @uses $_SESSION;
 * @return string a valid skin name
 */
function get_current_skin()
{
    global $USER;
    $skins = array('base', 'textonly', 'braille', 'big', 'lowvision');
    $skin = get_parameter_string('skin', isset($_SESSION['skin']) ? $_SESSION['skin'] : $USER->skin);
    return in_array($skin, $skins) ? $skin : 'base';
}
 /** construct a PageManager object (called from /program/main_admin.php)
  *
  * This initialises the PageManager, checks user permissions and 
  * finally dispatches the tasks. If the specified task is not
  * recognised, the default task TASK_TREEVIEW is executed.
  *
  * Note that allmost all commands act on the area contained in the
  * SESSION-variable current_area_id. Also, we almost always need
  * the tree of nodes in that area, so we read it once, _before_
  * dispatching the task at hand. This means that the current tree
  * in the current area is ALWAYS available. This means that none of
  * the other routines should have to worry about which area or
  * reading the tree; this information is already available in
  * $this->area_id and $this->tree.
  *
  *
  * @param object &$output collects the html output
  * @return void results are returned as output in $this->output
  */
 function PageManager(&$output)
 {
     global $USER;
     $this->output =& $output;
     $this->output->set_helptopic('pagemanager');
     $this->areas = get_area_records();
     // Do not maintain a 'current area' if it is no longer there (it might have been deleted by us or another admin)
     if (isset($_SESSION['current_area_id']) && !isset($this->areas[intval($_SESSION['current_area_id'])])) {
         unset($_SESSION['current_area_id']);
     }
     // Do we have a valid working area? If not, try to calculate one
     if (!isset($_SESSION['current_area_id']) || !isset($_SESSION['tree_mode']) || !isset($_SESSION['expanded_nodes'])) {
         // 1 -- Try the specified area aaa from command line (admin.php?area=aaa)
         $area_id = get_parameter_int('area', FALSE);
         if ($area_id !== FALSE) {
             if (!$USER->is_admin_pagemanager($area_id) || !isset($this->areas[$area_id])) {
                 logger(__FUNCTION__ . "(): weird: user '{$USER->username}' tried to access area '{$area_id}'");
                 $message = t('area_admin_access_denied', 'admin', array('{AREA}' => strval($area_id)));
                 $area_id = FALSE;
                 // sentinel
             }
         } else {
             // 2 -- try use the first available area for which the user has permissions...
             $message = t('no_areas_available', 'admin');
             // ... but assume the worst
             foreach ($this->areas as $id => $area) {
                 if ($USER->is_admin_pagemanager($id)) {
                     $area_id = $id;
                     break;
                 }
             }
         }
         if ($area_id === FALSE) {
             $this->output->add_message($message);
             $this->output->add_content($message);
             $this->show_area_menu();
             return;
         } else {
             $_SESSION['current_area_id'] = $area_id;
             $_SESSION['expanded_nodes'] = array();
             $_SESSION['tree_mode'] = TREE_VIEW_MINIMAL;
         }
     } else {
         // Do we (still) have enough permissions for the current area?
         $area_id = intval($_SESSION['current_area_id']);
         if (!$USER->is_admin_pagemanager($area_id) || !isset($this->areas[$area_id])) {
             // this is completely weird: the session has an invalid area? tsk tsk tsk
             logger(__FUNCTION__ . "(): weird: user '{$USER->username}' can no longer access area '{$area_id}'?");
             $message = t('area_admin_access_denied', 'admin', array('{AREA}' => strval($area_id)));
             $this->output->add_message($message);
             $this->output->add_content($message);
             $this->show_area_menu();
             return;
         }
     }
     //
     // At this point we have 3 valid variables in $_SESSION indicating
     // the current working area and the open/closed state of sections.
     // We now should read the corresponding tree in core and look which
     // task we need to perform.
     //
     $this->build_cached_tree(intval($_SESSION['current_area_id']));
     $task = get_parameter_string('task', TASK_TREEVIEW);
     switch ($task) {
         case TASK_TREEVIEW:
             $this->task_treeview();
             break;
         case TASK_TREEVIEW_SET:
             $this->task_treeview_set();
             break;
         case TASK_SUBTREE_EXPAND:
             $this->task_subtree_expand();
             break;
         case TASK_SUBTREE_COLLAPSE:
             $this->task_subtree_collapse();
             break;
         case TASK_SET_DEFAULT:
             $this->task_set_default();
             break;
         case TASK_ADD_PAGE:
         case TASK_ADD_SECTION:
             $this->task_node_add($task);
             break;
         case TASK_NODE_DELETE:
             $this->task_node_delete();
             break;
         case TASK_NODE_EDIT:
         case TASK_NODE_EDIT_ADVANCED:
             $this->task_node_edit($task);
             break;
         case TASK_NODE_EDIT_CONTENT:
             $this->task_node_edit_content();
             break;
         case TASK_SAVE_CONTENT:
             $this->task_save_content();
             break;
         case TASK_PAGE_PREVIEW:
             $this->task_page_preview();
             break;
         case TASK_SAVE_NODE:
             $this->task_save_node();
             break;
         case TASK_SAVE_NEWPAGE:
         case TASK_SAVE_NEWSECTION:
             $this->task_save_newnode($task);
             break;
         default:
             $s = utf8_strlen($task) <= 50 ? $task : utf8_substr($task, 0, 44) . ' (...)';
             $message = t('task_unknown', 'admin', array('{TASK}' => htmlspecialchars($s)));
             $this->output->add_message($message);
             logger(__FUNCTION__ . '(): unknown task: ' . htmlspecialchars($s));
             $this->task_treeview();
             break;
     }
 }
 /** construct a GroupManager object
  *
  * This initialises the GroupManager and also dispatches the task to do.
  *
  * @param object &$output collects the html output
  */
 function GroupManager(&$output)
 {
     global $CFG;
     $this->output =& $output;
     $this->output->set_helptopic('groupmanager');
     $this->groups = array();
     $task = get_parameter_string('task', TASK_GROUPS);
     switch ($task) {
         case TASK_GROUPS:
             $this->groups_overview();
             break;
         case TASK_GROUP_ADD:
             $this->group_add();
             break;
         case TASK_GROUP_SAVE_NEW:
             $this->group_savenew();
             break;
         case TASK_GROUP_EDIT:
             $this->group_edit();
             break;
         case TASK_GROUP_SAVE:
             $this->group_save();
             break;
         case TASK_GROUP_DELETE:
             $this->group_delete();
             break;
         case TASK_GROUP_CAPACITY_OVERVIEW:
             $this->capacity_overview();
             break;
         case TASK_GROUP_CAPACITY_INTRANET:
             $this->capacity_intranet();
             break;
         case TASK_GROUP_CAPACITY_ADMIN:
             $this->capacity_admin();
             break;
         case TASK_GROUP_CAPACITY_PAGEMANAGER:
             $this->capacity_pagemanager();
             break;
         case TASK_GROUP_CAPACITY_SAVE:
             $this->capacity_save();
             break;
         case TASK_GROUP_CAPACITY_MODULE:
             $this->output->add_message("STUB: task '{$task}' not yet implemented");
             $this->output->add_message('group = ' . get_parameter_string('group', '(unset)'));
             $this->output->add_message('capacity = ' . get_parameter_string('capacity', '(unset)'));
             $this->output->add_message('module = ' . get_parameter_string('module', '(unset)'));
             $this->groups_overview();
             break;
         default:
             $s = utf8_strlen($task) <= 50 ? $task : utf8_substr($task, 0, 44) . ' (...)';
             $message = t('task_unknown', 'admin', array('{TASK}' => htmlspecialchars($s)));
             $output->add_message($message);
             logger(sprintf('%s.%s(): unknown task: \'%s\'', __CLASS__, __FUNCTION__, htmlspecialchars($s)));
             $this->groups_overview();
             break;
     }
 }
/** main entry point for accountmanager (called from admin.php)
 *
 * this routing dispatches the tasks. If a specified task is not
 * recognised, the default task TASK_ACCOUNTS_OVERVIEW is
 * executed. Note that the User Manager and the Group Manager
 * are heavily interconnected. Therefore we use 1 common set
 * of tasks and distinguish between both managers via
 * sets of tasks, e.g. TASK_USER* point to the user manager
 * where TASK_GROUP* lead to the group manager.
 * 
 * @param object &$output collects the html output
 * @return void results are returned as output in $output
 */
function job_accountmanager(&$output)
{
    global $CFG;
    $output->set_helptopic('accountmanager');
    $task = get_parameter_string('task', TASK_ACCOUNTS);
    switch ($task) {
        case TASK_ACCOUNTS:
            show_accounts_intro($output);
            show_accounts_menu($output);
            break;
        case TASK_USERS:
        case TASK_USER_ADD:
        case TASK_USER_DELETE:
        case TASK_USER_EDIT:
        case TASK_USER_ADVANCED:
        case TASK_USER_GROUPS:
        case TASK_USER_GROUPADD:
        case TASK_USER_GROUPDELETE:
        case TASK_USER_GROUPSAVE:
        case TASK_USER_INTRANET:
        case TASK_USER_MODULE:
        case TASK_USER_ADMIN:
        case TASK_USER_PAGEMANAGER:
        case TASK_USER_TREEVIEW:
        case TASK_USER_SAVE:
        case TASK_USER_SAVE_NEW:
            include $CFG->progdir . '/lib/usermanager.class.php';
            $mgr = new UserManager($output);
            if ($mgr->show_parent_menu()) {
                show_accounts_menu($output, TASK_USERS);
            }
            break;
        case TASK_GROUPS:
        case TASK_GROUP_ADD:
        case TASK_GROUP_DELETE:
        case TASK_GROUP_EDIT:
        case TASK_GROUP_SAVE:
        case TASK_GROUP_SAVE_NEW:
            include $CFG->progdir . '/lib/groupmanager.class.php';
            $mgr = new GroupManager($output);
            if ($mgr->show_parent_menu()) {
                show_accounts_menu($output, TASK_GROUPS);
            }
            break;
        case TASK_GROUP_CAPACITY_OVERVIEW:
        case TASK_GROUP_CAPACITY_INTRANET:
        case TASK_GROUP_CAPACITY_MODULE:
        case TASK_GROUP_CAPACITY_ADMIN:
        case TASK_GROUP_CAPACITY_PAGEMANAGER:
        case TASK_GROUP_CAPACITY_SAVE:
            include $CFG->progdir . '/lib/groupmanager.class.php';
            $mgr = new GroupManager($output);
            if ($mgr->show_parent_menu()) {
                show_accounts_menu($output, TASK_GROUPS);
            }
            break;
        default:
            $s = utf8_strlen($task) <= 50 ? $task : utf8_substr($task, 0, 44) . ' (...)';
            $message = t('task_unknown', 'admin', array('{TASK}' => htmlspecialchars($s)));
            $output->add_message($message);
            logger('accountmanager: unknown task: ' . htmlspecialchars($s));
            show_accounts_intro($output);
            show_accounts_menu($output);
            break;
    }
}
 /** construct a UserManager object
  *
  * This initialises the UserManager and also dispatches the task to do.
  * This also loads the loginlib: we need that in order to manipulate the user password.
  *
  * @param object &$output collects the html output
  */
 function UserManager(&$output)
 {
     global $CFG;
     $this->output =& $output;
     $this->output->set_helptopic('usermanager');
     $this->users = array();
     require_once $CFG->progdir . '/lib/loginlib.php';
     $task = get_parameter_string('task', TASK_USERS);
     switch ($task) {
         case TASK_USERS:
             $this->users_overview();
             break;
         case TASK_USER_ADD:
             $this->user_add();
             break;
         case TASK_USER_SAVE_NEW:
             $this->user_savenew();
             break;
         case TASK_USER_DELETE:
             $this->user_delete();
             break;
         case TASK_USER_EDIT:
             $this->user_edit();
             break;
         case TASK_USER_SAVE:
             $this->user_save();
             break;
         case TASK_USER_GROUPS:
             $this->user_groups();
             break;
         case TASK_USER_GROUPADD:
             $this->user_groupadd();
             break;
         case TASK_USER_GROUPSAVE:
             $this->user_groupsave();
             break;
         case TASK_USER_GROUPDELETE:
             $this->user_groupdelete();
             break;
         case TASK_USER_INTRANET:
             $this->user_intranet();
             break;
         case TASK_USER_ADMIN:
             $this->user_admin();
             break;
         case TASK_USER_PAGEMANAGER:
             $this->user_pagemanager();
             break;
         case TASK_USER_ADVANCED:
         case TASK_USER_TREEVIEW:
         case TASK_USER_MODULE:
             $this->output->add_message('STUB: not implemented: ' . $task);
             $this->output->add_message('user = ' . get_parameter_string('user', '(unset)'));
             $this->show_parent_menu = TRUE;
             // STUB
             break;
         default:
             $s = utf8_strlen($task) <= 50 ? $task : utf8_substr($task, 0, 44) . ' (...)';
             $message = t('task_unknown', 'admin', array('{TASK}' => htmlspecialchars($s)));
             $output->add_message($message);
             logger('usermanager: unknown task: ' . htmlspecialchars($s));
             $this->users_overview();
             break;
     }
 }
 /** save the modified translations in a file in the tree CFG->datadir/languages/
  *
  * this routine validates the dialog data and attempts to save the changes in the file
  * $full_domain in the directory CFG->datadir/languages/$language_key/. Also, we may
  * need to send a message to the Website@School project with our changes (depending
  * on the flag _submit).
  *
  * @return void data saved and output written to browser via $this->output
  */
 function translation_save()
 {
     global $WAS_SCRIPT_NAME, $LANGUAGE;
     $language_key = get_parameter_string(TRANSLATETOOL_PARAM_LANGUAGE_KEY);
     // 1A -- basic sanity for language
     if ($this->languages === FALSE || !isset($this->languages[$language_key])) {
         logger(sprintf('%s.%s(): weird: user tried to edit translations for non-existing language \'%s\'', __CLASS__, __FUNCTION__, htmlspecialchars($language_key)));
         $params = array('{LANGUAGE_KEY}' => htmlspecialchars($language_key));
         $this->output->add_message(t('invalid_language', 'admin', $params));
         $this->languages_overview();
         return;
     }
     $full_domain = get_parameter_string(TRANSLATETOOL_PARAM_DOMAIN, 'was');
     // 1B -- basic sanity for domain
     if (!isset($this->domains[$full_domain])) {
         logger(sprintf('%s.%s(): weird: user requested non-existing domain \'%s\'', __CLASS__, __FUNCTION__, htmlspecialchars($full_domain)));
         $params = array('{FULL_DOMAIN}' => htmlspecialchars($full_domain));
         $this->output->add_message(t('invalid_language_domain', 'admin', $params));
         $this->languages_overview();
         return;
     }
     // 2 -- if the user cancelled the operation, there is no point in hanging 'round
     if (isset($_POST['button_cancel'])) {
         $this->output->add_message(t('cancelled', 'admin'));
         $this->languages_overview();
         return;
     }
     // 3 -- validate the dialog and maybe redo it
     $dialogdef = $this->get_dialogdef_language_domain($language_key, $full_domain);
     if (!dialog_validate($dialogdef)) {
         // there were errors, show them to the user and do it again
         foreach ($dialogdef as $k => $item) {
             if (isset($item['errors']) && $item['errors'] > 0) {
                 $this->output->add_message($item['error_messages']);
             }
         }
         $example_html = '<strong>';
         $example_variable = '{VALUE}';
         $example_tilde = '~';
         $examples = array('{EXAMPLE_HTML}' => $this->code_highlight($example_html), '{EXAMPLE_VARIABLE}' => $this->code_highlight($example_variable), '{EXAMPLE_TILDE}' => $this->code_highlight($example_tilde));
         $params = array('{LANGUAGE_KEY}' => $language_key, '{LANGUAGE_NAME}' => $this->languages[$language_key]['language_name'], '{FULL_DOMAIN}' => $this->domains[$full_domain]['title']);
         $this->output->add_content('<h2>' . t('translatetool_edit_language_domain_header', 'admin', $params) . '</h2>');
         $this->output->add_content(t('translatetool_edit_language_domain_explanation', 'admin', $examples));
         $this->output->add_content('<p>');
         $href = href($WAS_SCRIPT_NAME, $this->a_param(TRANSLATETOOL_CHORE_SAVE, $language_key, $full_domain));
         $this->output->add_content($this->render_translation_dialog($href, $dialogdef));
         // no menu this time, let user concentrate on task at hand ie errorfree data input
         return;
     }
     // 4 -- actually proceed with saving the data
     // 4A -- construct a diff between the system translation (if any) and the dialogdef values
     $strings = array();
     $dummy = array();
     $diff = array();
     $this->get_strings_system($language_key, $full_domain, $strings, $dummy);
     foreach ($dialogdef as $name => $item) {
         if (!isset($item['key']) || $item['type'] == F_SUBMIT) {
             // skip buttons and 'meta'-fields
             continue;
         }
         $key = $item['key'];
         # massage $value and standardise on \n as EOL
         $value = str_replace("\r\n", "\n", $item['value']);
         $value = str_replace("\n\r", "\n", $value);
         $value = str_replace("\r", "\n", $value);
         if (!isset($strings[$key]) || $strings[$key] != $value) {
             $diff[$key] = $value;
         }
     }
     // 4B -- if the diff is non-empty, go write and maybe submit it
     $params = array('{LANGUAGE_KEY}' => $language_key, '{LANGUAGE_NAME}' => $this->languages[$language_key]['language_name'], '{FULL_DOMAIN}' => $this->domains[$full_domain]['title']);
     $diff_count = sizeof($diff);
     if ($diff_count == 0) {
         // No changes need to be saved
         $this->output->add_message(t('translatetool_no_changes_to_save', 'admin', $params));
     } else {
         $diff['_submit'] = $dialogdef['_submit']['value'] == '1' ? '1' : '0';
         $diff['_full_name'] = $dialogdef['_full_name']['value'];
         $diff['_email'] = $dialogdef['_email']['value'];
         $diff['_notes'] = $dialogdef['_notes']['value'];
         $retval = $this->put_strings_userfile($language_key, $full_domain, $diff);
         if (!$retval) {
             logger(sprintf('%s.%s(): could not write translation file for %s - %s (%d items)', __CLASS__, __FUNCTION__, $language_key, $full_domain, $diff_count));
         } else {
             logger(sprintf('%s.%s(): success writing translation file for %s - %s: %d items', __CLASS__, __FUNCTION__, $language_key, $full_domain, $diff_count), WLOG_DEBUG);
             if (db_bool_is(FALSE, $this->languages[$language_key]['dialect_in_file'])) {
                 $table = 'languages';
                 $fields = array('dialect_in_file' => TRUE);
                 $where = array('language_key' => $language_key);
                 $retval = db_update($table, $fields, $where);
                 if ($retval) {
                     logger(sprintf('%s.%s(): updated language %s: dialect_in_file is now enabled', __CLASS__, __FUNCTION__, $language_key), WLOG_DEBUG);
                 } else {
                     logger(sprintf('%s.%s(): update of language %s failed: %s', __CLASS__, __FUNCTION__, $language_key, db_errormessage()));
                 }
             }
         }
         if ($retval) {
             $this->output->add_message(t('translatetool_translation_save_success', 'admin', $params));
         } else {
             $this->output->add_message(t('translatetool_translation_save_failure', 'admin', $params));
         }
         if ($diff['_submit'] == '1') {
             if ($this->submit_diff_to_project($language_key, $full_domain, $diff)) {
                 $this->output->add_message(t('translatetool_translation_submit_success', 'admin', $params));
                 logger(sprintf('%s.%s(): success submitting translations for %s - %s: %d items', __CLASS__, __FUNCTION__, $language_key, $full_domain, $diff_count), WLOG_DEBUG);
             } else {
                 logger(sprintf('%s.%s(): could not submit translations for %s - %s (%d items)', __CLASS__, __FUNCTION__, $language_key, $full_domain, $diff_count));
                 $this->output->add_message(t('translatetool_translation_submit_failure', 'admin', $params));
             }
         }
     }
     // 5 -- clear cache and force reread of translation just saved
     $LANGUAGE->reset_cache($language_key, $full_domain);
     $this->translation_edit();
 }