<commit_before><?php

namespace app\libraries;
use app\controllers\GlobalController;
use app\exceptions\OutputException;
use app\libraries\FileUtils;
use app\models\Breadcrumb;
use Aptoma\Twig\Extension\MarkdownEngine\ParsedownEngine;
use Aptoma\Twig\Extension\MarkdownExtension;

/**
 * Class Output
 *
 * We us this class to act as a wrapper around Twig as well as to hold our output
 * as we build it before final output either when we output at the end of the calling
 * class or if the application has thrown an uncaught exception
 */

class Output {
    /** @var bool */
    private $render = true;

    /** @var bool Should we  */
    private $buffer_output = true;

    private $output_buffer = "";
    private $breadcrumbs = array();
    private $loaded_views = array();
    private $css = array();
    private $js = array();
    
    private $use_header = true;
    private $use_footer = true;
    
    private $start_time;

    /** @var \Twig\Environment $twig */
    private $twig = null;
    /** @var \Twig\Loader\LoaderInterface $twig */
    private $twig_loader = null;
    /** @var GlobalController $controller */
    private $controller;

    /**
     * @var Core
     */
    private $core;
    
    public function __construct(Core $core) {
        $this->core = $core;
        $this->start_time = microtime(true);
        $this->controller = new GlobalController($core);
    }

    /**
     * Disables the render functions that call in a file/Twig, causing them to return null
     * immediately. This allows us to not have to mock out this class when we're using
     * it for the JSON response stuff.
     */
    public function disableRender() {
        $this->render = false;
    }

    /**
     * @return bool
     */
    public function getRender() {
        return $this->render;
    }

    public function loadTwig($full_load = true) {
        $template_root = FileUtils::joinPaths(dirname(__DIR__), 'templates');
        $cache_path = FileUtils::joinPaths(dirname(dirname(__DIR__)), 'cache', 'twig');
        $debug = $full_load && $this->core->getConfig()->isDebug();

        $this->twig_loader = new \Twig\Loader\FilesystemLoader($template_root);
        $this->twig = new \Twig\Environment($this->twig_loader, [
            'cache' => $debug ? false : $cache_path,
            'debug' => $debug
        ]);
        $this->twig->addGlobal("core", $this->core);

        $this->twig->addFunction(new \Twig\TwigFunction("render_template", function(... $args) {
            return call_user_func_array('self::renderTemplate', $args);
        }, ["is_safe" => ["html"]]));
        $this->twig->addFunction(new \Twig\TwigFunction('base64_image', function(string $path, string $title): string {
            $valid_image_subtypes = ['png', 'jpg', 'jpeg', 'gif'];
            list($mime_type, $mime_subtype) = explode('/', FileUtils::getMimeType($path), 2);
            if ($mime_type === "image" && in_array($mime_subtype, $valid_image_subtypes)) {
                // Read image path, convert to base64 encoding
                $image_data = base64_encode(file_get_contents($path));
                return <<<HTML
<img alt="${title}" src="data:image/${mime_subtype};base64,${image_data}" width="150" height="200" />
HTML;
            }
            throw new OutputException('Invalid path to image file');
        }, ['is_safe' => ['html']]));

        if ($full_load) {
            $this->twig->getExtension(\Twig\Extension\CoreExtension::class)
                ->setTimezone($this->core->getConfig()->getTimezone());
            if($this->core->getConfig()->wrapperEnabled()) {
                $this->twig_loader->addPath(
                    FileUtils::joinPaths($this->core->getConfig()->getCoursePath(), 'site'),
                    $namespace = 'site_uploads'
                );<commit_msg>[Feature:Developer] twig debug extension on developer environments (#4116)<commit_after><?php

namespace app\libraries;
use app\controllers\GlobalController;
use app\exceptions\OutputException;
use app\libraries\FileUtils;
use app\models\Breadcrumb;
use Aptoma\Twig\Extension\MarkdownEngine\ParsedownEngine;
use Aptoma\Twig\Extension\MarkdownExtension;

/**
 * Class Output
 *
 * We us this class to act as a wrapper around Twig as well as to hold our output
 * as we build it before final output either when we output at the end of the calling
 * class or if the application has thrown an uncaught exception
 */

class Output {
    /** @var bool */
    private $render = true;

    /** @var bool Should we  */
    private $buffer_output = true;

    private $output_buffer = "";
    private $breadcrumbs = array();
    private $loaded_views = array();
    private $css = array();
    private $js = array();
    
    private $use_header = true;
    private $use_footer = true;
    
    private $start_time;

    /** @var \Twig\Environment $twig */
    private $twig = null;
    /** @var \Twig\Loader\LoaderInterface $twig */
    private $twig_loader = null;
    /** @var GlobalController $controller */
    private $controller;

    /**
     * @var Core
     */
    private $core;
    
    public function __construct(Core $core) {
        $this->core = $core;
        $this->start_time = microtime(true);
        $this->controller = new GlobalController($core);
    }

    /**
     * Disables the render functions that call in a file/Twig, causing them to return null
     * immediately. This allows us to not have to mock out this class when we're using
     * it for the JSON response stuff.
     */
    public function disableRender() {
        $this->render = false;
    }

    /**
     * @return bool
     */
    public function getRender() {
        return $this->render;
    }

    public function loadTwig($full_load = true) {
        $template_root = FileUtils::joinPaths(dirname(__DIR__), 'templates');
        $cache_path = FileUtils::joinPaths(dirname(dirname(__DIR__)), 'cache', 'twig');
        $debug = $full_load && $this->core->getConfig()->isDebug();

        $this->twig_loader = new \Twig\Loader\FilesystemLoader($template_root);
        $this->twig = new \Twig\Environment($this->twig_loader, [
            'cache' => $debug ? false : $cache_path,
            'debug' => $debug
        ]);

        if($debug){
            $this->twig->addExtension(new \Twig\Extension\DebugExtension());
        }
        

        $this->twig->addGlobal("core", $this->core);

        $this->twig->addFunction(new \Twig\TwigFunction("render_template", function(... $args) {
            return call_user_func_array('self::renderTemplate', $args);
        }, ["is_safe" => ["html"]]));
        $this->twig->addFunction(new \Twig\TwigFunction('base64_image', function(string $path, string $title): string {
            $valid_image_subtypes = ['png', 'jpg', 'jpeg', 'gif'];
            list($mime_type, $mime_subtype) = explode('/', FileUtils::getMimeType($path), 2);
            if ($mime_type === "image" && in_array($mime_subtype, $valid_image_subtypes)) {
                // Read image path, convert to base64 encoding
                $image_data = base64_encode(file_get_contents($path));
                return <<<HTML
<img alt="${title}" src="data:image/${mime_subtype};base64,${image_data}" width="150" height="200" />
HTML;
            }
            throw new OutputException('Invalid path to image file');
        }, ['is_safe' => ['html']]));

        if ($full_load) {
            $this->twig->getExtension(\Twig\Extension\CoreExtension::class)
                ->setTimezone($this->core->getConfig()->getTimezone());
            if($this->core->getConfig()->wrapperEnabled()) {
                $this->twig_loader->addPath(
                    FileUtils::joinPaths($this->core->getConfig()->getCoursePath(), 'site'),
                    $namespace = 'site_uploads'
                );
{'fraction_non_alphanumeric': 0.14506844057822693, 'fraction_numerical': 0.00434949469105795, 'mean_word_length': 2.6877358490566037, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 79, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6933942', 'n_tokens_mistral': 2319, 'n_tokens_neox': 2247, 'n_words': 554}