1 <?php
  2 
  3 /**
  4  * @package redaxo\core\backend
  5  */
  6 class rex_be_controller
  7 {
  8     /**
  9      * @var string
 10      */
 11     private static $page;
 12 
 13     /**
 14      * @var array
 15      */
 16     private static $pageParts = [];
 17 
 18     /**
 19      * @var rex_be_page
 20      */
 21     private static $pageObject;
 22 
 23     /**
 24      * @var rex_be_page[]
 25      */
 26     private static $pages = [];
 27 
 28     /**
 29      * @param string $page
 30      */
 31     public static function setCurrentPage($page)
 32     {
 33         self::$page = trim($page, '/ ');
 34         self::$pageParts = explode('/', self::$page);
 35         self::$pageObject = null;
 36     }
 37 
 38     /**
 39      * @return string
 40      */
 41     public static function getCurrentPage()
 42     {
 43         return self::$page;
 44     }
 45 
 46     /**
 47      * @param null|int    $part    Part index, beginning with 1. If $part is null, an array of all current parts will be returned
 48      * @param null|string $default Default value
 49      *
 50      * @return array|string|null
 51      */
 52     public static function getCurrentPagePart($part = null, $default = null)
 53     {
 54         if ($part === null) {
 55             return self::$pageParts;
 56         }
 57         --$part;
 58         return isset(self::$pageParts[$part]) ? self::$pageParts[$part] : $default;
 59     }
 60 
 61     /**
 62      * @return rex_be_page
 63      */
 64     public static function getCurrentPageObject()
 65     {
 66         if (!self::$pageObject) {
 67             self::$pageObject = self::getPageObject(self::getCurrentPage());
 68         }
 69         return self::$pageObject;
 70     }
 71 
 72     /**
 73      * @param string|array $page
 74      *
 75      * @return rex_be_page
 76      */
 77     public static function getPageObject($page)
 78     {
 79         if (!is_array($page)) {
 80             $page = explode('/', $page);
 81         }
 82         if (!isset($page[0]) || !isset(self::$pages[$page[0]])) {
 83             return null;
 84         }
 85         $obj = self::$pages[$page[0]];
 86         for ($i = 1, $count = count($page); $i < $count; ++$i) {
 87             if ($new = $obj->getSubpage($page[$i])) {
 88                 $obj = $new;
 89             } else {
 90                 return null;
 91             }
 92         }
 93         return $obj;
 94     }
 95 
 96     /**
 97      * @return rex_be_page[]
 98      */
 99     public static function getPages()
100     {
101         return self::$pages;
102     }
103 
104     /**
105      * @param rex_be_page[] $pages
106      */
107     public static function setPages(array $pages)
108     {
109         self::$pages = $pages;
110     }
111 
112     public static function getPageTitle()
113     {
114         $parts = [];
115 
116         $activePageObj = self::getCurrentPageObject();
117         if ($activePageObj->getTitle()) {
118             $parts[] = $activePageObj->getTitle();
119         }
120         if (rex::getServerName()) {
121             $parts[] = rex::getServerName();
122         }
123         $parts[] = 'REDAXO CMS';
124 
125         return implode(' ยท ', $parts);
126     }
127 
128     public static function getSetupPage()
129     {
130         $page = new rex_be_page('setup', rex_i18n::msg('setup'));
131         $page->setPath(rex_path::core('pages/setup.php'));
132         return $page;
133     }
134 
135     public static function getLoginPage()
136     {
137         $page = new rex_be_page('login', 'Login');
138         $page->setPath(rex_path::core('pages/login.php'));
139         $page->setHasNavigation(false);
140         return $page;
141     }
142 
143     public static function appendLoggedInPages()
144     {
145         self::$pages['profile'] = (new rex_be_page('profile', rex_i18n::msg('profile')))
146             ->setPath(rex_path::core('pages/profile.php'))
147             ->setPjax();
148 
149         self::$pages['credits'] = (new rex_be_page('credits', rex_i18n::msg('credits')))
150             ->setPath(rex_path::core('pages/credits.php'));
151 
152         self::$pages['packages'] = (new rex_be_page_main('system', 'packages', rex_i18n::msg('addons')))
153             ->setPath(rex_path::core('pages/packages.php'))
154             ->setRequiredPermissions('isAdmin')
155             ->setPrio(60)
156             ->setPjax()
157             ->setIcon('rex-icon rex-icon-package-addon');
158 
159         $logsPage = (new rex_be_page('log', rex_i18n::msg('logfiles')))->setSubPath(rex_path::core('pages/system.log.php'));
160         $logsPage->addSubpage((new rex_be_page('redaxo', rex_i18n::msg('syslog_redaxo')))->setSubPath(rex_path::core('pages/system.log.redaxo.php')));
161         if (is_readable(ini_get('error_log'))) {
162             $logsPage->addSubpage((new rex_be_page('php', rex_i18n::msg('syslog_phperrors')))->setSubPath(rex_path::core('pages/system.log.external.php')));
163         }
164 
165         self::$pages['system'] = (new rex_be_page_main('system', 'system', rex_i18n::msg('system')))
166             ->setPath(rex_path::core('pages/system.php'))
167             ->setRequiredPermissions('isAdmin')
168             ->setPrio(70)
169             ->setPjax()
170             ->setIcon('rex-icon rex-icon-system')
171             ->addSubpage((new rex_be_page('settings', rex_i18n::msg('main_preferences')))->setSubPath(rex_path::core('pages/system.settings.php')))
172             ->addSubpage((new rex_be_page('lang', rex_i18n::msg('languages')))->setSubPath(rex_path::core('pages/system.clangs.php')))
173             ->addSubpage($logsPage)
174             ->addSubpage((new rex_be_page('report', rex_i18n::msg('system_report')))
175                 ->addSubpage((new rex_be_page('html', rex_i18n::msg('system_report')))->setSubPath(rex_path::core('pages/system.report.html.php')))
176                 ->addSubpage((new rex_be_page('markdown', rex_i18n::msg('system_report_markdown')))->setSubPath(rex_path::core('pages/system.report.markdown.php')))
177             )
178             ->addSubpage((new rex_be_page('phpinfo', 'phpinfo'))
179                 ->setHidden(true)
180                 ->setHasLayout(false)
181                 ->setPath(rex_path::core('pages/system.phpinfo.php'))
182             );
183     }
184 
185     public static function appendPackagePages()
186     {
187         $insertPages = [];
188         $addons = rex::isSafeMode() ? rex_addon::getSetupAddons() : rex_addon::getAvailableAddons();
189         foreach ($addons as $addon) {
190             $mainPage = self::pageCreate($addon->getProperty('page'), $addon, true);
191 
192             if (is_array($pages = $addon->getProperty('pages'))) {
193                 foreach ($pages as $key => $page) {
194                     if (strpos($key, '/') !== false) {
195                         $insertPages[$key] = [$addon, $page];
196                     } else {
197                         self::pageCreate($page, $addon, false, $mainPage, $key, true);
198                     }
199                 }
200             }
201 
202             // handle plugins
203             $plugins = rex::isSafeMode() ? $addon->getSystemPlugins() : $addon->getAvailablePlugins();
204             foreach ($plugins as $plugin) {
205                 self::pageCreate($plugin->getProperty('page'), $plugin, false, $mainPage);
206 
207                 if (is_array($pages = $plugin->getProperty('pages'))) {
208                     foreach ($pages as $key => $page) {
209                         if (strpos($key, '/') !== false) {
210                             $insertPages[$key] = [$plugin, $page];
211                         } else {
212                             self::pageCreate($page, $plugin, false, $mainPage, $key, true);
213                         }
214                     }
215                 }
216             }
217         }
218         foreach ($insertPages as $key => $packagePage) {
219             list($package, $page) = $packagePage;
220             $key = explode('/', $key);
221             if (!isset(self::$pages[$key[0]])) {
222                 continue;
223             }
224             $parentPage = self::$pages[$key[0]];
225             for ($i = 1, $count = count($key) - 1; $i < $count && $parentPage; ++$i) {
226                 $parentPage = $parentPage->getSubpage($key[$i]);
227             }
228             if ($parentPage) {
229                 self::pageCreate($page, $package, false, $parentPage, $key[$i], strtr($parentPage->getFullKey(), '/', '.') . '.' . $key[$i] . '.');
230             }
231         }
232     }
233 
234     /**
235      * @param rex_be_page|array $page
236      * @param rex_package       $package
237      * @param bool              $createMainPage
238      * @param rex_be_page|null  $parentPage
239      * @param string            $pageKey
240      * @param bool              $prefix
241      *
242      * @return null|rex_be_page
243      */
244     private static function pageCreate($page, rex_package $package, $createMainPage, rex_be_page $parentPage = null, $pageKey = null, $prefix = false)
245     {
246         if (is_array($page) && isset($page['title'])) {
247             $pageArray = $page;
248             $pageKey = $pageKey ?: $package->getName();
249             if ($createMainPage || isset($pageArray['main']) && $pageArray['main']) {
250                 $page = new rex_be_page_main('addons', $pageKey, $pageArray['title']);
251             } else {
252                 $page = new rex_be_page($pageKey, $pageArray['title']);
253             }
254             self::pageAddProperties($page, $pageArray, $package);
255         }
256 
257         if ($page instanceof rex_be_page) {
258             if (!is_string($prefix)) {
259                 $prefix = $prefix ? $page->getKey() . '.' : '';
260             }
261             if ($page instanceof rex_be_page_main) {
262                 if (!$page->hasPath()) {
263                     $page->setPath($package->getPath('pages/' . ($prefix ?: 'index.') . 'php'));
264                 }
265                 self::$pages[$page->getKey()] = $page;
266             } else {
267                 if (!$page->hasSubPath()) {
268                     $page->setSubPath($package->getPath('pages/' . ($prefix ?: 'index.') . 'php'));
269                 }
270                 if ($parentPage) {
271                     $parentPage->addSubpage($page);
272                 }
273             }
274             self::pageSetSubPaths($page, $package, $prefix);
275             return $page;
276         }
277         return null;
278     }
279 
280     /**
281      * @param rex_be_page $page
282      * @param rex_package $package
283      * @param string      $prefix
284      */
285     private static function pageSetSubPaths(rex_be_page $page, rex_package $package, $prefix = '')
286     {
287         foreach ($page->getSubpages() as $subpage) {
288             if (!$subpage->hasSubPath()) {
289                 $subpage->setSubPath($package->getPath('pages/' . $prefix . $subpage->getKey() . '.php'));
290             }
291             self::pageSetSubPaths($subpage, $package, $prefix . $subpage->getKey() . '.');
292         }
293     }
294 
295     /**
296      * @param rex_be_page $page
297      * @param array       $properties
298      * @param rex_package $package
299      */
300     private static function pageAddProperties(rex_be_page $page, array $properties, rex_package $package)
301     {
302         foreach ($properties as $key => $value) {
303             switch (strtolower($key)) {
304                 case 'subpages':
305                     if (is_array($value)) {
306                         foreach ($value as $pageKey => $subProperties) {
307                             if (isset($subProperties['title'])) {
308                                 $subpage = new rex_be_page($pageKey, $subProperties['title']);
309                                 $page->addSubpage($subpage);
310                                 self::pageAddProperties($subpage, $subProperties, $package);
311                             }
312                         }
313                     }
314                     break;
315 
316                 case 'itemattr':
317                 case 'linkattr':
318                     $setter = [$page, 'set' . ucfirst($key)];
319                     foreach ($value as $k => $v) {
320                         call_user_func($setter, $k, $v);
321                     }
322                     break;
323 
324                 case 'perm':
325                     $page->setRequiredPermissions($value);
326                     break;
327 
328                 case 'path':
329                 case 'subpath':
330                     if (file_exists($path = $package->getPath($value))) {
331                         $value = $path;
332                     }
333                     // no break
334                 default:
335                     $setter = [$page, 'add' . ucfirst($key)];
336                     if (is_callable($setter)) {
337                         foreach ((array) $value as $v) {
338                             call_user_func($setter, $v);
339                         }
340                         break;
341                     }
342                     $setter = [$page, 'set' . ucfirst($key)];
343                     if (is_callable($setter)) {
344                         call_user_func($setter, $value);
345                     }
346             }
347         }
348     }
349 
350     public static function checkPagePermissions(rex_user $user)
351     {
352         $check = function (rex_be_page $page) use (&$check, $user) {
353             if (!$page->checkPermission($user)) {
354                 return false;
355             }
356 
357             $subpages = $page->getSubpages();
358             foreach ($subpages as $key => $subpage) {
359                 if (!$check($subpage)) {
360                     unset($subpages[$key]);
361                 }
362             }
363             $page->setSubpages($subpages);
364 
365             return true;
366         };
367 
368         foreach (self::$pages as $key => $page) {
369             if (!$check($page)) {
370                 unset(self::$pages[$key]);
371             }
372         }
373         self::$pageObject = null;
374 
375         $page = self::getCurrentPageObject();
376         // --- page pruefen und benoetigte rechte checken
377         if (!$page) {
378             // --- fallback zur user startpage -> rechte checken
379             $page = self::getPageObject($user->getStartPage());
380             if (!$page) {
381                 // --- fallback zur system startpage -> rechte checken
382                 $page = self::getPageObject(rex::getProperty('start_page'));
383                 if (!$page) {
384                     // --- fallback zur profile page
385                     $page = self::getPageObject('profile');
386                 }
387             }
388             rex_response::setStatus(rex_response::HTTP_NOT_FOUND);
389             rex_response::sendRedirect($page->getHref());
390         }
391         if ($page !== $leaf = $page->getFirstSubpagesLeaf()) {
392             rex_response::setStatus(rex_response::HTTP_MOVED_PERMANENTLY);
393             $url = $leaf->hasHref() ? $leaf->getHref() : rex_context::fromGet()->getUrl(['page' => $leaf->getFullKey()], false);
394             rex_response::sendRedirect($url);
395         }
396     }
397 
398     /**
399      * Includes the current page. A page may be provided by the core, an addon or plugin.
400      */
401     public static function includeCurrentPage()
402     {
403         $currentPage = self::getCurrentPageObject();
404 
405         if (rex_request::isPJAXRequest() && !rex_request::isPJAXContainer('#rex-js-page-container')) {
406             // non-core pjax containers should not have a layout.
407             // they render their whole response on their own
408             $currentPage->setHasLayout(false);
409         }
410 
411         require rex_path::core('layout/top.php');
412 
413         self::includePath($currentPage->getPath());
414 
415         require rex_path::core('layout/bottom.php');
416     }
417 
418     /**
419      * Includes the sub-path of current page.
420      *
421      * @param array $context
422      *
423      * @return mixed
424      */
425     public static function includeCurrentPageSubPath(array $context = [])
426     {
427         $path = self::getCurrentPageObject()->getSubPath();
428 
429         if ('.md' !== strtolower(substr($path, -3))) {
430             return self::includePath($path, $context);
431         }
432 
433         $languagePath = substr($path, 0, -3).'.'.rex_i18n::getLanguage().'.md';
434         if (is_readable($languagePath)) {
435             $path = $languagePath;
436         }
437 
438         list($toc, $content) = rex_markdown::factory()->parseWithToc(rex_file::get($path));
439         $fragment = new rex_fragment();
440         $fragment->setVar('content', $content, false);
441         $fragment->setVar('toc', $toc, false);
442         $content = $fragment->parse('core/page/docs.php');
443 
444         $fragment = new rex_fragment();
445         $fragment->setVar('title', self::getCurrentPageObject()->getTitle(), false);
446         $fragment->setVar('body', $content, false);
447         echo $fragment->parse('core/page/section.php');
448     }
449 
450     /**
451      * Includes a path in correct package context.
452      *
453      * @param string $path
454      * @param array  $context
455      *
456      * @return mixed
457      */
458     private static function includePath($path, array $context = [])
459     {
460         $pattern = '@' . preg_quote(rex_path::src('addons/'), '@') . '([^/\\\]+)(?:[/\\\]plugins[/\\\]([^/\\\]+))?@';
461 
462         if (!preg_match($pattern, $path, $matches)) {
463             $__context = $context;
464             $__path = $path;
465             unset($context, $path, $pattern, $matches);
466             extract($__context, EXTR_SKIP);
467             return include $__path;
468         }
469 
470         $package = rex_addon::get($matches[1]);
471         if (isset($matches[2])) {
472             $package = $package->getPlugin($matches[2]);
473         }
474         return $package->includeFile(str_replace($package->getPath(), '', $path), $context);
475     }
476 }
477