source: trunk/lib/HTML.inc.php

Last change on this file was 763, checked in by anonymous, 2 years ago

Include boomerang in hidden input on login form so the user will be redirected if the revisit the login form after session is garbage collected. Add escape values used in html attributes.

File size: 17.4 KB
Line 
1<?php
2/*
3* The Strangecode Codebase - a general application development framework for PHP
4* For details visit the project site: <http://trac.strangecode.com/>
5* Copyright © 2014 Strangecode, LLC
6*
7* This program is free software: you can redistribute it and/or modify
8* it under the terms of the GNU General Public License as published by
9* the Free Software Foundation, either version 3 of the License, or
10* (at your option) any later version.
11*
12* This program is distributed in the hope that it will be useful,
13* but WITHOUT ANY WARRANTY; without even the implied warranty of
14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15* GNU General Public License for more details.
16*
17* You should have received a copy of the GNU General Public License
18* along with this program.  If not, see <http://www.gnu.org/licenses/>.
19*/
20
21/*
22* HTML()
23*
24* Tools for building HTML from PHP data.
25*
26* @author   Quinn Comendant <quinn@strangecode.com>
27* @version  1.0
28* @since    11 Sep 2014 21:08:06
29*
30* Example of use:
31---------------------------------------------------------------------
32echo HTML::buttons(array(
33    array('name' => 'btn_submit', 'value' => _("Save changes"), 'class' => 'small button', 'accesskey' => 's'),
34    array('name' => 'btn_reset', 'value' => _("Reset"), 'class' => 'small button secondary', 'accesskey' => 'r'),
35    array('name' => 'btn_cancel', 'value' => _("Cancel"), 'class' => 'small button secondary', 'accesskey' => 'c'),
36));
37---------------------------------------------------------------------
38*/
39
40class HTML
41{
42
43    // Browsers add names and ids of form controls as properties to the FORM. This results in the properties of the form being replaced.
44    // Use this list to warn the programmer if he uses an unsafe name.
45    // http://jibbering.com/faq/names/unsafe_names.html
46    static $unsafe_form_control_names = array('accept','acceptCharset','action','addBehavior','addEventListener','addEventSource','addRepetitionBlock','addRepetitionBlockByIndex','all','appendChild','applyElement','ariaBusy','ariaChecked','ariaControls','ariaDescribability','ariaDisabled','ariaExpanded','ariaFlowto','ariaHaspopup','ariaHidden','ariaInvalid','ariaLabelledby','ariaLevel','ariaMultiselect','ariaOwns','ariaPosinset','ariaPressed','ariaReadonly','ariaRequired','ariaSecret','ariaSelected','ariaSetsize','ariaValuemax','ariaValuemin','ariaValuenow','attachEvent','attributes','ATTRIBUTE_NODE','autocomplete','baseURI','behaviorUrns','blockDiraction','blur','canHaveChildren','canHaveHTML','CDATA_SECTION_NODE','checkValidity','childElementCount','childNodes','children','className','clearAttributes','click','clientHeight','clientLeft','clientTop','clientWidth','cloneNode','COMMENT_NODE','compareDocumentPosition','componentFromPoint','constructor','contains','contentEditable','currentStyle','data','detachEvent','dir','dispatchEvent','dispatchFormChange','dispatchFormInput','document','DOCUMENT_FRAGMENT_NODE','DOCUMENT_NODE','DOCUMENT_POSITION_CONTAINED_BY','DOCUMENT_POSITION_CONTAINS','DOCUMENT_POSITION_DISCONNECTED','DOCUMENT_POSITION_FOLLOWING','DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC','DOCUMENT_POSITION_PRECEDING','DOCUMENT_TYPE_NODE','dragDrop','elements','ELEMENT_NODE','encoding','enctype','ENTITY_NODE','ENTITY_REFERENCE_NODE','fireEvent','firstChild','firstElementChild','focus','getAdjacentText','getAttribute','getAttributeNode','getAttributeNodeNS','getAttributeNS','getBoundingClientRect','getClientRects','getElementsByClassName','getElementsByTagName','getElementsByTagNameNS','getExpression','getFeature','getUserData','hasAttribute','hasAttributeNS','hasAttributes','hasChildNodes','hasOwnProperty','hideFocus','id','innerHTML','innerText','insertAdjacentElement','insertAdjacentHTML','insertAdjacentText','insertBefore','isContentEditable','isDefaultNamespace','isDefaultNamespaceURI','isDisabled','isEqualNode','isMultiLine','isPrototypeOf','isSameNode','isSupported','isTextEdit','item','lang','language','lastChild','lastElementChild','length','localName',
47    'lookupPrefix','mergeAttributes','method','moveRepetitionBlock','msBlockProgression','msBoxSizing','name','namedItem','namespaceURI','nextSibling','nodeName','nodeType','nodeValue','normalize','NOTATION_NODE','offsetHeight','offsetWidth','onabort','onactivate','onbeforeactivate','onbeforecopy','onbeforecut','onbeforedeactivate','onbeforeeditfocus','onbeforepaste','onblur','onchage','onclick','onclosecapture','oncontextmenu','oncopy','oncut','ondblclick','ondeactivate','ondrag','ondragend','ondragenter','ondragleave','ondragover','onerror','onfocus','onfocusin','onfocusout','onhelp','oninput','onkeydown','onkeypress','onkeyup','onmousedown','onmouseenter','onmouseleave','onmousemove','onmousemultiwheel','onmouseout','onmouseover','onmouseup','onmousewheel','onmove','onmoveend','onmovestart','onOffBehavior','onpaste','onpropertychange','onreadystatechange','onresize','onresizeend','onresizestart','onscroll','onsearch','onselect','onselectstart','ontimeerror','onunload','outerHTML','outerText','ownerDocument','parentNode','parentTextEdit','prefix','previousElementSibling','previousSibling','PROCESSING_INSTRUCTION_NODE','propertyIsEnumerable','querySelector','querySelectorAll','quotes','releaseCapture','removeAttribute','removeAttributeNode','removeAttributeNS','removeBehavior','removeChild','removeEventListener','removeEventSource','removeExpression','removeNode','removeRepetitionBlock','repeatMax','repeatMin','repeatStart','repetitionBlocks','repetitionIndex','repetitionTemplate','repetitionType','replace','replaceAdjacentText','replaceChild','replaceNode','reset','resetFromData','role','runtimeStyle','schemaTypeInfo','scopeName','scrollByLines','scrollByPages','scrollHeight','scrollIntoView','scrollLeft','scrollTop','scrollWidth','selectNodes','selectSingleNode','setActive','setAttributeNode','setAttributeNodeNS','setAttributeNS','setCapture','setExpression','setIdAttribute','setIdAttributeNode','setIdAttributeNS','setUserData','sourceIndex','spellcheck','style','submit','swapNode','tabIndex','tagName','tagUrn','target','templateElements','text','textContent','TEXT_NODE','title','toLocaleString','toString','uniqueID','unselectable','unwatch','urns','valueOf','watch','window');
48
49    /**
50    * Prints submit buttons based on given array of submit button names and titles. If the array includes an 'href' key, the
51    * button is created using a <a> otherwise an <input type="
" /> is used.
52    *
53    * @access  public
54    * @param   array   $buttons     Array of buttons, the key being the button name, and value being the title of the button.
55    * @return  void
56    * @author   Quinn Comendant <quinn@strangecode.com>
57    * @version  1.0
58    * @since    12 Sep 2014 00:17:38
59    */
60    static public function printButtons($buttons=array(), $class='button-group')
61    {
62        $app =& App::getInstance();
63
64        if (!isset($buttons[0]) || !is_array($buttons[0])) {
65            $app =& App::getInstance();
66            $app->logMsg(sprintf('Incorrect parameters passed to %s: %s', __METHHOD__, getDump($options)), LOG_NOTICE, __FILE__, __LINE__);
67            return false;
68        }
69        if (empty($buttons)) {
70            return '';
71        }
72        ?><ul class="<?php echo oTxt($class) ?>"><?php
73        foreach ($buttons as $i => $b) {
74            $defaults = array();
75            $defaults['type'] = isset($b['type']) ? $b['type'] : 'submit';
76            $b = array_merge($defaults, $b);
77            if (isset($b['href'])) {
78                echo '<li><a';
79                foreach (array_diff_key($b, array('value' => null)) as $key => $value) {
80                    printf(' %s="%s"', oTxt($key), oTxt($value));
81                }
82                echo '>' . oTxt($b['value']) . '</a></li>';
83            } else if (isset($b['name'])) {
84                if (in_array($b['name'], self::$unsafe_form_control_names)) {
85                    $app->logMsg(sprintf('Unsafe form control name: %s', $b['name']), LOG_NOTICE, __FILE__, __LINE__);
86                }
87                $defaults['id'] = isset($b['id']) ? $b['id'] : sprintf('sc-%s-button', $b['name']);
88                echo '<li><input';
89                foreach ($b as $key => $value) {
90                    printf(' %s="%s"', oTxt($key), oTxt($value));
91                }
92                echo ' /></li>';
93            } else {
94                $app->logMsg(sprintf('Button missing name or href: %s', getDump($b)), LOG_ERR, __FILE__, __LINE__);
95                continue;
96            }
97        }
98        ?></ul><?php
99    }
100
101    /*
102    * Return an array of key-value pairs matching a database query for the given parameters. This is useful for
103    * injecting into HTML::printSelectOptions().
104    *
105    * @access   public
106    * @param  string $db_table         database table to lookup
107    * @param  string $key_column       column containing the human-readable titles for the select menu
108    * @param  string $val_column       column containing the computer values for the select menu
109    * @param  string $preselected      the currently selected value of the menu. compared to the $val_column
110    * @param  bool   $first            Optional first item; set true for a blank item, array for item with name and value.
111    * @param  string $extra_clause     SQL exclude clause. Something like "WHERE girls != 'buckteeth'"
112    * @return array                    Array of options suitable to pass into HTML::printSelectOptions().
113    * @author   Quinn Comendant <quinn@strangecode.com>
114    * @version  1.0
115    * @since    12 Sep 2014 11:43:23
116    */
117    static public function getSelectOptions($db_table, $key_column, $val_column, $preselected, $first=false, $extra_clause='', $sql_format='SELECT %1$s, %2$s FROM %3$s %4$s')
118    {
119        $db =& DB::getInstance();
120
121        // Sometimes preselected comes as a comma list.
122        if (!is_array($preselected)) {
123            $preselected = array($preselected);
124        }
125
126        $options = array();
127        if (true === $first) {
128            // Include a blank first option.
129            $options[] = array(
130                'value' => '',
131                'selected' => in_array('', $preselected),
132                'text' => '',
133                'raw' => null,
134            );
135        } else if (is_array($first)) {
136            // When the 'blank' first option needs a specific key->val pair.
137            foreach ($first as $key => $val) {
138                $options[] = array(
139                    'value' => $key,
140                    'selected' => in_array($key, $preselected),
141                    'text' => $val,
142                    'raw' => null,
143                );
144            }
145        }
146
147        $db =& DB::getInstance();
148        $qid = $db->query(sprintf($sql_format, $key_column, $val_column, $db_table, $extra_clause), false);
149        while ($row = mysql_fetch_assoc($qid)) {
150            $options[] = array(
151                'value' => $row[$val_column],
152                'selected' => in_array($row[$val_column], $preselected),
153                'text' => $row[$key_column],
154                'raw' => $row,
155            );
156        }
157        return $options;
158    }
159
160    /*
161    *
162    *
163    * @access   public
164    * @param    array   $options    Array of options, with keys: value, selected, text
165    * @param    array   $deselected Array of values to disable in options.
166    * @return   array               Same options, but those with a value matching an element in $deselected will have a 'disabled' element of true.
167    * @author   Quinn Comendant <quinn@strangecode.com>
168    * @version  1.0
169    * @since    24 Jul 2015 01:41:33
170    */
171    static public function disableSelectOptions($options, $deselected)
172    {
173        $app =& App::getInstance();
174        $n = sizeof($options);
175        for ($i=0; $i < $n; $i++) {
176            $app->logMsg(sprintf('Disable check: %s == %s', $options[$i]['value'], getDump($deselected)), LOG_DEBUG, __FILE__, __LINE__);
177            $options[$i]['disabled'] = in_array($options[$i]['value'], $deselected);
178        }
179        return $options;
180    }
181
182    /**
183     * Get an array of option fields for a select form. Works only with enum or set
184     * data types in table columns.
185     *
186     * @param  string $db_table     Database table to lookup
187     * @param  string $db_col       Database column to lookup
188     * @param  string $preselected  The currently selected value of the menu. compared to the $val_column
189     * @param  bool   $first        Optional first item; set true for a blank item, array for item with name and value.
190     * @param  bool   $sort         Sort the output.
191     */
192    static public function getSelectOptionsEnum($db_table, $db_col, $preselected, $first=false, $sort=false)
193    {
194        // Sometimes preselected comes as a comma list.
195        if (!is_array($preselected)) {
196            $preselected = array($preselected);
197        }
198
199        $options = array();
200        if (true === $first) {
201            // Include a blank first option.
202            $options[] = array(
203                'value' => '',
204                'selected' => in_array('', $preselected),
205                'text' => '',
206            );
207        } else if (is_array($first)) {
208            // When the 'blank' first option needs a specific key->val pair.
209            foreach ($first as $key => $val) {
210                $options[] = array(
211                    'value' => $key,
212                    'selected' => in_array($key, $preselected),
213                    'text' => $val,
214                );
215            }
216        }
217
218        $db =& DB::getInstance();
219        $values = $db->getEnumValues($db_table, $db_col, $sort);
220        foreach ($values as $v) {
221            $options[] = array(
222                'value' => $v,
223                'selected' => in_array($v, $preselected),
224                'text' => $v,
225            );
226        }
227
228        return $options;
229    }
230
231    /**
232     * Prints select menu options with the specified array of keys and values.
233     *
234     */
235    static public function printSelectOptions($options)
236    {
237        if (!isset($options) || !is_array($options)) {
238            $app =& App::getInstance();
239            $app->logMsg(sprintf('Incorrect parameters passed to %s: %s', __METHHOD__, getDump($options)), LOG_NOTICE, __FILE__, __LINE__);
240            return false;
241        }
242        if (empty($options)) {
243            return '';
244        }
245
246        foreach ($options as $o) {
247            printf('<option value="%s"%s%s%s>%s</option>',
248                oTxt($o['value']),
249                (isset($o['class']) && sprintf(' class="%s"', oTxt($o['class'])) ? : ''),
250                (isset($o['selected']) && oTxt($o['selected']) ? ' selected="selected"' : ''),
251                (isset($o['disabled']) && oTxt($o['disabled']) ? ' disabled="disabled"' : ''),
252                oTxt($o['text'])
253            );
254        }
255    }
256
257    /*
258    * Print select options for a list of US states.
259    *
260    * @access   public
261    * @param    string $preselected      the currently selected value of the menu. compared to the $val_column
262    * @return   void
263    * @author   Quinn Comendant <quinn@strangecode.com>
264    * @since    12 Feb 2020 22:26:34
265    */
266    static public function printStatesSelectOptions($preselected='', $first=false)
267    {
268        $options = ['AL' => 'Alabama', 'AK' => 'Alaska', 'AZ' => 'Arizona', 'AR' => 'Arkansas', 'CA' => 'California', 'CO' => 'Colorado', 'CT' => 'Connecticut', 'DE' => 'Delaware', 'DC' => 'District Of Columbia', 'FL' => 'Florida', 'GA' => 'Georgia', 'HI' => 'Hawaii', 'ID' => 'Idaho', 'IL' => 'Illinois', 'IN' => 'Indiana', 'IA' => 'Iowa', 'KS' => 'Kansas', 'KY' => 'Kentucky', 'LA' => 'Louisiana', 'ME' => 'Maine', 'MD' => 'Maryland', 'MA' => 'Massachusetts', 'MI' => 'Michigan', 'MN' => 'Minnesota', 'MS' => 'Mississippi', 'MO' => 'Missouri', 'MT' => 'Montana', 'NE' => 'Nebraska', 'NV' => 'Nevada', 'NH' => 'New Hampshire', 'NJ' => 'New Jersey', 'NM' => 'New Mexico', 'NY' => 'New York', 'NC' => 'North Carolina', 'ND' => 'North Dakota', 'OH' => 'Ohio', 'OK' => 'Oklahoma', 'OR' => 'Oregon', 'PA' => 'Pennsylvania', 'RI' => 'Rhode Island', 'SC' => 'South Carolina', 'SD' => 'South Dakota', 'TN' => 'Tennessee', 'TX' => 'Texas', 'UT' => 'Utah', 'VT' => 'Vermont', 'VA' => 'Virginia', 'WA' => 'Washington', 'WV' => 'West Virginia', 'WI' => 'Wisconsin', 'WY' => 'Wyoming'];
269
270        if (true === $first) {
271            // Include a blank first option.
272            $options = array_merge(['' => ''], $options);
273        } else if (is_array($first)) {
274            // When the 'blank' first option needs a specific key->val pair.
275            foreach ($first as $key => $val) {
276                $options = array_merge([$key => $val], $options);
277            }
278        }
279
280        foreach ($options as $value => $text) {
281            printf('<option value="%s"%s>%s</option>',
282                oTxt($value),
283                $preselected == $value ? ' selected="selected"' : '',
284                oTxt($text)
285            );
286        }
287    }
288
289    /**
290     * Get a Gravatar URL for a specified email address.
291     *
292     * @param string $email The email address
293     * @param string $size Size in pixels, defaults to 80px [ 1 - 2048 ]
294     * @param string $d Default imageset to use [ 404 | mp | identicon | monsterid | wavatar | retro | robohash | blank ]
295     *                  See what defaults look like at https://en.gravatar.com/site/implement/images/
296     * @param string $r Maximum rating (inclusive) [ g | pg | r | x ]
297     * @return String containing a URL to a gravatar image.
298     * @source http://gravatar.com/site/implement/images/php/
299     */
300    static public function getGravatarURL($email, $size=80, $defset='mp', $rating='pg') {
301        return sprintf('https://www.gravatar.com/avatar/%s?s=%s&d=%s&r=%s',
302            md5(strtolower(trim($email))),
303            $size,
304            $defset,
305            $rating
306        );
307    }
308}
Note: See TracBrowser for help on using the repository browser.