. /** * Low-level generator for HTML * * @package GNUsocial * @category Output * * @author Evan Prodromou * @author Sarven Capadisli * @copyright 2008-2019 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ defined('GNUSOCIAL') || die(); // 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. * * @see Action * @see XMLOutputter * * @copyright 2008-2019 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ 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 bool|null $indent Whether to indent output, if null it defaults to true * * @throws ServerException */ 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 null|string $type MIME type to use; default is to do negotation. * * @throws ClientException * @throws ServerException * * @return void * * @todo extract content negotiation code to an HTTP module or class. */ public function startHTML(?string $type = null): void { 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 } /** * @return void */ protected function writeDTD(): void { $this->xw->writeDTD( $this->DTD['doctype'], $this->DTD['spec'], $this->DTD['uri'] ); } /** * @return mixed (array|bool|string) */ public function getLanguage() { // FIXME: correct language for interface return common_language(); } /** * @param $doctype * @param $spec * @param $uri */ public function setDTD($doctype, $spec, $uri): void { $this->DTD = ['doctype' => $doctype, 'spec' => $spec, 'uri' => $uri]; } /** * Ends an HTML document * * @return void */ public function endHTML(): void { $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