/** 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>');
}
/** display an introductory text for update + status overview
 *
 * @param object &$output collects the html output
 * @return void results are returned as output in $output
 */
function update_show_overview(&$output)
{
    global $CFG;
    // 0 -- title and introduction
    $output->add_content('<h2>' . t('update_header', 'admin') . '</h2>');
    $output->add_content(t('update_intro', 'admin'));
    // 1 -- show core status in a 6-column HTML-table
    update_status_table_open($output);
    $class = 'odd';
    $attributes = array('class' => $class);
    $output->add_content('  ' . html_table_row($attributes));
    $output->add_content('    ' . html_table_cell($attributes, t('update_core', 'admin')));
    $output->add_content('    ' . html_table_cell($attributes, $CFG->version));
    $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars(WAS_VERSION)));
    $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars(WAS_RELEASE_DATE)));
    $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars(WAS_RELEASE)));
    if (intval($CFG->version) == intval(WAS_VERSION)) {
        $output->add_content('    ' . html_table_cell($attributes, t('update_status_ok', 'admin')));
    } else {
        $output->add_content('    ' . html_table_cell($attributes, update_status_anchor(TASK_UPDATE_CORE)));
    }
    $output->add_content('  ' . html_table_row_close());
    update_status_table_close($output);
    // 2 -- subsystem status
    $subsystems = array('languages' => array('table' => 'languages', 'fields' => array('language_key', 'version', 'manifest'), 'keyfield' => 'language_key', 'path' => $CFG->progdir . '/languages', 'install' => TASK_INSTALL_LANGUAGE, 'update' => TASK_UPDATE_LANGUAGE), 'modules' => array('table' => 'modules', 'fields' => array('name', 'version', 'manifest'), 'keyfield' => 'name', 'path' => $CFG->progdir . '/modules', 'install' => TASK_INSTALL_MODULE, 'update' => TASK_UPDATE_MODULE), 'themes' => array('table' => 'themes', 'fields' => array('name', 'version', 'manifest'), 'keyfield' => 'name', 'path' => $CFG->progdir . '/themes', 'install' => TASK_INSTALL_THEME, 'update' => TASK_UPDATE_THEME));
    foreach ($subsystems as $subsystem => $data) {
        // 2A -- retrieve all manifests (including un-installed subsystems)
        $manifests = get_manifests($data['path']);
        // 2B -- retrieve all installed subsystems by consulting the database
        $where = '';
        $order = $data['keyfield'];
        $records = db_select_all_records($data['table'], $data['fields'], $where, $order, $data['keyfield']);
        if ($records === FALSE) {
            logger(sprintf('%s(): error retrieving subsystems \'%s\'; continuing nevertheless: %s', __FUNCTION__, $subsystem, db_errormessage()));
            $records = array();
        }
        // 2C -- open a 6-column HTML-table for status overview
        $title = t('update_subsystem_' . $subsystem, 'admin');
        update_status_table_open($output, $title);
        $class = 'even';
        // 2D -- step through all available manifests and show diff's (if any)
        foreach ($manifests as $key => $manifest) {
            $version_manifest = isset($manifest['version']) ? $manifest['version'] : NULL;
            $version_database = isset($records[$key]['version']) ? $records[$key]['version'] : NULL;
            /*
             * At this point there are several possibilities for version_manifest (M) and version_database (D)
             * - both M and D are integers AND M == D: subsystem is up to date: show 'OK'
             * - both M and D are integers AND M > D: subsystem upgrade required: show 'Update' link
             * - both M and D are integers AND M < D: huh, subsystem downgrade?: show 'ERROR'
             * - M is an integer and D is NULL: subsystem apparently not yet installed: show 'Install' link
             * - M is NULL (and D is don't care): not a valid manifest, skip (but log) this one
             */
            if (is_null($version_manifest)) {
                logger(sprintf('%s(): subsystem \'%s/%s\' has no internal version; skipping this manifest', __FUNCTION__, $subsystem, $key));
                continue;
            } elseif (is_null($version_database)) {
                $version_database = '-';
                $status = update_status_anchor($data['install'], $key, t('update_status_install', 'admin'));
            } elseif (intval($version_manifest) == intval($version_database)) {
                $status = t('update_status_ok', 'admin');
            } elseif (intval($version_manifest) > intval($version_database)) {
                $status = update_status_anchor($data['update'], $key, t('update_status_update', 'admin'));
            } else {
                $status = t('update_status_error', 'admin');
                logger(sprintf('%s(): weird: \'%s/%s\' database version (%d) is greater than manifest version (%d)?', __FUNCTION__, $subsystem, $key, intval($version_database), intval($version_manifest)));
            }
            $class = $class == 'odd' ? 'even' : 'odd';
            $attributes = array('class' => $class);
            $release_date_manifest = isset($manifest['release_date']) ? $manifest['release_date'] : '';
            $release_manifest = isset($manifest['release']) ? $manifest['release'] : '';
            $output->add_content('  ' . html_table_row($attributes));
            $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($key)));
            $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($version_database)));
            $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($version_manifest)));
            $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($release_date_manifest)));
            $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($release_manifest)));
            $output->add_content('    ' . html_table_cell($attributes, $status));
            $output->add_content('  ' . html_table_row_close());
        }
        // 2E -- now check for orphans (database records without matching manifest)
        foreach ($records as $key => $record) {
            if (isset($manifests[$key])) {
                // already dealt with in the foreach loop over all manifests
                continue;
            }
            // Realisticly speaking there are two possibilities here:
            //  1. a new language was added locally but no 'official' language pack was installed, or
            //  2. a language was once installed but the manifest is lost in the mist of time (very unlikely)
            // The former case is perfectly possible, the latter is a real error.
            // Note, however, that case 1 is very unlikely for modules and themes: the user cannot simply
            // add a record to the modules or themes table like she can via 'Add a language' in the Translate Tool.
            //
            // The trigger for the error (case 2) is: a manifest name is mentioned in the $record but
            // we haven't seen that one before (in the foreach loop over all manifests).
            // This error condition yields question marks for the external version and release/release date
            // and 'ERROR' for status. Case 1 above yields dashes instead, with a status of OK.
            //
            $version_database = isset($record['version']) ? $record['version'] : '0';
            if (isset($record['manifest']) && !empty($record['manifest'])) {
                // Case 2 -- ERROR
                $version_release_date = '?';
                $status = t('update_status_error', 'admin');
                logger(sprintf('%s(): weird: \'%s/%s\' database version (%s) exists without corresponding manifest?', __FUNCTION__, $subsystem, $key, strval($version_database)));
            } else {
                // Case 1 - locally added language
                $version_release_date = '-';
                $status = t('update_status_ok', 'admin');
            }
            $class = $class == 'odd' ? 'even' : 'odd';
            $attributes = array('class' => $class);
            $output->add_content('  ' . html_table_row($attributes));
            $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($key)));
            $output->add_content('    ' . html_table_cell($attributes, htmlspecialchars($version_database)));
            $output->add_content('    ' . html_table_cell($attributes, $version_release_date));
            $output->add_content('    ' . html_table_cell($attributes, $version_release_date));
            $output->add_content('    ' . html_table_cell($attributes, $version_release_date));
            $output->add_content('    ' . html_table_cell($attributes, $status));
            $output->add_content('  ' . html_table_row_close());
        }
        update_status_table_close($output);
    }
}
/** present the user with a dialog to modify the workshop 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 JS)
 * $output->add_popup_top($message): make $message popup in the browser before loading the page (uses JS)
 * </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 crew_show_edit(&$output, $area_id, $node_id, $module, $viewonly, $edit_again, $href)
{
    global $USER;
    $module_id = intval($module['module_id']);
    $dialogdef = crew_get_dialogdef($output, $viewonly, $module_id, $area_id, $node_id, $USER->user_id);
    if ($edit_again) {
        // retrieve and (again) validate the POSTed values
        dialog_validate($dialogdef);
        // no need to show messages; we did that alread in crew_save() below
    }
    $output->add_content('<h2>' . t('crew_content_header', 'm_crew') . '</h2>');
    $output->add_content(t('crew_content_explanation', 'm_crew'));
    // Manually construct the form because of embedded HTML-table
    $in_table = FALSE;
    $postponed = array();
    $oddeven = 'even';
    $output->add_content(html_form($href));
    foreach ($dialogdef as $name => $item) {
        // this always works because the last item is not an acl field
        if ($in_table && substr($name, 0, 3) != 'acl') {
            $output->add_content(html_table_close());
            $in_table = FALSE;
        }
        if (!$in_table && substr($name, 0, 3) == 'acl') {
            $output->add_content(html_table(array('class' => 'acl_form')));
            $in_table = TRUE;
        }
        if (substr($name, 0, 3) == 'acl') {
            $oddeven = $oddeven == 'even' ? 'odd' : 'even';
            $attributes = array('class' => $oddeven);
            $output->add_content('  ' . html_table_row($attributes));
            $output->add_content('    ' . html_table_cell($attributes, dialog_get_label($item)));
            $widget = dialog_get_widget($item);
            if (is_array($widget)) {
                $output->add_content('    ' . html_table_cell($attributes));
                // add every radio button on a separate line
                $postfix = $item['type'] == F_RADIO ? '<br>' : '';
                foreach ($widget as $widget_line) {
                    $output->add_content('      ' . $widget_line . $postfix);
                }
                $output->add_content('    ' . html_table_cell_close());
            } else {
                $output->add_content('    ' . html_table_cell($attributes, $widget));
            }
            $output->add_content('  ' . html_table_row_close());
        } else {
            if ($item['type'] == F_SUBMIT) {
                $postponed[$name] = $item;
            } else {
                $output->add_content('<p>');
                $output->add_content(dialog_get_label($item) . '<br>');
                $widget = dialog_get_widget($item);
                if (is_array($widget)) {
                    // add every radio button on a separate line
                    $postfix = $item['type'] == F_RADIO ? '<br>' : '';
                    foreach ($widget as $widget_line) {
                        $output->add_content($widget_line . $postfix);
                    }
                } else {
                    $output->add_content($widget);
                }
            }
        }
    }
    foreach ($postponed as $item) {
        $output->add_content(dialog_get_widget($item));
    }
    $output->add_content('<p>');
    $output->add_content(html_form_close());
    return TRUE;
}
 /** display a list of subdirectories and files in directory $path
  *
  * This long routine displays the following items to the user
  *  - (optional) navigation link to add (upload) a file
  *  - (optional) navigation link to add (create) a directory
  *  - a 4, 5 or 6 column table with
  *    . navigation link to the parent directory
  *    . 0, 1 or more rows with delete and navigation links to subdirectories (if any)
  *    . 0, 1 or more rows with a checkbox and delete and preview links to files (if any)
  *    . (optional) a 'select all' checkbox
  *  - (optional) Delete-button to mass-delete files 
  *
  * The table can be ordered in various ways: by name, by size and by date,
  * either ascending or descending. Clicking the relevant column header yields
  * another sort order. This toggles between ascending and descending.
  * Default sort order is by name ascending.
  *
  * The checkbox 'select all' works with Javascript in the most simple way:
  * an ad-hoc script connected to the onclick attribute of the select all checkbox.
  * However, the select all checkbox itself is rendered via Javascript.
  * The effect is that this feature is only available if Javascript is enabled
  * in the browser. If it isn't, no select all is visible so it can not distract
  * the user. This is part of the attempt to make this CMS usable even without
  * Javascript.
  *
  * If the flag $show_thumbnails is set we display file entries as thumbnails.
  * This is done mostly to cater for the visual interactieve selection of images from FCK Editor.
  *
  * @param string $path the directory to display
  * @param bool $show_thumbnails if TRUE files are displayed as thumbnails, table rows otherwise
  * @return void output generated via $this->output
  * @uses $USER
  * @uses $CFG
  * @uses $WAS_SCRIPT_NAME
  * @todo This routine is way too long, it should be split up into smaller subroutines
  */
 function show_directories_and_files($path, $show_thumbnails = TRUE)
 {
     global $USER, $WAS_SCRIPT_NAME, $CFG;
     $path_components = $this->explode_path($path);
     $n = count($path_components);
     $branch = $path_components[0];
     $entries = $this->get_entries($path);
     $this->sort_entries($entries, $this->sort);
     // 1 -- check out permissions for add/delete file/directory
     $parent = FALSE;
     $add_file = FALSE;
     $add_directory = FALSE;
     $delete_file = FALSE;
     $delete_directory = FALSE;
     if ($n == 2 && $branch == 'users' && $path_components[1] == $USER->path) {
         $parent = '/';
         $add_file = TRUE;
         $add_directory = TRUE;
         $delete_file = TRUE;
         $delete_directory = TRUE;
     } else {
         $parent = '';
         for ($i = 0; $i < $n - 1; ++$i) {
             $parent .= '/' . $path_components[$i];
         }
         if ($branch == 'areas') {
             $area_path = $path_components[1];
             foreach ($this->areas as $area_id => $area) {
                 if ($area['path'] == $area_path) {
                     $perm = PERMISSION_NODE_ADD_PAGE | PERMISSION_AREA_ADD_PAGE;
                     $add_file = $USER->has_area_permissions($perm, $area_id);
                     $perm = PERMISSION_NODE_ADD_SECTION | PERMISSION_AREA_ADD_SECTION;
                     $add_directory = $USER->has_area_permissions($perm, $area_id);
                     $perm = PERMISSION_NODE_DROP_PAGE | PERMISSION_AREA_DROP_PAGE;
                     $delete_file = $USER->has_area_permissions($perm, $area_id);
                     $perm = PERMISSION_NODE_DROP_SECTION | PERMISSION_AREA_DROP_SECTION;
                     $delete_directory = $USER->has_area_permissions($perm, $area_id);
                     break;
                 }
             }
         } else {
             // branch = groups or users: always allowed to add/delete
             $add_file = TRUE;
             $add_directory = TRUE;
             $delete_file = TRUE;
             $delete_directory = TRUE;
         }
     }
     // 2 -- maybe show add file/directory links (before and outside the table with dirs/files)
     if ($add_file || $add_directory) {
         $this->output->add_content('<ul>');
         $html_tag_li = '  ' . html_tag('li', array('class' => 'level0'));
         $a_params = array('job' => $this->job, 'task' => TASK_ADD_FILE, PARAM_PATH => $path);
         if ($add_file) {
             $a_attr = array('title' => t('filemanager_add_file_title', 'admin'));
             $anchor = t('filemanager_add_file', 'admin');
             $this->output->add_content($html_tag_li . html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor));
         }
         if ($add_directory) {
             $a_params['task'] = TASK_ADD_DIRECTORY;
             $a_attr = array('title' => t('filemanager_add_directory_title', 'admin'));
             $anchor = t('filemanager_add_directory', 'admin');
             $this->output->add_content($html_tag_li . html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor));
         }
         $this->output->add_content('</ul>');
     }
     // 3A -- maybe open form (for multiple file manipulation)
     if ($delete_file) {
         $a_params = array('job' => $this->job, 'task' => TASK_REMOVE_MULTIPLE_FILES, PARAM_PATH => $path);
         $this->output->add_content(html_form(href($WAS_SCRIPT_NAME, $a_params)));
     }
     // 3B -- open 4, 5 or 6 column table including clickable headers to sort by file/size/date
     $this->output->add_content(html_table());
     $this->output->add_content('  ' . html_table_row(array('class' => 'header')));
     // quick&dirty minimum column width
     $spacer = $this->output->text_only ? '' : $this->output->skin->get_icon('blank');
     // 3Ba -- column with checkboxes (only if not in thumbnail view mode)
     if ($delete_file && !$show_thumbnails) {
         $this->output->add_content('    ' . html_table_head('', $spacer));
     }
     // 3Bb - column with delete icons
     if ($delete_file && !$show_thumbnails || $delete_directory) {
         $this->output->add_content('    ' . html_table_head('', $spacer));
     }
     // 3Bc - column with folder/preview icons
     $this->output->add_content('    ' . html_table_head('', $spacer));
     // 3Bd - column with filename
     $a_params = array('job' => $this->job, 'task' => TASK_CHANGE_DIRECTORY, PARAM_PATH => $path);
     $th_attr = array('align' => 'left');
     $sort = $this->sort == SORTBY_FILE_ASC ? SORTBY_FILE_DESC : SORTBY_FILE_ASC;
     $a_params['sort'] = $sort;
     $a_attr = array('title' => t($sort > 0 ? 'filemanager_sort_asc' : 'filemanager_sort_desc', 'admin'));
     $anchor = t('filemanager_column_file', 'admin');
     $this->output->add_content('    ' . html_table_head($th_attr, html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor)));
     // 3Be - column with size
     $th_attr = array('align' => 'right');
     $sort = $this->sort == SORTBY_SIZE_ASC ? SORTBY_SIZE_DESC : SORTBY_SIZE_ASC;
     $a_params['sort'] = $sort;
     $a_attr = array('title' => t($sort > 0 ? 'filemanager_sort_asc' : 'filemanager_sort_desc', 'admin'));
     $anchor = t('filemanager_column_size', 'admin');
     $this->output->add_content('    ' . html_table_head($th_attr, html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor)));
     // 3Bf - column with datim
     $th_attr = array('align' => 'left');
     $sort = $this->sort == SORTBY_DATE_ASC ? SORTBY_DATE_DESC : SORTBY_DATE_ASC;
     $a_params['sort'] = $sort;
     $a_attr = array('title' => t($sort > 0 ? 'filemanager_sort_asc' : 'filemanager_sort_desc', 'admin'));
     $anchor = t('filemanager_column_date', 'admin');
     $this->output->add_content('    ' . html_table_head($th_attr, html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor)));
     $this->output->add_content('  ' . html_table_row_close());
     // 4 -- prepare to iterate through all directory entries
     $oddeven = 'even';
     // 4A -- always add a link to the parent directory ($parent should not be FALSE)
     if ($parent !== FALSE) {
         $a_params = array('job' => $this->job, 'task' => TASK_CHANGE_DIRECTORY, PARAM_PATH => $parent);
         $title = t('filemanager_parent_title', 'admin');
         $a_attr = array('title' => $title);
         $anchor = t('filemanager_parent', 'admin');
         $alt = t('icon_open_parent_directory_alt', 'admin');
         $text = t('icon_open_parent_directory_text', 'admin');
         $icon = $this->output->skin->get_icon('folder_closed', $title, $alt, $text);
         $oddeven = $oddeven == 'even' ? 'odd' : 'even';
         $this->output->add_content('  ' . html_table_row(array('class' => $oddeven)));
         if ($delete_file && !$show_thumbnails) {
             $this->output->add_content('    ' . html_table_cell('', ''));
             // a: column with checkboxes empty
         }
         if ($delete_file && !$show_thumbnails || $delete_directory) {
             $this->output->add_content('    ' . html_table_cell('', ''));
             // b: column with delete icons empty
         }
         // c: column with folder icon
         $this->output->add_content('    ' . html_table_cell('', html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $icon)));
         // d: column with filename
         $this->output->add_content('    ' . html_table_cell('', html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor)));
         $this->output->add_content('    ' . html_table_cell('', ''));
         // e: column with size empty
         $this->output->add_content('    ' . html_table_cell('', ''));
         // f: column with date empty
         $this->output->add_content('  ' . html_table_row_close());
     }
     // 4B -- step through the entries
     $count_directories = 0;
     $count_files = 0;
     $files = array();
     foreach ($entries as $name => $entry) {
         $index = $entry['is_file'] ? $count_files++ : $count_directories++;
         // Maybe keep the files for later processing (do all directories first)
         if ($show_thumbnails && $entry['is_file']) {
             $files[$name] = $entry;
             continue;
         }
         $oddeven = $oddeven == 'even' ? 'odd' : 'even';
         $attributes = array('class' => $oddeven);
         // flag 'forbidden' files (ie. files which would be banned from uploading with the current settings)
         $file_forbidden = isset($entry['is_allowed']) && !$entry['is_allowed'] ? TRUE : FALSE;
         if ($file_forbidden) {
             $attributes['class'] .= ' error';
         }
         $this->output->add_content('  ' . html_table_row($attributes));
         // 4Ba: checkbox (for files only)
         if ($delete_file && !$show_thumbnails) {
             if ($entry['is_file']) {
                 $checkbox_def = array('type' => F_CHECKBOX, 'name' => sprintf('%s%d', PARAM_FILENAME, $index), 'options' => array($entry['name'] => ' '), 'title' => t('filemanager_select_file_entry_title', 'admin'), 'value' => '');
                 $widget = dialog_get_widget($checkbox_def);
                 if (is_array($widget)) {
                     $this->output->add_content('    ' . html_table_cell($attributes));
                     $this->output->add_content($widget);
                     $this->output->add_content('    ' . html_table_cell_close());
                 } else {
                     $this->output->add_content('    ' . html_table_cell($attributes, $widget));
                 }
             } else {
                 $this->output->add_content('    ' . html_table_cell($attributes, ''));
             }
         }
         // 4Bb: delete icon
         if ($delete_file && !$show_thumbnails || $delete_directory) {
             if ($delete_file && $entry['is_file']) {
                 $title = t('filemanager_delete_file', 'admin', array('{FILENAME}' => htmlspecialchars($entry['vname'])));
                 $a_params = array('job' => $this->job, 'task' => TASK_REMOVE_FILE, PARAM_PATH => $entry['path']);
                 $a_attr = array('title' => $title);
                 $alt = t('icon_delete_file_alt', 'admin');
                 $text = t('icon_delete_file_text', 'admin');
                 $anchor = $this->output->skin->get_icon('delete', $title, $alt, $text);
                 $cell = html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor);
             } elseif ($delete_directory && !$entry['is_file']) {
                 $title = t('filemanager_delete_directory', 'admin', array('{DIRECTORY}' => htmlspecialchars($entry['vname'])));
                 $a_params = array('job' => $this->job, 'task' => TASK_REMOVE_DIRECTORY, PARAM_PATH => $entry['path']);
                 $a_attr = array('title' => $title);
                 $alt = t('icon_delete_directory_alt', 'admin');
                 $text = t('icon_delete_directory_text', 'admin');
                 $anchor = $this->output->skin->get_icon('delete', $title, $alt, $text);
                 $cell = html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor);
             } else {
                 $cell = '';
             }
             $this->output->add_content('    ' . html_table_cell($attributes, $cell));
         }
         // else suppress this column completely
         if ($entry['is_file']) {
             // 4Bc (file): preview file icon
             $title = $entry['title'];
             $alt = t('icon_preview_file_alt', 'admin');
             $text = t('icon_preview_file_text', 'admin');
             $anchor = $this->output->skin->get_icon('view', $title, $alt, $text);
             if ($file_forbidden) {
                 // Show a 'dead' preview link with icon and a dead link with the filename;
                 // prevent that the user accidently displays a rogue file.
                 // Note the '[' and ']': this makes it visible even without stylesheets
                 $this->output->add_content('    ' . html_table_cell($attributes, $anchor));
                 $anchor = '[' . htmlspecialchars($entry['vname']) . ']';
                 $this->output->add_content('    ' . html_table_cell($attributes, $anchor));
             } else {
                 // OK. Acceptable file, carry on.
                 // Now construct the A tag for the preview button.
                 // This is tricky, because we want to present the preview in a separate
                 // window/popup. We don't want to double-escape html special chars, so we
                 // construct the url + params + attr manually here. The javascript routine is
                 // added to the output page in /program/main_admin.php.
                 //
                 $a_params = sprintf('job=%s&task=%s&%s=%s', $this->job, TASK_PREVIEW_FILE, PARAM_PATH, rawurlencode($entry['path']));
                 $url = $WAS_SCRIPT_NAME . '?' . htmlspecialchars($a_params);
                 $a_attr = sprintf('title="%s" target="_blank" onclick="popup(\'%s\'); return false;"', $title, $url);
                 $this->output->add_content('    ' . html_table_cell($attributes, html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor)));
                 // 4Bd (file): another A tag but now with the filename as anchor
                 // However, the action is different if we are in file browsing mode: in that case we select the
                 // file (for the FCK editor).
                 $anchor = htmlspecialchars($entry['vname']);
                 if ($this->job == JOB_FILEBROWSER || $this->job == JOB_IMAGEBROWSER || $this->job == JOB_FLASHBROWSER) {
                     // Note: we depend on Javascript here, but since FCK Editor is also a Javascript application...
                     // In other words: we would not be here in the first place if Javascript wasn't enabled.
                     // (The file preview option does not depend on Javascript, see task_preview_file().)
                     $url = $this->file_url($entry['path']);
                     $title = t('filemanager_select', 'admin', array('{FILENAME}' => htmlspecialchars($entry['name'])));
                     $a_attr = sprintf('title="%s" onclick="select_url(\'%s\'); return false;"', $title, $url);
                     $this->output->add_content('    ' . html_table_cell($attributes, html_a("#", NULL, $a_attr, $anchor)));
                 } else {
                     $this->output->add_content('    ' . html_table_cell($attributes, html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor)));
                 }
             }
             // 4Be (file): filesize (right aligned)
             $attributes['align'] = 'right';
             $size = $this->human_readable_size($entry['size']);
             $this->output->add_content('    ' . html_table_cell($attributes, $size));
         } else {
             // directory
             // 4Bc (dir): open directory icon
             $a_params = array('job' => $this->job, 'task' => TASK_CHANGE_DIRECTORY, PARAM_PATH => $entry['path']);
             $title = $entry['title'];
             $a_attr = array('title' => $title);
             $alt = t('icon_open_directory_alt', 'admin');
             $text = t('icon_open_directory_text', 'admin');
             $anchor = $this->output->skin->get_icon('folder_closed', $title, $alt, $text);
             $this->output->add_content('    ' . html_table_cell($attributes, html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor)));
             // 4Bd (dir): another A tag but now with the directory name as anchor
             $anchor = htmlspecialchars($entry['vname']);
             $this->output->add_content('    ' . html_table_cell($attributes, html_a($WAS_SCRIPT_NAME, $a_params, $a_attr, $anchor)));
             // 4Be (dir): skip 'size' of the directory (makes no sense)
             $this->output->add_content('    ' . html_table_cell($attributes, ''));
         }
         // 4Bf: mtime (as yyyy-mm-dd hh:mm:ss)
         $datim = strftime('%Y-%m-%d %T', $entry['mtime']);
         $attributes = array('class' => $oddeven);
         $this->output->add_content('    ' . html_table_cell($attributes, $datim));
         // close the table row
         $this->output->add_content('  ' . html_table_row_close());
     }
     // at this point we have shown all directory entries and maybe all file entries
     // Now we may want to add a 'select all' checkbox + Delete button
     if ($delete_file && $count_files > 0) {
         $oddeven = $oddeven == 'even' ? 'odd' : 'even';
         $attributes = array('class' => $oddeven);
         // Generate ad hoc javascript to check/uncheck all checkboxes
         $onclick = sprintf("for(var i=0; i<%d; ++i) {document.forms[0].elements['%s'+i].checked=this.checked;}", $count_files, PARAM_FILENAME);
         // Manually construct widget because we cannot squeeze in the onclick attribute with dialog_get_widget()
         $widget = html_tag('input', array('name' => sprintf('%s%s', PARAM_FILENAME, 'all'), 'type' => 'checkbox', 'value' => '1', 'title' => t('filemanager_select_file_entries_title', 'admin'), 'onclick' => $onclick));
         $label = t('filemanager_select_file_entries', 'admin');
         // Now conditionally write the last row in the table
         // This is done with Javascript generating HTML which in turn adds Javascript in the onclick attribute.
         // Mmmm, overly complicated, but the effect is: only when Javascript is enabled a 'select all'
         // checkbox is rendered. If Javascript is disabled, we don't show this stuff at all (no distractions).
         $this->output->add_content('  <script type="text/javascript"><!--');
         $this->output->add_content('    document.write("' . addslashes(html_table_row($attributes)) . '");');
         $this->output->add_content('    document.write("  ' . addslashes(html_table_cell($attributes, $widget)) . '");');
         $attributes['colspan'] = '5';
         $this->output->add_content('    document.write("  ' . addslashes(html_table_cell($attributes, $label)) . '");');
         $this->output->add_content('    document.write("' . addslashes(html_table_row_close()) . '");');
         $this->output->add_content('  //--></script>');
     }
     $this->output->add_content(html_table_close());
     if ($delete_file) {
         if ($count_files > 0) {
             $this->output->add_content(html_tag('input', array('type' => 'hidden', 'name' => PARAM_FILENAMES, 'value' => strval($count_files))));
             $widget = dialog_get_widget(dialog_buttondef(BUTTON_DELETE));
             $this->output->add_content('<p>');
             $this->output->add_content($widget);
         }
     }
     if ($show_thumbnails && $count_files > 0) {
         $index = 0;
         foreach ($files as $name => $entry) {
             $this->show_file_as_thumbnail($path, $entry, $delete_file, $index++);
         }
     }
     if ($delete_file) {
         $this->output->add_content(html_form_close());
     }
 }
 /** construct a form with a dialog in a table with 2 or 3 columns
  *
  * this constructs a 2- or 3-column table and fills it with data from
  * the dialogdef.
  *
  * The first column holds the labels for the widgets.
  * The second column holds the corresponding widget, e.g. a list box with roles.
  * The optional third column (depends on the flag $show_related) shows
  * related information. This is used to list group/capacities and roles
  * from related groups (ie. groups of which the user is a member).
  *
  * The table has headers for the columns: 'Realm','Role' and optional 'Related'.
  * Rows in the table can have alternating colours via the odd/even class.
  * This is done via the stylesheet.
  *
  * @param string $href the target of the HTML form
  * @param array &$dialogdef the array which describes the complete dialog
  * @return array constructed HTML-form with dialog, one line per array element
  * @todo bailing out on non-array is a crude way of error handling: this needs to be fixed
  */
 function dialog_tableform($href, &$dialogdef, $show_related = FALSE)
 {
     if (!is_array($dialogdef)) {
         logger('dialog_tableform(): weird: there is no valid dialogdef?');
         return array(t('error_retrieving_data', 'admin'));
     }
     // result starts with opening a form tag and a 2- or 3-column table
     $attributes = array('class' => 'header');
     $a = array(html_form($href), html_table(array('class' => 'acl_form')), '  ' . html_table_row($attributes), '    ' . html_table_head($attributes, t('acl_column_header_realm', 'admin')), '    ' . html_table_head($attributes, t('acl_column_header_role', 'admin')));
     if ($show_related) {
         $a[] = '    ' . html_table_head($attributes, t('acl_column_header_related', 'admin'));
     }
     $a[] = '  ' . html_table_row_close();
     $oddeven = 'even';
     $postponed = array();
     foreach ($dialogdef as $item) {
         if (!isset($item['name'])) {
             // skip spurious item (possibly empty array)
             continue;
         }
         if ($item['type'] == F_SUBMIT || isset($item['hidden']) && $item['hidden']) {
             // always postpone the buttons and hidden fields to the end
             $postponed[] = $item;
             continue;
         }
         $oddeven = $oddeven == 'even' ? 'odd' : 'even';
         $attributes = array('class' => $oddeven);
         $a[] = '  ' . html_table_row($attributes);
         //
         // column 1 - realm
         //
         if ($this->area_view_enabled) {
             if (isset($item['area_id'])) {
                 // site level or area level
                 $icon = $this->get_icon_area($item['area_id'], $item['area_is_open'], $item['area_offset']);
             } else {
                 // node level, show a blank icon to line things up
                 $icon = $this->output->skin->get_icon('blank');
             }
         } else {
             $icon = '';
         }
         $a[] = '    ' . html_table_cell($attributes, ltrim($icon . ' ' . dialog_get_label($item)));
         //
         // column 2 - role
         //
         $widget = dialog_get_widget($item);
         if (is_array($widget)) {
             $a[] = '    ' . html_table_cell($attributes);
             // add every radio button on a separate line
             $postfix = $item['type'] == F_RADIO ? '<br>' : '';
             foreach ($widget as $widget_line) {
                 $a[] = '      ' . $widget_line . $postfix;
             }
             $a[] = '    ' . html_table_cell_close();
         } else {
             $a[] = '    ' . html_table_cell($attributes, $widget);
         }
         //
         // column 3 (optional) - related items in a single comma delimited string
         //
         if ($show_related) {
             $related = isset($item['related']) && !empty($item['related']) ? $item['related'] : '';
             $cell_content = is_array($related) ? implode(',<br>', $related) : $related;
             $a[] = '    ' . html_table_cell($attributes, $cell_content);
         }
         $a[] = '  ' . html_table_row_close();
     }
     $a[] = html_table_close();
     // now handle the postponed fields such as the Save and Cancel buttons and the hidden fields
     if (sizeof($postponed) > 0) {
         foreach ($postponed as $item) {
             $a[] = dialog_get_widget($item);
         }
     }
     $a[] = html_form_close();
     return $a;
 }
