<commit_before><?php

namespace Bolt\Controllers;

use Bolt\Application;
use Bolt\Content;
use Bolt\Helpers\Input;
use Bolt\Library as Lib;
use Bolt\Pager;
use Bolt\Translation\Translator as Trans;
use Silex;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use utilphp\util;

/**
 * Standard Frontend actions.
 *
 * This file acts as a grouping for the default front-end controllers.
 *
 * For overriding the default behavior here, please reference
 * http://docs.bolt.cm/templates-routes#routing or the routing.yml
 * file in your configuration.
 */
class Frontend
{
    /**
     * Perform contenttype-based permission check, aborting with a 403
     * Forbidden as appropriate.
     *
     * @param \Silex\Application $app     The application/container
     * @param Content|string     $content The content to check
     */
    protected function checkFrontendPermission(Silex\Application $app, $content)
    {
        if ($app['config']->get('general/frontend_permission_checks')) {
            if ($content instanceof Content) {
                $contenttypeslug = $content->contenttype['slug'];
                $contentid = $content['id'];
            } else {
                $contenttypeslug = (string) $content;
                $contentid = null;
            }
            if (!$app['users']->isAllowed('frontend', $contenttypeslug, $contentid)) {
                $app->abort(403, 'Not allowed.');
            }
        }
    }

    /**
     * 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 mixed
     */
    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['storage']->getIntegrityChecker()->checkUserTableIntegrity() ||
            $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.
        // @see /app/app.php, $app->error()
        if ($app['config']->get('general/maintenance_mode')) {
            if (!$app['users']->isAllowed('maintenance-mode')) {
                $template = $app['templatechooser']->maintenance();
                $body = $app['render']->render($template);

                return new Response($body, 503);
            }
        }

        // Stop the 'stopwatch' for the profiler.
        $app['stopwatch']->stop('bolt.frontend.before');

        return null;
    }

    /**
     * Controller for the "Homepage" route. Usually the front page of the website.
     *
     * @param \Silex\Application $app The application/container
     *
     * @return mixed
     */
    public function homepage(Silex\Application $app)
    {
        $content = $app['storage']->getContent($app['config']->get('general/homepage'));

        $template = $app['templatechooser']->homepage();

        if (is_array($content)) {
            $first = $record = current($content);
            $app['twig']->addGlobal('records', $content);
            $app['twig']->addGlobal($first->contenttype['slug'], $content);
        } elseif (!empty($content)) {
            $record = $content;
            $app['twig']->addGlobal('record', $content);
            $app['twig']->addGlobal($content->contenttype['singular_slug'], $content);
        }

        if (!empty($record)) {
            $this->checkFrontendPermission($app, $record);
        }

        return $this->render($app, $template, 'homepage');
    }

    /**
     * Controller for a single record page, like '/page/about/' or '/entry/lorum'.
     *
     * @param \Silex\Application $app             The application/container
     * @param string             $contenttypeslug The content type slug
     * @param string             $slug            The content slug
     *
     * @return mixed
     */
    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) {
            $app->abort(404, "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));

        if (!$content && is_numeric($slug)) {
            // And otherwise try getting it by ID
            $content = $app['storage']->getContent($contenttype['slug'], array('id' => $slug, 'returnsingle' => true));
        }

        $this->checkFrontendPermission($app, $content);

        // No content, no page!
        if (!$content) {
            // There's one special edge-case we check for: if the request is for the backend, without trailing
            // slash and it is intercepted by custom routing, we forward the client to that location.
            if ($slug == trim($app['config']->get('general/branding/path'), '/')) {
                Lib::simpleredirect($app['config']->get('general/branding/path') . '/');
            }
            $app->abort(404, "Page $contenttypeslug/$slug not found.");
        }

        // Then, select which template to use, based on our 'cascading templates rules'
        $template = $app['templatechooser']->record($content);

        // Fallback: If file is not OK, show an error page
        $filename = $app['paths']['templatespath'] . "/" . $template;
        if (!file_exists($filename) || !is_readable($filename)) {
            $error = sprintf(
                "No template for '%s' defined. Tried to use '%s/%s'.",
                $content->getTitle(),
                basename($app['config']->get('general/theme')),
                $template
            );

            // Set/log errors and abort
            $this->setTemplateError($app, $error);
            $app['logger.system']->error($error, array('event' => 'template'));
            $app->abort(404, $error);
        }

        // Setting the canonical path and the editlink.
        $app['canonicalpath'] = $content->link();
        $app['paths'] = $app['resources']->getPaths();
        $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());
    }

    /**
     * The controller for previewing a content from posted data.
     *
     * @param Request            $request         The Symfony Request
     * @param \Silex\Application $app             The application/container
     * @param string             $contenttypeslug The content type slug
     *<commit_msg>Set the canonical for single record pages. Fixes #3039.<commit_after><?php

namespace Bolt\Controllers;

use Bolt\Application;
use Bolt\Content;
use Bolt\Helpers\Input;
use Bolt\Library as Lib;
use Bolt\Pager;
use Bolt\Translation\Translator as Trans;
use Silex;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use utilphp\util;

/**
 * Standard Frontend actions.
 *
 * This file acts as a grouping for the default front-end controllers.
 *
 * For overriding the default behavior here, please reference
 * http://docs.bolt.cm/templates-routes#routing or the routing.yml
 * file in your configuration.
 */
