| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * forms.php - html form functions |
| 5 | * |
| 6 | * Functions to build forms in a safe and consistent manner. |
| 7 | * All attribute values are sanitized with sm_encode_html_special_chars(). |
| 8 | //FIXME: I think the Template class might be better place to sanitize inside assign() method |
| 9 | * |
| 10 | * Currently functions don't provide simple wrappers for file and |
| 11 | * image input fields, support only submit and reset buttons and use |
| 12 | * html input tags for buttons. |
| 13 | * |
| 14 | * Since 1.5.1: |
| 15 | * |
| 16 | * * all form functions should support id tags. Original |
| 17 | * idea by dugan <at> passwall.com. Tags can be used for Section 508 |
| 18 | * or WAI compliance. |
| 19 | * |
| 20 | * * input tag functions accept extra html attributes that can be submitted |
| 21 | * in $aAttribs array. |
| 22 | * |
| 23 | * * default css class attributes are added. |
| 24 | * |
| 25 | * @link http://www.section508.gov/ Section 508 |
| 26 | * @link http://www.w3.org/WAI/ Web Accessibility Initiative (WAI) |
| 27 | * @link http://www.w3.org/TR/html4/ W3.org HTML 4.01 form specs |
| 28 | * @copyright 2004-2020 The SquirrelMail Project Team |
| 29 | * @license http://opensource.org/licenses/gpl-license.php GNU Public License |
| 30 | * @version $Id$ |
| 31 | * @package squirrelmail |
| 32 | * @subpackage forms |
| 33 | * @since 1.4.3 and 1.5.1 |
| 34 | */ |
| 35 | |
| 36 | /** |
| 37 | * Helper function to create form fields, not to be called directly, |
| 38 | * only by other functions below. |
| 39 | * |
| 40 | * Function used different syntax before 1.5.1 |
| 41 | * @param string $sType type of input field. Possible values (html 4.01 |
| 42 | * specs.): text, password, checkbox, radio, submit, reset, file, |
| 43 | * hidden, image, button. |
| 44 | * @param array $aAttribs (since 1.5.1) extra attributes. Array key is |
| 45 | * attribute name, array value is attribute value. Array keys must use |
| 46 | * lowercase. |
| 47 | * @return string html formated input field |
| 48 | * @deprecated use other functions that provide simple wrappers to this function |
| 49 | */ |
| 50 | function addInputField($sType, $aAttribs=array()) { |
| 51 | $sAttribs = ''; |
| 52 | // define unique identifier |
| 53 | if (! isset($aAttribs['id']) && isset($aAttribs['name']) && ! is_null($aAttribs['name'])) { |
| 54 | /** |
| 55 | * if 'id' is not set, set it to 'name' and replace brackets |
| 56 | * with underscores. 'name' might contain field name with squire |
| 57 | * brackets (array). Brackets are not allowed in id (validator.w3.org |
| 58 | * fails to validate document). According to html 4.01 manual cdata |
| 59 | * type description, 'name' attribute uses same type, but validator.w3.org |
| 60 | * does not barf on brackets in 'name' attributes. |
| 61 | */ |
| 62 | $aAttribs['id'] = strtr($aAttribs['name'],'[]','__'); |
| 63 | } |
| 64 | |
| 65 | global $oTemplate; |
| 66 | |
| 67 | $oTemplate->assign('type', $sType); |
| 68 | //FIXME: all the values in the $aAttribs list used to go thru sm_encode_html_special_chars()... I would propose that most everything that is assigned to the template should go thru that *in the template class* on its way between here and the actual template file. Otherwise we have to do something like: foreach ($aAttribs as $key => $value) $aAttribs[$key] = sm_encode_html_special_chars($value); |
| 69 | $oTemplate->assign('aAttribs', $aAttribs); |
| 70 | |
| 71 | return $oTemplate->fetch('input.tpl'); |
| 72 | |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Password input field |
| 77 | * @param string $sName field name |
| 78 | * @param string $sValue initial password value |
| 79 | * @param integer $iSize field size (number of characters) |
| 80 | * @param integer $iMaxlength maximum number of characters the user may enter |
| 81 | * @param array $aAttribs (since 1.5.1) extra attributes - should be given |
| 82 | * in the form array('attribute_name' => 'attribute_value', ...) |
| 83 | * @return string html formated password field |
| 84 | */ |
| 85 | function addPwField($sName, $sValue = '', $iSize = 0, $iMaxlength = 0, $aAttribs=array()) { |
| 86 | $aAttribs['name'] = $sName; |
| 87 | $aAttribs['value'] = $sValue; |
| 88 | if ($iSize) $aAttribs['size'] = (int)$iSize; |
| 89 | if ($iMaxlength) $aAttribs['maxlength'] = (int)$iMaxlength; |
| 90 | // add default css |
| 91 | if (! isset($aAttribs['class'])) $aAttribs['class'] = 'sqmpwfield'; |
| 92 | return addInputField('password',$aAttribs); |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * Form checkbox |
| 97 | * @param string $sName field name |
| 98 | * @param boolean $bChecked controls if field is checked |
| 99 | * @param string $sValue |
| 100 | * @param array $aAttribs (since 1.5.1) extra attributes |
| 101 | * @return string html formated checkbox field |
| 102 | */ |
| 103 | function addCheckBox($sName, $bChecked = false, $sValue = null, $aAttribs=array()) { |
| 104 | $aAttribs['name'] = $sName; |
| 105 | if ($bChecked) $aAttribs['checked'] = 'checked'; |
| 106 | if (! is_null($sValue)) $aAttribs['value'] = $sValue; |
| 107 | // add default css |
| 108 | if (! isset($aAttribs['class'])) $aAttribs['class'] = 'sqmcheckbox'; |
| 109 | return addInputField('checkbox',$aAttribs); |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Form radio box |
| 114 | * @param string $sName field name |
| 115 | * @param boolean $bChecked controls if field is selected |
| 116 | * @param string $sValue |
| 117 | * @param array $aAttribs (since 1.5.1) extra attributes. |
| 118 | * @return string html formated radio box |
| 119 | */ |
| 120 | function addRadioBox($sName, $bChecked = false, $sValue = null, $aAttribs=array()) { |
| 121 | $aAttribs['name'] = $sName; |
| 122 | if ($bChecked) $aAttribs['checked'] = 'checked'; |
| 123 | if (! is_null($sValue)) $aAttribs['value'] = $sValue; |
| 124 | if (! isset($aAttribs['id'])) $aAttribs['id'] = $sName . $sValue; |
| 125 | // add default css |
| 126 | if (! isset($aAttribs['class'])) $aAttribs['class'] = 'sqmradiobox'; |
| 127 | return addInputField('radio', $aAttribs); |
| 128 | } |
| 129 | |
| 130 | /** |
| 131 | * A hidden form field. |
| 132 | * @param string $sName field name |
| 133 | * @param string $sValue field value |
| 134 | * @param array $aAttribs (since 1.5.1) extra attributes |
| 135 | * @return html formated hidden form field |
| 136 | */ |
| 137 | function addHidden($sName, $sValue, $aAttribs=array()) { |
| 138 | $aAttribs['name'] = $sName; |
| 139 | $aAttribs['value'] = $sValue; |
| 140 | // add default css |
| 141 | if (! isset($aAttribs['class'])) $aAttribs['class'] = 'sqmhiddenfield'; |
| 142 | return addInputField('hidden', $aAttribs); |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * An input textbox. |
| 147 | * @param string $sName field name |
| 148 | * @param string $sValue initial field value |
| 149 | * @param integer $iSize field size (number of characters) |
| 150 | * @param integer $iMaxlength maximum number of characters the user may enter |
| 151 | * @param array $aAttribs (since 1.5.1) extra attributes - should be given |
| 152 | * in the form array('attribute_name' => 'attribute_value', ...) |
| 153 | * @return string html formated text input field |
| 154 | */ |
| 155 | function addInput($sName, $sValue = '', $iSize = 0, $iMaxlength = 0, $aAttribs=array()) { |
| 156 | $aAttribs['name'] = $sName; |
| 157 | $aAttribs['value'] = $sValue; |
| 158 | if ($iSize) $aAttribs['size'] = (int)$iSize; |
| 159 | if ($iMaxlength) $aAttribs['maxlength'] = (int)$iMaxlength; |
| 160 | // add default css |
| 161 | if (! isset($aAttribs['class'])) $aAttribs['class'] = 'sqmtextfield'; |
| 162 | return addInputField('text', $aAttribs); |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * Function to create a selectlist from an array. |
| 167 | * @param string $sName Field name |
| 168 | * @param array $aValues Field values array(key => value) results in: |
| 169 | * <option value="key">value</option>, |
| 170 | * although if $bUsekeys is FALSE, then it changes to: |
| 171 | * <option value="value">value</option> |
| 172 | * @param mixed $default The key(s) that will be selected (it is OK to pass |
| 173 | * in an array here in the case of multiple select lists) |
| 174 | * @param boolean $bUsekeys Use the keys of the array as option value or not |
| 175 | * @param array $aAttribs (since 1.5.1) Extra attributes |
| 176 | * @param boolean $bMultiple When TRUE, a multiple select list will be shown |
| 177 | * (OPTIONAL; default is FALSE (single select list)) |
| 178 | * @param int $iSize Desired height of multiple select boxes |
| 179 | * (OPTIONAL; default is SMOPT_SIZE_NORMAL) |
| 180 | * (only applicable when $bMultiple is TRUE) |
| 181 | * |
| 182 | * @return string html formated selection box |
| 183 | * @todo add attributes argument for option tags and default css |
| 184 | */ |
| 185 | function addSelect($sName, $aValues, $default = null, $bUsekeys = false, $aAttribs = array(), $bMultiple = FALSE, $iSize = SMOPT_SIZE_NORMAL) { |
| 186 | // only one element |
| 187 | if (!$bMultiple && count($aValues) == 1) { |
| 188 | $k = key($aValues); $v = array_pop($aValues); |
| 189 | return addHidden($sName, ($bUsekeys ? $k : $v), $aAttribs) |
| 190 | . sm_encode_html_special_chars($v); |
| 191 | } |
| 192 | |
| 193 | if (! isset($aAttribs['id'])) $aAttribs['id'] = $sName; |
| 194 | |
| 195 | // make sure $default is an array, since multiple select lists |
| 196 | // need the chance to have more than one default... |
| 197 | // |
| 198 | if (!is_array($default)) |
| 199 | $default = array($default); |
| 200 | |
| 201 | |
| 202 | global $oTemplate; |
| 203 | |
| 204 | //FIXME: all the values in the $aAttribs list and $sName and both the keys and values in $aValues used to go thru sm_encode_html_special_chars()... I would propose that most everything that is assigned to the template should go thru that *in the template class* on its way between here and the actual template file. Otherwise we have to do something like: foreach ($aAttribs as $key => $value) $aAttribs[$key] = sm_encode_html_special_chars($value); $sName = sm_encode_html_special_chars($sName); $aNewValues = array(); foreach ($aValues as $key => $value) $aNewValues[sm_encode_html_special_chars($key)] = sm_encode_html_special_chars($value); $aValues = $aNewValues; And probably this too because it has to be matched to a value that has already been sanitized: $default = sm_encode_html_special_chars($default); (oops, watch out for when $default is an array! (multiple select lists)) |
| 205 | $oTemplate->assign('aAttribs', $aAttribs); |
| 206 | $oTemplate->assign('aValues', $aValues); |
| 207 | $oTemplate->assign('bUsekeys', $bUsekeys); |
| 208 | $oTemplate->assign('default', $default); |
| 209 | $oTemplate->assign('name', $sName); |
| 210 | $oTemplate->assign('multiple', $bMultiple); |
| 211 | $oTemplate->assign('size', $iSize); |
| 212 | |
| 213 | return $oTemplate->fetch('select.tpl'); |
| 214 | } |
| 215 | |
| 216 | /** |
| 217 | * Normal button |
| 218 | * |
| 219 | * Note the switched value/name parameters! |
| 220 | * Note also that regular buttons are not very useful unless |
| 221 | * used with onclick handlers, thus are only really appropriate |
| 222 | * if you use them after having checked if JavaScript is turned |
| 223 | * on by doing this: if (checkForJavascript()) ... |
| 224 | * |
| 225 | * @param string $sValue button name |
| 226 | * @param string $sName key name |
| 227 | * @param array $aAttribs extra attributes |
| 228 | * |
| 229 | * @return string html formated submit input field |
| 230 | * |
| 231 | * @since 1.5.2 |
| 232 | */ |
| 233 | function addButton($sValue, $sName = null, $aAttribs=array()) { |
| 234 | $aAttribs['value'] = $sValue; |
| 235 | if (! is_null($sName)) $aAttribs['name'] = $sName; |
| 236 | // add default css |
| 237 | if (! isset($aAttribs['class'])) $aAttribs['class'] = 'sqmsubmitfield'; |
| 238 | return addInputField('button', $aAttribs); |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * Form submission button |
| 243 | * Note the switched value/name parameters! |
| 244 | * @param string $sValue button name |
| 245 | * @param string $sName submitted key name |
| 246 | * @param array $aAttribs (since 1.5.1) extra attributes |
| 247 | * @return string html formated submit input field |
| 248 | */ |
| 249 | function addSubmit($sValue, $sName = null, $aAttribs=array()) { |
| 250 | $aAttribs['value'] = $sValue; |
| 251 | if (! is_null($sName)) $aAttribs['name'] = $sName; |
| 252 | // add default css |
| 253 | if (! isset($aAttribs['class'])) $aAttribs['class'] = 'sqmsubmitfield'; |
| 254 | return addInputField('submit', $aAttribs); |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * Form reset button |
| 259 | * @param string $sValue button name |
| 260 | * @param array $aAttribs (since 1.5.1) extra attributes |
| 261 | * @return string html formated reset input field |
| 262 | */ |
| 263 | function addReset($sValue, $aAttribs=array()) { |
| 264 | $aAttribs['value'] = $sValue; |
| 265 | // add default css |
| 266 | if (! isset($aAttribs['class'])) $aAttribs['class'] = 'sqmresetfield'; |
| 267 | return addInputField('reset', $aAttribs); |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | * Textarea form element. |
| 272 | * |
| 273 | * @param string $sName field name |
| 274 | * @param string $sText initial field value (OPTIONAL; default empty) |
| 275 | * @param integer $iCols field width (number of chars) (OPTIONAL; default 40) |
| 276 | * @param integer $iRows field height (number of character rows) (OPTIONAL; default 10) |
| 277 | * @param array $aAttribs (since 1.5.1) extra attributes (OPTIONAL; default empty) |
| 278 | * |
| 279 | * @return string html formated text area field |
| 280 | * |
| 281 | */ |
| 282 | function addTextArea($sName, $sText = '', $iCols = 40, $iRows = 10, $aAttribs = array()) { |
| 283 | |
| 284 | // no longer accept string arguments for attribs; print |
| 285 | // backtrace to help people fix their code |
| 286 | //FIXME: throw error instead? |
| 287 | if (!is_array($aAttribs)) { |
| 288 | echo '$aAttribs argument to addTextArea() must be an array<br /><pre>'; |
| 289 | debug_print_backtrace(); |
| 290 | echo '</pre><br />'; |
| 291 | exit; |
| 292 | } |
| 293 | |
| 294 | // add default css |
| 295 | else if (!isset($aAttribs['class'])) $aAttribs['class'] = 'sqmtextarea'; |
| 296 | |
| 297 | if ( empty( $aAttribs['id'] ) ) { |
| 298 | $aAttribs['id'] = strtr($sName,'[]','__'); |
| 299 | } |
| 300 | |
| 301 | global $oTemplate; |
| 302 | |
| 303 | //FIXME: all the values in the $aAttribs list as well as $sName and $sText used to go thru sm_encode_html_special_chars()... I would propose that most everything that is assigned to the template should go thru that *in the template class* on its way between here and the actual template file. Otherwise we have to do something like: foreach ($aAttribs as $key => $value) $aAttribs[$key] = sm_encode_html_special_chars($value); $sName = sm_encode_html_special_chars($sName); $sText = sm_encode_html_special_chars($sText); |
| 304 | $oTemplate->assign('aAttribs', $aAttribs); |
| 305 | $oTemplate->assign('name', $sName); |
| 306 | $oTemplate->assign('text', $sText); |
| 307 | $oTemplate->assign('cols', (int)$iCols); |
| 308 | $oTemplate->assign('rows', (int)$iRows); |
| 309 | |
| 310 | return $oTemplate->fetch('textarea.tpl'); |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * Make a <form> start-tag. |
| 315 | * |
| 316 | * @param string $sAction form handler URL |
| 317 | * @param string $sMethod http method used to submit form data. 'get' or 'post' |
| 318 | * @param string $sName form name used for identification (used for backward |
| 319 | * compatibility). Use of id is recommended instead. |
| 320 | * @param string $sEnctype content type that is used to submit data. html 4.01 |
| 321 | * defaults to 'application/x-www-form-urlencoded'. Form |
| 322 | * with file field needs 'multipart/form-data' encoding type. |
| 323 | * @param string $sCharset charset that is used for submitted data |
| 324 | * @param array $aAttribs (since 1.5.1) extra attributes |
| 325 | * @param boolean $bAddToken (since 1.5.2) When given as a string or as boolean TRUE, |
| 326 | * a hidden input is also added to the form containing a |
| 327 | * security token. When given as TRUE, the input name is |
| 328 | * "smtoken"; otherwise the name is the string that is |
| 329 | * given for this parameter. When FALSE, no hidden token |
| 330 | * input field is added. (OPTIONAL; default not used) |
| 331 | * |
| 332 | * @return string html formated form start string |
| 333 | * |
| 334 | */ |
| 335 | function addForm($sAction, $sMethod = 'post', $sName = '', $sEnctype = '', $sCharset = '', $aAttribs = array(), $bAddToken = FALSE) { |
| 336 | |
| 337 | global $oTemplate; |
| 338 | |
| 339 | //FIXME: all the values in the $aAttribs list as well as $charset used to go thru sm_encode_html_special_chars()... I would propose that most everything that is assigned to the template should go thru that *in the template class* on its way between here and the actual template file. Otherwise we have to do something like: foreach ($aAttribs as $key => $value) $aAttribs[$key] = sm_encode_html_special_chars($value); $sCharset = sm_encode_html_special_chars($sCharset); |
| 340 | $oTemplate->assign('aAttribs', $aAttribs); |
| 341 | $oTemplate->assign('name', $sName); |
| 342 | $oTemplate->assign('method', $sMethod); |
| 343 | $oTemplate->assign('action', $sAction); |
| 344 | $oTemplate->assign('enctype', $sEnctype); |
| 345 | $oTemplate->assign('charset', $sCharset); |
| 346 | |
| 347 | $sForm = $oTemplate->fetch('form.tpl'); |
| 348 | |
| 349 | if ($bAddToken) { |
| 350 | $sForm .= addHidden((is_string($bAddToken) ? $bAddToken : 'smtoken'), |
| 351 | sm_generate_security_token()); |
| 352 | } |
| 353 | |
| 354 | return $sForm; |
| 355 | } |
| 356 | |
| 357 | /** |
| 358 | * Creates unique widget names |
| 359 | * |
| 360 | * Names are formatted as such: "send1", "send2", "send3", etc., |
| 361 | * where "send" in this example is what was given for $base_name |
| 362 | * |
| 363 | * @param string $base_name The name upon which to base the |
| 364 | * returned widget name. |
| 365 | * @param boolean $return_count When TRUE, this function will |
| 366 | * return the last number used to |
| 367 | * create a widget name for $base_name |
| 368 | * (OPTIONAL; default = FALSE). |
| 369 | * |
| 370 | * @return mixed When $return_output is FALSE, a string containing |
| 371 | * the unique widget name; otherwise an integer with |
| 372 | * the last number used to create the last widget |
| 373 | * name for the given $base_name (where 0 (zero) means |
| 374 | * that no such widgets have been created yet). |
| 375 | * |
| 376 | * @since 1.5.2 |
| 377 | * |
| 378 | */ |
| 379 | function unique_widget_name($base_name, $return_count=FALSE) |
| 380 | { |
| 381 | static $counts = array(); |
| 382 | |
| 383 | if (!isset($counts[$base_name])) |
| 384 | $counts[$base_name] = 0; |
| 385 | |
| 386 | if ($return_count) |
| 387 | return $counts[$base_name]; |
| 388 | |
| 389 | ++$counts[$base_name]; |
| 390 | return $base_name . $counts[$base_name]; |
| 391 | } |
| 392 | |