/** display an introductory text for the account manager + menu
 *
 * @param object &$output collects the html output
 * @return void results are returned as output in $output
 */
function show_accounts_intro(&$output)
{
    global $DB;
    $tables = array('users' => array('pkey' => 'user_id', 'active' => 0, 'inactive' => 0, 'total' => 0), 'groups' => array('pkey' => 'group_id', 'active' => 0, 'inactive' => 0, 'total' => 0));
    $output->add_content('<h2>' . t('accountmanager_header', 'admin') . '</h2>');
    $output->add_content(t('accountmanager_intro', 'admin'));
    $class = 'header';
    $output->add_content('<p>');
    $output->add_content(html_table());
    $output->add_content('  ' . html_table_row(array('class' => $class)));
    $output->add_content('    ' . html_table_head(NULL, t('accountmanager_summary', 'admin')));
    $output->add_content('    ' . html_table_head(NULL, t('accountmanager_active', 'admin')));
    $output->add_content('    ' . html_table_head(NULL, t('accountmanager_inactive', 'admin')));
    $output->add_content('    ' . html_table_head(NULL, t('accountmanager_total', 'admin')));
    $output->add_content('  ' . html_table_row_close());
    foreach ($tables as $table_name => $table) {
        $class = $class == 'odd' ? 'even' : 'odd';
        $sql = sprintf("SELECT is_active, COUNT(%s) AS total FROM %s%s GROUP BY is_active", $table['pkey'], $DB->prefix, $table_name);
        if (($DBResult = $DB->query($sql)) !== FALSE) {
            $records = $DBResult->fetch_all_assoc();
            $DBResult->close();
            foreach ($records as $record) {
                $key = db_bool_is(TRUE, $record['is_active']) ? 'active' : 'inactive';
                $tables[$table_name][$key] += intval($record['total']);
                $tables[$table_name]['total'] += intval($record['total']);
            }
        }
        $attributes = array('class' => $class);
        $output->add_content('  ' . html_table_row($attributes));
        $output->add_content('    ' . html_table_cell($attributes, t('accountmanager_' . $table_name, 'admin')));
        $attributes['align'] = 'right';
        $output->add_content('    ' . html_table_cell($attributes, $tables[$table_name]['active']));
        $output->add_content('    ' . html_table_cell($attributes, $tables[$table_name]['inactive']));
        $output->add_content('    ' . html_table_cell($attributes, $tables[$table_name]['total']));
        $output->add_content('  ' . html_table_row_close());
    }
    $output->add_content(html_table_close());
}