]> git.mxchange.org Git - friendica.git/commitdiff
Add Page class for Frontend
authorPhilipp Holzer <admin+github@philipp.info>
Thu, 15 Aug 2019 18:52:42 +0000 (20:52 +0200)
committerPhilipp Holzer <admin+github@philipp.info>
Thu, 15 Aug 2019 18:52:42 +0000 (20:52 +0200)
- Move frontend logic (stylesheet, footerscript, content) to Page class

src/App.php
src/App/Page.php [new file with mode: 0644]
static/dependencies.config.php

index 749cbe9cc9ded105e4a49361c6c36507307ad4eb..e7381ab125419ea55ed2fcdab8418e0772a8affd 100644 (file)
@@ -5,11 +5,10 @@
 namespace Friendica;
 
 use Detection\MobileDetect;
-use DOMDocument;
-use DOMXPath;
 use Exception;
 use Friendica\App\Arguments;
 use Friendica\App\BaseURL;
+use Friendica\App\Page;
 use Friendica\Core\Config\Cache\ConfigCache;
 use Friendica\Core\Config\Configuration;
 use Friendica\Core\Config\PConfiguration;
@@ -44,6 +43,9 @@ class App
 {
        /** @deprecated 2019.09 - use App\Arguments->getQueryString() */
        public $query_string;
+       /**
+        * @var Page The current page environment
+        */
        public $page;
        public $profile;
        public $profile_uid;
@@ -77,9 +79,7 @@ class App
        public $videoheight             = 350;
        public $force_max_items         = 0;
        public $theme_events_in_profile = true;
-
-       public $stylesheets   = [];
-       public $footerScripts = [];
+       public $queue;
 
        /**
         * @var App\Mode The Mode of the Application
@@ -213,43 +213,23 @@ class App
        }
 
        /**
-        * Register a stylesheet file path to be included in the <head> tag of every page.
-        * Inclusion is done in App->initHead().
-        * The path can be absolute or relative to the Friendica installation base folder.
-        *
-        * @param string $path
-        *
-        * @see initHead()
-        *
+        * @deprecated 2019.09 - use Page->registerStylesheet instead
+        * @see Page::registerStylesheet()
         */
        public function registerStylesheet($path)
        {
-               if (mb_strpos($path, $this->getBasePath() . DIRECTORY_SEPARATOR) === 0) {
-                       $path = mb_substr($path, mb_strlen($this->getBasePath() . DIRECTORY_SEPARATOR));
-               }
-
-               $this->stylesheets[] = trim($path, '/');
+               $this->page->registerStylesheet($path);
        }
 
        /**
-        * Register a javascript file path to be included in the <footer> tag of every page.
-        * Inclusion is done in App->initFooter().
-        * The path can be absolute or relative to the Friendica installation base folder.
-        *
-        * @param string $path
-        *
-        * @see initFooter()
-        *
+        * @deprecated 2019.09 - use Page->registerFooterScript instead
+        * @see Page::registerFooterScript()
         */
        public function registerFooterScript($path)
        {
-               $url = str_replace($this->getBasePath() . DIRECTORY_SEPARATOR, '', $path);
-
-               $this->footerScripts[] = trim($url, '/');
+               $this->page->registerFooterScript($path);
        }
 
-       public $queue;
-
        /**
         * @param Database        $database     The Friendica Database
         * @param Configuration   $config       The Configuration
@@ -262,7 +242,7 @@ class App
         * @param App\Arguments   $args         The Friendica Arguments of the call
         * @param MobileDetect    $mobileDetect A mobile detection class
         */
-       public function __construct(Database $database, Configuration $config, App\Mode $mode, App\Router $router, BaseURL $baseURL, LoggerInterface $logger, Profiler $profiler, L10n $l10n, Arguments $args, App\Module $module, MobileDetect $mobileDetect)
+       public function __construct(Database $database, Configuration $config, App\Mode $mode, App\Router $router, BaseURL $baseURL, LoggerInterface $logger, Profiler $profiler, L10n $l10n, Arguments $args, App\Module $module, App\Page $page, MobileDetect $mobileDetect)
        {
                $this->database     = $database;
                $this->config       = $config;
@@ -280,25 +260,13 @@ class App
                $this->argc         = $args->getArgc();
                $this->query_string = $args->getQueryString();
                $this->module       = $module->getName();
+               $this->page = $page;
 
                $this->is_mobile = $mobileDetect->isMobile();
                $this->is_tablet = $mobileDetect->isTablet();
 
                $this->isAjax = strtolower(defaults($_SERVER, 'HTTP_X_REQUESTED_WITH', '')) == 'xmlhttprequest';
 
-               $this->page = [
-                       'aside'       => '',
-                       'bottom'      => '',
-                       'content'     => '',
-                       'footer'      => '',
-                       'htmlhead'    => '',
-                       'nav'         => '',
-                       'page_title'  => '',
-                       'right_aside' => '',
-                       'template'    => '',
-                       'title'       => ''
-               ];
-
                $this->load();
        }
 
@@ -418,119 +386,6 @@ class App
                return $this->baseURL->getUrlPath();
        }
 