class Frontend
{
    /**
     * Perform contenttype-based permission check, aborting with a 403
     * Forbidden as appropriate.
     *
     * @param \Silex\Application $app     The application/container
     * @param Content|string     $content The content to check
     */
    protected function checkFrontendPermission(Silex\Application $app, $content)
    {
        if ($app['config']->get('general/frontend_permission_checks')) {
            if ($content instanceof Content) {
                $contenttypeslug = $content->contenttype['slug'];
                $contentid = $content['id'];
            } else {
                $contenttypeslug = (string) $content;
                $contentid = null;
            }
            if (!$app['users']->isAllowed('frontend', $contenttypeslug, $contentid)) {
                $app->abort(403, 'Not allowed.');
            }
        }
    }

    /**
     * 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 mixed
     */
    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['storage']->getIntegrityChecker()->checkUserTableIntegrity() ||
            $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.
        // @see /app/app.php, $app->error()
        if ($app['config']->get('general/maintenance_mode')) {
            if (!$app['users']->isAllowed('maintenance-mode')) {
                $template = $app['templatechooser']->maintenance();
                $body = $app['render']->render($template);

                return new Response($body, 503);
            }
        }

        // Stop the 'stopwatch' for the profiler.
        $app['stopwatch']->stop('bolt.frontend.before');

        return null;
    }

    /**
     * Controller for the "Homepage" route. Usually the front page of the website.
     *
     * @param \Silex\Application $app The application/container
     *
     * @return mixed
     */
    public function homepage(Silex\Application $app)
    {
        $content = $app['storage']->getContent($app['config']->get('general/homepage'));

        $template = $app['templatechooser']->homepage();

        if (is_array($content)) {
            $first = $record = current($content);
            $app['twig']->addGlobal('records', $content);
            $app['twig']->addGlobal($first->contenttype['slug'], $content);
        } elseif (!empty($content)) {
            $record = $content;
            $app['twig']->addGlobal('record', $content);
            $app['twig']->addGlobal($content->contenttype['singular_slug'], $content);
        }

        if (!empty($record)) {
            $this->checkFrontendPermission($app, $record);
        }

        return $this->render($app, $template, 'homepage');
    }

    /**
     * Controller for a single record page, like '/page/about/' or '/entry/lorum'.
     *
     * @param \Silex\Application $app             The application/container
     * @param string             $contenttypeslug The content type slug
     * @param string             $slug            The content slug
     *
     * @return mixed
     */
    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) {
            $app->abort(404, "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));

        if (!$content && is_numeric($slug)) {
            // And otherwise try getting it by ID
            $content = $app['storage']->getContent($contenttype['slug'], array('id' => $slug, 'returnsingle' => true));
        }

        $this->checkFrontendPermission($app, $content);

        // No content, no page!
        if (!$content) {
            // There's one special edge-case we check for: if the request is for the backend, without trailing
            // slash and it is intercepted by custom routing, we forward the client to that location.
            if ($slug == trim($app['config']->get('general/branding/path'), '/')) {
                Lib::simpleredirect($app['config']->get('general/branding/path') . '/');
            }
            $app->abort(404, "Page $contenttypeslug/$slug not found.");
        }

        // Then, select which template to use, based on our 'cascading templates rules'
        $template = $app['templatechooser']->record($content);

        // Fallback: If file is not OK, show an error page
        $filename = $app['paths']['templatespath'] . "/" . $template;
        if (!file_exists($filename) || !is_readable($filename)) {
            $error = sprintf(
                "No template for '%s' defined. Tried to use '%s/%s'.",
                $content->getTitle(),
                basename($app['config']->get('general/theme')),
                $template
            );

            // Set/log errors and abort
            $this->setTemplateError($app, $error);
            $app['logger.system']->error($error, array('event' => 'template'));
            $app->abort(404, $error);
        }

        // Setting the canonical path and the editlink.
        $paths = $app['resources']->getPaths();
        $app['resources']->setUrl('canonicalurl', sprintf('%s://%s%s', $paths['protocol'], $paths['canonical'], $content->link()));
        $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());
    }

    /**
     * The controller for previewing a content from posted data.
     *
     * @param Request            $request         The Symfony Request
     * @param \Silex\Application $app             The application/container
     * @param string             $contenttypeslug The content type slug
     *
{'fraction_non_alphanumeric': 0.1506416217221499, 'fraction_numerical': 0.0024796974769078174, 'mean_word_length': 2.3385761589403975, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 122, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6317263', 'n_tokens_mistral': 4544, 'n_tokens_neox': 4443, 'n_words': 1197}