/** * beforeCallback. * * This callback adds the CSS/JS for the localeswitcher on the backend * and checks that we are on a valid locale when on the frontend */ public function beforeCallback(Request $request) { $routeParams = $request->get('_route_params'); if ($this->app['config']->getWhichEnd() == 'backend') { if (array_key_exists('contenttypeslug', $routeParams)) { $this->addCss('assets/css/field_locale.css'); if (!empty($routeParams['id'])) { $this->addJavascript('assets/js/field_locale.js', array('late' => true)); } } } else { if (isset($routeParams['_locale'])) { $this->app['menu'] = $this->app->share(function ($app) { $builder = new Menu\LocalizedMenuBuilder($app); return $builder; }); $locales = $this->app['config']->get('general/locales'); foreach ($locales as $isolocale => $locale) { if ($locale['slug'] == $routeParams['_locale']) { $foundLocale = $isolocale; } } if (isset($foundLocale)) { setlocale(LC_ALL, $foundLocale); $this->app['config']->set('general/locale', $foundLocale); } else { $routeParams['_locale'] = reset($locales)['slug']; return $this->app->redirect(Lib::path($request->get('_route'), $routeParams)); } } } }
public function testPath() { $app = $this->getApp(); $app->run(); $this->expectOutputRegex('#Redirecting to /bolt/#'); $basic = 'homepage'; $this->assertEquals('/', Library::path($basic)); $this->assertEquals('/pages/content', Library::path('contentlink', ['contenttypeslug' => 'pages', 'slug' => 'content'])); $query = 'testing=yes'; $this->assertEquals('/search?testing=yes', Library::path('search', [], $query)); }
/** * Collect the date for the Toolbar item. * * @param Request $request * @param Response $response * @param \Exception $exception */ public function collect(Request $request, Response $response, \Exception $exception = null) { $this->data = array('version' => $this->app->getVersion(false), 'name' => $this->app['bolt_name'], 'fullversion' => 'Version: ' . $this->app->getVersion(true), 'payoff' => 'Sophisticated, lightweight & simple CMS', 'aboutlink' => sprintf("<a href=\"%s\">%s</a>", Lib::path('about'), 'About'), 'branding' => null, 'editlink' => null, 'edittitle' => null); if ($this->app['config']->get('general/branding/provided_by/0')) { $this->data['branding'] = sprintf("%s <a href=\"mailto:%s\">%s</a>", Trans::__('Provided by:'), $this->app['config']->get('general/branding/provided_by/0'), $this->app['config']->get('general/branding/provided_by/1')); } if (!empty($this->app['editlink'])) { $this->data['editlink'] = $this->app['editlink']; $this->data['edittitle'] = $this->app['edittitle']; } }
public function testPath() { $app = $this->getApp(); $app->run(); $this->expectOutputRegex('#Redirecting to /bolt/#'); $basic = "homepage"; $this->assertEquals("/", Library::path($basic)); $this->assertEquals('/pages/content', Library::path('contentlink', array('contenttypeslug' => 'pages', 'slug' => 'content'))); $query = "testing=yes"; $this->assertEquals("/search?testing=yes", Library::path("search", array(), $query)); }
public function record(\Silex\Application $app, $contenttypeslug, $slug = '') { $contenttype = $app['storage']->getContentType($contenttypeslug); // If the contenttype is 'viewless', don't show the record page. if (isset($contenttype['viewless']) && $contenttype['viewless'] === true) { return $app->abort(Response::HTTP_NOT_FOUND, "Page {$contenttypeslug}/{$slug} not found."); } // Perhaps we don't have a slug. Let's see if we can pick up the 'id', instead. if (empty($slug)) { $slug = $app['request']->get('id'); } $slug = $app['slugify']->slugify($slug); // First, try to get it by slug. $content = $app['storage']->getContent($contenttype['slug'], array('slug' => $slug, 'returnsingle' => true, 'log_not_found' => !is_numeric($slug))); if (!$content && !is_numeric($slug)) { // And otherwise try getting it by translated slugs $match = $this->matchTranslatedSlug($app, $contenttype['slug'], $slug); if (!empty($match)) { $content = $app['storage']->getContent($contenttype['slug'], array('id' => $match['content_type_id'], 'returnsingle' => true)); } } if (!$content && is_numeric($slug)) { // And otherwise try getting it by ID $content = $app['storage']->getContent($contenttype['slug'], array('id' => $slug, 'returnsingle' => true)); } // No content, no page! if (!$content) { return $app->abort(Response::HTTP_NOT_FOUND, "Page {$contenttypeslug}/{$slug} not found."); } // Then, select which template to use, based on our 'cascading templates rules' $template = $app['templatechooser']->record($content); $paths = $app['resources']->getPaths(); // Setting the canonical URL. if ($content->isHome() && $template == $app['config']->get('general/homepage_template')) { $app['resources']->setUrl('canonicalurl', $paths['rooturl']); } else { $url = $paths['canonical'] . $content->link(); $app['resources']->setUrl('canonicalurl', $url); } // Setting the editlink $app['editlink'] = Lib::path('editcontent', array('contenttypeslug' => $contenttype['slug'], 'id' => $content->id)); $app['edittitle'] = $content->getTitle(); // Make sure we can also access it as {{ page.title }} for pages, etc. We set these in the global scope, // So that they're also available in menu's and templates rendered by extensions. $app['twig']->addGlobal('record', $content); $app['twig']->addGlobal($contenttype['singular_slug'], $content); // Render the template and return. return $this->render($app, $template, $content->getTitle()); }
/** * Updates a menu item to have at least a 'link' key. * * @param array $item * * @return array Keys 'link' and possibly 'label', 'title' and 'path' */ private function menuHelper($item) { // recurse into submenu's if (isset($item['submenu']) && is_array($item['submenu'])) { $item['submenu'] = $this->menuHelper($item['submenu']); } if (isset($item['route'])) { $param = !empty($item['param']) ?: array(); $add = !empty($item['add']) ?: ''; $item['link'] = Lib::path($item['route'], $param, $add); } elseif (isset($item['path'])) { $item = $this->resolvePathToContent($item); } return $item; }
/** * Sanity checks for doubles in in contenttypes. */ public function checkConfig() { $slugs = array(); $wrongctype = false; foreach ($this->data['contenttypes'] as $key => $ct) { /** * Make sure any field that has a 'uses' parameter actually points to a field that exists. * * For example, this will show a notice: * entries: * name: Entries * singular_name: Entry * fields: * title: * type: text * class: large * slug: * type: slug * uses: name */ foreach ($ct['fields'] as $fieldname => $field) { // Verify that the contenttype doesn't try to add fields that are reserved. if ($fieldname != 'slug' && in_array($fieldname, $this->reservedFieldNames)) { $error = Trans::__('contenttypes.generic.reserved-name', array('%contenttype%' => $key, '%field%' => $fieldname)); $this->app['session']->getFlashBag()->add('error', $error); return; } // Check 'uses'. If it's an array, split it up, and check the separate parts. We also need to check // for the fields that are always present, like 'id'. if (is_array($field) && !empty($field['uses'])) { foreach ($field['uses'] as $useField) { if (!empty($field['uses']) && empty($ct['fields'][$useField]) && !in_array($useField, $this->reservedFieldNames)) { $error = Trans::__('contenttypes.generic.wrong-use-field', array('%contenttype%' => $key, '%field%' => $fieldname, '%uses%' => $useField)); $this->app['session']->getFlashBag()->add('error', $error); return; } } } // Make sure the 'type' is in the list of allowed types if (!isset($field['type']) || !$this->fields->has($field['type'])) { $error = Trans::__('contenttypes.generic.no-proper-type', array('%contenttype%' => $key, '%field%' => $fieldname, '%type%' => $field['type'])); $this->app['session']->getFlashBag()->add('error', $error); $wrongctype = true && $this->app['users']->getCurrentUsername(); } } // Keep a running score of used slugs. if (!isset($slugs[$ct['slug']])) { $slugs[$ct['slug']] = 0; } $slugs[$ct['slug']]++; if (!isset($slugs[$ct['singular_slug']])) { $slugs[$ct['singular_slug']] = 0; } if ($ct['singular_slug'] != $ct['slug']) { $slugs[$ct['singular_slug']]++; } } // Check DB-tables integrity if (!$wrongctype && $this->app['integritychecker']->needsCheck() && count($this->app['integritychecker']->checkTablesIntegrity()) > 0 && $this->app['users']->getCurrentUsername()) { $msg = Trans::__("The database needs to be updated/repaired. Go to 'Configuration' > '<a href=\"%link%\">Check Database</a>' to do this now.", array('%link%' => Lib::path('dbcheck'))); $this->app['session']->getFlashBag()->add('error', $msg); return; } // Sanity checks for taxonomy.yml foreach ($this->data['taxonomy'] as $key => $taxo) { // Show some helpful warnings if slugs or keys are not set correctly. if ($taxo['slug'] != $key) { $error = Trans::__("The identifier and slug for '%taxonomytype%' are the not the same ('%slug%' vs. '%taxonomytype%'). Please edit taxonomy.yml, and make them match to prevent inconsistencies between database storage and your templates.", array('%taxonomytype%' => $key, '%slug%' => $taxo['slug'])); $this->app['session']->getFlashBag()->add('error', $error); return; } } // if there aren't any other errors, check for duplicates across contenttypes. if (!$this->app['session']->getFlashBag()->has('error')) { foreach ($slugs as $slug => $count) { if ($count > 1) { $error = Trans::__("The slug '%slug%' is used in more than one contenttype. Please edit contenttypes.yml, and make them distinct.", array('%slug%' => $slug)); $this->app['session']->getFlashBag()->add('error', $error); return; } } } }
/** * Stub for edit_post_link. */ function edit_post_link($text = null, $before = '', $after = '', $id = 0) { global $post, $currentuser; if (!is_object($post) || empty($currentuser['username'])) { return; } $path = \Bolt\Library::path('editcontent', ['contenttypeslug' => $post->contenttype['slug'], 'id' => $post['id']]); if (null === $text) { $text = __('Edit This'); } $link = '<a class="post-edit-link" href="' . $path . '">' . $text . '</a>'; /** * Filter the post edit link anchor tag. * * @since 2.3.0 * * @param string $link Anchor tag for the edit link. * @param int $post_id Post ID. * @param string $text Anchor text. */ echo $before . apply_filters('edit_post_link', $link, $post['id'], $text) . $after; }
/** * Creates a link to EDIT this record, if the user is logged in. * * @return string */ public function editlink() { $perm = "contenttype:" . $this->contenttype['slug'] . ":edit:" . $this->id; if ($this->app['users']->isAllowed($perm)) { return Lib::path('editcontent', array('contenttypeslug' => $this->contenttype['slug'], 'id' => $this->id)); } else { return false; } }
/** * Updates a menu item to have at least a 'link' key. * * @param array $item * * @return array Keys 'link' and possibly 'label', 'title' and 'path' */ private function menuHelper($item) { if (isset($item['submenu']) && is_array($item['submenu'])) { $item['submenu'] = $this->menuHelper($item['submenu']); } if (isset($item['path']) && $item['path'] == "homepage") { $item['link'] = $this->app['paths']['root']; } elseif (isset($item['route'])) { $param = empty($item['param']) ? array() : $item['param']; $add = empty($item['add']) ? '' : $item['add']; $item['link'] = Lib::path($item['route'], $param, $add); } elseif (isset($item['path']) && !isset($item['link'])) { if (preg_match('#^(https?://|//)#i', $item['path'])) { // We have a mistakenly placed URL, allow it but log it. $item['link'] = $item['path']; $this->app['logger.system']->error(Trans::__('Invalid menu path (%PATH%) set in menu.yml. Probably should be a link: instead!', array('%PATH%' => $item['path'])), array('event' => 'config')); } else { // Get a copy of the path minus trainling/leading slash $path = ltrim(rtrim($item['path'], '/'), '/'); // Pre-set our link in case the match() throws an exception $item['link'] = '/' . $path; try { // See if we have a 'content/id' or 'content/slug' path if (preg_match('#^([a-z0-9_-]+)/([a-z0-9_-]+)$#i', $path)) { // Determine if the provided path first matches any routes // that we have, this will catch any valid configured // contenttype slug and record combination, or throw a // ResourceNotFoundException exception otherwise $this->app['url_matcher']->match('/' . $path); // If we found a valid routing match then we're still here, // attempt to retrive the actual record. $content = $this->app['storage']->getContent($path); if ($content instanceof \Bolt\Content) { if (empty($item['label'])) { $item['label'] = !empty($content->values['title']) ? $content->values['title'] : ""; } if (empty($item['title'])) { $item['title'] = !empty($content->values['subtitle']) ? $content->values['subtitle'] : ""; } $item['link'] = $content->link(); } } else { $item['link'] = $this->app['request']->getBasePath() . '/' . $path; } } catch (ResourceNotFoundException $e) { $this->app['logger.system']->error(Trans::__('Invalid menu path (%PATH%) set in menu.yml. Does not match any configured contenttypes or routes.', array('%PATH%' => $item['path'])), array('event' => 'config')); } } } return $item; }
/** * Create the first user. * * @param Application $app * @param Request $request * * @return \Twig_Markup|\Symfony\Component\HttpFoundation\RedirectResponse */ public function userFirst(Application $app, Request $request) { // We should only be here for creating the first user if ($app['integritychecker']->checkUserTableIntegrity() && $app['users']->hasUsers()) { return Lib::redirect('dashboard'); } // Get and empty user array $user = $app['users']->getEmptyUser(); // Add a note, if we're setting up the first user using SQLite. $dbdriver = $app['config']->get('general/database/driver'); if ($dbdriver === 'sqlite' || $dbdriver === 'pdo_sqlite') { $note = Trans::__('page.edit-users.note-sqlite'); } else { $note = ''; } // If we get here, chances are we don't have the tables set up, yet. $app['integritychecker']->repairTables(); // Grant 'root' to first user by default $user['roles'] = array(Permissions::ROLE_ROOT); // Get the form $form = $this->getUserForm($app, $user, true); // Set the validation $form = $this->setUserFormValidation($app, $form, true); /** @var \Symfony\Component\Form\Form */ $form = $form->getForm(); // Check if the form was POST-ed, and valid. If so, store the user. if ($request->isMethod('POST')) { if ($this->validateUserForm($app, $form, true)) { // To the dashboard, where 'login' will be triggered return $app->redirect(Lib::path('dashboard')); } } $context = array('kind' => 'create', 'form' => $form->createView(), 'note' => $note, 'displayname' => $user['displayname']); return $app['render']->render('firstuser/firstuser.twig', array('context' => $context)); }
/** * Create a link to edit a .yml file, if a filename is detected in the string. Mostly * for use in Flashbag messages, to allow easy editing. * * @param string $str * @param boolean $safe * * @return string Resulting string */ public function ymllink($str, $safe) { // There is absolutely no way anyone could possibly need this in a "safe" context if ($safe) { return null; } $matches = array(); if (preg_match('/ ([a-z0-9_-]+\\.yml)/i', $str, $matches)) { $path = Lib::path('fileedit', array('namespace' => 'config', 'file' => $matches[1])); $link = sprintf(' <a href="%s">%s</a>', $path, $matches[1]); $str = preg_replace('/ ([a-z0-9_-]+\\.yml)/i', $link, $str); } return $str; }
/** * Return the URI for a package's config file edit window. * * @param string $name * * @return string */ private function linkConfig($name) { // Generate the configfilename from the extension $name $configfilename = join(".", array_reverse(explode("/", $name))) . '.yml'; // Check if we have a config file, and if it's readable. (yet) $configfilepath = $this->app['resources']->getPath('extensionsconfig/' . $configfilename); if (is_readable($configfilepath)) { return Lib::path('fileedit', array('namespace' => 'config', 'file' => 'extensions/' . $configfilename)); } return null; }
protected function showCleanup($output, $name, $version) { $pack = array(); foreach ($output as $item) { if (strpos($item, ' : ') !== false) { $split = explode(' : ', $item); $split[0] = str_replace('.', '', $split[0]); $pack[trim($split[0])] = trim($split[1]); $pack['version'] = $version; } } if (count($pack) < 1) { $pack['name'] = $name; $pack['version'] = 'unknown'; $pack['type'] = 'unknown'; $pack['descrip'] = 'Not yet installed'; } // flatten the composer array one level to make working easier $initializedExtensions = array(); foreach ($this->app['extensions']->composer as $val) { $initializedExtensions += $val; } // For Bolt, we also need to know if the extension has a 'README' and a 'config.yml' file. // Note we only do this for successfully loaded extensions. if (isset($initializedExtensions[$name])) { $paths = $this->app['resources']->getPaths(); if (is_readable($paths['extensionspath'] . '/vendor/' . $pack['name'] . '/README.md')) { $pack['readme'] = $pack['name'] . '/README.md'; } elseif (is_readable($paths['extensionspath'] . '/vendor/' . $pack['name'] . '/readme.md')) { $pack['readme'] = $pack['name'] . '/readme.md'; } if (!empty($pack['readme'])) { $pack['readmelink'] = $paths['async'] . 'readme/' . $pack['readme']; } // generate the configfilename from the extension $name $configfilename = join(".", array_reverse(explode("/", $name))) . '.yml'; // Check if we have a config file, and if it's readable. (yet) $configfilepath = $paths['extensionsconfig'] . '/' . $configfilename; if (is_readable($configfilepath)) { $configfilename = 'extensions/' . $configfilename; $pack['config'] = Lib::path('fileedit', array('namespace' => 'config', 'file' => $configfilename)); } // as a bonus we add the extension title to the pack $pack['title'] = $initializedExtensions[$name]['name']; $pack['authors'] = $initializedExtensions[$name]['json']['authors']; } return $pack; }