<?php namespace ProcessWire;

/**
 * PrivacyWire
 * This module adds management options for GDPR-relevant elements (loading maps, videos etc. only after accepting
 * external media) and cookies.
 *
 * @author blaueQuelle
 *
 * ProcessWire 3.x
 * Copyright (C) 2011 by Ryan Cramer
 * Licensed under GNU/GPL v2, see LICENSE.TXT
 *
 * http://www.processwire.com
 * http://www.ryancramer.com
 *
 */
class PrivacyWire extends WireData implements Module, ConfigurableModule
{

    var $modulePath;
    var $moduleUrl;
    var $headContent = "";
    var $bodyContent = "";
    var $lang;

    public static function getModuleInfo()
    {
        return [
            'title' => "PrivacyWire",
            'summary' => "This module adds management options for GDPR-relevant elements (loading maps, videos etc. only after accepting external media) and cookies.",
            'author' => "blaueQuelle",
            'href' => "https://github.com/blaueQuelle/privacywire",
            'version' => "1.1.10",
            'autoload' => true,
            'singular' => true,
            'requires' => ["PHP>=7.2", "ProcessWire>=3.0.110"],
            'installs' => ["TextformatterPrivacyWire"],
            'icon' => 'eye-slash'
        ];
    }

    public function ready()
    {
        if (
            $this->wire('page')->template == 'admin' || // exclude admin pages
            $this->wire('page')->template == 'form-builder' || // exclude from form-builder iframe
            ! $this->is_active // exclude, if PrivacyWire is NOT active
        ) {
            return;
        }

        // 1. - 5. within initiatePrivacyWire method
        $this->wire('page')->addHookBefore('render', $this, 'initiatePrivacyWire');

        // 6. Render everything!
        /*
         * Hint: If you want to render everything manually, make sure to insert the PrivacyWireConfig (step 2), PrivacyWireCore (step 3), Banner Markup (step 4) and if you want to display the ask-for-consent windows also the consent blueprint (step 5) somewhere in your template.
         * The banner markup and consent window blueprint can be loaded anywhere, for example at the end of the body tag.
         */
        if (!$this->render_manually) {
            $this->wire('page')->addHookAfter('render', $this, 'render', ['priority' => 101]);
        }
    }

    public function initiatePrivacyWire(HookEvent $event)
    {

        $this->modulePath = $this->wire('config')->paths->$this;
        $this->moduleUrl = $this->wire('config')->urls->$this;
        $this->lang = ($this->wire('languages') && !$this->wire('user')->language->isDefault()) ? '__' . $this->wire('user')->language->id : '';

        // 1. Add some styling via inline CSS (if wanted, configured via backend)
        if ($this->add_basic_css_styling) {
            $this->headContent .= $this->renderPrivacyWireStyles();
        }

        // 2. Insert PrivacyWire Configuration Object as inline JS (hookable method if you want to override something)
        $this->headContent .= $this->renderPrivacyWireConfigAsInlineJs();

        // 3. Insert JS File (hookable method) - either modern ES6 version or legacy version with IE support.
        // Output modes: a) regular script tag b) ProCache script tag c) inline script
        $this->headContent .= $this->renderPrivacyWireCoreJs();

        // 4. Insert PrivacyWire Banner Markup
        $this->bodyContent .= $this->renderPrivacyWireBannerTemplate();

        // 5. Insert PrivacyWire Blueprint for Consent Window Markup
        $this->bodyContent .= $this->renderPrivacyWireConsentBlueprint();

        // 6. (within ready method)

    }

    public function render(HookEvent $event)
    {
        $event->return = str_replace("</head>", "{$this->headContent}</head>", $event->return);
        $event->return = str_replace("</body>", "{$this->bodyContent}</body>", $event->return);
    }

    public function ___renderPrivacyWireStyles()
    {
        $file = $this->getPrivacyWireStyles();
        return "<style>" . file_get_contents($file->path) . "</style>";
    }

    public function ___renderPrivacyWireConfigAsInlineJs()
    {
        return "<script>var PrivacyWireSettings={$this->getPrivacyWireConfigObject()};</script>";
    }

