diff --git a/lib/kolab_html.php b/lib/kolab_html.php index 2317bd0..895647e 100644 --- a/lib/kolab_html.php +++ b/lib/kolab_html.php @@ -1,510 +1,515 @@ | +--------------------------------------------------------------------------+ | Author: Aleksander Machniak | +--------------------------------------------------------------------------+ */ /** * HTML output generation */ class kolab_html { public static $common_attribs = array('id', 'class', 'style', 'title', 'align', 'dir'); public static $event_attribs = array('onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout'); public static $input_event_attribs = array('onfocus', 'onblur', 'onkeypress', 'onkeydown', 'onkeyup', 'onsubmit', 'onreset', 'onselect', 'onchange'); public static $table_attribs = array('summary'); public static $tr_attribs = array(); public static $td_attribs = array('colspan', 'rowspan'); public static $textarea_attribs = array('cols', 'rows', 'disabled', 'name', 'readonly', 'tabindex'); public static $input_attribs = array('checked', 'disabled', 'name', 'readonly', 'tabindex', 'type', 'size', 'maxlength', 'value', 'autofocus'); public static $select_attribs = array('multiple', 'name', 'size', 'disabled', 'readonly', 'autofocus'); public static $option_attribs = array('selected', 'value', 'disabled', 'readonly'); public static $a_attribs = array('href', 'name', 'rel', 'tabindex', 'target'); public static $form_attribs = array('action', 'enctype', 'method', 'name', 'target'); public static $label_attribs = array('for'); /** * Table element (TABLE). * * @param array $attribs Table attributes * @param string $content Optional table content. If empty * head, body, foot attributes will be used. * * @return string HTML output of the table */ public static function table($attribs = array(), $content = null) { $table_attribs = array_merge(self::$table_attribs, self::$common_attribs, self::$event_attribs); $table = ''; if ($content) { $table .= $content; } else { if (!empty($attribs['head']) && is_array($attribs['head'])) { $table .= ''; foreach ($attribs['head'] as $row) { $table .= "\n" . self::tr($row, null, true); } $table .= ''; } if (!empty($attribs['body']) && is_array($attribs['body'])) { $table .= ''; foreach ($attribs['body'] as $row) { $table .= "\n" . self::tr($row); } $table .= ''; } if (!empty($attribs['foot']) && is_array($attribs['foot'])) { $table .= ''; foreach ($attribs['foot'] as $row) { $table .= "\n" . self::tr($row); } $table .= ''; } } $table .= "\n"; return $table; } /** * Table row (TR). * * @param array $attribs Row attributes * @param string $is_head Set to true if it is a part of table head. * * @return string HTML output of the row */ public static function tr($attribs = array(), $is_head = false) { $row_attribs = array_merge(self::$tr_attribs, self::$common_attribs, self::$event_attribs); $row = ''; if (!empty($attribs['cells']) && is_array($attribs['cells'])) { foreach ($attribs['cells'] as $cell) { $row .= "\n" . self::td($cell, $is_head); } } $row .= "\n"; return $row; } /** * Table cell (TD or TH). * * @param array $attribs Cell attributes * @param string $is_head Set to true if it is a part of table head. * * @return string HTML output of the cell */ public static function td($attribs = array(), $is_head = false) { $cell_attribs = array_merge(self::$td_attribs, self::$common_attribs, self::$event_attribs); $tag = $is_head ? 'th' : 'td'; $cell .= '<' . $tag . self::attrib_string($attribs, $cell_attribs) . '>'; if (isset($attribs['body'])) { $cell .= $attribs['body']; } $cell .= ""; return $cell; } /** * Input element. * * @param array $attribs Element attributes * * @return string HTML output of the input */ public static function input($attribs = array()) { $elem_attribs = array_merge(self::$input_attribs, self::$input_event_attribs, self::$common_attribs, self::$event_attribs); + if ($attribs['type'] == 'checkbox' && $attribs['readonly']) { + // readonly checkbox should be disabled, otherwise the user could still check or uncheck the box + $attribs['disabled'] = 'disabled'; + } + return sprintf('', self::attrib_string($attribs, $elem_attribs)); } /** * Input element for mail quota. user can select the unit (GB, MB, KB) * * @param array $attribs Element attributes * * @return string HTML output of the input */ public static function inputquota($attribs = array()) { if ($attribs['value'] % 1024 == 0) { if ($attribs['value'] >= 1024) { $attribs['value'] /= 1024; $unit = 'mb'; } if ($attribs['value'] % 1024 == 0 && $attribs['value'] >= 1024) { $attribs['value'] /= 1024; $unit = 'gb'; } } if (empty($attribs['size'])) { $attribs['size'] = 10; } // show no select dropdown box if value is readonly if (!empty($attribs['readonly'])) { if ($unit) { $attribs['value'] .= ' ' . strtoupper($unit); } return self::input($attribs); } $select = array( 'name' => $attribs['name'] . '-unit', 'options' => array(), ); foreach (array('kb', 'mb', 'gb') as $u) { $select['options'][] = array( 'value' => $u, 'content' => strtoupper($u), 'selected' => $unit == $u, ); } $attribs['data-type'] = 'quota'; return self::input($attribs) . self::select($select); } /** * Textarea element. * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the textarea */ public static function textarea($attribs = array(), $escape = false) { $elem_attribs = array_merge(self::$textarea_attribs, self::$input_event_attribs, self::$common_attribs, self::$event_attribs); $content = isset($attribs['value']) ? $attribs['value'] : ''; if ($escape) { $content = self::escape($content); } return sprintf('%s', self::attrib_string($attribs, $elem_attribs), $content); } /** * Select element. * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the select tag */ public static function select($attribs = array(), $escape = false) { $elem_attribs = array_merge(self::$select_attribs, self::$input_event_attribs, self::$common_attribs, self::$event_attribs); $content = array(); if (!empty($attribs['options']) && is_array($attribs['options'])) { foreach ($attribs['options'] as $idx => $option) { if (!is_array($option)) { $option = array('content' => $option); } if (empty($option['value'])) { $option['value'] = $idx; } if (!empty($attribs['value'])) { if (is_array($attribs['value'])) { $option['selected'] = in_array($option['value'], $attribs['value']); } else if ($attribs['value'] == $option['value']) { $option['selected'] = true; } } // make a select really readonly by disabling options else if (!empty($attribs['disabled']) || !empty($attribs['readonly'])) { $option['disabled'] = true; } $content[] = self::option($option, $escape); } } return sprintf('%s', self::attrib_string($attribs, $elem_attribs), implode("\n", $content)); } /** * Option element. * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the option tag */ public static function option($attribs = array(), $escape = false) { $elem_attribs = array_merge(self::$option_attribs, self::$common_attribs); $content = isset($attribs['content']) ? $attribs['content'] : ''; if ($escape) { $content = self::escape($content); } return sprintf('%s', self::attrib_string($attribs, $elem_attribs), $content); } /** * Fieldset element. * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the fieldset tag */ public static function fieldset($attribs = array(), $escape = false) { $elem_attribs = array_merge(self::$common_attribs); $legend = isset($attribs['legend']) ? $attribs['legend'] : $attribs['label']; $content = isset($attribs['content']) ? $attribs['content'] : ''; if ($escape) { $legend = self::escape($legend); } return sprintf('%s%s', self::attrib_string($attribs, $elem_attribs), $legend, $content); } /** * Link element (A). * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the link */ public static function a($attribs = array(), $escape = false) { $elem_attribs = array_merge(self::$a_attribs, self::$common_attribs, self::$event_attribs); $content = isset($attribs['content']) ? $attribs['content'] : ''; if ($escape) { $content = self::escape($content); } return sprintf('%s', self::attrib_string($attribs, $elem_attribs), $content); } /** * Label element. * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the label tag */ public static function label($attribs = array(), $escape = false) { $elem_attribs = array_merge(self::$label_attribs, self::$common_attribs); $content = isset($attribs['content']) ? $attribs['content'] : ''; if ($escape) { $content = self::escape($content); } return sprintf('%s', self::attrib_string($attribs, $elem_attribs), $content); } /** * Division element. * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the div tag */ public static function div($attribs = array(), $escape = false) { $elem_attribs = array_merge(self::$common_attribs, self::$event_attribs); $content = isset($attribs['content']) ? $attribs['content'] : ''; if ($escape) { $content = self::escape($content); } return sprintf('%s', self::attrib_string($attribs, $elem_attribs), $content); } /** * Span element. * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the span tag */ public static function span($attribs = array(), $escape = false) { $elem_attribs = array_merge(self::$common_attribs, self::$event_attribs); $content = isset($attribs['content']) ? $attribs['content'] : ''; if ($escape) { $content = self::escape($content); } return sprintf('%s', self::attrib_string($attribs, $elem_attribs), $content); } /** * Form element. * * @param array $attribs Element attributes * @param string $escape Content of the form * * @return string HTML output of the form tag */ public static function form($attribs = array(), $content = null) { $elem_attribs = array_merge(self::$form_attribs, self::$common_attribs, self::$event_attribs); return sprintf('%s', self::attrib_string($attribs, $elem_attribs), $content); } /** * Script element. * * @param array $attribs Element attributes * @param bool $escape Enables escaping of the content * * @return string HTML output of the script tag */ public static function script($content = null, $escape = false) { if ($escape) { $content = self::escape($content); } return sprintf('', $content); } /** * Create string with attributes * * @param array $attrib Associative array with tag attributes * @param array $allowed List of allowed attributes * * @return string Valid attribute string */ public static function attrib_string($attrib = array(), $allowed = array()) { if (empty($attrib)) { return ''; } $allowed = array_flip((array)$allowed); $attrib_arr = array(); foreach ($attrib as $key => $value) { // skip size if not numeric if (($key == 'size' && !is_numeric($value))) { continue; } // ignore empty values if ($value === null || $value === '') { continue; } // ignore unpermitted attributes, allow "data-" if (!empty($allowed) && strpos($key, 'data-') !== 0 && !isset($allowed[$key])) { continue; } // skip empty eventhandlers if (preg_match('/^on[a-z]+/', $key) && !$value) { continue; } // boolean attributes if (preg_match('/^(checked|multiple|disabled|selected|readonly|autofocus)$/', $key)) { if ($value) { $attrib_arr[] = sprintf('%s="%s"', $key, $key); } } // the rest else { $attrib_arr[] = sprintf('%s="%s"', $key, self::escape($value)); } } return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : ''; } /** * Escape special characters into HTML entities. * * @param string|array $value Value to escape * * @return string|array Escaped value */ public static function escape($value) { if (is_array($value)) { foreach ($value as $idx => $val) { $value[$idx] = self::escape($val); } return $value; } return htmlspecialchars($value, ENT_COMPAT, KADM_CHARSET); } }