. * * @category Output * @package StatusNet * @author Evan Prodromou * @author Sarven Capadisli * @copyright 2008 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ if (!defined('GNUSOCIAL')) { exit(1); } // Can include XHTML options but these are too fragile in practice. define('PAGE_TYPE_PREFS', 'text/html'); /** * Low-level generator for HTML * * Abstracts some of the code necessary for HTML generation. Especially * has methods for generating HTML form elements. Note that these have * been created kind of haphazardly, not with an eye to making a general * HTML-creation class. * * @category Output * @package StatusNet * @author Evan Prodromou * @author Sarven Capadisli * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ * * @see Action * @see XMLOutputter */ class HTMLOutputter extends XMLOutputter { protected $DTD = ['doctype' => 'html', 'spec' => '-//W3C//DTD XHTML 1.0 Strict//EN', 'uri' => 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd']; /** * Constructor * * Just wraps the XMLOutputter constructor. * * @param string $output URI to output to, default = stdout * @param boolean $indent Whether to indent output, default true */ public function __construct($output = 'php://output', $indent = null) { parent::__construct($output, $indent); } /** * Start an HTML document * * If $type isn't specified, will attempt to do content negotiation. * * Attempts to do content negotiation for language, also. * * @param string $type MIME type to use; default is to do negotation. * * @return void * @throws ClientException * @todo extract content negotiation code to an HTTP module or class. * */ public function startHTML($type = null) { if (!$type) { $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : null; // XXX: allow content negotiation for RDF, RSS, or XRDS $cp = common_accept_to_prefs($httpaccept); $sp = common_accept_to_prefs(PAGE_TYPE_PREFS); $type = common_negotiate_type($cp, $sp); if (!$type) { // TRANS: Client exception 406 throw new ClientException(_('This page is not available in a ' . 'media type you accept'), 406); } } header('Content-Type: ' . $type); // Output anti-framing headers to prevent clickjacking (respected by newer // browsers). if (common_config('javascript', 'bustframes')) { header('X-XSS-Protection: 1; mode=block'); // detect XSS Reflection attacks header('X-Frame-Options: SAMEORIGIN'); // no rendering if origin mismatch } $this->extraHeaders(); if (preg_match("/.*\/.*xml/", $type)) { // Required for XML documents $this->startXML(); } $this->writeDTD(); $language = $this->getLanguage(); $attrs = [ 'xmlns' => 'http://www.w3.org/1999/xhtml', 'xml:lang' => $language, 'lang' => $language ]; if (Event::handle('StartHtmlElement', [$this, &$attrs])) { $this->elementStart('html', $attrs); Event::handle('EndHtmlElement', [$this, &$attrs]); } } /** * To specify additional HTTP headers for the action * * @return void */ public function extraHeaders() { // Needs to be overloaded } protected function writeDTD() { $this->xw->writeDTD( $this->DTD['doctype'], $this->DTD['spec'], $this->DTD['uri'] ); } public function getLanguage() { // FIXME: correct language for interface return common_language(); } public function setDTD($doctype, $spec, $uri) { $this->DTD = ['doctype' => $doctype, 'spec' => $spec, 'uri' => $uri]; } /** * Ends an HTML document * * @return void */ public function endHTML() { $this->elementEnd('html'); $this->endXML(); } /** * Output an HTML text input element * * Despite the name, it is specifically for outputting a * text input element, not other elements. It outputs * a cluster of elements, including a