3 * StatusNet, the distributed open-source microblogging tool
5 * Low-level generator for HTML
9 * LICENCE: This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * @author Evan Prodromou <evan@status.net>
25 * @author Sarven Capadisli <csarven@status.net>
26 * @copyright 2008 StatusNet, Inc.
27 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
28 * @link http://status.net/
31 if (!defined('STATUSNET') && !defined('LACONICA')) {
35 require_once INSTALLDIR.'/lib/xmloutputter.php';
37 define('PAGE_TYPE_PREFS',
38 'text/html,application/xhtml+xml,'.
39 'application/xml;q=0.3,text/xml;q=0.2');
42 * Low-level generator for HTML
44 * Abstracts some of the code necessary for HTML generation. Especially
45 * has methods for generating HTML form elements. Note that these have
46 * been created kind of haphazardly, not with an eye to making a general
47 * HTML-creation class.
51 * @author Evan Prodromou <evan@status.net>
52 * @author Sarven Capadisli <csarven@status.net>
53 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
54 * @link http://status.net/
60 class HTMLOutputter extends XMLOutputter
65 * Just wraps the XMLOutputter constructor.
67 * @param string $output URI to output to, default = stdout
68 * @param boolean $indent Whether to indent output, default true
71 function __construct($output='php://output', $indent=true)
73 parent::__construct($output, $indent);
77 * Start an HTML document
79 * If $type isn't specified, will attempt to do content negotiation.
81 * Attempts to do content negotiation for language, also.
83 * @param string $type MIME type to use; default is to do negotation.
85 * @todo extract content negotiation code to an HTTP module or class.
90 function startHTML($type=null)
93 $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ?
94 $_SERVER['HTTP_ACCEPT'] : null;
96 // XXX: allow content negotiation for RDF, RSS, or XRDS
98 $cp = common_accept_to_prefs($httpaccept);
99 $sp = common_accept_to_prefs(PAGE_TYPE_PREFS);
101 $type = common_negotiate_type($cp, $sp);
104 throw new ClientException(_('This page is not available in a '.
105 'media type you accept'), 406);
109 header('Content-Type: '.$type.'; charset=UTF-8');
111 $this->extraHeaders();
112 if (preg_match("/.*\/.*xml/", $type)) {
113 // Required for XML documents
114 $this->xw->startDocument('1.0', 'UTF-8');
116 $this->xw->writeDTD('html',
117 '-//W3C//DTD XHTML 1.0 Strict//EN',
118 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd');
120 $language = $this->getLanguage();
122 $this->elementStart('html', array('xmlns' => 'http://www.w3.org/1999/xhtml',
123 'xml:lang' => $language,
124 'lang' => $language));
127 function getLanguage()
129 // FIXME: correct language for interface
130 return common_language();
134 * Ends an HTML document
140 $this->elementEnd('html');
145 * To specify additional HTTP headers for the action
149 function extraHeaders()
151 // Needs to be overloaded
155 * Output an HTML text input element
157 * Despite the name, it is specifically for outputting a
158 * text input element, not other <input> elements. It outputs
159 * a cluster of elements, including a <label> and an associated
162 * @param string $id element ID, must be unique on page
163 * @param string $label text of label for the element
164 * @param string $value value of the element, default null
165 * @param string $instructions instructions for valid input
167 * @todo add a $name parameter
168 * @todo add a $maxLength parameter
169 * @todo add a $size parameter
174 function input($id, $label, $value=null, $instructions=null)
176 $this->element('label', array('for' => $id), $label);
177 $attrs = array('name' => $id,
181 $attrs['value'] = $value;
183 $this->element('input', $attrs);
185 $this->element('p', 'form_guide', $instructions);
190 * output an HTML checkbox and associated elements
192 * Note that the value is default 'true' (the string), which can
193 * be used by Action::boolean()
195 * @param string $id element ID, must be unique on page
196 * @param string $label text of label for the element
197 * @param string $checked if the box is checked, default false
198 * @param string $instructions instructions for valid input
199 * @param string $value value of the checkbox, default 'true'
200 * @param string $disabled show the checkbox disabled, default false
204 * @todo add a $name parameter
207 function checkbox($id, $label, $checked=false, $instructions=null,
208 $value='true', $disabled=false)
210 $attrs = array('name' => $id,
211 'type' => 'checkbox',
212 'class' => 'checkbox',
215 $attrs['value'] = $value;
218 $attrs['checked'] = 'checked';
221 $attrs['disabled'] = 'true';
223 $this->element('input', $attrs);
225 $this->element('label', array('class' => 'checkbox',
230 $this->element('p', 'form_guide', $instructions);
235 * output an HTML combobox/select and associated elements
237 * $content is an array of key-value pairs for the dropdown, where
238 * the key is the option value attribute and the value is the option
239 * text. (Careful on the overuse of 'value' here.)
241 * @param string $id element ID, must be unique on page
242 * @param string $label text of label for the element
243 * @param array $content options array, value => text
244 * @param string $instructions instructions for valid input
245 * @param string $blank_select whether to have a blank entry, default false
246 * @param string $selected selected value, default null
250 * @todo add a $name parameter
253 function dropdown($id, $label, $content, $instructions=null,
254 $blank_select=false, $selected=null)
256 $this->element('label', array('for' => $id), $label);
257 $this->elementStart('select', array('id' => $id, 'name' => $id));
259 $this->element('option', array('value' => ''));
261 foreach ($content as $value => $option) {
262 if ($value == $selected) {
263 $this->element('option', array('value' => $value,
264 'selected' => 'selected'),
267 $this->element('option', array('value' => $value), $option);
270 $this->elementEnd('select');
272 $this->element('p', 'form_guide', $instructions);
277 * output an HTML hidden element
279 * $id is re-used as name
281 * @param string $id element ID, must be unique on page
282 * @param string $value hidden element value, default null
283 * @param string $name name, if different than ID
288 function hidden($id, $value, $name=null)
290 $this->element('input', array('name' => ($name) ? $name : $id,
297 * output an HTML password input and associated elements
299 * @param string $id element ID, must be unique on page
300 * @param string $label text of label for the element
301 * @param string $instructions instructions for valid input
305 * @todo add a $name parameter
308 function password($id, $label, $instructions=null)
310 $this->element('label', array('for' => $id), $label);
311 $attrs = array('name' => $id,
312 'type' => 'password',
313 'class' => 'password',
315 $this->element('input', $attrs);
317 $this->element('p', 'form_guide', $instructions);
322 * output an HTML submit input and associated elements
324 * @param string $id element ID, must be unique on page
325 * @param string $label text of the button
326 * @param string $cls class of the button, default 'submit'
327 * @param string $name name, if different than ID
331 * @todo add a $name parameter
334 function submit($id, $label, $cls='submit', $name=null, $title=null)
336 $this->element('input', array('type' => 'submit',
338 'name' => ($name) ? $name : $id,
345 * output a script (almost always javascript) tag
347 * @param string $src relative or absolute script path
348 * @param string $type 'type' attribute value of the tag
352 function script($src, $type='text/javascript')
354 $url = parse_url($src);
355 if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment))
357 $src = common_path($src) . '?version=' . STATUSNET_VERSION;
359 $this->element('script', array('type' => $type,
367 * @param string $src relative path within the theme directory, or an absolute path
368 * @param string $theme 'theme' that contains the stylesheet
369 * @param string media 'media' attribute of the tag
373 function cssLink($src,$theme=null,$media=null)
375 $url = parse_url($src);
376 if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment))
378 if(file_exists(theme_file($src,$theme))){
379 $src = theme_path($src, $theme) . '?version=' . STATUSNET_VERSION;
381 $src = common_path($src);
384 $this->element('link', array('rel' => 'stylesheet',
385 'type' => 'text/css',
391 * output an HTML textarea and associated elements
393 * @param string $id element ID, must be unique on page
394 * @param string $label text of label for the element
395 * @param string $content content of the textarea, default none
396 * @param string $instructions instructions for valid input
400 * @todo add a $name parameter
401 * @todo add a $cols parameter
402 * @todo add a $rows parameter
405 function textarea($id, $label, $content=null, $instructions=null)
407 $this->element('label', array('for' => $id), $label);
408 $this->element('textarea', array('rows' => 3,
412 ($content) ? $content : '');
414 $this->element('p', 'form_guide', $instructions);
420 * Internal script to autofocus the given element on page onload.
422 * @param string $id element ID, must refer to an existing element
427 function autofocus($id)
429 $this->elementStart('script', array('type' => 'text/javascript'));
430 $this->raw('/*<![CDATA[*/'.
431 ' $(document).ready(function() {'.
432 ' var el = $("#' . $id . '");'.
433 ' if (el.length) { el.focus(); }'.
436 $this->elementEnd('script');