private function loadThemeConfig(Config $config, $theme, $childTheme) { $themePath = $this->app['resources']->getPath('themebase') . '/' . $theme; if (file_exists($themePath)) { $configFile = $themePath . '/config.yml'; // insert parent path right after child path. $twigPath = $config->get('twigpath'); if ($twigPath) { $childThemePath = $this->app['resources']->getPath('themebase') . '/' . $childTheme; $key = array_search($childThemePath, $twigPath); if ($key !== false) { array_splice($twigPath, $key, 1, array($childThemePath, $themePath)); } $config->set('twigpath', $twigPath); } if (file_exists($configFile)) { $themeConfig = self::mergeConfigFile($configFile); if ($themeConfig) { // load parent theme config, but without overwriting, because child prevails $config->set('theme', Arr::mergeRecursiveDistinct($themeConfig, $config->get('theme'))); // multiple levels allowed if (!empty($themeConfig['parent'])) { $this->loadThemeConfig($config, $themeConfig['parent'], $theme); } } } } }
/** * {@inheritdoc} */ public function check(ExceptionControllerInterface $exceptionController) { /** @var Controller\Exception $exceptionController */ $dbConfig = $this->config->get('general/database'); $driver = $dbConfig['driver']; if ($driver === 'pdo_sqlite') { return $this->doDatabaseSqliteCheck($exceptionController, $dbConfig); } if (!in_array($driver, ['pdo_mysql', 'pdo_pgsql'])) { return $exceptionController->databaseDriver('unsupported', null, $driver); } if ($driver === 'pdo_mysql' && extension_loaded('pdo_mysql') === false) { return $exceptionController->databaseDriver('missing', 'MySQL', 'pdo_mysql'); } if ($driver === 'pdo_pgsql' && extension_loaded('pdo_pgsql') === false) { return $exceptionController->databaseDriver('missing', 'PostgreSQL', 'pdo_pgsql'); } if (empty($dbConfig['dbname'])) { return $exceptionController->databaseDriver('parameter', null, $driver, 'databasename'); } if (empty($dbConfig['user'])) { return $exceptionController->databaseDriver('parameter', null, $driver, 'username'); } if (empty($dbConfig['password']) && $dbConfig['user'] === 'root') { return $exceptionController->databaseDriver('insecure', null, $driver); } return null; }
/** * Initialize the configuration value. */ public function initialize() { if (!$this->checked) { $this->config->checkConfig(); $this->checked = true; $this->data = $this->config->get($this->path, $this->default); } }
/** * Add base snippets to the response. */ protected function addSnippets() { $this->queue->add(Target::END_OF_HEAD, '<meta name="generator" content="Bolt">'); if ($this->config->get('general/canonical')) { $canonical = $this->resources->getUrl('canonicalurl'); $this->queue->add(Target::END_OF_HEAD, $this->encode('<link rel="canonical" href="%s">', $canonical)); } if ($favicon = $this->config->get('general/favicon')) { $host = $this->resources->getUrl('hosturl'); $theme = $this->resources->getUrl('theme'); $this->queue->add(Target::END_OF_HEAD, $this->encode('<link rel="shortcut icon" href="%s%s%s">', $host, $theme, $favicon)); } }
/** * Add base snippets to the response. */ protected function addSnippets() { $generatorSnippet = (new Snippet())->setLocation(Target::END_OF_HEAD)->setCallback('<meta name="generator" content="Bolt">'); $this->queue->add($generatorSnippet); $canonicalUrl = $this->canonical->getUrl(); $canonicalSnippet = (new Snippet())->setLocation(Target::END_OF_HEAD)->setCallback($this->encode('<link rel="canonical" href="%s">', $canonicalUrl)); $this->queue->add($canonicalSnippet); if ($favicon = $this->config->get('general/favicon')) { $faviconUrl = $this->packages->getUrl($favicon, 'theme'); $faviconSnippet = (new Snippet())->setLocation(Target::END_OF_HEAD)->setCallback($this->encode('<link rel="shortcut icon" href="%s">', $faviconUrl)); $this->queue->add($faviconSnippet); } }
/** * Kernel request listener callback. * * @param GetResponseEvent $event */ public function onKernelRequest(GetResponseEvent $event) { if (!$event->isMasterRequest()) { return; } $contenttypes = $this->config->get('contenttypes', []); foreach ($contenttypes as $contenttype) { $contenttype = $this->em->getContentType($contenttype['slug']); // Check if we need to 'publish' any 'timed' records, or 'depublish' any expired records. $this->em->publishTimedRecords($contenttype); $this->em->depublishExpiredRecords($contenttype); } }
/** * Check (and update) any records that need to be updated from "published" to "held". */ public function holdExpiredRecords() { foreach ($this->contentTypeNames as $contentTypeName) { $this->timedHandleRecords($contentTypeName, 'hold'); } $this->cache->save(self::CACHE_KEY_HOLD, true, $this->config->get('general/caching/duration', 10)); }
/** * Handle errors thrown in the application. * * @param GetResponseForExceptionEvent $event */ public function onKernelException(GetResponseForExceptionEvent $event) { if ($this->isProfilerRequest($event->getRequest())) { return; } $exception = $event->getException(); $message = $exception->getMessage(); $statusCode = Response::HTTP_INTERNAL_SERVER_ERROR; if ($exception instanceof HttpExceptionInterface) { $statusCode = $exception->getStatusCode(); } // Log the error message $level = LogLevel::CRITICAL; if ($exception instanceof HttpExceptionInterface && $exception->getStatusCode() < 500) { $level = LogLevel::WARNING; } $this->logger->log($level, $message, ['event' => 'exception', 'exception' => $exception]); // Get and send the response if ($this->isJsonRequest($event->getRequest())) { $response = new JsonResponse(['success' => false, 'errorType' => get_class($exception), 'code' => $statusCode, 'message' => $message]); } elseif ($this->config->get('general/debug_error_use_symfony')) { return null; } else { $response = $this->exceptionController->kernelException($event); } $response->setStatusCode($statusCode); $event->setResponse($response); }
/** * Build a valid AJAX response for in-place saves that account for pre/post * save events. * * @param Entity\Content $content * @param boolean $flush * * @return JsonResponse */ private function createJsonUpdate(Entity\Content $content, $flush) { /* * Flush any buffers from saveConent() dispatcher hooks * and make sure our JSON output is clean. * * Currently occurs due to exceptions being generated in the dispatchers * in \Bolt\Storage::saveContent() * StorageEvents::PRE_SAVE * StorageEvents::POST_SAVE */ if ($flush) { Response::closeOutputBuffers(0, false); } $val = $content->toArray(); if (isset($val['datechanged'])) { $val['datechanged'] = (new Carbon($val['datechanged']))->toIso8601String(); } // Adjust decimal point as some locales use a comma and… JavaScript $lc = localeconv(); $fields = $this->config->get('contenttypes/' . $content->getContenttype() . '/fields'); foreach ($fields as $key => $values) { if ($values['type'] === 'float' && $lc['decimal_point'] === ',') { $val[$key] = str_replace('.', ',', $val[$key]); } } // Unset flashbag for ajax $this->loggerFlash->clear(); return new JsonResponse($val); }
/** * Return an array of ContentTypes with the table name is the key. * * @param Config $config * * @return array */ private function getNormalisedContentTypes(Config $config) { $normalised = []; $contentTypes = $config->get('contenttypes'); foreach ($contentTypes as $contentType) { $normalised[$contentType['tablename']] = $contentType; } return $normalised; }
/** * Fetch a listing of ContentType records. * * @param string $contentTypeSlug * @param ListingOptions $options */ public function action($contentTypeSlug, ListingOptions $options) { // Order has to be set carefully. Either set it explicitly when the user // sorts, or fall back to what's defined in the contenttype. Except for // a ContentType that has a "grouping taxonomy", as that should override // it. That exception state is handled by the query OrderHandler. $contenttype = $this->em->getContentType($contentTypeSlug); $contentParameters = ['paging' => true, 'hydrate' => true, 'order' => $options->getOrder() ?: $contenttype['sort'], 'page' => $options->getPage(), 'filter' => $options->getFilter()]; // Set the amount of items to show per page if (!empty($contenttype['recordsperpage'])) { $contentParameters['limit'] = $contenttype['recordsperpage']; } else { $contentParameters['limit'] = $this->config->get('general/recordsperpage'); } // Filter on taxonomies if ($options->getTaxonomies() !== null) { foreach ($options->getTaxonomies() as $taxonomy => $value) { $contentParameters[$taxonomy] = $value; } } return $this->getContent($contentTypeSlug, $contentParameters, $options); }
/** * Does some checks to see whether a ContentType should appear in search results. * This is based on ContentType options. * * @param string $contentType * * @return boolean */ protected function isInvisible($contentType) { $info = $this->config->get('contenttypes/' . $contentType); if ($info) { if (array_key_exists('viewless', $info) && $info['viewless'] == true) { return true; } if (array_key_exists('searchable', $info) && $info['searchable'] == false) { return true; } } return false; }
/** * Build the schema for Bolt ContentType tables. * * @param Schema $schema * @param Config $config * * @return \Doctrine\DBAL\Schema\Table[] */ public function getSchemaTables(Schema $schema, Config $config) { /** @var $fieldManager FieldManager */ $fieldManager = $config->getFields(); $contentTypes = $config->get('contenttypes'); $tables = []; foreach ($this->tables->keys() as $name) { $contentType = $contentTypes[$name]; $tables[$name] = $this->tables[$name]->buildTable($schema, $this->prefix . $name, $name); if (isset($contentType['fields']) && is_array($contentType['fields'])) { $this->addContentTypeTableColumns($this->tables[$name], $tables[$name], $contentType['fields'], $fieldManager); } } return $tables; }
/** * 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); } }
/** * 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->config->get('general/add_jquery', false) && !$this->config->get('theme/add_jquery', false)) { return $html; } $zone = Zone::FRONTEND; if ($request = $this->requestStack->getCurrentRequest()) { $zone = Zone::get($request); } $regex = '/<script(.*)jquery(-latest|-[0-9\\.]*)?(\\.min)?\\.js/'; if ($zone === Zone::FRONTEND && !preg_match($regex, $html)) { $jqueryfile = $this->resources->getPath('app/view/js/jquery-2.1.4.min.js'); $asset = (new Snippet())->setLocation(Target::BEFORE_JS)->setCallback('<script src="' . $jqueryfile . '"></script>'); $html = $this->injector->inject($asset, $asset->getLocation(), $html); } return $html; }
/** * Gets slugs from config imploded for regex. * * @param string $key contenttypes or taxonomy * @param bool $singular Include singular slugs * * @return string */ protected function configAssert($key, $singular) { $types = $this->config->get($key); // No types, nothing to assert. The route _DOES_ expect a string, so // we return a regex that never matches. if (empty($types)) { return '$.'; } $slugs = []; foreach ($types as $type) { $slugs[] = $type['slug']; if ($singular) { $slugs[] = $type['singular_slug']; } } return implode('|', $slugs); }
/** * StorageEvents::PRE_SAVE event callback. * * @param StorageEvent $event */ public function preSave(StorageEvent $event) { $request = $this->requestStack->getCurrentRequest(); if ($request === null) { return; } $contentType = $this->boltConfig->get('contenttypes/' . $event->getContentType()); $translatableFields = $this->getTranslatableFields($contentType['fields']); /** @var Content $record */ $record = $event->getContent(); $values = $record->serialize(); $localeValues = []; if (empty($translatableFields)) { return; } $localeSlug = $request->get('_locale'); $record->set($localeSlug . 'slug', $values['slug']); $locales = $this->config->getLocales(); if ($values['_locale'] == reset($locales)->getSlug()) { $record->set($localeSlug . 'data', '[]'); return; } if ($values['id']) { /** @var Content $defaultContent */ $defaultContent = $this->query->getContent($event->getContentType(), ['id' => $values['id'], 'returnsingle' => true]); } if (in_array('templatefields', $translatableFields)) { $templateFields = $this->boltConfig->get('theme/templatefields/' . $values['template'] . '/fields'); foreach ($templateFields as $key => $field) { if ($field['type'] === 'repeater') { $values['templatefields'][$key] = json_encode($values['templatefields'][$key]); } } } foreach ($translatableFields as $field) { $localeValues[$field] = $values[$field]; if ($values['id']) { $record->set($field, $defaultContent->get($field)); } else { $record->set($field, ''); } } $localeJson = json_encode($localeValues); $record->set($localeSlug . 'data', $localeJson); }
/** * Add an extension to be registered. * * @param ExtensionInterface $extension * @param DirectoryInterface $baseDir * @param string $relativeUrl * @param string|null $composerName * * @throws \RuntimeException * * @return ResolvedExtension */ public function add(ExtensionInterface $extension, DirectoryInterface $baseDir, $relativeUrl, $composerName = null) { if ($this->registered) { throw new \RuntimeException(Trans::__('Can not add extensions after they are registered.')); } // Set paths in the extension $extension->setBaseDirectory($baseDir)->setRelativeUrl($relativeUrl); // Determine if enabled $enabled = $this->config->get('extensions/' . $extension->getId(), true); if ($composerName !== null) { // Map composer name to ID $this->composerNames[$composerName] = $extension->getId(); // Check if enabled by composer name $enabled = $this->config->get("extensions/{$composerName}", $enabled); } // Instantiate resolved extension and mark enabled/disabled $resolved = (new ResolvedExtension($extension))->setEnabled($enabled); return $this->extensions[$extension->getId()] = $resolved; }
/** * Determine which templates will result in templatefields. * * @param ContentType $contentType * @param Content $content * * @return array */ private function getTemplateFieldTemplates(ContentType $contentType, Content $content) { $templateFieldTemplates = []; $templateFieldsConfig = $this->config->get('theme/templatefields'); if ($templateFieldsConfig) { $templateFieldTemplates = array_keys($templateFieldsConfig); // Special case for default template $toRepair = []; foreach ($contentType['fields'] as $name => $field) { if ($field['type'] === 'templateselect' && !empty($content->values[$name])) { $toRepair[$name] = $content->values[$name]; $content->set($name, ''); } } if ($content->hasTemplateFields()) { $templateFieldTemplates[] = ''; } foreach ($toRepair as $name => $value) { $content->set($name, $value); } } return $templateFieldTemplates; }
/** * Get the array of configured acceptable file extensions. * * @return array */ public function getAllowedUploadExtensions() { return $this->config->get('general/accept_file_types'); }