/** * The default before filter for the controllers in this file. * * Refer to the routing.yml config file for overridding. * * @param Request $request The Symfony Request * * @return null|BoltResponse|RedirectResponse */ public function before(Request $request) { // Start the 'stopwatch' for the profiler. $this->app['stopwatch']->start('bolt.frontend.before'); // If there are no users in the users table, or the table doesn't exist. // Repair the DB, and let's add a new user. if (!$this->hasUsers()) { $this->flashes()->info(Trans::__('general.phrase.users-none-create-first')); return $this->redirectToRoute('userfirst'); } // If we are in maintenance mode and current user is not logged in, show maintenance notice. if ($this->getOption('general/maintenance_mode')) { if (!$this->isAllowed('maintenance-mode')) { $template = $this->templateChooser()->maintenance(); $response = $this->render($template); $response->setStatusCode(Response::HTTP_SERVICE_UNAVAILABLE); return $response; } } // If we have a valid cache respose, return it. if ($response = $this->app['render']->fetchCachedRequest()) { // Stop the 'stopwatch' for the profiler. $this->app['stopwatch']->stop('bolt.frontend.before'); // Short-circuit the request, return the HTML/response. YOLO. return $response; } // Stop the 'stopwatch' for the profiler. $this->app['stopwatch']->stop('bolt.frontend.before'); return null; }
/** * Check whether or not the GD-library can be used in PHP. Needed for making thumbnails. */ protected function gdCheck() { if (!function_exists('imagecreatetruecolor')) { $notice = "The current version of PHP doesn't have the GD library enabled. Without this, Bolt will not be able to generate thumbnails. Please enable <tt>php-gd</tt>, or ask your system-administrator to do so."; $this->app['logger.flash']->configuration(Trans::__($notice)); } }
/** * Handle a login attempt. * * @param \Silex\Application $app The application/container * @param Request $request The Symfony Request * * @return \Symfony\Component\HttpFoundation\RedirectResponse */ public function postLogin(Silex\Application $app, Request $request) { switch ($request->get('action')) { case 'login': // Log in, if credentials are correct. $result = $app['users']->login($request->get('username'), $request->get('password')); if ($result) { $app['logger.system']->info('Logged in: ' . $request->get('username'), array('event' => 'authentication')); $retreat = $app['session']->get('retreat'); $redirect = !empty($retreat) && is_array($retreat) ? $retreat : array('route' => 'dashboard', 'params' => array()); return Lib::redirect($redirect['route'], $redirect['params']); } return $this->getLogin($app, $request); case 'reset': // Send a password request mail, if username exists. $username = trim($request->get('username')); if (empty($username)) { $app['users']->session->getFlashBag()->add('error', Trans::__('Please provide a username', array())); } else { $app['users']->resetPasswordRequest($request->get('username')); return Lib::redirect('login'); } return $this->getLogin($app, $request); } // Let's not disclose any internal information. $app->abort(Response::HTTP_BAD_REQUEST, 'Invalid request'); }
/** * List browse on the server, so we can insert them in the file input. * * @param Request $request * @param string $namespace * @param string $path * * @return \Bolt\Response\BoltResponse */ public function browse(Request $request, $namespace, $path) { // No trailing slashes in the path. $path = rtrim($path, '/'); $filesystem = $this->filesystem()->getFilesystem($namespace); // $key is linked to the fieldname of the original field, so we can // Set the selected value in the proper field $key = $request->query->get('key'); // Get the pathsegments, so we can show the path. $pathsegments = []; $cumulative = ''; if (!empty($path)) { foreach (explode('/', $path) as $segment) { $cumulative .= $segment . '/'; $pathsegments[$cumulative] = $segment; } } try { $filesystem->listContents($path); } catch (\Exception $e) { $msg = Trans::__("Folder '%s' could not be found, or is not readable.", ['%s' => $path]); $this->flashes()->error($msg); } list($files, $folders) = $filesystem->browse($path, $this->app); $context = ['namespace' => $namespace, 'files' => $files, 'folders' => $folders, 'pathsegments' => $pathsegments, 'key' => $key]; return $this->render('files_async/files_async.twig', ['context' => $context], ['title', Trans::__('Files in %s', ['%s' => $path])]); }
/** * Set up Composer JSON file. * * @return array|null */ public function updateJson() { if (!is_file($this->getOption('composerjson'))) { $this->initJson($this->getOption('composerjson')); } $jsonFile = new JsonFile($this->getOption('composerjson')); if ($jsonFile->exists()) { $json = $jsonorig = $jsonFile->read(); // Workaround Bolt 2.0 installs with "require": [] if (isset($json['require']) && empty($json['require'])) { unset($json['require']); } $json = $this->setJsonDefaults($json); } else { // Error $this->messages[] = Trans::__("The Bolt extensions file '%composerjson%' isn't readable.", ['%composerjson%' => $this->getOption('composerjson')]); $this->app['extend.writeable'] = false; $this->app['extend.online'] = false; return null; } // Write out the file, but only if it's actually changed, and if it's writable. if ($json != $jsonorig) { try { umask(00); $jsonFile->write($json); } catch (\Exception $e) { $this->messages[] = Trans::__('The Bolt extensions Repo at %repository% is currently unavailable. Check your connection and try again shortly.', ['%repository%' => $this->app['extend.site']]); } } return $json; }
/** * Set up Composer JSON file. * * @return array|null */ public function update() { /** @var \Bolt\Filesystem\Handler\JsonFile $jsonFile */ $jsonFile = $this->app['filesystem']->get('extensions://composer.json', new JsonFile()); if (!$jsonFile->exists()) { try { $this->init('extensions://composer.json'); } catch (IOException $e) { $this->messages[] = Trans::__("The Bolt extensions composer.json isn't readable."); $this->app['extend.writeable'] = false; $this->app['extend.online'] = false; return; } } $json = $jsonOrig = $jsonFile->parse(); // Workaround Bolt 2.0 installs with "require": [] if (isset($json['require']) && empty($json['require'])) { unset($json['require']); } $json = $this->setJsonDefaults($json); $json = $this->setJsonLocal($json); // Write out the file, but only if it's actually changed, and if it's writable. if ($json != $jsonOrig) { try { $jsonFile->dump($json); } catch (IOException $e) { $this->messages[] = Trans::__("The Bolt extensions composer.json isn't writable."); } } return $json; }
public function initialize() { // For europeana, stupid hardcoded redirect for a domain: if (strpos($_SERVER['HTTP_HOST'], "europeanacreative.eu") !== false) { \Bolt\Library::simpleredirect('http://pro.europeana.eu/get-involved/projects/project-list/europeana-creative'); die; } // structure tree overview $this->boltPath = $this->app['config']->get('general/branding/path'); // listings if ($this->app['config']->getWhichEnd() === 'backend' || $this->app['config']->getWhichEnd() === 'cli') { // back-end listings $this->addMenuOption(\Bolt\Translation\Translator::__('Structure Tree'), "{$this->boltPath}/structure-tree/overview", "fa:sitemap"); $this->app->get("{$this->boltPath}/structure-tree/overview", array($this, 'structureTreeOverview'))->bind('structureTreeOverview'); // convert legacy relationships to column values in contenttypes. $this->app->get("{$this->boltPath}/structure-tree/convert", array($this, 'structureTreeConvert'))->bind('structureTreeConvert'); } else { // slug listing $this->app->match("/{slug}", array($this, 'slugTreeRecord'))->assert('slug', '[a-zA-Z0-9_\\-]+[^(sitemap)^(search)]')->bind('slugTreeRecord'); // strucutureslug / slug listing $this->app->match("/{structureSlugs}/{slug}", array($this, 'structureTreeRecord'))->assert('structureSlugs', '(?!(async|_profiler)).*')->assert('slug', '[a-zA-Z0-9_\\-]+')->bind('structureTreeRecord'); } // twig functions $this->addTwigFunction('structurelink', 'getStructureLink'); $this->addTwigFunction('structurecontenttype', 'getContenttypeByStructure'); $this->addTwigFunction('breadcrumb', 'breadCrumb'); $this->addTwigFunction('subsite', 'subSite'); $this->addTwigFunction('sortRecords', 'sortObject'); $this->addTwigFunction('getContenttype', 'getContenttype'); $this->addTwigFunction('getTreeChildren', 'getTreeChildren'); $this->contenttypeslugs = $this->config['contenttypes']; }
/** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $passwordConstraints = []; if ($this->requirePassword) { $passwordConstraints = [new Assert\NotBlank(), new Assert\Length(['min' => 6])]; } $builder->add('email', EmailType::class, ['label' => Trans::__($this->config->getLabel('email')), 'attr' => ['placeholder' => $this->config->getPlaceholder('email')], 'constraints' => [new Assert\Email(['message' => 'The address "{{ value }}" is not a valid email.', 'checkMX' => true])]])->add('password', RepeatedType::class, ['type' => PasswordType::class, 'first_options' => ['label' => Trans::__($this->config->getLabel('password_first')), 'attr' => ['placeholder' => $this->config->getPlaceholder('password_first')], 'constraints' => $passwordConstraints], 'second_options' => ['label' => Trans::__($this->config->getLabel('password_second')), 'attr' => ['placeholder' => $this->config->getPlaceholder('password_second')], 'constraints' => $passwordConstraints], 'empty_data' => null, 'required' => $this->requirePassword])->add('submit', SubmitType::class, ['label' => Trans::__($this->config->getLabel('profile_save'))]); }
public function initialize() { if ($this->app['config']->getWhichEnd() === 'backend') { $path = $this->app['config']->get('general/branding/path') . '/extensions/image-cleanup'; $this->app->mount($path, new Controller\ImageCleanupAdminController()); $this->app['extensions.Image cleanup for Bolt']->addMenuOption(Translator::__('Image Cleanup'), $this->app['resources']->getUrl('bolt') . 'extensions/image-cleanup', 'fa:pencil-square-o'); } }
public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('name', 'text', ['label' => Trans::__('Internal name for field'), 'read_only' => true, 'attr' => ['help' => Trans::__('Only letters, numbers and underscores allowed')]])->add('label', 'text', ['label' => Trans::__('Label for this form field'), 'attr' => ['help' => Trans::__('This is the user-visible label')]])->add('type', 'choice', ['label' => Trans::__('Type of form element'), 'choices' => ['text' => Trans::__('Text'), 'textarea' => Trans::__('Text Area'), 'choice' => Trans::__('Select Dropdown'), 'submit' => Trans::__('Submit Button')]])->add('required', 'checkbox', ['label' => Trans::__('Required Field'), 'required' => false]); $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { $form = $event->getForm(); $data = $event->getData(); $form->add('choices', 'choice', ['label' => Trans::__('Options to show'), 'required' => false, 'multiple' => true, 'attr' => ['help' => Trans::__('Setup the available choices')], 'choices' => array_combine((array) $data['choices'], (array) $data['choices'])]); }); }
/** * No mail transport has been set. We should gently nudge the user to set * the mail configuration. * * @see https://github.com/bolt/bolt/issues/2908 * * @param Request $request */ protected function mailConfigCheck(Request $request) { if (!$request->hasPreviousSession()) { return; } if (!$this->app['config']->get('general/mailoptions') && $this->app['users']->getCurrentuser() && $this->app['users']->isAllowed('files:config')) { $error = "The mail configuration parameters have not been set up. This may interfere with password resets, and extension functionality. Please set up the 'mailoptions' in config.yml."; $this->app['logger.flash']->error(Trans::__($error)); } }
/** * No Mail transport has been set. We should gently nudge the user to set * the mail configuration. * * For now, we only pester the user, if an extension needs to be able to * send mail, but it's not been set up. * * @see: the issue at https://github.com/bolt/bolt/issues/2908 * * @param Request $request */ protected function mailConfigCheck(Request $request) { if (!$request->hasPreviousSession()) { return; } if ($this->app['users']->getCurrentuser() && !$this->app['config']->get('general/mailoptions') && $this->app['extensions']->hasMailSenders()) { $error = "One or more installed extensions need to be able to send email. Please set up the 'mailoptions' in config.yml."; $this->app['logger.flash']->error(Trans::__($error)); } }
/** * 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 = ['version' => Bolt\Version::VERSION, 'payoff' => 'Sophisticated, lightweight & simple CMS', 'dashboardlink' => sprintf('<a href="%s">%s</a>', $this->app['url_generator']->generate('dashboard'), 'Dashboard'), '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']; } }
/** * 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 = ['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>', $this->app->generatePath('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']; } }
function initialize() { $this->path = $this->app['config']->get('general/branding/path') . '/extensions/google-analytics'; $this->app->match($this->path, array($this, 'GoogleAnalytics')); $this->app['htmlsnippets'] = true; if ($this->app['config']->getWhichEnd() == 'frontend') { $this->addSnippet('endofhead', 'insertAnalytics'); } else { $this->app->before(array($this, 'before')); } if (isset($this->config['backend']) && $this->config['backend']) { $this->addMenuOption(Trans::__('Statistics'), $this->app['paths']['bolt'] . 'extensions/google-analytics', "fa:area-chart"); } }
public function signin(Request $request) { $login = new \Bolt\Extension\Blimp\Client\AccessControl\Login($this->app); $login->setRequest($request); $query = $request->query->all(); $code = array_key_exists('code', $query) ? $query['code'] : null; $state = array_key_exists('state', $query) ? $query['state'] : null; $error = array_key_exists('error', $query) ? $query['error'] : null; $error_description = array_key_exists('error_description', $query) ? $query['error_description'] : null; $destination = $this->app['blimp_client.session_from_code']($code, $state, $error, $error_description); $access_token = $this->app['blimp_client.access_token'](); $logged_in = !empty($access_token) ? $this->app['blimp_client.validate_access_token']($access_token, true, true) : false; if (!$logged_in) { $this->flashes()->error(Trans::__($error_description ?: $error)); $response = new RedirectResponse($destination); } else { $user_id = $logged_in['profile_id']; if (!($userEntity = $this->getUser($user_id))) { $users_repo = $this->app['storage']->getRepository('Bolt\\Storage\\Entity\\Users'); $userEntity = new \Bolt\Storage\Entity\Users(); $userEntity->setUsername($user_id); $userEntity->setPassword('not_local_user'); $userEntity->setEmail($logged_in['profile']['email']); $userEntity->setDisplayname($logged_in['profile']['name']); $userEntity->setEnabled(true); $userEntity->setRoles(explode(' ', $logged_in['scope'])); $users_repo->save($userEntity); } if (!$userEntity->getEnabled()) { $this->flashLogger->error(Trans::__('Your account is disabled. Sorry about that.')); $url = $this->app['blimp_client.request_code'](['return_to' => $destination]); $response = new RedirectResponse($url); } else { $login->loginFinish($userEntity); // Authentication data is cached in the session and if we can't get it // now, everyone is going to have a bad day. Make that obvious. if (!($token = $this->session()->get('authentication'))) { $this->flashes()->error(Trans::__("Unable to retrieve login session data. Please check your system's PHP session settings.")); $url = $this->app['blimp_client.request_code'](['return_to' => $destination]); $response = new RedirectResponse($url); } else { // Log in, if credentials are correct. $this->app['logger.system']->info('Logged in: ' . $user_id, ['event' => 'authentication']); $response = $this->setAuthenticationCookie(new RedirectResponse($destination), (string) $token); } } } return $response; }
public function publish(Content $content, $comment = null) { $contenttype = $content->contenttype; $fieldvalues = $content->values; // Test to see if this is a new record, or an update if (empty($fieldvalues['blimp_id'])) { $create = true; } else { $create = false; } // Update the content object $content->setValues($fieldvalues); $extra = []; if (!empty($contenttype['relations'])) { $relations = $content->related(); if (!empty($relations)) { foreach ($relations as $related) { if (!empty($related->values['blimp_id'])) { if (!empty($contenttype['relations'][$related->contenttype['slug']])) { $multiple = !empty($contenttype['relations'][$related->contenttype['slug']]['multiple']) && $contenttype['relations'][$related->contenttype['slug']]['multiple']; if ($multiple) { if (empty($extra[$related->contenttype['slug']])) { $extra[$related->contenttype['slug']] = []; } $extra[$related->contenttype['slug']][] = $related->values['blimp_id']; } else { $extra[$related->contenttype['singular_slug']] = $related->values['blimp_id']; } } } } } } // Decide whether to insert a new record, or update an existing one. $ok = false; if ($create) { $ok = $this->insertContent($content, $comment, $extra); } else { $ok = $this->updateContent($content, $comment, $extra); } if ($ok !== true) { return $ok; } $content->setValue('status', 'published'); $comment = Trans::__('The content has been published.'); $result = $this->app['storage']->saveContent($content, $comment); return $result; }
/** * Check the database, create tables, add missing/new columns to tables. * * @param Request $request The Symfony Request * * @return \Symfony\Component\HttpFoundation\RedirectResponse */ public function update(Request $request) { $output = $this->schemaManager()->repairTables()->getResponseStrings(); // If 'return=edit' is passed, we should return to the edit screen. // We do redirect twice, yes, but that's because the newly saved // contenttype.yml needs to be re-read. $return = $request->get('return'); if ($return === 'edit') { if (empty($output)) { $content = Trans::__('Your database is already up to date.'); } else { $content = Trans::__('Your database is now up to date.'); } $this->flashes()->success($content); return $this->redirectToRoute('fileedit', ['namespace' => 'config', 'file' => 'contenttypes.yml']); } else { $this->session()->set('dbupdate_result', $output); return $this->redirectToRoute('dbupdate_result'); } }
public function getTranslationAction(Application $app, Request $request) { $default_locale = $app['config']->get('general/locale', 'en_GB'); $prefix = $app['config']->get('general/database/prefix', 'bolt_'); $translation_table_name = $prefix . 'translation'; $locale = $request->query->get('locale'); $content_type = $request->query->get('content_type'); $content_type_id = $request->query->get('content_type_id'); $content_type_config = $app['config']->get('contenttypes/' . $content_type); $translatable_fields = $this->getTranslatableFields($content_type_config['fields']); if (!$app['users']->isAllowed('contenttype:' . $content_type . ':edit')) { return new Response(Trans::__('Permission denied'), Response::HTTP_FORBIDDEN); } $response = array(); // Return default record if default locale if ($locale === $default_locale) { $query = 'SELECT * FROM ' . $prefix . $content_type . ' WHERE id = :content_type_id'; $default_content = $app['db']->fetchAssoc($query, array(':content_type_id' => $content_type_id)); foreach ($translatable_fields as $translatable_field) { $element = new \stdClass(); $element->field = $translatable_field; $element->value = $default_content[$translatable_field]; $response[] = $element; } return $app->json($response); } $query = 'SELECT field, value FROM ' . $translation_table_name . ' WHERE locale = :locale AND content_type = :content_type AND content_type_id = :content_type_id'; $translated_content = $app['db']->fetchAll($query, array(':locale' => $locale, ':content_type' => $content_type, ':content_type_id' => $content_type_id)); foreach ($translatable_fields as $translatable_field) { $element = new \stdClass(); $element->field = $translatable_field; $element->value = ''; foreach ($translated_content as $content) { if ($content['field'] === $translatable_field) { $element->value = $content['value']; } } $response[] = $element; } return $app->json($response); }
/** * The default before filter for the controllers in this file. * * Refer to the routing.yml config file for overridding. * * @param Request $request The Symfony Request * @param Application $app The application/container * * @return null|Response|RedirectResponse */ public function before(Request $request, Application $app) { // Start the 'stopwatch' for the profiler. $app['stopwatch']->start('bolt.frontend.before'); // If there are no users in the users table, or the table doesn't exist. Repair // the DB, and let's add a new user. if (!$app['users']->getUsers()) { $app['session']->getFlashBag()->add('info', Trans::__('There are no users in the database. Please create the first user.')); return Lib::redirect('useredit', array('id' => '')); } $app['debugbar'] = true; $app['htmlsnippets'] = true; // If we are in maintenance mode and current user is not logged in, show maintenance notice. if ($app['config']->get('general/maintenance_mode')) { if (!$app['users']->isAllowed('maintenance-mode')) { $template = $app['templatechooser']->maintenance(); $body = $app['render']->render($template)->getContent(); return new Response($body, Response::HTTP_SERVICE_UNAVAILABLE); } } // Stop the 'stopwatch' for the profiler. $app['stopwatch']->stop('bolt.frontend.before'); return null; }
/** * System log overview route * * @param Request $request * * @return \Bolt\Response\BoltResponse|\Symfony\Component\HttpFoundation\RedirectResponse */ public function systemOverview(Request $request) { $action = $request->query->get('action'); if ($action == 'clear') { $this->manager()->clear('system'); $this->flashes()->success(Trans::__('The system log has been cleared.')); return $this->redirectToRoute('systemlog'); } elseif ($action == 'trim') { $this->manager()->trim('system'); $this->flashes()->success(Trans::__('The system log has been trimmed.')); return $this->redirectToRoute('systemlog'); } // Test/get page number $page = $this->app['pager']->getCurrentPage('activity'); $options = ['level' => $request->query->get('level'), 'context' => $request->query->get('context')]; $activity = $this->manager()->getActivity('system', $page, 16, $options); return $this->render('@bolt/activity/systemlog.twig', ['entries' => $activity]); }
/** * Check for a user with the 'root' role. * * There should always be at least one If there isn't we promote the current * user. * * @return boolean */ public function checkForRoot() { // Don't check for root, if we're not logged in. if ($this->getCurrentUsername() === false) { return false; } // Loop over the users, check if anybody's root. foreach ($this->getUsers() as $user) { if (in_array('root', $user['roles'])) { // We have a 'root' user. return true; } } // Make sure the DB is updated. Note, that at this point we currently don't have // the permissions to do so, but if we don't, update the DB, we can never add the // role 'root' to the current user. $this->app['schema']->update(); // Show a helpful message to the user. $this->app['logger.flash']->info(Trans::__("There should always be at least one 'root' user. You have just been promoted. Congratulations!")); // If we reach this point, there is no user 'root'. We promote the current user. return $this->addRole($this->getCurrentUsername(), 'root'); }
/** * Generate tab groups. * * @param array $contenttype * @param array $has * * @return array */ private function createGroupTabs(array $contenttype, array $has) { $groups = []; $groupIds = []; $addGroup = function ($group, $label) use(&$groups, &$groupIds) { $nr = count($groups) + 1; $id = rtrim('tab-' . Slugify::create()->slugify($group), '-'); if (isset($groupIds[$id]) || $id === 'tab') { $id .= '-' . $nr; } $groups[$group] = ['label' => $label, 'id' => $id, 'is_active' => $nr === 1, 'fields' => []]; $groupIds[$id] = 1; }; foreach ($contenttype['groups'] ? $contenttype['groups'] : ['ungrouped'] as $group) { if ($group === 'ungrouped') { $addGroup($group, Trans::__('contenttypes.generic.group.ungrouped')); } elseif ($group !== 'meta' && $group !== 'relations' && $group !== 'taxonomy') { $default = ['DEFAULT' => ucfirst($group)]; $key = ['contenttypes', $contenttype['slug'], 'group', $group]; $addGroup($group, Trans::__($key, $default)); } } if ($has['relations'] || $has['incoming_relations']) { $addGroup('relations', Trans::__('contenttypes.generic.group.relations')); $groups['relations']['fields'][] = '*relations'; } if ($has['taxonomy'] || is_array($contenttype['groups']) && in_array('taxonomy', $contenttype['groups'])) { $addGroup('taxonomy', Trans::__('contenttypes.generic.group.taxonomy')); $groups['taxonomy']['fields'][] = '*taxonomy'; } if ($has['templatefields'] || is_array($contenttype['groups']) && in_array('template', $contenttype['groups'])) { $addGroup('template', Trans::__('Template')); $groups['template']['fields'][] = '*template'; } $addGroup('meta', Trans::__('contenttypes.generic.group.meta')); $groups['meta']['fields'][] = '*meta'; // References fields in tab group data. foreach ($contenttype['fields'] as $fieldname => $field) { $groups[$field['group']]['fields'][] = $fieldname; } return $groups; }
/** * Generate a copy of a theme package. * * @param Request $request * * @throws PackageManagerException * * @return Response */ public function generateTheme(Request $request) { $theme = $request->get('theme'); $newName = $request->get('name'); if (empty($theme)) { return new Response(Trans::__('No theme name found. Theme is not generated.')); } if (!$newName) { $newName = basename($theme); } $source = $this->resources()->getPath('extensions/vendor/' . $theme); $destination = $this->resources()->getPath('themebase/' . $newName); if (is_dir($source)) { try { $filesystem = new Filesystem(); $filesystem->mkdir($destination); $filesystem->mirror($source, $destination); if (file_exists($destination . "/config.yml.dist")) { $filesystem->copy($destination . "/config.yml.dist", $destination . "/config.yml"); } return new Response(Trans::__('Theme successfully generated. You can now edit it directly from your theme folder.')); } catch (\Exception $e) { return new Response(Trans::__('We were unable to generate the theme. It is likely that your theme directory is not writable by Bolt. Check the permissions and try reinstalling.')); } } throw new PackageManagerException("Invalid theme source directory: {$source}"); }
/** * Process an individual file upload. * * @param string $namespace * @param string $path * @param string $filename * @param array $fileToProcess * * @return void */ private function processUpload($namespace, $path, $filename, array $fileToProcess) { $this->app['upload.namespace'] = $namespace; $handler = $this->app['upload']; $handler->setPrefix($path . '/'); $result = $handler->process($fileToProcess); if ($result->isValid()) { $this->flashes()->info(Trans::__('page.file-management.message.upload-success', ['%file%' => $filename])); // Add the file to our stack. $this->app['stack']->add($path . '/' . $filename); $result->confirm(); } else { foreach ($result->getMessages() as $message) { $this->flashes()->error((string) $message); } } }
/** * Check that the user has a valid GSRF token and the required access control * to action the record. * * @param Request $request * @param string $contenttypeslug * @param integer $id * * @return bool|\Symfony\Component\HttpFoundation\RedirectResponse */ private function checkEditAccess(Request $request, $contenttypeslug, $id) { // Is the record new or existing $new = empty($id) ?: false; // Check for a valid CSRF token if ($request->isMethod('POST') && !$this->checkAntiCSRFToken()) { $this->app->abort(Response::HTTP_BAD_REQUEST, Trans::__('Something went wrong')); } /* * Check the user is allowed to create/edit this record, based on: * contenttype-all: * contenttype-default: * contenttypes: * edit: [] * create: [] */ $perm = $new ? "contenttype:{$contenttypeslug}:create" : "contenttype:{$contenttypeslug}:edit:{$id}"; if (!$this->isAllowed($perm)) { $action = $new ? 'create' : 'edit'; $this->flashes()->error(Trans::__("You do not have the right privileges to {$action} that record.")); return $this->redirectToRoute('dashboard'); } return false; }
/** * Attempt to save the POST data for a translation file edit. * * @param string $contents * @param array $tr * * @return boolean|\Symfony\Component\HttpFoundation\RedirectResponse */ private function saveTranslationFile($contents, array &$tr) { $contents = Input::cleanPostedData($contents) . "\n"; // Before trying to save a yaml file, check if it's valid. try { Yaml::parse($contents); } catch (ParseException $e) { $msg = Trans::__("File '%s' could not be saved:", ['%s' => $tr['shortPath']]); $this->flashes()->error($msg . ' ' . $e->getMessage()); return false; } // Clear any warning for file not found, we are creating it here // we'll set an error if someone still submits the form and write is not allowed $this->flashes()->clear(); try { $fs = new Filesystem(); $fs->dumpFile($tr['path'], $contents); } catch (IOException $e) { $msg = Trans::__("The file '%s' is not writable. You will have to use your own editor to make modifications to this file.", ['%s' => $tr['shortPath']]); $this->flashes()->error($msg); $tr['writeallowed'] = false; return false; } $msg = Trans::__("File '%s' has been saved.", ['%s' => $tr['shortPath']]); $this->flashes()->info($msg); return $this->redirectToRoute('translation', ['domain' => $tr['domain'], 'tr_locale' => $tr['locale']]); }
/** * No Mail transport has been set. We should gently nudge the user to set the mail configuration. * * @see: the issue at https://github.com/bolt/bolt/issues/2908 * * For now, we only pester the user, if an extension needs to be able to send * mail, but it's not been set up. */ public function initMailCheck() { if (!$this['config']->get('general/mailoptions') && $this['extensions']->hasMailSenders()) { $error = "One or more installed extensions need to be able to send email. Please set up the 'mailoptions' in config.yml."; $this['session']->getFlashBag()->add('error', Trans::__($error)); } }
/** * Helper function to wrap an image in a Magnific popup HTML tag, with thumbnail. * * example: {{ content.image|popup(320, 240) }} * example: {{ popup(content.image, 320, 240) }} * example: {{ content.image|popup(width=320, height=240, title="My Image") }} * * Note: This function used to be called 'fancybox', but Fancybox was * deprecated in favour of the Magnific Popup library. * * @param string $filename Image filename * @param integer $width Image width * @param integer $height Image height * @param string $crop Crop image string identifier * @param string $title Display title for image * * @return string HTML output */ public function popup($filename = '', $width = 100, $height = 100, $crop = '', $title = '') { if (!empty($filename)) { $thumbconf = $this->app['config']->get('general/thumbnails'); $fullwidth = !empty($thumbconf['default_image'][0]) ? $thumbconf['default_image'][0] : 1000; $fullheight = !empty($thumbconf['default_image'][1]) ? $thumbconf['default_image'][1] : 800; $thumbnail = $this->thumbnail($filename, $width, $height, $crop); $large = $this->thumbnail($filename, $fullwidth, $fullheight, 'r'); if (empty($title)) { $title = sprintf('%s: %s', Trans::__('Image'), $filename); } $output = sprintf('<a href="%s" class="magnific" title="%s"><img src="%s" width="%s" height="%s"></a>', $large, $title, $thumbnail, $width, $height); } else { $output = ' '; } return $output; }
/** * Sanity checks for doubles in in contenttypes. */ public function checkConfig() { $slugs = []; 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', ['%contenttype%' => $key, '%field%' => $fieldname]); $this->app['logger.flash']->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 (!empty($field['uses']) && is_array($field['uses'])) { foreach ((array) $field['uses'] as $useField) { if (!empty($field['uses']) && empty($ct['fields'][$useField]) && !in_array($useField, $this->reservedFieldNames)) { $error = Trans::__('contenttypes.generic.wrong-use-field', ['%contenttype%' => $key, '%field%' => $fieldname, '%uses%' => $useField]); $this->app['logger.flash']->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', ['%contenttype%' => $key, '%field%' => $fieldname, '%type%' => $field['type']]); $this->app['logger.flash']->error($error); unset($ct['fields'][$fieldname]); } } // 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']]++; } } // 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.", ['%taxonomytype%' => $key, '%slug%' => $taxo['slug']]); $this->app['logger.flash']->error($error); return; } } // if there aren't any other errors, check for duplicates across contenttypes. if (!$this->app['logger.flash']->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.", ['%slug%' => $slug]); $this->app['logger.flash']->error($error); return; } } } }