/** * Set the 'X-Frame-Options' headers to prevent click-jacking, unless * specifically disabled. Backend only! * * @param Request $request * @param Response $response */ protected function setFrameOptions(Request $request, Response $response) { if (Zone::isBackend($request) && $this->app['config']->get('general/headers/x_frame_options')) { $response->headers->set('X-Frame-Options', 'SAMEORIGIN'); $response->headers->set('Frame-Options', 'SAMEORIGIN'); } }
/** * Render the not found page if on frontend and http exception * * @param GetResponseForExceptionEvent $event */ public function onKernelException(GetResponseForExceptionEvent $event) { $exception = $event->getException(); if (!$exception instanceof HttpExceptionInterface || Zone::isBackend($event->getRequest())) { return; } if ($exception->getStatusCode() !== Response::HTTP_NOT_FOUND) { return; } // If $notFoundPage is referencing a template, render it and be done. if ($this->render->hasTemplate($this->notFoundPage)) { try { $this->renderNotFound($event, $this->notFoundPage, []); } catch (TwigErrorLoader $e) { // Template not found, fall though to see if we can render a // record, failing that let the exception handler take over } } // Next try for referencing DB content. $content = $this->storage->getContent($this->notFoundPage, ['returnsingle' => true]); if (!$content instanceof Content || empty($content->id)) { return; } $template = $this->templateChooser->record($content); $this->renderNotFound($event, $template, $content->getTemplateContext()); }
public function getGlobals() { /** @var \Bolt\Config $config */ $config = $this->app['config']; $configVal = $this->safe ? null : $config; /** @var \Bolt\Users $users */ $users = $this->app['users']; /** @var \Bolt\Configuration\ResourceManager $resources */ $resources = $this->app['resources']; $zone = null; /** @var RequestStack $requestStack */ $requestStack = $this->app['request_stack']; if ($request = $requestStack->getCurrentRequest()) { $zone = Zone::get($request); } // User calls can cause exceptions that block the exception handler try { /** @deprecated Deprecated since 3.0, to be removed in 4.0. */ $usersVal = $this->safe ? null : $users->getUsers(); $usersCur = $users->getCurrentUser(); } catch (\Exception $e) { $usersVal = null; $usersCur = null; } // Structured to allow PHPStorm's SymfonyPlugin to provide code completion return ['bolt_name' => $this->app['bolt_name'], 'bolt_version' => $this->app['bolt_version'], 'frontend' => $zone === Zone::FRONTEND, 'backend' => $zone === Zone::BACKEND, 'async' => $zone === Zone::ASYNC, 'paths' => $resources->getPaths(), 'theme' => $config->get('theme'), 'user' => $usersCur, 'users' => $usersVal, 'config' => $configVal]; }
/** * Sets the request's zone if needed and returns it. * * @param Request $request * * @return string */ public function setZone(Request $request) { if ($zone = Zone::get($request)) { return $zone; } $zone = $this->determineZone($request); Zone::set($request, $zone); return $zone; }
/** * @covers \Bolt\Controller\Zone::get * @covers \Bolt\Controller\Zone::isBackend */ public function testControllerZone() { $app = $this->getApp(); $this->setRequest(Request::create('/bolt')); $request = $this->getRequest(); $kernel = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); $app['dispatcher']->dispatch(KernelEvents::REQUEST, new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST)); $this->assertEquals('backend', Zone::get($request)); $this->assertTrue(Zone::isBackend($request)); }
/** * Check if snippets are allowed for this request. * * @param FilterResponseEvent $event */ protected function isEnabled(FilterResponseEvent $event) { if (!$event->isMasterRequest()) { return false; } if (Zone::isFrontend($event->getRequest())) { return true; } return $event->getRequest()->attributes->get('allow_snippets', false); }
/** * @covers \Bolt\Controller\Zone::get * @covers \Bolt\Controller\Zone::isAsync */ public function testControllerZone() { $app = $this->getApp(); $this->allowLogin($app); $this->setRequest(Request::create('/async')); $request = $this->getRequest(); $request->cookies->set($app['token.authentication.name'], 'dropbear'); $kernel = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); $app['dispatcher']->dispatch(KernelEvents::REQUEST, new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST)); $this->assertEquals('async', Zone::get($request)); $this->assertTrue(Zone::isAsync($request)); }
/** * Render the not found page if on frontend and http exception * * @param GetResponseForExceptionEvent $event */ public function onKernelException(GetResponseForExceptionEvent $event) { if (!$event->getException() instanceof HttpExceptionInterface || Zone::isBackend($event->getRequest())) { return; } $content = $this->storage->getContent($this->notFoundPage, ['returnsingle' => true]); if (!$content instanceof Content || empty($content->id)) { return; } $template = $this->templateChooser->record($content); $response = $this->render->render($template, $content->getTemplateContext()); $event->setResponse($response); }
/** * Callback for reponse event. * * @param FilterResponseEvent $event */ public function onResponse(FilterResponseEvent $event) { if (!$event->isMasterRequest()) { return; } $response = $event->getResponse(); if (strpos($response->headers->get('Content-Type'), 'text/html') === false) { return; } if (!Zone::isAsync($event->getRequest())) { $this->addSnippets(); } $response->setContent($this->render->postProcess($response)); }
/** * Handle errors thrown in the application. * * @param GetResponseForExceptionEvent $event */ public function onKernelException(GetResponseForExceptionEvent $event) { $exception = $event->getException(); // Log the error message $message = $exception->getMessage(); $this->logger->critical($message, ['event' => 'exception', 'exception' => $exception]); if ($exception instanceof HttpExceptionInterface && !Zone::isBackend($event->getRequest())) { $message = "The page could not be found, and there is no 'notfound' set in 'config.yml'. Sorry about that."; } $context = ['class' => get_class($exception), 'message' => $message, 'code' => $exception->getCode(), 'trace' => $this->getSafeTrace($exception)]; // Note: This uses the template from app/theme_defaults. Not app/view/twig. $response = $this->render->render('error.twig', ['context' => $context]); $event->setResponse($response); }
/** * Route for kernel exception handling. * * @param GetResponseForExceptionEvent $event * * @return Response */ public function kernelException(GetResponseForExceptionEvent $event) { if ($this->app === null) { throw new \RuntimeException('Exception controller being used outside of request cycle.'); } $exception = $event->getException(); $message = $exception->getMessage(); if ($exception instanceof HttpExceptionInterface && !Zone::isBackend($event->getRequest())) { $message = "The page could not be found, and there is no 'notfound' set in 'config.yml'. Sorry about that."; } $context = $this->getContextArray($exception); $context['type'] = 'general'; $context['message'] = $message; $html = $this->app['twig']->render('@bolt/exception/general.twig', $context); $response = new Response($html, Response::HTTP_OK); $response->headers->set('X-Debug-Exception-Handled', time()); return $response; }
public function getGlobals() { /** @var \Bolt\Config $config */ $config = $this->app['config']; /** @var \Bolt\Users $users */ $users = $this->app['users']; /** @var \Bolt\Configuration\ResourceManager $resources */ $resources = $this->app['resources']; $configVal = $this->safe ? null : $config; $usersVal = $this->safe ? null : $users->getUsers(); $zone = null; /** @var RequestStack $requestStack */ $requestStack = $this->app['request_stack']; if ($request = $requestStack->getCurrentRequest()) { $zone = Zone::get($request); } // Structured to allow PHPStorm's SymfonyPlugin to provide code completion return ['bolt_name' => $this->app['bolt_name'], 'bolt_version' => $this->app['bolt_version'], 'frontend' => $zone === Zone::FRONTEND, 'backend' => $zone === Zone::BACKEND, 'async' => $zone === Zone::ASYNC, 'paths' => $resources->getPaths(), 'theme' => $config->get('theme'), 'user' => $users->getCurrentUser(), 'users' => $usersVal, 'config' => $configVal]; }
/** * Render the not found page if on frontend and http exception * * @param GetResponseForExceptionEvent $event */ public function onKernelException(GetResponseForExceptionEvent $event) { if (!$event->getException() instanceof HttpExceptionInterface || Zone::isBackend($event->getRequest())) { return; } // If $notFoundPage is referencing a template, render it and be done. if ($this->render->hasTemplate($this->notFoundPage)) { $response = $this->render->render($this->notFoundPage); $event->setResponse($response); return; } // Next try for referencing DB content. $content = $this->storage->getContent($this->notFoundPage, ['returnsingle' => true]); if (!$content instanceof Content || empty($content->id)) { return; } $template = $this->templateChooser->record($content); $response = $this->render->render($template, [], $content->getTemplateContext()); $event->setResponse($response); }
/** * Process a single asset. * * @param FileAssetInterface $asset * @param Request $request * @param Response $response */ protected function processAsset(FileAssetInterface $asset, Request $request, Response $response) { if ($asset->getZone() !== Zone::get($request)) { return; } elseif ($asset->isLate()) { if ($asset->getLocation() === null) { $location = Target::END_OF_BODY; } else { $location = $asset->getLocation(); } } elseif ($asset->getLocation() !== null) { $location = $asset->getLocation(); } else { $location = Target::END_OF_HEAD; } $this->injector->inject($asset, $location, $response); }
/** * Get the parameters that will be used to update Bolt's registered Twig * globals. * * This is here as a transitory measure. * * @param bool $safe * * @return array */ private function setGlobals($safe) { /** @var \Twig_Environment $twig */ $twig = $safe ? $this->app['safe_twig'] : $this->app['twig']; /** @var \Bolt\Config $config */ $config = $this->app['config']; $configVal = $safe ? null : $config; /** @var \Bolt\Users $users */ $users = $this->app['users']; /** @var \Bolt\Configuration\ResourceManager $resources */ $resources = $this->app['resources']; $zone = null; /** @var RequestStack $requestStack */ $requestStack = $this->app['request_stack']; if ($request = $requestStack->getCurrentRequest()) { $zone = Zone::get($request); } // User calls can cause exceptions that block the exception handler try { /** @deprecated Deprecated since 3.0, to be removed in 4.0. */ $usersVal = $safe ? null : $users->getUsers(); $usersCur = $users->getCurrentUser(); } catch (\Exception $e) { $usersVal = null; $usersCur = null; } $twig->addGlobal('bolt_name', Bolt\Version::name()); $twig->addGlobal('bolt_version', Bolt\Version::VERSION); $twig->addGlobal('bolt_stable', Bolt\Version::isStable()); $twig->addGlobal('frontend', $zone === Zone::FRONTEND); $twig->addGlobal('backend', $zone === Zone::BACKEND); $twig->addGlobal('async', $zone === Zone::ASYNC); $twig->addGlobal('paths', $resources->getPaths()); $twig->addGlobal('theme', $config->get('theme')); $twig->addGlobal('user', $usersCur); $twig->addGlobal('users', $usersVal); $twig->addGlobal('config', $configVal); }
private function determineZone() { if (PHP_SAPI === 'cli') { return 'cli'; } /** @var \Symfony\Component\HttpFoundation\RequestStack $stack */ $stack = $this->app['request_stack']; $request = $stack->getCurrentRequest() ?: Request::createFromGlobals(); if ($zone = Zone::get($request)) { return $zone; } /** @var \Bolt\EventListener\ZoneGuesser $guesser */ $guesser = $this->app['listener.zone_guesser']; return $guesser->setZone($request); }
/** * Insert jQuery, if it's not inserted already. * * Some of the patterns that 'match' are: * - jquery.js * - jquery.min.js * - jquery-latest.js * - jquery-latest.min.js * - jquery-1.8.2.min.js * - jquery-1.5.js * * @param Request $request * @param Response $response */ protected function addJquery(Request $request, Response $response) { if (!$this->config->get('general/add_jquery', false) && !$this->config->get('theme/add_jquery', false)) { return; } if (Zone::isFrontend($request) === false) { return; } $html = $response->getContent(); $regex = '/<script(.*)jquery(-latest|-[0-9\\.]*)?(\\.min)?\\.js/'; if (!preg_match($regex, $html)) { $jqueryfile = $this->resources->getPath('app/view/js/jquery-2.2.4.min.js'); $asset = (new Snippet())->setLocation(Target::BEFORE_JS)->setCallback('<script src="' . $jqueryfile . '"></script>'); $this->injector->inject($asset, $asset->getLocation(), $response); } }
/** * Process a single asset. * * @param FileAssetInterface $asset * @param Request $request * @param Response $response */ protected function processAsset(FileAssetInterface $asset, Request $request, Response $response) { if ($asset->getZone() !== Zone::get($request)) { return; } elseif ($asset->isLate()) { $this->injector->inject($asset, Target::END_OF_BODY, $response); } elseif ($asset->getType() === 'stylesheet') { $this->injector->inject($asset, Target::BEFORE_CSS, $response); } elseif ($asset->getType() === 'javascript') { $this->injector->inject($asset, Target::AFTER_JS, $response); } }
/** * Insert jQuery, if it's not inserted already. * * Some of the patterns that 'match' are: * - jquery.js * - jquery.min.js * - jquery-latest.js * - jquery-latest.min.js * - jquery-1.8.2.min.js * - jquery-1.5.js * * @param string $html * * @return string HTML */ protected function addJquery($html) { if (!$this->app['config']->get('general/add_jquery', false) && !$this->app['config']->get('theme/add_jquery', false)) { return $html; } $zone = Zone::FRONTEND; /** @var RequestStack $requestStack */ $requestStack = $this->app['request_stack']; if ($request = $requestStack->getCurrentRequest()) { $zone = Zone::get($request); } $regex = '/<script(.*)jquery(-latest|-[0-9\\.]*)?(\\.min)?\\.js/'; if ($zone === Zone::FRONTEND && !preg_match($regex, $html)) { $jqueryfile = $this->app['resources']->getPath('app/view/js/jquery-2.1.4.min.js'); $asset = new Snippet(Target::BEFORE_JS, '<script src="' . $jqueryfile . '"></script>'); $html = $this->app['asset.injector']->inject($asset, $asset->getLocation(), $html); } return $html; }
public function onRequest(GetResponseEvent $event) { if (!Zone::isBackend($event->getRequest())) { return; } foreach ($this->config->getRolesAdmin() as $role) { if ($this->users->isAllowed($role)) { return; } } throw new AccessDeniedException('Logged in user does not have the correct rights to use this class.'); }
/** * Parse textquery into useable arguments. * * This is tightly coupled to $this->getContent() * * @see $this->decodeContentQuery() * * @param $textquery * @param array $decoded a pre-set decoded array to fill * @param array $metaParameters meta parameters * @param array $ctypeParameters contenttype parameters */ private function parseTextQuery($textquery, array &$decoded, array &$metaParameters, array &$ctypeParameters) { // Our default callback $decoded['queries_callback'] = [$this, 'executeGetContentQueries']; // Some special cases, like 'entry/1' or 'page/about' need to be caught before further processing. if (preg_match('#^/?([a-z0-9_-]+)/([0-9]+)$#i', $textquery, $match)) { // like 'entry/12' or '/page/12345' $decoded['contenttypes'] = $this->decodeContentTypesFromText($match[1]); $decoded['return_single'] = true; // if allow_numeric_slug option is set on contenttype, interpret number as slug instead of id $contenttype = $this->getContentType($decoded['contenttypes'][0]); $field = $contenttype['allow_numeric_slugs'] === true ? 'slug' : 'id'; $ctypeParameters[$field] = $match[2]; } elseif (preg_match('#^/?([a-z0-9_(\\),-]+)/search(/([0-9]+))?$#i', $textquery, $match)) { // like 'page/search or '(entry,page)/search' $decoded['contenttypes'] = $this->decodeContentTypesFromText($match[1]); $metaParameters['order'] = [$this, 'compareSearchWeights']; if (count($match) >= 3) { $metaParameters['limit'] = $match[3]; } $decoded['queries_callback'] = [$this, 'executeGetContentSearch']; } elseif (preg_match('#^/?([a-z0-9_-]+)/([a-z0-9_-]+)$#i', $textquery, $match)) { // like 'page/lorem-ipsum-dolor' or '/page/home' $decoded['contenttypes'] = $this->decodeContentTypesFromText($match[1]); $decoded['return_single'] = true; $ctypeParameters['slug'] = $match[2]; } elseif (preg_match('#^/?([a-z0-9_-]+)/(latest|first)/([0-9]+)$#i', $textquery, $match)) { // like 'page/latest/5' $decoded['contenttypes'] = $this->decodeContentTypesFromText($match[1]); if (!isset($metaParameters['order']) || $metaParameters['order'] === false) { $metaParameters['order'] = 'datepublish ' . ($match[2] == 'latest' ? 'DESC' : 'ASC'); } if (!isset($metaParameters['limit'])) { $metaParameters['limit'] = $match[3]; } } elseif (preg_match('#^/?([a-z0-9_-]+)/random/([0-9]+)$#i', $textquery, $match)) { // like 'page/random/4' $decoded['contenttypes'] = $this->decodeContentTypesFromText($match[1]); $dboptions = $this->app['config']->get('general/database'); $metaParameters['order'] = $dboptions['randomfunction']; // 'RAND()' or 'RANDOM()' if (!isset($metaParameters['limit'])) { $metaParameters['limit'] = $match[2]; } } else { $decoded['contenttypes'] = $this->decodeContentTypesFromText($textquery); if (isset($ctypeParameters['id']) && is_numeric($ctypeParameters['id'])) { $decoded['return_single'] = true; } } // When using from the frontend, we assume (by default) that we only want published items, // unless something else is specified explicitly $request = $this->app['request_stack']->getCurrentRequest(); $isBackend = $request ? Zone::isBackend($request) : false; if (!$isBackend && empty($ctypeParameters['status'])) { $ctypeParameters['status'] = 'published'; } if (isset($metaParameters['returnsingle'])) { $decoded['return_single'] = $metaParameters['returnsingle']; unset($metaParameters['returnsingle']); } }