Source of file TwigLayout.php

Size: 5,845 Bytes - Last Modified: 2019-07-02T15:03:52+00:00

/home/vagrant/www/document-templates/src/Layouts/TwigLayout.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
<?php

namespace BWF\DocumentTemplates\Layouts;

use BWF\DocumentTemplates\EditableTemplates\EditableTemplate;
use BWF\DocumentTemplates\EditableTemplates\EditableTemplateInterface;
use BWF\DocumentTemplates\EditableTemplates\HtmlTemplate;
use BWF\DocumentTemplates\Sandbox\SecurityPolicy;
use Twig\Environment;
use Twig\Extension\SandboxExtension;
use Twig\Loader\ArrayLoader;
use Twig\Loader\FilesystemLoader;

class TwigLayout extends Layout implements LayoutInterface
{
    /**
     * @var Environment
     */
    protected $twig;

    /**
     * @var \Twig\Extension\SandboxExtension
     */
    protected $sandbox;

    /**
     * @var \Twig\TemplateWrapper
     */
    protected $layout;

    /**
     * @var string Base path for template loading
     */
    protected $basePath;

    /**
     * TwigLayout constructor.
     */
    public function __construct()
    {
        $this->basePath = config('document_templates.layout_path');
        $this->createEnvironment();
    }

    /**
     * Creates the twig environment and sets the sets up the security policy.
     */
    private function createEnvironment(){
        $loader = new FilesystemLoader($this->basePath);
        $this->twig = new Environment($loader, config('document_templates.twig.environment'));

        $policy = new SecurityPolicy(
            config('document_templates.template_sandbox.allowedTags'),
            config('document_templates.template_sandbox.allowedFilters'),
            config('document_templates.template_sandbox.allowedMethods'),
            config('document_templates.template_sandbox.allowedProperties'),
            config('document_templates.template_sandbox.allowedFunctions')
        );
        $this->sandbox = new \Twig\Extension\SandboxExtension($policy);
        $this->twig->addExtension($this->sandbox);
    }

    /**
     * @param string $template
     * @return mixed|void
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function load($template)
    {
        $this->createEnvironment();
        $templateName = basename($template);
        $this->setName($templateName);
        $this->layout = $this->twig->load($templateName);
    }

    /**
     * return string[]
     */
    public function getAvailableLayouts()
    {
        $files = [];
        $iterator = new \DirectoryIterator($this->basePath);
        /** @var \SplFileInfo $fileinfo */
        foreach ($iterator as $fileinfo) {
            if (!$fileinfo->isDot() && $fileinfo->getExtension() === 'twig') {
                $files[] = $fileinfo->getBasename();
            }
        }

        return collect($files);
    }

    public function getTemplates()
    {
        $templates = [];

        if(!$this->layout) {
            throw new \Exception('Layout is not loaded!');
        }

        //It is necessary to load the template again, without this, the rendering fails when used with Laravel queues.
        $this->load($this->name);
        $context = $this->layout->getSourceContext();
        $blocks = $this->layout->getBlockNames([$context]);


        foreach ($blocks as $block) {
            $template = new EditableTemplate();
            $template->setName($block);

            $templates[] = $template;
        }

        return collect($templates);
    }

    protected function renderWithSandbox(EditableTemplateInterface $template, $templateData)
    {
        $this->sandbox->enableSandbox();

        $templateName = 'template_' . $template->getName();
        $loader = new ArrayLoader([
            $templateName => $template->getContent()
        ]);
        $this->twig->setLoader($loader);
        $rendered = $this->twig->render($templateName, $templateData);

        $this->sandbox->disableSandbox();

        return $rendered;
    }

    /**
     * Generates a new template by extending the layout with the given templates
     *
     * @param array $templates
     * @param  array $templateData
     * @return string
     */
    protected function extendLayout($templates, $templateData)
    {
        $extendedTemplate = '{% extends layout %}';

        foreach ($templates as $template) {
            $extendedTemplate .= sprintf(
                '{%% block %1$s %%}%2$s{%% endblock %1$s %%}',
                $template->getName(),
                $this->renderWithSandbox($template, $templateData)
            );
        }

        return $extendedTemplate;
    }

    /**
     * @param \BWF\DocumentTemplates\TemplateDataSources\TemplateDataSourceInterface[] $dataSources
     * @return array
     */
    protected function generateTemplateData($dataSources)
    {
        $templateData = [];

        foreach ($dataSources as $dataSource) {
            $templateData = array_merge($templateData, $dataSource->getTemplateData());
        }

        return $templateData;
    }

    /**
     * @param \BWF\DocumentTemplates\EditableTemplates\EditableTemplate[] $templates
     * @param \BWF\DocumentTemplates\TemplateDataSources\TemplateDataSourceInterface[] $dataSources
     * @return string
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function render($templates, $dataSources)
    {
        $templateData = $this->generateTemplateData($dataSources);
        $templateData['layout'] = $this->layout;

        $loader = new ArrayLoader([
            'extendedLayout' => $this->extendLayout($templates, $templateData),
        ]);

        $this->twig->setLoader($loader);

        return $this->twig->render('extendedLayout', $templateData);

    }

    public function renderSingle(EditableTemplateInterface $template, $dataSources)
    {
        return $this->renderWithSandbox($template, $this->generateTemplateData($dataSources));
    }
}