/lib/App/Web.php
PHP | 655 lines | 468 code | 48 blank | 139 comment | 30 complexity | 975ecbfad215fc9ca3b82fc42ee5ebfa MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
- <?php
- /**
- * App_Web extends an APP of CommandLine applications with knowledge of HTML
- * templates, understanding of pages and routing.
- */
- class App_Web extends App_CLI
- {
- /**
- * Cleaned up name of the currently requested page.
- */
- public $page = null;
- /**
- * Root page where URL will send when ('/') is encountered.
- *
- * @todo: make this work properly
- */
- public $index_page = 'index';
- /**
- * Recorded time when execution has started.
- */
- public $start_time = null;
- /**
- * Skin for web application templates.
- */
- public $skin;
- /**
- * Set a title of your application, will appear in <title> tag.
- */
- public $title;
- /**
- * Authentication object
- *
- * @see Auth_Basic::init()
- * @var Auth_Basic
- */
- public $auth;
- /**
- * jQuery object. Initializes only if you add jQuery in your app.
- *
- * @see jQuery::init()
- * @var jQuery
- */
- public $jquery;
- /**
- * jQuery UI object. Initializes only if you add jQuery UI in your app.
- *
- * @see jUI::init()
- * @var jUI
- */
- public $jui;
- /** @var App_Web */
- public $app;
- /** @var array For internal use */
- protected $included;
- /** @var array For internal use */
- protected $rendered;
- // {{{ Start-up
- public function __construct($realm = null, $skin = 'default', $options = array())
- {
- $this->start_time = time() + preg_replace('/ .*/', '', microtime());
- $this->skin = $skin;
- try {
- parent::__construct($realm, $options);
- } catch (Exception $e) {
- // This exception is used to abort initialisation of the objects,
- // but when normal rendering is still required
- if ($e instanceof Exception_Stop) {
- return;
- }
- $this->caughtException($e);
- }
- }
- /**
- * Executed before init, this method will initialize PageManager and
- * pathfinder.
- */
- public function _beforeInit()
- {
- $this->pm = $this->add($this->pagemanager_class, $this->pagemanager_options);
- /** @type Controller_PageManager $this->pm */
- $this->pm->parseRequestedURL();
- parent::_beforeInit();
- }
- /**
- * Redefine this function instead of default constructor.
- */
- public function init()
- {
- $this->getLogger();
- // Verify Licensing
- //$this->licenseCheck('atk4');
- // send headers, no caching
- $this->sendHeaders();
- $this->cleanMagicQuotes();
- parent::init();
- // in addition to default initialization, set up logger and template
- $this->initializeTemplate();
- if (get_class($this) == 'App_Web') {
- $this->setConfig(array('url_postfix' => '.php', 'url_prefix' => ''));
- }
- }
- /**
- * Magic Quotes were a design error. Let's strip them if they are enabled.
- */
- public function cleanMagicQuotes()
- {
- if (!function_exists('stripslashes_array')) {
- function stripslashes_array(&$array, $iterations = 0)
- {
- if ($iterations < 3) {
- foreach ($array as $key => $value) {
- if (is_array($value)) {
- stripslashes_array($array[$key], $iterations + 1);
- } else {
- $array[$key] = stripslashes($array[$key]);
- }
- }
- }
- }
- }
- if (get_magic_quotes_gpc()) {
- stripslashes_array($_GET);
- stripslashes_array($_POST);
- stripslashes_array($_COOKIE);
- }
- }
- /**
- * Sends default headers. Re-define to send your own headers.
- */
- public function sendHeaders()
- {
- header('Content-Type: text/html; charset=utf-8');
- header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
- header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); // always modified
- header('Cache-Control: no-store, no-cache, must-revalidate'); // HTTP/1.1
- header('Cache-Control: post-check=0, pre-check=0', false);
- header('Pragma: no-cache'); // HTTP/1.0
- }
- /**
- * Call this method if you want to see execution time on the bottom of your pages.
- */
- public function showExecutionTime()
- {
- $this->addHook('post-render-output', array($this, '_showExecutionTime'));
- $this->addHook('post-js-execute', array($this, '_showExecutionTimeJS'));
- }
- /** @ignore */
- public function _showExecutionTime()
- {
- echo 'Took '.(time() + microtime() - $this->start_time).'s';
- }
- /** @ignore */
- public function _showExecutionTimeJS()
- {
- echo "\n\n/* Took ".number_format(time() + microtime() - $this->start_time, 5).'s */';
- }
- // }}}
- // {{{ Obsolete
- /**
- * This method is called when exception was caught in the application.
- *
- * @param Exception $e
- */
- public function caughtException($e)
- {
- $this->hook('caught-exception', array($e));
- throw $e;
- /* unreachable code
- echo '<span style="color:red">Problem with your request.</span>';
- echo "<p>Please use 'Logger' class for more sophisticated output<br>\$app->add('Logger');</p>";
- exit;
- */
- }
- /**
- * @todo Description
- *
- * @param string $msg
- * @param int $shift
- *
- * @return bool|void
- */
- public function outputWarning($msg, $shift = 0)
- {
- if ($this->hook('output-warning', array($msg, $shift))) {
- return true;
- }
- echo '<span style="color:red">', $msg, '</span>';
- }
- /**
- * @todo Description
- *
- * @param string $msg
- * @param int $shift
- *
- * @return bool|void
- */
- public function outputDebug($msg, $shift = 0)
- {
- if ($this->hook('output-debug', array($msg, $shift))) {
- return true;
- }
- echo '<span style="color:blue">', $msg, '</font><br />';
- }
- // }}}
- // {{{ Sessions
- /**
- * Initializes existing or new session.
- *
- * Attempts to re-initialize session. If session is not found,
- * new one will be created, unless $create is set to false. Avoiding
- * session creation and placing cookies is to enhance user privacy.
- * Call to memorize() / recall() will automatically create session
- *
- * @param bool $create
- */
- public $_is_session_initialized = false;
- public function initializeSession($create = true)
- {
- if ($this->_is_session_initialized || session_id()) {
- return;
- }
- // Change settings if defined in settings file
- $params = session_get_cookie_params();
- $params['httponly'] = true; // true by default
- foreach ($params as $key => $default) {
- $params[$key] = $this->app->getConfig('session/'.$key, $default);
- }
- if ($create === false && !isset($_COOKIE[$this->name])) {
- return;
- }
- $this->_is_session_initialized = true;
- session_set_cookie_params(
- $params['lifetime'],
- $params['path'],
- $params['domain'],
- $params['secure'],
- $params['httponly']
- );
- session_name($this->name);
- session_start();
- }
- /**
- * Completely destroy existing session.
- */
- public function destroySession()
- {
- if ($this->_is_session_initialized) {
- $_SESSION = array();
- if (isset($_COOKIE[$this->name])) {
- setcookie($this->name/*session_name()*/, '', time() - 42000, '/');
- }
- session_destroy();
- $this->_is_session_initialized = false;
- }
- }
- // }}}
- // {{{ Sticky GET Argument implementation. Register stickyGET to have it appended to all generated URLs
- public $sticky_get_arguments = array();
- /**
- * Make current get argument with specified name automatically appended to all generated URLs.
- *
- * @param string $name
- *
- * @return string
- */
- public function stickyGet($name)
- {
- $this->sticky_get_arguments[$name] = @$_GET[$name];
- return $_GET[$name];
- }
- /**
- * Remove sticky GET which was set by stickyGET.
- *
- * @param string $name
- */
- public function stickyForget($name)
- {
- unset($this->sticky_get_arguments[$name]);
- }
- /** @ignore - used by URL class */
- public function getStickyArguments()
- {
- return $this->sticky_get_arguments;
- }
- /**
- * @todo Description
- *
- * @param string $name
- *
- * @return string
- */
- public function get($name)
- {
- return $_GET[$name];
- }
- /**
- * @todo Description
- *
- * @param string $file
- * @param string $ext
- * @param string $locate
- *
- * @return $this
- */
- public function addStylesheet($file, $ext = '.css', $locate = 'css')
- {
- //$file = $this->app->locateURL('css', $file . $ext);
- if (@$this->included[$locate.'-'.$file.$ext]++) {
- return;
- }
- if (strpos($file, 'http') !== 0 && $file[0] != '/') {
- $url = $this->locateURL($locate, $file.$ext);
- } else {
- $url = $file;
- }
- $this->template->appendHTML(
- 'js_include',
- '<link type="text/css" href="'.$url.'" rel="stylesheet" />'."\n"
- );
- return $this;
- }
- // }}}
- // {{{ Very Important Methods
- /**
- * Call this method from your index file. It is the main method of Agile Toolkit.
- */
- public function main()
- {
- try {
- // Initialize page and all elements from here
- $this->initLayout();
- } catch (Exception $e) {
- if (!($e instanceof Exception_Stop)) {
- return $this->caughtException($e);
- }
- //$this->caughtException($e);
- }
- try {
- $this->hook('post-init');
- $this->hook('afterInit');
- $this->hook('pre-exec');
- $this->hook('beforeExec');
- if (isset($_GET['submit']) && $_POST) {
- $this->hook('submitted');
- }
- $this->hook('post-submit');
- $this->hook('afterSubmit');
- $this->execute();
- } catch (Exception $e) {
- $this->caughtException($e);
- }
- $this->hook('saveDelayedModels');
- }
- /**
- * Main execution loop.
- */
- public function execute()
- {
- $this->rendered['sub-elements'] = array();
- try {
- $this->hook('pre-render');
- $this->hook('beforeRender');
- $this->recursiveRender();
- if (isset($_GET['cut_object'])) {
- throw new BaseException("Unable to cut object with name='".$_GET['cut_object']."'. ".
- "It wasn't initialized");
- }
- if (isset($_GET['cut_region'])) {
- // @todo Imants: This looks something obsolete. At least property cut_region_result is never defined.
- if (!$this->cut_region_result) {
- throw new BaseException("Unable to cut region with name='".$_GET['cut_region']."'");
- }
- echo $this->cut_region_result;
- return;
- }
- } catch (Exception $e) {
- if ($e instanceof Exception_Stop) {
- $this->hook('cut-output');
- if (isset($e->result)) {
- echo $e->result;
- }
- $this->hook('post-render-output');
- return;
- }
- throw $e;
- }
- }
- /**
- * Renders all objects inside applications and echo all output to the browser.
- */
- public function render()
- {
- $this->hook('pre-js-collection');
- if (isset($this->app->jquery) && $this->app->jquery) {
- $this->app->jquery->getJS($this);
- }
- if (!($this->template)) {
- throw new BaseException('You should specify template for APP object');
- }
- $this->hook('pre-render-output');
- if (headers_sent($file, $line)) {
- echo "<br />Direct output (echo or print) detected on $file:$line. <a target='_blank' "
- ."href='http://agiletoolkit.org/error/direct_output'>Use \$this->add('Text') instead</a>.<br />";
- }
- echo $this->template->render();
- $this->hook('post-render-output');
- }
- // }}}
- // {{{ Miscelanious Functions
- /**
- * Render only specified object or object with specified name.
- *
- * @param mixed $object
- *
- * @return $this
- */
- public function cut($object)
- {
- $_GET['cut_object'] = is_object($object) ? $object->name : $object;
- return $this;
- }
- /**
- * Perform instant redirect to another page.
- *
- * @param string $page
- * @param array $args
- */
- public function redirect($page = null, $args = array())
- {
- /*
- * Redirect to specified page. $args are $_GET arguments.
- * Use this function instead of issuing header("Location") stuff
- */
- $url = $this->url($page, $args);
- if ($this->app->isAjaxOutput()) {
- if ($_GET['cut_page']) {
- echo '<script>'.$this->app->js()->redirect($url).'</script>Redirecting page...';
- exit;
- } else {
- $this->app->js()->redirect($url)->execute();
- }
- }
- header('Location: '.$url);
- exit;
- }
- /**
- * Called on all templates in the system, populates some system-wide tags.
- *
- * @param Template $t
- */
- public function setTags($t)
- {
- // Determine Location to atk_public
- if ($this->app->pathfinder && $this->app->pathfinder->atk_public) {
- $q = $this->app->pathfinder->atk_public->getURL();
- } else {
- $q = 'http://www.agiletoolkit.org/';
- }
- $t->trySet('atk_path', $q.'/');
- $t->trySet('base_path', $q = $this->app->pm->base_path);
- // We are using new capability of SMlite to process tags individually
- try {
- $t->eachTag($tag = 'template', array($this, '_locateTemplate'));
- $t->eachTag($tag = 'public', array($this, '_locatePublic'));
- $t->eachTag($tag = 'js', array($this, '_locateJS'));
- $t->eachTag($tag = 'css', array($this, '_locateCSS'));
- $t->eachTag($tag = 'page', array($this, 'getBaseURL'));
- } catch (BaseException $e) {
- throw $e
- ->addMoreInfo('processing_tag', $tag)
- ->addMoreInfo('template', $t->template_file)
- ;
- }
- $this->hook('set-tags', array($t));
- }
- /**
- * Returns true if browser is going to EVAL output.
- *
- * @todo rename into isJSOutput();
- *
- * @return bool
- */
- public function isAjaxOutput()
- {
- return isset($_POST['ajax_submit']) || ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest');
- }
- /** @private - NO PRIVATE !!! */
- public function _locateTemplate($path)
- {
- return $this->locateURL('public', $path);
- }
- public function _locatePublic($path)
- {
- return $this->locateURL('public', $path);
- }
- public function _locateJS($path)
- {
- return $this->locateURL('js', $path);
- }
- public function _locateCSS($path)
- {
- return $this->locateURL('css', $path);
- }
- public function _locatePage($path)
- {
- return $this->url($path);
- }
- /**
- * Only show $object in the final rendering.
- *
- * @deprecated 4.4
- */
- public function renderOnly($object)
- {
- return $this->cut($object);
- }
- // }}}
- // {{{ Layout implementation
- protected $layout_initialized = false;
- /**
- * Implements Layouts.
- * Layout is region in shared template which may be replaced by object.
- */
- public function initLayout()
- {
- if ($this->layout_initialized) {
- throw $this->exception('Please do not call initLayout() directly from init()', 'Obsolete');
- }
- $this->layout_initialized = true;
- }
- // TODO: layouts need to be simplified and obsolete, because we have have other layouts now.
- // doc/layouts
- //
- /**
- * Register new layout, which, if has method and tag in the template, will be rendered.
- *
- * @param string $name
- *
- * @return $this
- */
- public function addLayout($name)
- {
- if (!$this->template) {
- return;
- }
- // TODO: change to functionExists()
- if (method_exists($this, $lfunc = 'layout_'.$name)) {
- if ($this->template->is_set($name)) {
- $this->$lfunc();
- }
- }
- return $this;
- }
- /**
- * Default handling of Content page. To be replaced by App_Frontend
- * This function initializes content. Content is page-dependant.
- */
- public function layout_Content()
- {
- $page = str_replace('/', '_', $this->page);
- if (method_exists($this, $pagefunc = 'page_'.$page)) {
- $p = $this->add('Page', $this->page, 'Content');
- $this->$pagefunc($p);
- } else {
- $this->app->locate('page', str_replace('_', '/', $this->page).'.php');
- $this->add('page_'.$page, $page, 'Content');
- //throw new BaseException("No such page: ".$this->page);
- }
- }
- /**
- * Default template for the application. Redefine to add your own rules.
- *
- * @return array|string
- */
- public function defaultTemplate()
- {
- return array('html');
- }
- // }}}
- }