/** * Display a link to the Sitegear website. * * @param \Sitegear\View\ViewInterface $view */ public function sitegearLinkComponent(ViewInterface $view) { LoggerRegistry::debug('VersionModule::sitegearLinkComponent()'); $view['link-url'] = $this->getEngine()->getApplicationInfo()->getSitegearHomepage(); $view['link-tooltip'] = sprintf('Running version: %s', $this->getEngine()->getApplicationInfo()->getSitegearVersionIdentifier()); $view['display-name'] = $this->getEngine()->getApplicationInfo()->getSitegearDisplayName(); }
/** * @param \Sitegear\Engine\SitegearEngine $engine * @param string|null $filename */ public function __construct(SitegearEngine $engine, $filename = null) { LoggerRegistry::debug('new SitegearApplicationInfoProvider({engine}, {filename})', array('engine' => TypeUtilities::describe($engine), 'filename' => TypeUtilities::describe($filename))); $this->engine = $engine; $filename = $filename ?: sprintf('%s/%s', dirname($this->getSitegearRoot()), $filename ?: 'composer.json'); $this->data = file_exists($filename) ? json_decode(file_get_contents($filename), true) : array(); }
/** * Controller to display the image data. * * @return Response */ public function imageController() { LoggerRegistry::debug('LocationsModule::imageController()'); ob_start(); $this->getCaptcha()->writeImage(); return new Response(ob_get_clean(), 200); }
/** * Setup the route parameters. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * * @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ public function onKernelRequest(GetResponseEvent $event) { LoggerRegistry::debug('EngineRouterListener performing start() on REQUEST event'); $this->setRoutingAttributes($event->getRequest()); $this->setTemplateAttributes($event->getRequest()); LoggerRegistry::debug('EngineRouterListener will proceed with standard rendering'); }
/** * @inheritdoc */ public function upload($selector) { LoggerRegistry::debug('FileModule::upload({selector})', array('selector' => TypeUtilities::describe($selector))); $filename = sprintf('%s/%s', $this->getEngine()->getSiteInfo()->getSiteRoot(), ltrim($selector, '/')); mkdir(dirname($filename), 0777, true); // TODO Implement me }
/** * Display the breadcrumbs navigation helper component. * * @param \Sitegear\View\ViewInterface $view * @param \Symfony\Component\HttpFoundation\Request $request * @param string|null $url Allows breadcrumb to be displayed for a different page; if null, the URL is taken from * the passed-in Request object. * * @return null|boolean */ public function breadcrumbsComponent(ViewInterface $view, Request $request, $url = null) { LoggerRegistry::debug('NavigationModule::breadcrumbsComponent([view], [request], {url})', array('url' => TypeUtilities::describe($url))); $url = trim(!is_null($url) ? $url : $request->getPathInfo(), '/'); if (empty($url) && !$this->config('components.breadcrumbs.show-on-homepage')) { // This is the home page, and we are configured not to show the breadcrumbs here. return false; } $data = $this->getData(MountableModuleInterface::NAVIGATION_DATA_MODE_EXPANDED); $path = $this->getNavigationPath($url, $data); if (is_null($path)) { // The URL cannot be found in the navigation, don't show the breadcrumb because there is nothing to show. return false; } $trail = array(); $breadcrumbKeys = array('url' => true, 'label' => true); if (strlen($url) > 0 && $this->config('components.breadcrumbs.prepend-homepage')) { $trail[] = array_intersect_key($this->getNavigationItem('', $data), $breadcrumbKeys); } foreach ($path as $pathItem) { $trail[] = array_intersect_key($data[$pathItem], $breadcrumbKeys); $data = isset($data[$pathItem]['children']) ? $data[$pathItem]['children'] : array(); } $view['trail'] = $trail; return null; }
/** * @inheritdoc */ public function load($args) { LoggerRegistry::debug('JsonFileLoader::load({args})', array('args' => TypeUtilities::describe($args))); if (!$this->supports($args)) { throw new \InvalidArgumentException(sprintf('JsonFileLoader attempting to load unsupported config file "%s".', $args)); } return json_decode(file_get_contents($args), true); }
/** * @inheritdoc */ public function decorate($content, ViewInterface $view = null, Request $request = null) { $renderTime = $this->formatTime($view->getEngine()->getTimestamp()); $version = $view->getEngine()->getApplicationInfo()->getSitegearVersionIdentifier(); $userManager = $view->getEngine()->getUserManager(); $comment = sprintf('<!-- %s%s :: %s :: %s -->%s<!-- %s :: %s :: %s -->', $request->getUri(), is_null($view->getEngine()->getEnvironmentInfo()->getEnvironment()) ? '' : sprintf(' :: %s environment', $view->getEngine()->getEnvironmentInfo()->getEnvironment()), $userManager->isLoggedIn() ? 'logged in as ' . ($userManager->getLoggedInUserEmail() ?: 'guest') : 'not logged in', $renderTime, PHP_EOL, $view->getEngine()->getApplicationInfo()->getSitegearHomepage(), $version, $this->formatNow()); LoggerRegistry::log($this->logLevel(), '{pathInfo} {renderTime} by {version}', array('pathInfo' => $request->getPathInfo(), 'renderTime' => $renderTime, 'version' => $version)); return $content . $comment . PHP_EOL; }
/** * @inheritdoc */ public function load($args) { LoggerRegistry::debug('PhpFileLoader::load({args})', array('args' => TypeUtilities::describe($args))); if (!$this->supports($args)) { throw new \InvalidArgumentException(sprintf('PhpFileLoader attempting to load unsupported config file "%s".', $args)); } /** @noinspection PhpIncludeInspection */ return require $args; }
/** * @inheritdoc */ public function render($path, ViewInterface $view) { LoggerRegistry::debug('RendererRegistry::render({path}, [view])', array('path' => TypeUtilities::describe($path))); $result = null; foreach ($this->registry as $renderer) { /** @var \Sitegear\View\Renderer\RendererInterface $renderer */ if (is_null($result) && $renderer->supports($path)) { $result = $renderer->render($path, $view); } } return $result; }
/** * Perform standard rendering. This event only fires if the previous (REQUEST) event did not generate a response. * * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event */ public function onKernelView(GetResponseForControllerResultEvent $event) { LoggerRegistry::debug('EngineRendererListener performing render() on VIEW event'); $request = $event->getRequest(); $path = explode(':', $request->attributes->get('_route')); // Set the module attribute. $request->attributes->set('_module', $path[0]); // Use either the controller result or the 'natural' name of the view based on the route specifier. $request->attributes->set('_view', is_null($event->getControllerResult()) ? $path[1] : $event->getControllerResult()); // Let the engine render the response and add instrumentation headers; set back to the event. $response = $this->getEngine()->renderPage($request); $event->setResponse($this->getEngine()->instrumentResponse($response)); }
/** * @inheritdoc */ public function render($path, ViewInterface $view) { LoggerRegistry::debug('PhpRenderer::render({path}, [view])', array('path' => TypeUtilities::describe($path))); $renderPath = null; foreach ($this->getExtensions() as $extension) { if (is_null($renderPath) && file_exists($path . $extension)) { $renderPath = $path . $extension; } } if (is_null($renderPath)) { throw new \InvalidArgumentException(sprintf('The path "%s" cannot be rendered by PhpRenderer', $path)); } return PhpRendererEvaluationSandbox::render($renderPath, $view); }
/** * @inheritdoc */ public function checkCredentials($email, array $credentials) { LoggerRegistry::debug('PlainTextPasswordAuthenticator::checkCredentials({email}, [credentials])', array('email' => TypeUtilities::describe($email))); if (!isset($credentials['password'])) { throw new \InvalidArgumentException('PlainTextPasswordAuthenticator expects "password" credential key, insufficient credentials supplied.'); } $result = null; if ($this->getStorage()->hasUser($email)) { $data = $this->getStorage()->getData($email); if (isset($data['password']) && $data['password'] === $credentials['password']) { $result = $email; } } return $result; }
/** * @inheritdoc */ public function guess($path) { LoggerRegistry::debug('ExtensionMimeTypeGuesser::guess({path})', array('path' => TypeUtilities::describe($path))); if (!is_file($path)) { throw new FileNotFoundException($path); } if (!is_readable($path)) { throw new AccessDeniedException($path); } if (empty($this->data)) { throw new \LogicException('The data file could not be loaded or contains no data'); } $extension = pathinfo($path, PATHINFO_EXTENSION); return is_string($extension) && isset($this->data[$extension]) ? $this->data[$extension] : null; }
/** * Start the engine and bootstrap the modules specified by the engine's bootstrap sequence. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException */ public function onKernelRequest(GetResponseEvent $event) { LoggerRegistry::debug('EngineBootstrapListener responding to REQUEST kernel event'); // Start the engine. $request = $event->getRequest(); /** @var \Symfony\Component\HttpFoundation\Request $request */ $response = $this->getEngine()->start($request); if (!is_null($response)) { // Set the response directly; prevent further processing. $this->getEngine()->instrumentResponse($response); $event->setResponse($response); $event->stopPropagation(); LoggerRegistry::debug('EngineBootstrapListener received Response from bootstrap'); } }
/** * @inheritdoc */ public function resourceController(Request $request) { LoggerRegistry::debug('ResourcesIntegrationModule::resourceController()'); $location = $request->attributes->get('location'); switch ($location) { case self::LOCATION_ATTRIBUTE_ENGINE: $path = $this->getEngine()->getSiteInfo()->getPublicPath(ResourceLocations::RESOURCE_LOCATION_ENGINE, $request->attributes->get('path')); break; case self::LOCATION_ATTRIBUTE_VENDOR: $path = $this->getEngine()->getSiteInfo()->getPublicPath(ResourceLocations::RESOURCE_LOCATION_VENDOR, $request->attributes->get('path')); break; default: $path = $this->getEngine()->getSiteInfo()->getPublicPath(ResourceLocations::RESOURCE_LOCATION_MODULE, $request->attributes->get('path'), $this->getEngine()->getModule($location)); } if (!file_exists($path)) { throw new FileNotFoundException($path); } return $this->getEngine()->createFileResponse($request, $path); }
/** * Handle exceptions that occur during the request handling process. * * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event * * @throws \Exception|\Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException|\Symfony\Component\HttpKernel\Exception\HttpException * As a fallback behaviour only, if a sensible error page cannot be generated. */ public function onKernelException(GetResponseForExceptionEvent $event) { LoggerRegistry::debug('EngineExceptionListener performing handleException() on EXCEPTION event'); $exception = $event->getException(); try { // Delete any buffered page content. while (ob_get_level() > 1) { ob_end_clean(); } // Determine the status code to use for the error page (and which error page is displayed). $statusCode = $exception instanceof HttpException ? $exception->getStatusCode() : ($exception instanceof FileNotFoundException ? 404 : 500); // Set route and template attributes to error page defaults. $event->getRequest()->attributes->add(array('_status' => $statusCode, '_route' => $this->getEngine()->getErrorRoute($statusCode), '_route_params' => array('exception' => $exception, 'statusCode' => $statusCode), '_template' => $this->getEngine()->getErrorTemplate())); // Store the exception and status code in the top-level view data. $page = $this->getEngine()->getViewFactory()->getPage(); $page['exception'] = $exception; $page['status-code'] = $statusCode; // Perform standard rendering using a proxy event. $renderer = new EngineRendererListener($this->getEngine()); $viewScript = sprintf('error-%3d', $statusCode); // TODO Handle view scripts that don't exist, default back to 500 or 404 $renderEvent = new GetResponseForControllerResultEvent($event->getKernel(), $event->getRequest(), HttpKernelInterface::SUB_REQUEST, $viewScript); $renderer->onKernelView($renderEvent); // Copy the response from the proxy event back to the event being handled. $event->setResponse($renderEvent->getResponse()); } catch (\Exception $e) { try { // Delete any buffered page content. while (ob_get_level() > 1) { ob_end_clean(); } // If there is an error above, try to display the fallback error page, i.e. black-and-white error // message. $event->setResponse(Response::create(HtmlUtilities::exception($exception, $this->getEngine()->getSiteInfo()->getAdministratorName(), $this->getEngine()->getSiteInfo()->getAdministratorEmail()))); } catch (\Exception $e2) { // If another error occurs, the best thing to do is throw the original error. throw $exception; } } }
/** * @param \Sitegear\Engine\EngineInterface $engine Container. */ public function __construct(EngineInterface $engine) { LoggerRegistry::debug('new AbstractModule()'); $this->engine = $engine; }
/** * @inheritdoc */ public function checkCredentials($email, array $credentials) { LoggerRegistry::debug('SaltedPasswordAuthenticator::checkCredentials({email}, [credentials])', array('email' => TypeUtilities::describe($email))); // TODO Implement me throw new \InvalidArgumentException('Not implemented'); }
/** * @inheritdoc */ public function clearTargets() { LoggerRegistry::debug('AbstractView::clearTargets()'); $this->targets = array(); return $this; }
/** * Show a componentised view of the latest headlines. * * @param \Sitegear\View\ViewInterface $view * @param int|null $itemLimit Number of items to display, or null to use the value from the configuration. * @param int|null $excerptLength Number of characters of the news item text to display in each preview, or null to * use the value from the configuration. * @param string|null $readMore Text to use for "read more" links */ public function latestHeadlinesComponent(ViewInterface $view, $itemLimit = null, $excerptLength = null, $readMore = null) { LoggerRegistry::debug('NewsModule::latestHeadlinesComponent([view], %d, %d, %s)', array('itemLimit' => TypeUtilities::describe($itemLimit), 'excerptLength' => TypeUtilities::describe($excerptLength), 'readMore' => TypeUtilities::describe($readMore))); $itemLimit = intval(!is_null($itemLimit) ? $itemLimit : $this->config('component.latest-headlines.item-limit')); $view['items'] = $this->getRepository('Item')->findLatestItems($itemLimit); $view['date-format'] = $this->config('component.latest-headlines.date-format'); $view['excerpt-length'] = !is_null($excerptLength) ? $excerptLength : $this->config('component.latest-headlines.excerpt-length'); if ($readMore) { $view['read-more'] = $readMore; } }
/** * Instantiate a module instance for this engine. * * @param string $name Module to load. * * @return \Sitegear\Module\ModuleInterface * * @throws \InvalidArgumentException If the named module does not exist. * @throws \DomainException If the named module does not implement ModuleInterface. */ protected function createModule($name) { LoggerRegistry::debug('AbstractEngine::createModule({name})', array('name' => TypeUtilities::describe($name))); try { return TypeUtilities::buildTypeCheckedObject($this->getModuleClassName($name) ?: '', 'module', null, '\\Sitegear\\Module\\ModuleInterface', array($this)); } catch (\DomainException $e) { throw new \InvalidArgumentException(sprintf('AbstractEngine cannot create module "%s" because it does not exist', $name), 0, $e); } }
/** * Apply all children of the given config key from this module, as values stored in the given view. This is a * handy shortcut for making all the top-level configuration items available to the view without requiring an * additional parent key. * * @param string $configKey * @param \Sitegear\View\ViewInterface $view */ protected function applyConfigToView($configKey, ViewInterface $view) { LoggerRegistry::debug('AbstractConfigurableModule::applyConfigToView({configKey}, [view])', array('configKey' => TypeUtilities::describe($configKey))); if (is_array($config = $this->config($configKey))) { foreach ($config as $key => $value) { $view[$key] = $value; } } }
/** * @inheritdoc */ public function save($selector, $value) { LoggerRegistry::debug('DoctrineModule::save({selector}, {value})', array('selector' => TypeUtilities::describe($selector), 'value' => TypeUtilities::describe($value))); $selector = $this->parseSelector($selector); $query = $this->getEntityManager()->createQuery(sprintf('update %s%s item set item.%s = :value where item.%s = :match', $selector['entity-alias'], $selector['entity-name'], $selector['value-field-name'], $selector['match-field-name'])); $query->setParameter('value', $value); $query->setParameter('match', $selector['match-field-value']); return $query->execute() !== false; }
/** * Display a form. * * @param \Sitegear\View\ViewInterface $view * @param \Symfony\Component\HttpFoundation\Request $request * @param string $formKey Unique key of the form, used for session storage and also is the key used to retrieve the * form data, if it is not supplied directly. * @param array|null $values Values to set manually into the form before displaying it. Note this is used for * rendering the form only, these values are not set into the session. These values are merged into the values * currently stored in the session (these values take precedence). * @param array[]|null $errors Errors to set manually into the form before displaying it. These errors are merged * into the errors currently stored in the session (these errors take precedence). */ public function formComponent(ViewInterface $view, Request $request, $formKey, array $values = null, array $errors = null) { LoggerRegistry::debug('FormsModule::formComponent()'); // Retrieve the form object. $form = $this->registry()->getForm($formKey, $request); // Disable the back button if the previous step is not available. $currentStep = $this->registry()->getCurrentStep($formKey); $availableSteps = $this->registry()->getAvailableSteps($formKey); if (!in_array($currentStep - 1, $availableSteps) && is_array($form->getBackButtonAttributes())) { $form->setBackButtonAttributes(array_merge($form->getBackButtonAttributes(), array('disabled' => 'disabled'))); } // Setup the view. $view['form-renderer'] = $this->createRendererFactory()->createFormRenderer($form, $currentStep); // TODO Something better here $view['form-renderer']->setRenderOption('attributes', ArrayUtilities::mergeHtmlAttributes(array('id' => $formKey . '-form'), $view['form-renderer']->getRenderOption('attributes'))); $view['values'] = array_merge($this->registry()->getValues($formKey), $values ?: array()); $view['errors'] = array_merge($this->registry()->getErrors($formKey), $errors ?: array()); // Remove errors as they are about to be displayed (they are already set in the view), and we don't want to // show the same errors again. $this->registry()->clearErrors($formKey); }
/** * @param string|null $environment */ public function __construct($environment = null) { LoggerRegistry::debug('new SitegearEnvironmentInfoProvider({environment})', array('environment' => TypeUtilities::describe($environment))); $this->environment = $environment; }
/** * The location search form. * * @param ViewInterface $view * @param string|null $query Previous query, to populate the value. * @param string|null $radius Previous radius selection, to populate the selected option. */ public function searchFormComponent(ViewInterface $view, $query = null, $radius = null) { LoggerRegistry::debug('LocationsModule::searchFormComponent([view], {query}, {radius})', array('query' => TypeUtilities::describe($query), 'radius' => TypeUtilities::describe($radius))); $view['action-url'] = $this->getRouteUrl('search'); if (!is_null($query)) { $view['query'] = $query; } if (!is_null($radius)) { $view['radius'] = $radius; } }
/** * Loa the data from the given location, according to any available loaders. * * If the given argument is a filename, also load the relevant environment-specific override file. * * @param array|string|\ArrayObject|\Sitegear\Config\Configuration $config Configuration data, filename or * configuration object. * * @return array Loaded data. * * @throws \InvalidArgumentException If the given argument is not a string or an array. */ public function load($config) { LoggerRegistry::debug('ConfigLoader::load({config})', array('config' => TypeUtilities::describe($config))); $result = $this->normalise($config); if (is_string($config) && !is_null($this->environmentInfo) && !is_null($this->environmentInfo->getEnvironment())) { $pathinfo = pathinfo($config); $dirname = array_key_exists('dirname', $pathinfo) ? strval($pathinfo['dirname']) : ''; $filename = array_key_exists('filename', $pathinfo) ? strval($pathinfo['filename']) : ''; $extension = array_key_exists('extension', $pathinfo) ? strval($pathinfo['extension']) : ''; $envFilename = sprintf('%s/%s.%s.%s', $dirname, $filename, $this->environmentInfo->getEnvironment(), $extension); $envConfig = $this->loadFile($envFilename); $result = ArrayUtilities::combine($result, $envConfig); } return $result; }
/** * @inheritdoc */ protected function buildRoutes() { LoggerRegistry::debug('{class}::buildRoutes(), mounted to "{mountedUrl}"', array('class' => (new \ReflectionClass($this))->getShortName(), 'mountedUrl' => $this->getMountedUrl())); $routes = new RouteCollection(); // Check for an index controller and add a route for the module root. if ((new \ReflectionObject($this))->hasMethod('indexController')) { $routes->add('index', new Route($this->getMountedUrl())); } // Load routes from file. $filename = sprintf('%s/%s/%s', $this->getModuleRoot(), ResourceLocations::RESOURCES_DIRECTORY, self::FILENAME_ROUTES); $container = new Configuration($this->getConfigLoader()); $container->merge($filename); // Add a route for each record in the routes file. foreach ($container->all() as $name => $parameters) { $defaults = array(); $requirements = array(); $options = array(); $path = sprintf('%s/%s', $this->getMountedUrl(), $this->config(sprintf('routes.%s', $name), $name)); foreach ($parameters ?: array() as $parameter) { $parameterName = $parameter['name']; $path = sprintf('%s/{%s}', $path, $parameterName); if (isset($parameter['default'])) { $defaults[$parameterName] = $parameter['default']; } if (isset($parameter['requirements'])) { $requirements[$parameterName] = $parameter['requirements']; } if (isset($parameter['options'])) { $options[$parameterName] = $parameter['options']; } } $routes->add($name, new Route($path, $defaults, $requirements, $options)); } return $routes; }
/** * @inheritdoc */ public function render() { LoggerRegistry::debug('View::render()'); $content = null; $this->rendering = true; if ($this->getTargetCount() === 2) { $request = $this->getRequest(); // The arguments to the module definition are decorators, set them now call_user_func_array(array($this, 'activateDecorators'), $this->getTargetArguments(self::TARGET_LEVEL_MODULE)); // Create a relevant context $context = $this->getEngine()->getViewFactory()->buildViewContext($this, $this->getRequest()); // Check for and execute a target controller $targetController = $context->getTargetController($this, $request); $targetControllerResult = null; if (!is_null($targetController) && is_callable($targetController)) { $targetControllerResult = TypeUtilities::invokeCallable($targetController, null, array($this, $request), $this->getTargetArguments(self::TARGET_LEVEL_METHOD) ?: array()); } // A result of false means don't render anything. if ($targetControllerResult !== false) { // Use the context to render the result $content = $context->render($this->getEngine()->getViewFactory()->getRendererRegistry(), $targetControllerResult) ?: ''; // Decorate using active decorators foreach ($this->getActiveDecorators() as $active) { $decorator = $this->getEngine()->getViewFactory()->getDecoratorRegistry()->getDecorator($active['name']); $content = TypeUtilities::invokeCallable(array($decorator, 'decorate'), array($content), array($this, $request), $active['arguments']); } } } else { // Incorrect number of targets; exactly 2 expected $targets = $this->getTargetCount() === 1 ? 'target' : 'targets'; throw new \LogicException(sprintf('Error in view script; exactly 2 targets expected ("$view->module()->method()"); %d %s encountered', $this->getTargetCount(), $targets)); } // Ensure we are returning a string value $this->rendering = false; return $content ?: ''; }