/** 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 the contact form * * this displays the contact form. Every destination gets a * separate DIV just below the listbox, with the additional * information for that destination. If JavaScript is NOT * enabled, all DIVs are displayed, otherwise only the * currently selected destination is displayed and the * others are not. IOW: this form is still usable even * without JS enabled AND it is screenreader-friendly. * * If there is only a single destination, the listbox is * not defined in the dialogdef and hence not rendered at all: * there is no point in showing a list of options * if there is nothing to choose from. This means that the * necessary javascript is NOT added and also no DIVs are * shown. So: a clean, uncluttered form in case of a single * destination. * * @param object &$theme collects the (html) output * @param array mailpage configuration data in a (nested) array * @param array $dialogdef array that defines the input fields * @return void output writted to $theme */ function mailpage_show_form(&$theme, $config, $dialogdef) { // // 1 -- maybe output a header and an introduction // $header = trim($config['header']); if (!empty($header)) { $theme->add_content(html_tag('h2', '', $header)); } $introduction = trim($config['introduction']); if (!empty($introduction)) { $theme->add_content($introduction); } $href = was_node_url($theme->node_record); // // 2 -- Render the dialog (maybe including the additional DIVs) // $postponed = array(); $theme->add_content(html_form($href)); foreach ($dialogdef as $name => $item) { if ($item['type'] == F_SUBMIT || isset($item['hidden']) && $item['hidden']) { $postponed[$name] = $item; } else { $theme->add_content('<p>'); $theme->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) { $theme->add_content($widget_line . $postfix); } } else { $theme->add_content($widget); } } if ($name == 'destination') { foreach ($item['options'] as $index => $option) { $theme->add_content(sprintf('<div class="%s" id="%s%d">%s: %s</div>', 'mailpage_destination_option', 'mailpage_destination_', $index, htmlspecialchars($option['option']), htmlspecialchars($option['title']))); } // This suppresses the DIVs that correspond to currently // not selected options in the listbox $js = "<script><!--\n" . "var sel=document.getElementById('mailpage_destination');\n" . "sel.onchange=function() {\n" . " var div;\n" . " for(var i=0; i<this.length; ++i) {\n" . " div=document.getElementById('mailpage_destination_'+this.options[i].value);\n" . " div.style.display=(this.options[i].selected)?'block':'none';\n" . " }\n" . "}\n" . "sel.onchange();\n" . "--></script>\n"; $theme->add_content($js); } } $theme->add_content('<p>'); foreach ($postponed as $item) { $theme->add_content(dialog_get_widget($item)); } $theme->add_content(html_form_close()); }
/** construct a generic form with a dialog * * this constructs an HTML form with a simple dialog where * * - every label and every widget has its own line * (enforced by a BR-tag) * - label/widget-combinations are separated with a P-tag * - buttons are stringed together on a single line (ie no trailing BR) * * This should be sufficient for many dialogs. * If the layout needs to be more complex a custom dialog can * always be constructed using functions {@link dialog_get_label()} * and {@link dialog_get_widget()}. * * @param string $href the target of the HTML form * @param array &$dialogdef the array which describes the complete dialog * @param string $method method to submit data to the server, either 'post' or 'get' * @param string|array $attributes holds the attributes to add to the form tag * @return array constructed HTML-form with dialog, one line per array element * @uses html_form() */ function dialog_quickform($href, &$dialogdef, $method = 'post', $attributes = '') { $buttons_seen = FALSE; $a = array(0 => html_form($href, $method, $attributes)); // result starts with opening a form tag foreach ($dialogdef as $item) { if (!isset($item['name'])) { // skip spurious item (possibly empty array) continue; } $label = dialog_get_label($item); if (!empty($label)) { $a[] = '<p>'; $a[] = $label . '<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) { $a[] = $widget_line . $postfix; } } else { // quick and dirty: // add a <p> before the first button in a dialog // add a <br> after every 1-line item except buttons // result: fields line up nicely and buttons are on a single row $postfix = ''; if ($item['type'] == F_SUBMIT) { if (!$buttons_seen) { $buttons_seen = TRUE; $a[] = '<p>'; } } elseif (!(isset($item['hidden']) && $item['hidden'])) { $postfix = '<br>'; } $a[] = $widget . $postfix; } } $a[] = '<p>'; $a[] = html_form_close(); return $a; }
/** show the (visually almost) empty page and load or continue with the JS popup window * * this routine is responsible for showing an 'empty' page and maybe for generating * a JS popup window (if $first==TRUE). The 'empty' page contains only a form with * single textarea. However, this textarea is not displayed (display:none) so the * casual user sees nothing (but obviously without CSS it is a different matter). * This textarea is used by the CREW code to store the edited document before * submitting the form. Since there are no buttons of any kind, it is completely * up to the JS code to generate the necessary DOM elements that are required to * successfully save the document. * * If $first is TRUE, we have to setup the popup window. This is quite complicated * because generate the necessary JS-code at runtime using JS. One of the reasons * is that I want to set the correct translations in the popup window. There may * be an easier way. * * The Websocket protocol is used to talk to the Websocket server which is configured * for this site. This setting can be manipulated using the Module Manager. In order * to authenticate ourselves against the websocket server we use the following mechanism. * There are a few important variables used in authenticating: * * - $origin: this is the website's hostname as seen by the user's browser * - $request_uri: a string that uniquely identifies the node within the origin * - $full_name: the full name of the current user (ie. $USER->full_name) * - $username: the (short) name/userid of the curent user (ie. $USER->username) * - $request_date: the current time (GMT) in the format "yyyy-mm-dd hh:mm:ss". * * and also * * - $secret_key: a secret shared with the Websocket server * - $location: the URL of the Websocket server * * The authentication works as follows. The variables $origin, $request_uri, $full_name, * $username and $request_date are concatenated in a $message. Then the $message and * the $secret_key are used to calculate a hashed message authentication code (HMAC) * according to RFC2104 (see function {@see hmac()} in waslib.php). * * When connecting to the Websocket server the parameters $request_uri, $full_name, * $username and $request_date are sent, together with the HMAC. The server then * calculates the HMAC too and if it matches the HMAC that was sent, access is * granted. * * Note that the variable $origin is only used here to calculate the HMAC; it is * not sent to the Websocket server like the other parameters. Instead we use the * Origin as seen by the user's web browser. Obviously the two should match or else * authentication fails. This way we check the browser's idea of where the web page * is located. Also note that we made the current date/time part of the HMAC. That * is done to prevent replay-attacks (the other variables are quasi-static between * CREW editing sessions). It is up to the Websocket server to determine if the * timestamp is (still) valid or not. This depends on a certain clock synchronisation * between the webserver and the Websocket server. * * Also note that the shared secret never leaves the webserver, only the hashed * message is sent from webserver to Websocket server. However, the secret has to * be the same on both ends. * * @param object &$theme collects the (html) output * @param int $module_id identifies the crew module (need that for getting module properties) * @param bool $first if TRUE we generate code to generate a popup * @return bool TRUE on success+output generated via $theme, FALSE otherwise */ function crew_view_show_edit(&$theme, $module_id, $first = FALSE) { global $USER, $WAS_SCRIPT_NAME, $CFG; // 1A -- fetch the latest version of the document (we always need that)... $node_id = intval($theme->node_record['node_id']); if (($record = crew_view_get_workshop_data($node_id)) === FALSE) { $theme->add_message(t('error_retrieving_workshop_data', 'm_crew')); return FALSE; } // 1B -- and tell the user the date/time/user of latest update in content area $params = array('{USERNAME}' => is_null($record['username']) ? $record['muser_id'] : $record['username'], '{FULL_NAME}' => is_null($record['full_name']) ? $record['muser_id'] : $record['full_name'], '{DATIM}' => $record['mtime']); $attr = array('class' => 'crew_datim'); $theme->add_content(html_tag('p', $attr, t('last_updated_by', 'm_crew', $params))); // 1C -- prepare a hidden textarea with the current document text /* <noscript>requires javascript</noscript> * <div> * <form> * <textarea>$document</textarea> * </form> * </div> */ $theme->add_content(html_tag('noscript', '', t('crew_requires_js_and_ws', 'm_crew'))); $attr = array('id' => 'crew_start_edit', 'style' => 'display: none;'); $theme->add_content(html_tag('div', $attr)); $href = was_node_url($theme->node_record); $attr = array('id' => 'frmEdit'); $theme->add_content(html_form($href, 'post', $attr)); $attr = array('id' => 'txtText', 'rows' => 10, 'cols' => 80, 'name' => 'text'); $theme->add_content(html_tag('textarea', $attr, htmlspecialchars($record['document']))); $theme->add_content(html_form_close()); $theme->add_content(html_tag_close('div')); // At this point we're done IF this was a repeat call. // If it was the first call we need to do some more, like popping up the edit window if (!$first) { return TRUE; } // Still here, so this is the first time // 2 -- prepare all information for popup // 2A -- which skin? $dialogdef = crew_view_dialogdef(); if (!dialog_validate($dialogdef)) { // somehow an error; default to first skin $value = '0'; } else { $value = $dialogdef['skin']['value']; } $skin = $dialogdef['skin']['options'][$value]['css']; // 2B -- which location,origin,secret (from module_properties) $table = 'modules_properties'; $fields = array('name', 'value'); $where = array('module_id' => $module_id); $order = array('sort_order'); $keyfield = 'name'; if (($properties = db_select_all_records($table, $fields, $where, $order, $keyfield)) === FALSE) { logger(sprintf('%s(): module properties error: %s', __FUNCTION__, db_errormessage())); $theme->add_message(t('error_retrieving_workshop_data', 'm_crew')); return FALSE; } $org = $properties['origin']['value']; $loc = $properties['location']['value']; $secret = $properties['secret']['value']; // 2C -- prepare variables for and perform hmac calculation $workshop = trim($record['header']); if (empty($workshop)) { $workshop = trim($node_record['link_text']); } $uri = sprintf('%s/%d/%s', $WAS_SCRIPT_NAME, $node_id, friendly_bookmark($workshop)); $name = $USER->full_name; $nick = $USER->username; $datim = gmstrftime('%Y-%m-%d %T'); $hmac_key = $secret; $hmac_msg = $org . $uri . $name . $nick . $datim; $sig = hmac($hmac_key, $hmac_msg); $progcrew = $CFG->progwww_short . '/modules/crew'; $css = $progcrew . '/' . $skin; if ($CFG->debug || !file_exists($CFG->progdir . '/modules/crew/crew.min.js')) { $js = $progcrew . '/crew.js'; } else { $js = $progcrew . '/crew.min.js'; } $theme->add_content(html_tag('script')); $theme->add_content(crew_screen($loc, $nick, $name, $uri, $workshop, $org, $datim, $sig, $css, $js, $progcrew)); $theme->add_content(html_tag_close('script')); 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; }
/** construct a simple jumplist to navigate to other areas * * this constructs a listbox with areas to which the current user has access. * The user can pick an area from the list and press the [Go] button to navigate * to that area. Only the active areas are displayed. Private areas are only displayed * when the user actually has access to those areas. * * This routine always shows the Submit-button even when JavaScript is turned 'off'. If it is 'on', * a tiny snippet auto-submits the form whenever the user selects another area; no need * press any button anymore. However, pressing the Go button is necessary when Javascript is 'off'. * Rationale: the user will find out soon enough that pressing the button is superfluous, and * as a benefit we keep the same look and feel no matter the state of Javascript. * * We rely on the constructor to provide us with an array of area_id=>area_title pairs * in the $this->jumps array. * * The special preview-mode is implemented by adding the necessary hash in the preview parameter * via a hidden field. This will ultimately lead to ourselves, with the preview code so we can * never leave for another area in preview mode. * * @param string $m add readabiliy to output * @return string properly indented ready-to-use HTML or an empty string on error * @uses dialog_get_widget() */ function get_jumpmenu($m = '') { // 1 -- KISS form with a whiff of javascript (but don't get rid of the Go-button) $title = t('jumpmenu_area_title', $this->domain); $attributes = array('name' => 'area', 'title' => $title, 'onchange' => 'this.form.submit();'); $jumpmenu = $m . html_form(was_node_url(), 'get') . "\n" . $m . ' ' . t('jumpmenu_area', $this->domain) . "\n"; // 2 -- maybe add the hidden field that keeps us in preview-mode if ($this->preview_mode) { $hash = md5($_SESSION['preview_salt'] . $_SESSION['preview_node']); $jumpmenu .= $m . ' ' . html_tag('input', array('type' => 'hidden', 'name' => 'preview', 'value' => $hash)) . "\n"; } // 3 -- add a select box with available areas $jumpmenu .= $m . " " . html_tag('select', $attributes) . "\n"; foreach ($this->jumps as $k => $v) { $attributes = array('title' => $title, 'value' => $k); if ($k == $this->area_id) { $attributes['selected'] = NULL; } $jumpmenu .= $m . ' ' . html_tag('option', $attributes, $v) . "\n"; } $jumpmenu .= $m . " </select>\n"; // 4 -- add button and close all open tags. $jumpmenu .= $m . " " . dialog_get_widget(dialog_buttondef(BUTTON_GO)) . "\n" . $m . html_form_close() . "\n"; return $jumpmenu; }
/** render a translation dialog based on a dialog definition * * This routine looks a bit like the generic {@link dialog_quickform()}. * The differences are: * - we show a comment (if any) in a box before label and input * - the labels don't have hotkeys based on tildes at all (except the submit buttons) * - comments and labels are wrapped in separate div's especially for the occasion * * We do take any errors into account: fields with errors are displayed using the * additional error class (which shows a label completely in red to indicate the error). * * @param string $href the target of the HTML form * @param array &$dialogdef the array which describes the complete dialog * @param string $method method to submit data to the server, either 'post' or 'get' * @param string|array $attributes holds the attributes to add to the form tag * @return array constructed HTML-form with dialog, one line per array element * @uses html_form() */ function render_translation_dialog($href, &$dialogdef, $method = 'post', $attributes = '') { $buttons_seen = FALSE; $a = array(0 => html_form($href, $method, $attributes)); // result starts with opening a form tag foreach ($dialogdef as $item) { if (!isset($item['name'])) { // skip spurious item (possibly empty array) continue; } if (isset($item['comment']) && !empty($item['comment'])) { $a[] = '<div class="translatetool_comment">'; $a[] = $item['comment']; $a[] = '</div>'; } if (isset($item['label']) && !empty($item['label'])) { $errorclass = isset($item['errors']) && $item['errors'] > 0 ? ' error' : ''; $a[] = sprintf('<div class="translatetool_label%s">', $errorclass); $a[] = $item['label']; $a[] = '</div>'; } $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) { $a[] = $widget_line . $postfix; } } else { // quick and dirty: // add a <p> before the first button in a dialog // add a <p> after every regular input item except buttons and hidden fields // result: fields line up nicely and buttons are on a single row $postfix = ''; if ($item['type'] == F_SUBMIT) { if (!$buttons_seen) { $buttons_seen = TRUE; $a[] = '<p>'; } } elseif (!(isset($item['hidden']) && $item['hidden'])) { $postfix = '<p>'; } $a[] = $widget . $postfix; } } $a[] = '<p>'; $a[] = html_form_close(); return $a; }