Files
2024-07-15 11:28:08 +02:00

475 lines
10 KiB
PHP

<?php
/**
* @package awf
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Awf\Document;
use Awf\Container\Container;
use Awf\Document\Menu\MenuManager;
use Awf\Document\Toolbar\Toolbar;
/**
* Class Document
*
* Generic output document implementation
*
* @package Awf\Document
*/
abstract class Document
{
/** @var string The output data buffer */
protected $buffer = '';
/** @var array An array of all externally defined JavaScript files */
protected $scripts = array();
/** @var array An array of all inline JavaScript scripts */
protected $scriptDeclarations = array();
/** @var array An array of all external CSS files */
protected $styles = array();
/** @var array An array of all inline CSS styles */
protected $styleDeclarations = array();
/**
* Array of scripts options
*
* @var array
*/
protected $scriptOptions = array();
/** @var array Cache of all document instances known to us */
private static $instances = array();
/** @var MenuManager The menu manager for this document */
protected $menu;
/** @var Toolbar The toolbar for this document */
protected $toolbar;
/** @var Container The container this menu manager is attached to */
protected $container;
/** @var string The MIME type of the request */
protected $mimeType = 'text/html';
/** @var array Optional HTTP headers to send right before rendering */
protected $HTTPHeaders = array();
/** @var null|string The base name of the returned document. If set, the browser will initiate a download instead of displaying content inline. */
protected $name = null;
public function __construct(Container $container)
{
$viewPath = $container->basePath . '/View';
$viewPath_alt = $container->basePath . '/views';
$this->menu = new MenuManager($container);
$this->menu->initialiseFromDirectory($viewPath);
$this->menu->initialiseFromDirectory($viewPath_alt, false);
$this->toolbar = new Toolbar($container);
$this->container = $container;
}
/**
* Return the static instance of the document
*
* @param string $type The document type (html or json)
* @param Container $container The application to which the document is attached
* @param string $classPrefix The prefix of the document class to use
*
* @return \Awf\Document\Document
*/
public static function getInstance($type, Container $container, $classPrefix = '\\Awf')
{
if (!array_key_exists($type, self::$instances))
{
$className = $classPrefix . '\\Document\\' . ucfirst($type);
if (!class_exists($className))
{
$className = '\\Awf\\Document\\Html';
}
self::$instances[$type] = new $className($container);
}
return self::$instances[$type];
}
/**
* Sets the buffer (contains the main content of the HTML page or the entire JSON response)
*
* @param string $buffer
*
* @return \Awf\Document\Document
*/
public function setBuffer($buffer)
{
$this->buffer = $buffer;
return $this;
}
/**
* Returns the contents of the buffer
*
* @return string
*/
public function getBuffer()
{
return $this->buffer;
}
/**
* Adds an external script to the page
*
* @param string $url The URL of the script file
* @param boolean $before (optional) Should I add this before the template's scripts?
* @param string $type (optional) The MIME type of the script file
*
* @return \Awf\Document\Document
*/
public function addScript($url, $before = false, $type = "text/javascript")
{
$this->scripts[$url]['mime'] = $type;
$this->scripts[$url]['before'] = $before;
return $this;
}
/**
* Adds an inline script to the page's header
*
* @param string $content The contents of the script (without the script tag)
* @param string $type (optional) The MIME type of the script data
*
* @return \Awf\Document\Document
*/
public function addScriptDeclaration($content, $type = 'text/javascript')
{
if (!isset($this->scriptDeclarations[strtolower($type)]))
{
$this->scriptDeclarations[strtolower($type)] = $content;
}
else
{
$this->scriptDeclarations[strtolower($type)] .= chr(13) . $content;
}
return $this;
}
/**
* Add option for script
*
* @param string $key Name in Storage
* @param mixed $options Scrip options as array or string
* @param bool $merge Whether merge with existing (true) or replace (false)
*
* @return Document instance of $this to allow chaining
*/
public function addScriptOptions($key, $options, $merge = true)
{
if (empty($this->scriptOptions[$key]))
{
$this->scriptOptions[$key] = array();
}
if ($merge && is_array($options))
{
$this->scriptOptions[$key] = array_replace_recursive($this->scriptOptions[$key], $options);
}
else
{
$this->scriptOptions[$key] = $options;
}
return $this;
}
/**
* Get script(s) options
*
* @param string $key Name in Storage
*
* @return array Options for given $key, or all script options
*/
public function getScriptOptions($key = null)
{
if ($key)
{
return (empty($this->scriptOptions[$key])) ? array() : $this->scriptOptions[$key];
}
else
{
return $this->scriptOptions;
}
}
/**
* Adds an external stylesheet to the page
*
* @param string $url The URL of the stylesheet file
* @param boolean $before (optional) Should I add this before the template's scripts?
* @param string $type (optional) The MIME type of the stylesheet file
* @param string $media (optional) The media target of the stylesheet file
*
* @return \Awf\Document\Document
*/
public function addStyleSheet($url, $before = false, $type = 'text/css', $media = null)
{
$this->styles[$url]['mime'] = $type;
$this->styles[$url]['media'] = $media;
$this->styles[$url]['before'] = $before;
return $this;
}
/**
* Adds an inline stylesheet to the page's header
*
* @param string $content The contents of the stylesheet (without the style tag)
* @param string $type (optional) The MIME type of the stylesheet data
*
* @return \Awf\Document\Document
*/
public function addStyleDeclaration($content, $type = 'text/css')
{
if (!isset($this->styleDeclarations[strtolower($type)]))
{
$this->styleDeclarations[strtolower($type)] = $content;
}
else
{
$this->styleDeclarations[strtolower($type)] .= chr(13) . $content;
}
return $this;
}
/**
* Return the array with external scripts
*
* @return array
*/
public function getScripts()
{
return $this->scripts;
}
/**
* Return the array with script declarations
*
* @return array
*/
public function getScriptDeclarations()
{
return $this->scriptDeclarations;
}
/**
* Return the array with external stylesheets
*
* @return array
*/
public function getStyles()
{
return $this->styles;
}
/**
* Return the array with style declarations
*
* @return array
*/
public function getStyleDeclarations()
{
return $this->styleDeclarations;
}
/**
* Each document class implements its own renderer which outputs the buffer
* to the browser using the appropriate template.
*
* @return void
*/
abstract public function render();
/**
* Returns an instance of the menu manager
*
* @return MenuManager
*/
public function &getMenu()
{
return $this->menu;
}
/**
* Returns a reference to our Toolbar object
*
* @return Toolbar
*/
public function &getToolbar()
{
return $this->toolbar;
}
/**
* Returns a reference to our Application object
*
* @return \Awf\Application\Application
*/
public function getApplication()
{
return $this->container->application;
}
/**
* Returns a reference to our Container object
*
* @return \Awf\Container\Container
*/
public function getContainer()
{
return $this->container;
}
/**
* Set the MIME type of the document
*
* @param string $mimeType
*/
public function setMimeType($mimeType)
{
$this->mimeType = $mimeType;
}
/**
* Get the MIME type of the document
*
* @return string
*/
public function getMimeType()
{
return $this->mimeType;
}
/**
* Add an HTTP header
*
* @param string $header The HTTP header to add, e.g. Content-Type
* @param string $content The content of the HTTP header, e.g. text/plain
* @param boolean $overwrite Should I overwrite an existing header?
*
* @return void
*/
public function addHTTPHeader($header, $content, $overwrite = true)
{
if (!$overwrite && isset($this->HTTPHeaders[$header]))
{
return;
}
$this->HTTPHeaders[$header] = $content;
}
/**
* Remove an HTTP header if set
*
* @param string $header The header to remove, e.g. Content-Type
*
* @return void
*/
public function removeHTTPHeader($header)
{
if (isset($this->HTTPHeaders[$header]))
{
unset($this->HTTPHeaders[$header]);
}
}
/**
* Get the contents of an HTTP header defined in the document
*
* @param string $header The HTTP header to return
* @param string $default The default value if it's not already set
*
* @return string The HTTP header's value
*/
public function getHTTPHeader($header, $default = null)
{
if (isset($this->HTTPHeaders[$header]))
{
return $this->HTTPHeaders[$header];
}
else
{
return $default;
}
}
/**
* Returns the raw HTTP headers as a hash array
*
* @return array Key = header, value = header value.
*/
public function getHTTPHeaders()
{
return $this->HTTPHeaders;
}
/**
* Output the HTTP headers to the browser
*
* @return void
*/
public function outputHTTPHeaders()
{
if (!empty($this->HTTPHeaders) && !headers_sent())
{
foreach ($this->HTTPHeaders as $header => $value)
{
if (substr($header, 0, 5) == 'HTTP/')
{
header($header . ' ' . $value);
}
else
{
header($header . ': ' . $value);
}
}
}
}
/**
* Set the document's name
*
* @param null|string $name
*
* @return void
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get the document's name
*
* @return null|string
*/
public function getName()
{
return $this->name;
}
}