* use Icinga\Application\Web; * Web::start(); * */ class Web extends EmbeddedWeb { /** * View object * * @var View */ private $viewRenderer; /** * Zend front controller instance * * @var Zend_Controller_Front */ private $frontController; /** * Session object * * @var BaseSession */ private $session; /** * User object * * @var User */ private $user; /** * Identify web bootstrap * * @var bool */ protected $isWeb = true; /** * Initialize all together * * @return $this */ protected function bootstrap() { return $this ->setupLogging() ->setupErrorHandling() ->loadLibraries() ->loadConfig() ->setupLogger() ->setupRequest() ->setupSession() ->setupNotifications() ->setupResponse() ->setupZendMvc() ->prepareInternationalization() ->setupModuleManager() ->loadSetupModuleIfNecessary() ->loadEnabledModules() ->setupRoute() ->setupPagination() ->setupUserBackendFactory() ->setupUser() ->setupTimezone() ->setupInternationalization() ->setupFatalErrorHandling(); } /** * Get themes provided by Web 2 and all enabled modules * * @return string[] Array of theme names as keys and values */ public function getThemes() { $themes = array(StyleSheet::DEFAULT_THEME); $applicationThemePath = $this->getBaseDir('public/css/themes'); if (DirectoryIterator::isReadable($applicationThemePath)) { foreach (new DirectoryIterator($applicationThemePath, 'less') as $name => $theme) { $themes[] = substr($name, 0, -5); } } $mm = $this->getModuleManager(); foreach ($mm->listEnabledModules() as $moduleName) { $moduleThemePath = $mm->getModule($moduleName)->getCssDir() . '/themes'; if (! DirectoryIterator::isReadable($moduleThemePath)) { continue; } foreach (new DirectoryIterator($moduleThemePath, 'less') as $name => $theme) { $themes[] = $moduleName . '/' . substr($name, 0, -5); } } return array_combine($themes, $themes); } /** * Prepare routing * * @return $this */ private function setupRoute() { $this->frontController->getRouter()->addRoute( 'module_javascript', new Zend_Controller_Router_Route( 'js/components/:module_name/:file', array( 'controller' => 'static', 'action' => 'javascript' ) ) ); return $this; } /** * Getter for frontController * * @return Zend_Controller_Front */ public function getFrontController() { return $this->frontController; } /** * Getter for view * * @return View */ public function getViewRenderer() { return $this->viewRenderer; } private function hasAccessToSharedNavigationItem(&$config, Config $navConfig) { // TODO: Provide a more sophisticated solution if (isset($config['owner']) && strtolower($config['owner']) === strtolower($this->user->getUsername())) { unset($config['owner']); unset($config['users']); unset($config['groups']); return true; } if (isset($config['parent']) && $navConfig->hasSection($config['parent'])) { unset($config['owner']); if (isset($this->accessibleMenuItems[$config['parent']])) { return $this->accessibleMenuItems[$config['parent']]; } $parentConfig = $navConfig->getSection($config['parent']); $this->accessibleMenuItems[$config['parent']] = $this->hasAccessToSharedNavigationItem( $parentConfig, $navConfig ); return $this->accessibleMenuItems[$config['parent']]; } if (isset($config['users'])) { $users = array_map('trim', explode(',', strtolower($config['users']))); if (in_array('*', $users, true) || in_array(strtolower($this->user->getUsername()), $users, true)) { unset($config['owner']); unset($config['users']); unset($config['groups']); return true; } } if (isset($config['groups'])) { $groups = array_map('trim', explode(',', strtolower($config['groups']))); if (in_array('*', $groups, true)) { unset($config['owner']); unset($config['users']); unset($config['groups']); return true; } $userGroups = array_map('strtolower', $this->user->getGroups()); $matches = array_intersect($userGroups, $groups); if (! empty($matches)) { unset($config['owner']); unset($config['users']); unset($config['groups']); return true; } } return false; } /** * Load and return the shared navigation of the given type * * @param string $type * * @return Navigation */ public function getSharedNavigation($type) { $config = Config::navigation($type === 'dashboard-pane' ? 'dashlet' : $type); if ($type === 'dashboard-pane') { $panes = array(); foreach ($config as $dashletName => $dashletConfig) { if ($this->hasAccessToSharedNavigationItem($dashletConfig)) { // TODO: Throw ConfigurationError if pane or url is missing $panes[$dashletConfig->pane][$dashletName] = $dashletConfig->url; } } $navigation = new Navigation(); foreach ($panes as $paneName => $dashlets) { $navigation->addItem( $paneName, array( 'type' => 'dashboard-pane', 'dashlets' => $dashlets ) ); } } else { $items = array(); foreach ($config as $name => $typeConfig) { if (isset($this->accessibleMenuItems[$name])) { if ($this->accessibleMenuItems[$name]) { $items[$name] = $typeConfig; } } else { if ($this->hasAccessToSharedNavigationItem($typeConfig, $config)) { $this->accessibleMenuItems[$name] = true; $items[$name] = $typeConfig; } else { $this->accessibleMenuItems[$name] = false; } } } $navigation = Navigation::fromConfig($items); } return $navigation; } /** * Dispatch public interface */ public function dispatch() { $this->frontController->dispatch($this->getRequest(), $this->getResponse()); } /** * Prepare Zend MVC Base * * @return $this */ private function setupZendMvc() { Zend_Layout::startMvc( array( 'layout' => 'layout', 'layoutPath' => $this->getApplicationDir('/layouts/scripts') ) ); $this->setupFrontController(); $this->setupViewRenderer(); return $this; } /** * Create user object * * @return $this */ private function setupUser() { $auth = Auth::getInstance(); if (! $this->request->isXmlHttpRequest() && $this->request->isApiRequest() && ! $auth->isAuthenticated()) { $auth->authHttp(); } if ($auth->isAuthenticated()) { $user = $auth->getUser(); $this->getRequest()->setUser($user); $this->user = $user; if ($user->can('user/application/stacktraces')) { $displayExceptions = $this->user->getPreferences()->getValue( 'icingaweb', 'show_stacktraces' ); if ($displayExceptions !== null) { $this->frontController->setParams( array( 'displayExceptions' => $displayExceptions ) ); } } } return $this; } /** * Initialize a session provider * * @return $this */ private function setupSession() { $this->session = Session::create(); return $this; } /** * Initialize notifications to remove them immediately from session * * @return $this */ private function setupNotifications() { Notification::getInstance(); return $this; } /** * Instantiate front controller * * @return $this */ private function setupFrontController() { $this->frontController = Zend_Controller_Front::getInstance(); $this->frontController->setDispatcher(new Dispatcher()); $this->frontController->setRequest($this->getRequest()); $this->frontController->setControllerDirectory($this->getApplicationDir('/controllers')); $displayExceptions = $this->config->get('global', 'show_stacktraces', true); $this->frontController->setParams( array( 'displayExceptions' => $displayExceptions ) ); return $this; } /** * Register helper paths and views for renderer * * @return $this */ private function setupViewRenderer() { $view = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); /** @var \Zend_Controller_Action_Helper_ViewRenderer $view */ $view->setView(new View()); $view->view->addHelperPath($this->getApplicationDir('/views/helpers')); $view->view->setEncoding('UTF-8'); $view->view->headTitle()->prepend($this->config->get('global', 'project', 'Icinga')); $view->view->headTitle()->setSeparator(' :: '); $this->viewRenderer = $view; return $this; } /** * Configure pagination settings * * @return $this */ private function setupPagination() { // TODO: document what we need for whatever reason?! Zend_Paginator::addScrollingStylePrefixPath( 'Icinga_Web_Paginator_ScrollingStyle_', $this->getLibraryDir('Icinga/Web/Paginator/ScrollingStyle') ); Zend_Paginator::addScrollingStylePrefixPath( 'Icinga_Web_Paginator_ScrollingStyle', 'Icinga/Web/Paginator/ScrollingStyle' ); Zend_Paginator::setDefaultScrollingStyle('SlidingWithBorder'); Zend_View_Helper_PaginationControl::setDefaultViewPartial( array('mixedPagination.phtml', 'default') ); return $this; } /** * Fatal error handling configuration * * @return $this */ protected function setupFatalErrorHandling() { register_shutdown_function(function () { $error = error_get_last(); if ($error !== null && $error['type'] === E_ERROR) { $frontController = Icinga::app()->getFrontController(); $response = $frontController->getResponse(); $response->setException(new ErrorException( $error['message'], 0, $error['type'], $error['file'], $error['line'] )); // Clean PHP's fatal error stack trace and replace it with ours ob_end_clean(); $frontController->dispatch($frontController->getRequest(), $response); } }); return $this; } /** * (non-PHPDoc) * @see ApplicationBootstrap::detectTimezone() For the method documentation. */ protected function detectTimezone() { $auth = Auth::getInstance(); if (! $auth->isAuthenticated() || ($timezone = $auth->getUser()->getPreferences()->getValue('icingaweb', 'timezone')) === null ) { $detect = new TimezoneDetect(); $timezone = $detect->getTimezoneName(); } return $timezone; } /** * Setup internationalization using gettext * * Uses the preferred user language or the browser suggested language or our default. * * @return string Detected locale code */ protected function detectLocale() { $auth = Auth::getInstance(); if ($auth->isAuthenticated() && ($locale = $auth->getUser()->getPreferences()->getValue('icingaweb', 'language')) !== null ) { return $locale; } /** @var GettextTranslator $translator */ $translator = StaticTranslator::$instance; if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { return (new Locale())->getPreferred($_SERVER['HTTP_ACCEPT_LANGUAGE'], $translator->listLocales()); } return $translator->getDefaultLocale(); } }