    public function ___renderPrivacyWireCoreJs()
    {
        if ($this->output_mode === "inline") {
            $output = "<script type='module'>" . file_get_contents($this->modulePath . "js/PrivacyWire.js") . "</script>";
            $output .= "<script nomodule type='text/javascript'>" . file_get_contents($this->modulePath . "js/ie_polyfill.js") . "</script>";
            $output .= "<script nomodule type='text/javascript'>" . file_get_contents($this->modulePath . "js/PrivacyWire_legacy.js") . "</script>";
            return $output;
        }

        $output = "<script type='module' src='" . $this->moduleUrl . "js/PrivacyWire.js" . "'></script>";
        $output .= "<script nomodule type='text/javascript' src='" . $this->moduleUrl . "js/ie_polyfill.js" . "'></script>";
        $output .= "<script nomodule type='text/javascript' src='" . $this->moduleUrl . "js/PrivacyWire_legacy.js" . "'></script>";
        return $output;

    }

    public function ___renderPrivacyWireBannerTemplate()
    {
        return $this->renderTemplate('PrivacyWireBanner.php', $this->alternate_banner_template);
    }

    public function ___renderPrivacyWireConsentBlueprint()
    {
        return $this->renderTemplate('PrivacyWireConsentBlueprint.php', $this->alternate_inline_consent_template);
    }

    protected function renderTemplate($filename, $alternatePath = '')
    {
        $filePath = $this->wire('config')->paths->$this . $filename;

        $alternatePath = ltrim($alternatePath, '/');

        if (
            !empty($alternatePath) &&
            file_exists($this->wire('config')->paths->root . $alternatePath)) {
            $filePath = $this->wire('config')->paths->root . $alternatePath;
        }

        return wireRenderFile($filePath, ['module' => $this]);
    }

    /**
     * Get the PrivacyWire stylesheet as Object with path and url
     * @return \StdClass $file with $file->path and $file->url
     */
    public function ___getPrivacyWireStyles(): \StdClass
    {
        $file = new \StdClass;
        $file->name = "css/PrivacyWire.css";
        $file->path = $this->modulePath . $file->name;
        $file->url = $this->moduleUrl . $file->name;
        return $file;
    }

    /**
     * @param bool $legacy
     * @return \StdClass $file with $file->path and $file->url
     * @deprecated since version 1.0.2
     * Get the PrivacyWire Core file as Object with path and url
     */
    public function ___getPrivacyWireCore(bool $legacy = false): \StdClass
    {
        $file = new \StdClass;
        $file->name = "js/PrivacyWire" . ($legacy ? '_legacy' : '') . ".js";
        $file->path = $this->modulePath . $file->name;
        $file->url = $this->moduleUrl . $file->name;

        return $file;
    }

    /**
     * Get the current PrivacyWire config options and output them as JSON Object
     * @return string   JSON Object of PrivacyWire config options
     */
    public function ___getPrivacyWireConfigObject(): string
    {
        $privacyWireSettings = new \StdClass;
        $privacyWireSettings->version = $this->version;
        $privacyWireSettings->dnt = ($this->respectDNT) ? "1" : "0";
        $privacyWireSettings->bots = ($this->checkForBots) ? "1" : "0";
        $privacyWireSettings->customFunction = ($this->wire('sanitizer')->text($this->trigger_custom_js_function)) ?? "";
        $privacyWireSettings->messageTimeout = ($this->messageTimeout && intval($this->messageTimeout) > 1) ? intval($this->messageTimeout) : 1500;
        $privacyWireSettings->consentByClass = ($this->detect_consents_by_class) ? "1" : "0";
        $privacyWireSettings->cookieGroups = [
            'necessary' => $this->get("cookies_necessary_label{$this->lang}|cookies_necessary_label"),
            'functional' => $this->get("cookies_functional_label{$this->lang}|cookies_functional_label"),
            'statistics' => $this->get("cookies_statistics_label{$this->lang}|cookies_statistics_label"),
            'marketing' => $this->get("cookies_marketing_label{$this->lang}|cookies_marketing_label"),
            'external_media' => $this->get("cookies_external_media_label{$this->lang}|cookies_external_media_label"),
        ];
        return json_encode($privacyWireSettings);
    }
}