-       /**
-        * Initializes App->page['htmlhead'].
-        *
-        * Includes:
-        * - Page title
-        * - Favicons
-        * - Registered stylesheets (through App->registerStylesheet())
-        * - Infinite scroll data
-        * - head.tpl template
-        */
-       private function initHead(App\Module $module, PConfiguration $pconfig)
-       {
-               $interval = ((local_user()) ? $pconfig->get(local_user(), 'system', 'update_interval') : 40000);
-
-               // If the update is 'deactivated' set it to the highest integer number (~24 days)
-               if ($interval < 0) {
-                       $interval = 2147483647;
-               }
-
-               if ($interval < 10000) {
-                       $interval = 40000;
-               }
-
-               // Default title: current module called
-               if (empty($this->page['title']) && $module->getName()) {
-                       $this->page['title'] = ucfirst($module->getName());
-               }
-
-               // Prepend the sitename to the page title
-               $this->page['title'] = $this->config->get('config', 'sitename', '') . (!empty($this->page['title']) ? ' | ' . $this->page['title'] : '');
-
-               if (!empty(Core\Renderer::$theme['stylesheet'])) {
-                       $stylesheet = Core\Renderer::$theme['stylesheet'];
-               } else {
-                       $stylesheet = $this->getCurrentThemeStylesheetPath();
-               }
-
-               $this->registerStylesheet($stylesheet);
-
-               $shortcut_icon = $this->config->get('system', 'shortcut_icon');
-               if ($shortcut_icon == '') {
-                       $shortcut_icon = 'images/friendica-32.png';
-               }
-
-               $touch_icon = $this->config->get('system', 'touch_icon');
-               if ($touch_icon == '') {
-                       $touch_icon = 'images/friendica-128.png';
-               }
-
-               Core\Hook::callAll('head', $this->page['htmlhead']);
-
-               $tpl = Core\Renderer::getMarkupTemplate('head.tpl');
-               /* put the head template at the beginning of page['htmlhead']
-                * since the code added by the modules frequently depends on it
-                * being first
-                */
-               $this->page['htmlhead'] = Core\Renderer::replaceMacros($tpl, [
-                               '$local_user'      => local_user(),
-                               '$generator'       => 'Friendica' . ' ' . FRIENDICA_VERSION,
-                               '$delitem'         => $this->l10n->t('Delete this item?'),
-                               '$update_interval' => $interval,
-                               '$shortcut_icon'   => $shortcut_icon,
-                               '$touch_icon'      => $touch_icon,
-                               '$block_public'    => intval($this->config->get('system', 'block_public')),
-                               '$stylesheets'     => $this->stylesheets,
-                       ]) . $this->page['htmlhead'];
-       }
-
-       /**
-        * Initializes App->page['footer'].
-        *
-        * Includes:
-        * - Javascript homebase
-        * - Mobile toggle link
-        * - Registered footer scripts (through App->registerFooterScript())
-        * - footer.tpl template
-        */
-       private function initFooter()
-       {
-               // If you're just visiting, let javascript take you home
-               if (!empty($_SESSION['visitor_home'])) {
-                       $homebase = $_SESSION['visitor_home'];
-               } elseif (local_user()) {
-                       $homebase = 'profile/' . $this->user['nickname'];
-               }
-
-               if (isset($homebase)) {
-                       $this->page['footer'] .= '<script>var homebase="' . $homebase . '";</script>' . "\n";
-               }
-
-               /*
-                * Add a "toggle mobile" link if we're using a mobile device
-                */
-               if ($this->is_mobile || $this->is_tablet) {
-                       if (isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) {
-                               $link = 'toggle_mobile?address=' . urlencode(curPageURL());
-                       } else {
-                               $link = 'toggle_mobile?off=1&address=' . urlencode(curPageURL());
-                       }
-                       $this->page['footer'] .= Core\Renderer::replaceMacros(Core\Renderer::getMarkupTemplate("toggle_mobile_footer.tpl"), [
-                               '$toggle_link' => $link,
-                               '$toggle_text' => $this->l10n->t('toggle mobile')
-                       ]);
-               }
-
-               Core\Hook::callAll('footer', $this->page['footer']);
-
-               $tpl                  = Core\Renderer::getMarkupTemplate('footer.tpl');
-               $this->page['footer'] = Core\Renderer::replaceMacros($tpl, [
-                               '$footerScripts' => $this->footerScripts,
-                       ]) . $this->page['footer'];
-       }
-
        /**
         * @brief      Removes the base url from an url. This avoids some mixed content problems.
         *
@@ -1034,9 +889,6 @@ class App
                                $this->internalRedirect('search');
                        }
 
-                       // Initialize module that can set the current theme in the init() method, either directly or via App->profile_uid
-                       $this->page['page_title'] = $moduleName;
-
                        // determine the module class and save it to the module instance
                        // @todo there's an implicit dependency due SESSION::start(), so it has to be called here (yet)
                        $module = $module->determineClass($this->args, $router, $this->config);
@@ -1048,124 +900,7 @@ class App
                        ModuleHTTPException::rawContent($e);
                }
 
-               $content = '';
-
-               try {
-                       $moduleClass = $module->getClassName();
-
-                       $arr = ['content' => $content];
-                       Core\Hook::callAll($moduleClass . '_mod_content', $arr);
-                       $content = $arr['content'];
-                       $arr     = ['content' => call_user_func([$moduleClass, 'content'])];
-                       Core\Hook::callAll($moduleClass . '_mod_aftercontent', $arr);
-                       $content .= $arr['content'];
-               } catch (HTTPException $e) {
-                       $content = ModuleHTTPException::content($e);
-               }
-
-               // initialise content region
-               if ($this->mode->isNormal()) {
-                       Core\Hook::callAll('page_content_top', $this->page['content']);
-               }
-
-               $this->page['content'] .= $content;
-
-               /* Create the page head after setting the language
-                * and getting any auth credentials.
-                *
-                * Moved initHead() and initFooter() to after
-                * all the module functions have executed so that all
-                * theme choices made by the modules can take effect.
-                */
-               $this->initHead($module, $pconfig);
-
-               /* Build the page ending -- this is stuff that goes right before
-                * the closing </body> tag
-                */
-               $this->initFooter();
-
-               if (!$this->isAjax()) {
-                       Core\Hook::callAll('page_end', $this->page['content']);
-               }
-
-               // Add the navigation (menu) template
-               if ($moduleName != 'install' && $moduleName != 'maintenance') {
-                       $this->page['htmlhead'] .= Core\Renderer::replaceMacros(Core\Renderer::getMarkupTemplate('nav_head.tpl'), []);
-                       $this->page['nav']      = Content\Nav::build($this);
-               }
-
-               // Build the page - now that we have all the components
-               if (isset($_GET["mode"]) && (($_GET["mode"] == "raw") || ($_GET["mode"] == "minimal"))) {
-                       $doc = new DOMDocument();
-
-                       $target = new DOMDocument();
-                       $target->loadXML("<root></root>");
-
-                       $content = mb_convert_encoding($this->page["content"], 'HTML-ENTITIES', "UTF-8");
-
-                       /// @TODO one day, kill those error-surpressing @ stuff, or PHP should ban it
-                       @$doc->loadHTML($content);
-
-                       $xpath = new DOMXPath($doc);
-
-                       $list = $xpath->query("//*[contains(@id,'tread-wrapper-')]");  /* */
-
-                       foreach ($list as $item) {
-                               $item = $target->importNode($item, true);
-
-                               // And then append it to the target
-                               $target->documentElement->appendChild($item);
-                       }
-
-                       if ($_GET["mode"] == "raw") {
-                               header("Content-type: text/html; charset=utf-8");
-
-                               echo substr($target->saveHTML(), 6, -8);
-
-                               exit();
-                       }
-               }
-
-               $page    = $this->page;
-               $profile = $this->profile;
-
-               header("X-Friendica-Version: " . FRIENDICA_VERSION);
-               header("Content-type: text/html; charset=utf-8");
-
-               if ($this->config->get('system', 'hsts') && ($this->baseURL->getSSLPolicy() == BaseURL::SSL_POLICY_FULL)) {
-                       header("Strict-Transport-Security: max-age=31536000");
-               }
-
-               // Some security stuff
-               header('X-Content-Type-Options: nosniff');
-               header('X-XSS-Protection: 1; mode=block');
-               header('X-Permitted-Cross-Domain-Policies: none');
-               header('X-Frame-Options: sameorigin');
-
-               // Things like embedded OSM maps don't work, when this is enabled
-               // header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' https: data:; media-src 'self' https:; child-src 'self' https:; object-src 'none'");
-
-               /* We use $_GET["mode"] for special page templates. So we will check if we have
-                * to load another page template than the default one.
-                * The page templates are located in /view/php/ or in the theme directory.
-                */
-               if (isset($_GET["mode"])) {
-                       $template = Core\Theme::getPathForFile($_GET["mode"] . '.php');
-               }
-
-               // If there is no page template use the default page template
-               if (empty($template)) {
-                       $template = Core\Theme::getPathForFile("default.php");
-               }
-
-               // Theme templates expect $a as an App instance
-               $a = $this;
-
-               // Used as is in view/php/default.php
-               $lang = $this->l10n->getCurrentLang();
-
-               /// @TODO Looks unsafe (remote-inclusion), is maybe not but Core\Theme::getPathForFile() uses file_exists() but does not escape anything
-               require_once $template;
+               $this->page->run($this, $this->baseURL, $this->mode, $module, $this->l10n, $this->config, $pconfig);
        }
 
        /**
diff --git a/src/App/Page.php b/src/App/Page.php
new file mode 100644 (file)
index 0000000..1fb927b
--- /dev/null
@@ -0,0 +1,466 @@
+<?php
+
+namespace Friendica\App;
+
+use ArrayAccess;
+use DOMDocument;
+use DOMXPath;
+use Friendica\App;
+use Friendica\Content\Nav;
+use Friendica\Core\Config\Configuration;
+use Friendica\Core\Config\PConfiguration;
+use Friendica\Core\Hook;
+use Friendica\Core\L10n\L10n;
+use Friendica\Core\Renderer;
+use Friendica\Core\Theme;
+use Friendica\Module\Special\HTTPException as ModuleHTTPException;
+use Friendica\Network\HTTPException;
+
+/**
+ * Contains the page specific environment variables for the current Page
+ * - Contains all stylesheets
+ * - Contains all footer-scripts
+ * - Contains all page specific content (header, footer, content, ...)
+ *
+ * The run() method is the single point where the page will get printed to the screen
+ */
+class Page implements ArrayAccess
+{
+       /**
+        * @var array Contains all stylesheets, which should get loaded during page
+        */
+       private $stylesheets;
+       /**
+        * @var array Contains all scripts, which are added to the footer at last
+        */
+       private $footerScripts;
+       /**
+        * @var array The page content, which are showed directly
+        */
+       private $page;
+       /**
+        * @var string The basepath of the page
+        */
+       private $basePath;
+
+       /**
+        * @param string $basepath The Page basepath
+        */
+       public function __construct(string $basepath)
+       {
+               $this->basePath = $basepath;
+
+               $this->page = [
+                       'aside'       => '',
+                       'bottom'      => '',
+                       'content'     => '',
+                       'footer'      => '',
+                       'htmlhead'    => '',
+                       'nav'         => '',
+                       'page_title'  => '',
+                       'right_aside' => '',
+                       'template'    => '',
+                       'title'       => ''
+               ];
+       }
+
+       /**
+        * Whether a offset exists
+        *
+        * @link  https://php.net/manual/en/arrayaccess.offsetexists.php
+        *
+        * @param mixed $offset <p>
+        *                      An offset to check for.
+        *                      </p>
+        *
+        * @return boolean true on success or false on failure.
+        * </p>
+        * <p>
+        * The return value will be casted to boolean if non-boolean was returned.
+        * @since 5.0.0
+        */
+       public function offsetExists($offset)
+       {
+               return isset($this->page[$offset]);
+       }
+
+       /**
+        * Offset to retrieve
+        *
+        * @link  https://php.net/manual/en/arrayaccess.offsetget.php
+        *
+        * @param mixed $offset <p>
+        *                      The offset to retrieve.
+        *                      </p>
+        *
+        * @return mixed Can return all value types.
+        * @since 5.0.0
+        */
+       public function offsetGet($offset)
+       {
+               return $this->page[$offset] ?? null;
+       }
+
+       /**
+        * Offset to set
+        *
+        * @link  https://php.net/manual/en/arrayaccess.offsetset.php
+        *
+        * @param mixed $offset <p>
+        *                      The offset to assign the value to.
+        *                      </p>
+        * @param mixed $value  <p>
+        *                      The value to set.
+        *                      </p>
+        *
+        * @return void
+        * @since 5.0.0
+        */
+       public function offsetSet($offset, $value)
+       {
+               $this->page[$offset] = $value;
+       }
+
+       /**
+        * Offset to unset
+        *
+        * @link  https://php.net/manual/en/arrayaccess.offsetunset.php
+        *
+        * @param mixed $offset <p>
+        *                      The offset to unset.
+        *                      </p>
+        *
+        * @return void
+        * @since 5.0.0
+        */
+       public function offsetUnset($offset)
+       {
+               if (isset($this->page[$offset])) {
+                       unset($this->page[$offset]);
+               }
+       }
+
+       /**
+        * Register a stylesheet file path to be included in the <head> tag of every page.
+        * Inclusion is done in App->initHead().
+        * The path can be absolute or relative to the Friendica installation base folder.
+        *
+        * @param string $path
+        *
+        * @see Page::initHead()
+        *
+        */
+       public function registerStylesheet($path)
+       {
+               if (mb_strpos($path, $this->basePath . DIRECTORY_SEPARATOR) === 0) {
+                       $path = mb_substr($path, mb_strlen($this->basePath . DIRECTORY_SEPARATOR));
+               }
+
+               $this->stylesheets[] = trim($path, '/');
+       }
+
+       /**
+        * Initializes Page->page['htmlhead'].
+        *
+        * Includes:
+        * - Page title
+        * - Favicons
+        * - Registered stylesheets (through App->registerStylesheet())
+        * - Infinite scroll data
+        * - head.tpl template
+        *
+        * @param App            $app     The Friendica App instance
+        * @param Module         $module  The loaded Friendica module
+        * @param L10n           $l10n    The l10n language instance
+        * @param Configuration  $config  The Friendica configuration
+        * @param PConfiguration $pConfig The Friendica personal configuration (for user)
+        *
+        * @throws HTTPException\InternalServerErrorException
+        */
+       private function initHead(App $app, Module $module, L10n $l10n, Configuration $config, PConfiguration $pConfig)
+       {
+               $interval = ((local_user()) ? $pConfig->get(local_user(), 'system', 'update_interval') : 40000);
+
+               // If the update is 'deactivated' set it to the highest integer number (~24 days)
+               if ($interval < 0) {
+                       $interval = 2147483647;
+               }
+
+               if ($interval < 10000) {
+                       $interval = 40000;
+               }
+
+               // Default title: current module called
+               if (empty($this->page['title']) && $module->getName()) {
+                       $this->page['title'] = ucfirst($module->getName());
+               }
+
+               // Prepend the sitename to the page title
+               $this->page['title'] = $config->get('config', 'sitename', '') . (!empty($this->page['title']) ? ' | ' . $this->page['title'] : '');
+
+               if (!empty(Renderer::$theme['stylesheet'])) {
+                       $stylesheet = Renderer::$theme['stylesheet'];
+               } else {
+                       $stylesheet = $app->getCurrentThemeStylesheetPath();
+               }
+
+               $this->registerStylesheet($stylesheet);
+
+               $shortcut_icon = $config->get('system', 'shortcut_icon');
+               if ($shortcut_icon == '') {
+                       $shortcut_icon = 'images/friendica-32.png';
+               }
+
+               $touch_icon = $config->get('system', 'touch_icon');
+               if ($touch_icon == '') {
+                       $touch_icon = 'images/friendica-128.png';
+               }
+
+               Hook::callAll('head', $this->page['htmlhead']);
+
+               $tpl = Renderer::getMarkupTemplate('head.tpl');
+               /* put the head template at the beginning of page['htmlhead']
+                * since the code added by the modules frequently depends on it
+                * being first
+                */
+               $this->page['htmlhead'] = Renderer::replaceMacros($tpl, [
+                               '$local_user'      => local_user(),
+                               '$generator'       => 'Friendica' . ' ' . FRIENDICA_VERSION,
+                               '$delitem'         => $l10n->t('Delete this item?'),
+                               '$update_interval' => $interval,
+                               '$shortcut_icon'   => $shortcut_icon,
+                               '$touch_icon'      => $touch_icon,
+                               '$block_public'    => intval($config->get('system', 'block_public')),
+                               '$stylesheets'     => $this->stylesheets,
+                       ]) . $this->page['htmlhead'];
+       }
+
+       /**
+        * Initializes Page->page['footer'].
+        *
+        * Includes:
+        * - Javascript homebase
+        * - Mobile toggle link
+        * - Registered footer scripts (through App->registerFooterScript())
+        * - footer.tpl template
+        *
+        * @param App  $app  The Friendica App instance
+        * @param L10n $l10n The l10n instance
+        *
+        * @throws HTTPException\InternalServerErrorException
+        */
+       private function initFooter(App $app, L10n $l10n)
+       {
+               // If you're just visiting, let javascript take you home
+               if (!empty($_SESSION['visitor_home'])) {
+                       $homebase = $_SESSION['visitor_home'];
+               } elseif (local_user()) {
+                       $homebase = 'profile/' . $app->user['nickname'];
+               }
+
+               if (isset($homebase)) {
+                       $this->page['footer'] .= '<script>var homebase="' . $homebase . '";</script>' . "\n";
+               }
+
+               /*
+                * Add a "toggle mobile" link if we're using a mobile device
+                */
+               if ($app->is_mobile || $app->is_tablet) {
+                       if (isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) {
+                               $link = 'toggle_mobile?address=' . urlencode(curPageURL());
+                       } else {
+                               $link = 'toggle_mobile?off=1&address=' . urlencode(curPageURL());
+                       }
+                       $this->page['footer'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate("toggle_mobile_footer.tpl"), [
+                               '$toggle_link' => $link,
+                               '$toggle_text' => $l10n->t('toggle mobile')
+                       ]);
+               }
+
+               Hook::callAll('footer', $this->page['footer']);
+
+               $tpl                  = Renderer::getMarkupTemplate('footer.tpl');
+               $this->page['footer'] = Renderer::replaceMacros($tpl, [
+                               '$footerScripts' => $this->footerScripts,
+                       ]) . $this->page['footer'];
+       }
+
+       /**
+        * Initializes Page->page['content'].
+        *
+        * Includes:
+        * - module content
+        * - hooks for content
+        *
+        * @param Module $module The module
+        * @param Mode   $mode   The Friendica execution mode
+        *
+        * @throws HTTPException\InternalServerErrorException
+        */
+       private function initContent(Module $module, Mode $mode)
+       {
+               $content = '';
+
+               try {
+                       $moduleClass = $module->getClassName();
+
+                       $arr = ['content' => $content];
+                       Hook::callAll($moduleClass . '_mod_content', $arr);
+                       $content = $arr['content'];
+                       $arr     = ['content' => call_user_func([$moduleClass, 'content'])];
+                       Hook::callAll($moduleClass . '_mod_aftercontent', $arr);
+                       $content .= $arr['content'];
+               } catch (HTTPException $e) {
+                       $content = ModuleHTTPException::content($e);
+               }
+
+               // initialise content region
+               if ($mode->isNormal()) {
+                       Hook::callAll('page_content_top', $this->page['content']);
+               }
+
+               $this->page['content'] .= $content;
+       }
+
+       /**
+        * Register a javascript file path to be included in the <footer> tag of every page.
+        * Inclusion is done in App->initFooter().
+        * The path can be absolute or relative to the Friendica installation base folder.
+        *
+        * @param string $path
+        *
+        * @see Page::initFooter()
+        *
+        */
+       public function registerFooterScript($path)
+       {
+               $url = str_replace($this->basePath . DIRECTORY_SEPARATOR, '', $path);
+
+               $this->footerScripts[] = trim($url, '/');
+       }
+
+       /**
+        * Executes the creation of the current page and prints it to the screen
+        *
+        * @param App            $app     The Friendica App
+        * @param BaseURL        $baseURL The Friendica Base URL
+        * @param Mode           $mode    The current node mode
+        * @param Module         $module  The loaded Friendica module
+        * @param L10n           $l10n    The l10n language class
+        * @param Configuration  $config  The Configuration of this node
+        * @param PConfiguration $pconfig The personal/user configuration
+        *
+        * @throws HTTPException\InternalServerErrorException
+        */
+       public function run(App $app, BaseURL $baseURL, Mode $mode, Module $module, L10n $l10n, Configuration $config, PConfiguration $pconfig)
+       {
+               $moduleName = $module->getName();
+
+               // Initialize module that can set the current theme in the init() method, either directly or via App->profile_uid
+               $this->page['page_title'] = $moduleName;
+
+               /* Create the page content.
+                * Calls all hooks which are including content operations
+                *
+                * Sets the $Page->page['content'] variable
+                */
+               $this->initContent($module, $mode);
+
+               /* Create the page head after setting the language
+                * and getting any auth credentials.
+                *
+                * Moved initHead() and initFooter() to after
+                * all the module functions have executed so that all
+                * theme choices made by the modules can take effect.
+                */
+               $this->initHead($app, $module, $l10n, $config, $pconfig);
+
+               /* Build the page ending -- this is stuff that goes right before
+                * the closing </body> tag
+                */
+               $this->initFooter($app, $l10n);
+
+               if (!$app->isAjax()) {
+                       Hook::callAll('page_end', $this->page['content']);
+               }
+
+               // Add the navigation (menu) template
+               if ($moduleName != 'install' && $moduleName != 'maintenance') {
+                       $this->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('nav_head.tpl'), []);
+                       $this->page['nav']      = Nav::build($app);
+               }
+
+               // Build the page - now that we have all the components
+               if (isset($_GET["mode"]) && (($_GET["mode"] == "raw") || ($_GET["mode"] == "minimal"))) {
+                       $doc = new DOMDocument();
+
+                       $target = new DOMDocument();
+                       $target->loadXML("<root></root>");
+
+                       $content = mb_convert_encoding($this->page["content"], 'HTML-ENTITIES', "UTF-8");
+
+                       /// @TODO one day, kill those error-surpressing @ stuff, or PHP should ban it
+                       @$doc->loadHTML($content);
+
+                       $xpath = new DOMXPath($doc);
+
+                       $list = $xpath->query("//*[contains(@id,'tread-wrapper-')]");  /* */
+
+                       foreach ($list as $item) {
+                               $item = $target->importNode($item, true);
+
+                               // And then append it to the target
+                               $target->documentElement->appendChild($item);
+                       }
+
+                       if ($_GET["mode"] == "raw") {
+                               header("Content-type: text/html; charset=utf-8");
+
+                               echo substr($target->saveHTML(), 6, -8);
+
+                               exit();
+                       }
+               }
+
+               $page    = $this->page;
+               $profile = $app->profile;
+
+               header("X-Friendica-Version: " . FRIENDICA_VERSION);
+               header("Content-type: text/html; charset=utf-8");
+
+               if ($config->get('system', 'hsts') && ($baseURL->getSSLPolicy() == BaseURL::SSL_POLICY_FULL)) {
+                       header("Strict-Transport-Security: max-age=31536000");
+               }
+
+               // Some security stuff
+               header('X-Content-Type-Options: nosniff');
+               header('X-XSS-Protection: 1; mode=block');
+               header('X-Permitted-Cross-Domain-Policies: none');
+               header('X-Frame-Options: sameorigin');
+
+               // Things like embedded OSM maps don't work, when this is enabled
+               // header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' https: data:; media-src 'self' https:; child-src 'self' https:; object-src 'none'");
+
+               /* We use $_GET["mode"] for special page templates. So we will check if we have
+                * to load another page template than the default one.
+                * The page templates are located in /view/php/ or in the theme directory.
+                */
+               if (isset($_GET["mode"])) {
+                       $template = Theme::getPathForFile($_GET["mode"] . '.php');
+               }
+
+               // If there is no page template use the default page template
+               if (empty($template)) {
+                       $template = Theme::getPathForFile("default.php");
+               }
+
+               // Theme templates expect $a as an App instance
+               $a = $this;
+
+               // Used as is in view/php/default.php
+               $lang = $l10n->getCurrentLang();
+
+               /// @TODO Looks unsafe (remote-inclusion), is maybe not but Core\Theme::getPathForFile() uses file_exists() but does not escape anything
+               require_once $template;
+       }
+}
index e32f99a60ca9a0ee3f7ce5ce54f19f332b49969a..d923c67bf2155ea4f70fdbb64bdc876d3b455478 100644 (file)
@@ -95,6 +95,11 @@ return [
                        $_SERVER,
                ],
        ],
+       App\Page::class => [
+               'constructParams' => [
+                       [Dice::INSTANCE => '$basepath'],
+               ],
+       ],
        /**
         * Create a Logger, which implements the LoggerInterface
         *