<?php
/**
 * Please see wp-cookie-allow.php for more details.
 *
 * @author $Vincent Weber <weepie-plugins@outlook.com>$
 */

use WpieFw\Templates\WpieTemplate;
use WpieFw\Templates\Files\WpieTemplatesFileFinder;
use WpieFw\Helpers\WpieMiscHelper;
use WpieFw\Helpers\WpieCookieHelper;
use WpieFw\Helpers\WpieMultisiteHelper;
use WpieFw\Exceptions\WpieExceptionLogger;
use WpieFw\Exceptions\WpieExceptionInterface;

if( ! defined( 'ABSPATH' ) ) exit;

if( !class_exists( 'WpcaModuleFrontend' ) ) {

	/**
	 * WpcaFrontend Class
	 *
	 * Module to handle the front-end lay-outs
	 *
	 * @author $Vincent Weber <weepie-plugins@outlook.com>$
	 *
	 * @since 2.3.3
	 */
	class WpcaModuleFrontend extends WpieFw\Modules\Iterator\WpieModule {


		/**
		 * The selected lay-out
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		public $layout = 'bar';


		/**
		 * Flag if closing (X) should be used as a 'dismiss'
		 *
		 * @since 3.1.2
		 *
		 * @var bool
		 */
		public $useXasDismiss = false;


		/**
		 * Flag if a privacy page is present
		 *
		 * @since 3.0
		 *
		 * @var bool
		 */
		public $hasCookiePolicyPage = false;


		/**
		 * Flag if privacy page is a PDF
		 *
		 * @since 3.1
		 *
		 * @var bool
		 */
		public $cookiePolicyIsPdf = false;


		/**
		 * Flag if consent has been given
		 *
		 * @since 3.1.2
		 *
		 * @var bool
		 */
		public $accepted = false;


		/**
		 * Flag if user declined
		 *
		 * @since 3.1
		 *
		 * @var bool
		 */
		public $declined = false;


		/**
		 * Flag if is 1st visit
		 *
		 * @since 3.1.2
		 *
		 * @var bool
		 */
		public $firstVisit = false;


		/**
		 * Flag if user is bypassing consent
		 *
		 * @since 3.1
		 *
		 * @var bool
		 */
		public $bypassingConsent = false;


		/**
		 * URL to the cookie policy page
		 *
		 * since 3.0
		 *
		 * @var string
		 */
		public $cookiePolicyUrl = '';

		/**
		 * Relative URL to the cookie policy page
		 *
		 * since 3.2.3
		 *
		 * @var string
		 */
		public $cookiePolicyUrlRel = '';


		/**
		 * Flag if the bar/box should be displayed on the cookie policy page
		 *
		 * @since 3.2
		 *
		 * @var string
		 */
		public $showBarBoxOnCookiePolicyPage = false;

		/**
		 * General CSS class for the policy page links
		 *
		 * since 3.2.11
		 *
		 * @var string
		 */
		private $_policyCssClass = 'wpca-policy-link';

		/**
		 * CSS class for the cookie policy page link
		 *
		 * since 3.0
		 *
		 * @var string
		 */
		private $_cookiePolicyCssClass = 'wpca-cookie-policy-link';


		/**
		 * Privacy page Post ID
		 *
		 * @since 3.0
		 *
		 * @var int
		 */
		private $_cookiePolicyPageId = 0;


		/**
		 * Flag to show or not show the box or bar
		 *
		 * @since 3.1
		 *
		 * @var bool
		 */
		private $_showBarBox = false;


		/**
		 * The HTML for the layout (bar or box)
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		private $_html = '';


		/**
		 * Roor URL for the templates folder
		 *
		 * @since 3.0.2
		 *
		 * @var string
		 */
		private $_templateRootUri = '';


		/**
		 * Cookie path for settings cookies
		 *
		 * @since 3.1
		 *
		 * @var string
		 */
		private $_cookiePath = '';

		/**
		 * Cookie domain for settings cookies
		 *
		 * @since 3.1
		 *
		 * @var string
		 */
		private $_cookieDomain = '';

		/**
		 * Flaf if no cache headers should be added
		 *
		 * @since 3.2.3
		 *
		 * @var string
		 */
		private $_noCacheHeaders = false;

		/**
		 * AJAX context
		 *
		 * @since 3.4
		 *
		 * @var string
		 */
		private $_ajaxContext = '';

		/**
		 * Template file name for the inline CSS
		 *
		 * @since 3.4.5
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_INLINE_CSS = 'wpca-frontend-inline-css.php';

		/**
		 * Template file name for the wrapper
		 *
		 * @since 3.4
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_LAYOUT_WRAPPER = 'wpca-frontend-layout-wrapper.php';

		/**
		 * Template file name for the bar
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_BAR = 'wpca-frontend-bar.php';


		/**
		 * Template file name for the box
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_BOX = 'wpca-frontend-box.php';


		/**
		 * Template file name for the Accept button
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_BTN_ACCEPT = 'wpca-frontend-btn-accept.php';


		/**
		 * Template file name for the Decline button
		 *
		 * @since 3.1
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_BTN_DECLINE = 'wpca-frontend-btn-decline.php';


		/**
		 * Template file name for the reset consent button
		 *
		 * @since 3.1
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_BTN_RESET_CONSENT = 'wpca-frontend-btn-reset-consent.php';


		/**
		 * Template file name for the Cookie Category settings button
		 *
		 * @since 3.2.11
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_BTN_CC_SETTINGS = 'wpca-frontend-btn-cc-settings.php';


		/**
		 * Template file name for the cookie policy page link
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_POLICY_LINK = 'wpca-frontend-link-policy.php';


		/**
		 * Template file name for the popup
		 *
		 * @since 3.2.11
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_POPUP = 'wpca-frontend-popup.php';


		/**
		 * Template file name for the layer
		 *
		 * @since 3.2.11
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_LAYER = 'wpca-frontend-layer.php';


		/**
		 * Template file name for the reconsider image
		 *
		 * @since 3.4
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_RECONSIDER_IMG = 'wpca-frontend-reconsider-img.php';


		/**
		 * Template file name for the Google Consent Mode v2 gtag
		 *
		 * @since 3.4.8
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_GCMV2 = 'wpca-frontend-gcm.php';


		/**
		 * File name for the frontend JavaScript
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		const FILE_NAME_JS = 'wpca-frontend';


		/**
		 * File name for the frontend CSS
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		const FILE_NAME_CSS = 'wpca-frontend';

		/**
		 * The query var param name for bypassing the cookie consent process
		 *
		 * @since 2.3.3
		 *
		 * @var string
		 */
		const QUERY_VAR_BYPASS_COOKIE_CONSENT = 'wpca_bypass_consent';

		/**
		 * The query var param name for resetting the cookie consent
		 *
		 * @since 3.1
		 *
		 * @var string
		 */
		const QUERY_VAR_RESET_COOKIE_CONSENT = 'wpca_reset_consent';

		/**
		 * The query var param name for indicating to use the no cache headers
		 *
		 * @since 3.2.3
		 *
		 * @var string
		 */
		const QUERY_VAR_NO_CACHE_HEADERS = 'wpca_no_cache_headers';

		/**
		 * Context for kses allowed HTML filter
		 *
		 * @since 3.3
		 *
		 * @var string
		 */
		const KSES_FRONTEND_CONTEXT = 'kses_wpca_frontend';

		/**
		 * The local storage key that contains the users Google consent
		 *
		 * @since 3.4.8
		 *
		 * @var string
		 */
		const LOCAL_STORAGE_KEY_GCM = 'wpcaConsentMode';		


		/**
		 * Constructor
		 *
		 * @since 3.0.1
		 *
		 * @acces public
		 */
		public function __construct() {
			$this->_ajaxContext = md5( 'wpc@_fr()nt3nd' );
		}


		/* (non-PHPdoc)
		 * @see WpiePluginModule::start()
		 */
		public function start()
		{
			$this->_templateRootUri = $this->globals->get( 'moduleUri' ) . '/'. parent::getIndex() .'/templates/';
		}


		/**
		 * Render the wrapper HTML
		 *
		 * @access public
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.4
		 */
		public function renderWrapper()
		{
			try {
				$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_LAYOUT_WRAPPER, false );
				$wrapper = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_LAYOUT_WRAPPER );
				$html = $wrapper->render( false, true );

				$this->_showBarBox = true;
			} catch( \Throwable $exc ) {
				// log only when logged in
				WpieExceptionLogger::log( $exc, true );
				// on frontend do not show
				$html = '';
			}

			/**
			 * Let others alter the rendered wrapper HTML
			 *
			 * @param string $html
			 *
			 * @since 3.4.2
			 */
			$this->_html = apply_filters( 'wpca_frontend_wrapper_html', $html );
		}


		/**
		 * Render the Templates
		 *
		 * @access public
		 *
		 * @uses WpieTemplate
		 *
		 * @since 2.3.3
		 */
		public function render()
		{
		    /**
			 * Let others hook before the render logic
			 *
			 * @param string $layout
			 *
			 * @since 3.2.11
			 * @deprecated since 3.2.11 the layout is removed from the filter name and added as argument
		     */
			do_action( 'wpca_before_render_'.$this->layout );

			/**
			 * Let others hook before the render logic
			 *
			 * @param string $layout
			 *
			 * @since 3.2.11
			 */
			do_action( 'wpca_before_render', $this->layout );

			/** @var WpcaModuleCore $moduleCore */
			$moduleCore = $this->getModule( 'core' );

			$showBtnAccept = !$moduleCore->forceHideBtnAccept();
			$btnDeclinePosition = $this->settingsProcessor->get( 'style' )->get( 'style_button_decline_pos' );
			$showIconClose = $this->settingsProcessor->get( 'general' )->get( 'general_show_x' );
			$showBtnDecline = $this->settingsProcessor->get( 'general' )->get( 'general_show_btn_decline' );
			$showIconSettings = $this->settingsProcessor->get( 'general' )->get( 'general_show_icon_cc_settings' );
			$settingsTrigger = $this->settingsProcessor->get( 'general' )->get( 'general_cc_settings_trigger' );
			$text = $this->settingsProcessor->get( 'content' )->get( 'content_text' );
			$shadow = $this->settingsProcessor->get( 'style' )->get( 'style_box_shadow' );
			$iconSettingHoverText = $this->settingsProcessor->get( 'content' )->get( 'content_cc_settings_trigger_txt' );

			// force hiding the decline button if 3rd party cookies are set before consent
			if( $showBtnDecline && $moduleCore->do3rdPartyCookiesBeforeConsent() ) {
				$showBtnDecline = false;
			}

			// show or hide buttons
			$showBtns =  ( $showBtnDecline || $showBtnAccept || $showIconSettings );

			// text and procesisng text for button accept and decline
			$btnAcceptTxt = $this->settingsProcessor->get('content')->get( 'content_button_accept_txt' );
			$btnDeclineTxt = $this->settingsProcessor->get('content')->get( 'content_button_decline_txt' );
			$btnAcceptTxtProcessing = $this->settingsProcessor->get('content')->get( 'content_button_accept_txt_accepting' );
			$btnDeclineTxtProcessing = $this->settingsProcessor->get('content')->get( 'content_button_decline_txt_declining' );

			$linkCookiePolicy = '';
			$closeXColor = '';
			$closeXinside = false;

			if( $this->hasCookiePolicyPage ) {
				$linkCookiePolicy = $this->renderLinkPolicy( array(
				    'href' => $this->cookiePolicyUrl,
				    'txt' => $this->settingsProcessor->get( 'content' )->get( 'content_cookie_policy_link_text' ),
				    'class' => $this->_cookiePolicyCssClass,
				    'target' => $this->settingsProcessor->get( 'content' )->get( 'content_cookie_policy_page_target' )
				) );
			}

			/**
			 * Alter the current scope variables
			 *
			 * Before the bar/box template is instantiated,
			 * others may alter the set of variables to change the layout
			 *
			 * @since 3.2.11
			 *
			 * @var array $vars
			 * @var strinf $layout (bar or box)
			 */
			$vars = apply_filters( 'wpca_instantiate_layout_template', get_defined_vars(), $this->layout );
			extract( $vars );

			try {
				switch ( $this->layout )
				{
					case 'box':
						$template = $this->_renderBox();
						break;
					case 'bar':
					default:
						$template = $this->_renderBar();
						break;
				}
			} catch( \Throwable $e ) {
				WpieExceptionLogger::log( $e );
				return;
			}

			// retreive the css class string
			$class = $template->getVar( 'class' );

			// add a css class indicating a that a shadow should be added
			if( $shadow ) {
				$class .= ' wpca-shadow';
			}

			// color for settings icon
			$settingsIconColor = ( $showIconSettings ) ? $this->settingsProcessor->get( 'style' )->get( 'style_icon_cc_settings_color' ) : '';
			$class .= ( '' !== $settingsIconColor ) ? ' wpca-cc-settings-has wpca-cc-settings-'.$settingsTrigger : ' wpca-cc-settings-not';

			if( $showIconClose ) {
			    $closeXColor = $this->settingsProcessor->get( 'style' )->get( 'style_close_color' );
				$closeXinside = $this->settingsProcessor->get( 'general' )->get( 'general_close_inside' );
			}
			$class .= ( '' !== $closeXColor ) ? ' wpca-close-x-has' : ' wpca-close-x-not';
			$class .= ( $closeXinside ) ? ' wpca-close-x-inside' : ' wpca-close-x-outside';

			// accept all shortcodes
			$text = do_shortcode( $text );

			$text = wpautop( $text );

			// ensure allowed html
			$text = wp_kses( $text, wp_kses_allowed_html( self::KSES_FRONTEND_CONTEXT ) );

			$class .= ( $showBtnDecline ) ? ' wpca-decline-has' : ' wpca-decline-not';

			// update the css class string
			$template->setVar( 'class', $class );

			// setup Template vars
			$template->setVar( 'content_text', $text );
			$template->setVar( 'show_btns', $showBtns );
			$template->setVar( 'show_btn_decline', $showBtnDecline );
			$template->setVar( 'show_btn_accept', $showBtnAccept );
			$template->setVar( 'btn_decline_right', $btnDeclinePosition === 'right' );
			$template->setVar( 'show_icon_close', $showIconClose );
			$template->setVar( 'show_icon_settings', $showIconSettings );
			$template->setVar( 'color_close_x_icon', $closeXColor );
			$template->setVar( 'color_settings_icon', $settingsIconColor );
			$template->setVar( 'settings_trigger_icon', $settingsTrigger === 'icon' );
			$template->setVar( 'icon_settings_hover_txt', $iconSettingHoverText );
			// set Template tags(s)
			$template->setTag( 'WPCA_PRIVACY_LINK', $linkCookiePolicy );
			// render the buttons, for the button accept: the 3rd arg $cc needs to be empty!
			$template->setVar( 'btn_accept', $this->renderBtnAccept( $btnAcceptTxt, $btnAcceptTxtProcessing, '', false ) );
			$template->setVar( 'btn_decline', $this->renderBtnDecline( $btnDeclineTxt, $btnDeclineTxtProcessing, false ) );

			// render
			$html = $template->render( false, true );

			// Add the layer HTML template
			$html .= $this->_renderLayer();

			// Add the icon
			$html .= $this->_renderReconsiderIcon( $iconSettingHoverText );

			// Add the popup HTML template
			$html .= $this->_renderPopup();

			/**
			 * Let others alter the rendered HTML
			 *
			 * @param string $html
			 *
			 * @since 3.2.11
			 */
			return apply_filters( 'wpca_frontend_html', $html );
		}


		/**
		 * Render the privacy button / link
		 *
		 * @access public
		 *
		 * @param array $args
		 *
		 * @uses WpieTemplate
		 *
		 * @since 2.3.3
		 * @since 3.2.11 renamed to WpcaModuleFrontend::renderLinkPolicy()
		 * @since 3.2.11 replaced arguments with $args array
		 * @since 3.2.11 removed $cache argument, asuming the policy link is rendered only once, caching is not needed
		 *
		 * @return string the rendered html if $echo is bool false
		 */
		public function renderLinkPolicy( $args = array() )
		{
		    if( !is_array( $args ) ) {
		        return '';
		    } else {
		        $args = wp_parse_args( $args, array(
		            'href' => '',
		            'txt' => '',
		            'class' => '',
		            'target' => '_self',
		            'echo' => false
		        ) );
		    }

		    if( '' === $args['txt'] || '' == $args['href'] ) {
		        return '';
		    }

		    extract( $args );

		    $class = "{$this->_policyCssClass} $class";

			try {
			    $finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_POLICY_LINK, false );
				$link = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_POLICY_LINK );
				$link->setVar( 'class' , esc_attr( $class ) );
				$link->setVar( 'link_txt', $txt );
				$link->setVar( 'href', esc_attr( $href ) );
				$link->setVar( 'target', esc_attr( $target ) );
				$html = $link->render( false, true );

			} catch( \Throwable $exc ) {
				// log only when logged in
				WpieExceptionLogger::log( $exc, true );
				// on frontend do not show message
				$html = '';
			}

			if( $echo ) {
				echo $html;
			}	else {
				return $html;
			}
		}


		/**
		 * Render the Accept button
		 *
		 * @access 	public
		 *
		 * @param	string $txt
		 * @param 	string $txtProcessing
		 * @param 	string $cc
		 * @param 	bool $cache
		 * @param	bool $echo
		 *
		 * @uses 	WpieTemplate
		 *
		 * @since 	2.3.3
		 * @since 	2.3.3 added static cache
		 * @since   3.2.11 added the cc parameter
		 *
		 * @return string the rendered html if $echo is bool false
		 */
		public function renderBtnAccept( $txt = '', $txtProcessing = '', $cc = '', $cache = true, $echo = false )
		{
			static $cached = array();

			$cacheKey = ( '' === $txt && '' === $txtProcessing && '' === $cc ) ? '_' : md5($txt.$txtProcessing.$cc);
			if( !empty( $cached ) && isset($cached[$cacheKey] ) ) {
				if( $echo ) {
					echo $cached[$cacheKey];
				} else {
					return $cached[$cacheKey];
				}
			}

			if( '' === $txt ) {
			    if( '' !== $cc ) {
			        $txt = $this->settingsProcessor->get('content')->get( 'content_button_accept_cc_txt' );
			    } else {
			        $txt = $this->settingsProcessor->get('content')->get( 'content_button_accept_txt' );
			    }
			}
			if( '' === $txtProcessing ) {
				$txtProcessing = $this->settingsProcessor->get('content')->get( 'content_button_accept_txt_accepting' );
			}

			$class = 'wpca-btn-accept wpca-btn wpca-btn-hide';
			if( '' !== $cc ) {
			    $class .= ' wpca-btn-accept-cc';
			}

			try {
				$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_BTN_ACCEPT, false );
				$btn = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_BTN_ACCEPT );
				$btn->setVar( 'class', $class );
				$btn->setVar( 'txt', $txt );
				$btn->setVar( 'txt_processing', $txtProcessing );
				$btn->setVar( 'cc', $cc );
				$btn->setTag( 'CC', $cc );
				$html = $btn->render( false, true );

				if( $cache ) {
					$cached[$cacheKey] = $html;
				}
			} catch( \Throwable $e ) {
				// log only when logged in
				WpieExceptionLogger::log( $e, true );
				// on frontend do not show message
				$html = '';
			}

			if( $echo ) {
				echo $html;
			}	else {
				return $html;
			}
		}


		/**
		 * Render the Decline button
		 *
		 * @access public
		 *
		 * @param	string $txt
		 * @param 	string $txtProcessing
		 * @param 	bool $cache
		 * @param	bool $echo
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.1
		 *
		 * @return string the rendered html if $echo is bool false
		 */
		public function renderBtnDecline( $txt = '', $txtProcessing = '', $cache = true, $echo = false )
		{
			static $cached = array();

			$cacheKey = ( '' === $txt && '' === $txtProcessing) ? '_' : md5($txt.$txtProcessing);
			if( !empty( $cached ) && isset($cached[$cacheKey] ) ) {
				if( $echo ) {
					echo $cached[$cacheKey];
				} else {
					return $cached[$cacheKey];
				}
			}

			if( '' === $txt ) {
				$txt = $this->settingsProcessor->get('content')->get( 'content_button_decline_txt' );
			}
			if( '' === $txtProcessing ) {
				$txtProcessing = $this->settingsProcessor->get('content')->get( 'content_button_decline_txt_declining' );
			}

			$class = 'wpca-btn-decline wpca-btn wpca-btn-hide';

			try {
				$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_BTN_DECLINE, false );
				$btn = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_BTN_DECLINE );
				$btn->setVar( 'class', $class );
				$btn->setVar( 'txt', $txt );
				$btn->setVar( 'txt_processing', $txtProcessing );
				$html = $btn->render( false, true );

				if( $cache ) {
					$cached[$cacheKey] = $html;
				}
			} catch( \Throwable $e ) {
				// log only when logged in
				WpieExceptionLogger::log( $e, true );
				// on frontend do not show message
				$html = '';
			}

			if( $echo ) {
				echo $html;
			}	else {
				return $html;
			}
		}


		/**
		 * Render the Reset consent button
		 *
		 * @access public
		 *
		 * @param	string $txt
		 * @param 	string $txtProcessing
		 * @param 	bool $cache
		 * @param	bool $echo
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.1
		 *
		 * @return string the rendered html if $echo is bool false
		 */
		public function renderBtnResetConsent( $txt = '', $txtProcessing = '', $cache = true, $echo = false )
		{
			static $cached = array();

			$cacheKey = ( '' === $txt && '' === $txtProcessing) ? '_' : md5($txt.$txtProcessing);
			if( !empty( $cached ) && isset($cached[$cacheKey] ) ) {
				if( $echo ) {
					echo $cached[$cacheKey];
				} else {
					return $cached[$cacheKey];
				}
			}

			if( '' === $txt ) {
				$txt = $this->settingsProcessor->get('content')->get( 'content_button_reset_consent_txt' );
			}
			if( '' === $txtProcessing ) {
				$txtProcessing = $this->settingsProcessor->get('content')->get( 'content_button_reset_consent_txt_resetting' );
			}

			try {
				$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_BTN_RESET_CONSENT, false );
				$btn = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_BTN_RESET_CONSENT );
				$btn->setVar( 'txt', $txt );
				$btn->setVar( 'txt_processing', $txtProcessing );
				$html = $btn->render( false, true ).$this->renderBtnAccept();

				if( $cache ) {
					$cached[$cacheKey] = $html;
				}
			} catch( \Throwable $e ) {
				// log only when logged in
				WpieExceptionLogger::log( $e, true );
				// on frontend do not show messages
				$html = '';
			}

			if( $echo ) {
				echo $html;
			}	else {
				return $html;
			}
		}


		/**
		 * Render the Cookie Category settings button
		 *
		 * @access public
		 *
		 * @param array $args
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.2.11
		 *
		 * @return string the rendered html if $echo is bool false
		 */
		public function renderBtnCcSettings( $txt = '', $cache = true, $echo = false )
		{
		    static $cached = array();

		    $cacheKey = ( '' === $txt ) ? '_' : md5($txt);
		    if( !empty( $cached ) && isset($cached[$cacheKey] ) ) {
		        if( $echo ) {
		            echo $cached[$cacheKey];
		        } else {
		            return $cached[$cacheKey];
		        }
		    }

		    if( '' === $txt ) {
		        $txt = $this->settingsProcessor->get('content')->get( 'content_button_cc_settings_txt' );
		    }

		    $class = 'wpca-btn-cc-settings wpca-btn wpca-btn-hide';
			$hoverText = $this->settingsProcessor->get( 'content' )->get( 'content_cc_settings_trigger_txt' );

		    try {
		        $finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_BTN_CC_SETTINGS, false );
		        $btn = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_BTN_CC_SETTINGS );
		        $btn->setVar( 'class', $class );
		        $btn->setVar( 'txt', $txt );
				$btn->setVar( 'hover_txt', $hoverText );
		        $html = $btn->render( false, true );

		        if( $cache ) {
		            $cached[$cacheKey] = $html;
		        }
		    } catch( \Throwable $e ) {
		        // log only when logged in
		        WpieExceptionLogger::log( $e, true );
		        // on frontend do not show message
		        $html = '';
		    }

		    if( $echo ) {
		        echo $html;
		    }	else {
		        return $html;
		    }
		}


		/**
		 * Callback for the wpca_styles_frontend_inline hook
		 *
		 * Register inilne styles for the bar and box.
		 *
		 * @access public
		 * @uses WpcaFrontend::_rendeInlineCSS()
		 *
		 * @since 3.4.5
		 */
		public function setInlineStyles( $inline )
		{
			$html = $this->_rendeInlineCSS();

			return $inline . $html;			
		}


		/**
		 * Callback for the wp_footer hook
		 *
		 * Add the bar HTML to the current page
		 *
		 * @access public
		 *
		 * @since 2.3.3
		 */
		public function addHtml()
		{
			if( $this->_showBarBox ) {
				echo $this->_html;
			}
		}
		

		/**
		 * Callback for the wp_head hook
		 *
		 * Adds the Google Consent mMde defaults to the DataLayer
		 *
		 * @access public
		 * @uses WpcaModuleFrontend::_renderGoogleConsentModeDefaultScript()
		 * @since 3.4.8
		 */
		public function setGoogleConsentModeDefaults()
		{
			$generalSettings = $this->settingsProcessor->get( 'general' );
			$enable = $generalSettings->get( 'general_enable_gcmv2' );
			$disableDefaults = $generalSettings->get( 'general_disable_gcmv2_defaults' );

			if($enable && !$disableDefaults) {
				echo $this->_renderGoogleConsentModeDefaultScript();
			}
		}


		/**
		 * Callback for the wpca_scripts_frontend hook
		 *
		 * Registers the frontend script
		 *
		 * @access public
		 *
		 * @param WP_Scripts $wp_scripts
		 * @param array $excludes items to exclude
		 * @param bool $isScriptDebug
		 *
		 * @since 2.3.3
		 */
		public function setScripts( $scripts )
		{
			$relModulePath = parent::getIndex() . '/js/' . self::FILE_NAME_JS;
			$path = $this->globals->get( 'moduleUri' ) . '/' . $relModulePath;

			$script = new stdClass();
			$script->main = true;
			$script->path = $path;
			$script->dep = [ 'jquery' ];
			$script->args = [ 'in_footer' => true ];

			$scripts[] = $script;

			return $scripts;
		}


		/**
		 * Callback for the wpca_styles_frontend hook
		 *
		 * Registers the main frontend styles
		 *
		 * @access public
		 *
		 * @param array $styles
		 * 
		 * @since 2.3.3
		 */
		public function setStyles( $styles )
		{
			$relModulePath = parent::getIndex() . '/css/' . self::FILE_NAME_CSS;
			$path = $this->globals->get( 'moduleUri' ) . '/' . $relModulePath;

			$style = new stdClass();
			$style->main = true;
			$style->path = $path;

			$styles[] = $style;

			return $styles;
		}


		/**
		 * Callback for the wpca_script_frontend_vars_before hook
		 *
		 * Add the expire time to the global JavaScript vars array
		 *
		 * @access public
		 *
		 * @param array $vars
		 *
		 * @since 2.3.3
		 * @since 3.4.5 renamed to setScriptVars
		 *
		 * @return array
		 */
		public function setScriptVars( $vars )
		{
			/** @var WpcaModuleCore $moduleCore */
			$moduleCore = $this->getModule( 'core' );
			/** @var WpcaModuleAutomate $moduleAutomate */
			$moduleAutomate = $this->getModule( 'automate' );
			$settingsGeneral = $this->settingsProcessor->get( 'general' );
			$settingsStyle = $this->settingsProcessor->get( 'style' );

			// Google Tag Manager
			$gtm = new stdClass();
			$gtm->optimize = $settingsGeneral->get( 'general_gtm_optimize' );
			$gtm->dataLayerName = $settingsGeneral->get( 'general_gtm_datalayer_name' );
			$gtm->dataLayerVar = $settingsGeneral->get( 'general_gtm_dl_var' );
			$gtm->dataLayerEventPrefix = $settingsGeneral->get( 'general_gtm_dl_event_prefix' );
			$gtm->enableGCMv2 = $settingsGeneral->get( 'general_enable_gcmv2' );
			$gtm->localStorageKey = self::LOCAL_STORAGE_KEY_GCM;


			$vars['loadHtmlEarly']         = $settingsGeneral->get( 'general_load_html_early', true );
			$vars['fetchMethod']           = $settingsGeneral->get( 'general_fetch_method', 'adminajax' );
			$vars['layout']                = $this->layout;
			$vars['cookieExpire']          = (int)$settingsGeneral->get( 'general_expire_time' );
			$vars['cookiesBeforeConsent']  = $moduleCore->cookiesBeforeConsent;
			$vars['consentMethod']         = $moduleCore->consentMethod;
			$vars['policyCssClass']        = $this->_policyCssClass;
			$vars['cookiePolicyPathRel']   = $this->cookiePolicyUrlRel;
			$vars['queryVarBypass']        = self::QUERY_VAR_BYPASS_COOKIE_CONSENT;
			$vars['showLayer']             = $settingsGeneral->get( 'general_show_layer' );
			$vars['hasClose']              = $settingsGeneral->get( 'general_show_x' );
			$vars['useXAsDismiss']         = $this->useXasDismiss;
			$vars['minScrollTop']          = (int)$settingsGeneral->get( 'general_min_scroll_top' );
			$vars['cookiePath']            = $this->_cookiePath;
			$vars['cookieDomain']          = $this->_cookieDomain;
			$vars['bypassingConsent']      = $this->bypassingConsent;
			$vars['allowedCc']             = $moduleAutomate->getAllowedCookieCategories();
			$vars['requiredCc']            = $moduleAutomate->getRequiredCookieCategories();
			$vars['replaceResetBtn']       = $settingsGeneral->get( 'general_replace_reset_by_accept_button' );
			$vars['reloadAfterConsent']    = $settingsGeneral->get( 'general_reload_after_consent' );
			$vars['animateDuration']       = (int)$settingsStyle->get( 'style_animate_duration' );
			$vars['gtm']                   = $gtm;
			$vars['ajaxContextFrontend']   = $this->_ajaxContext;
			$vars['restUrl']               = esc_url_raw( get_rest_url() );
			$vars['restNonce']             = wp_create_nonce( 'wp_rest' );


			unset( $moduleCore, $moduleAutomate, $settingsGeneral, $settingsStyle );

			return $vars;
		}


		/**
		 * Callback for the wp_kses_allowed_html hook
		 *
		 * @param array $allowedtags
		 * @param string $context
		 *
		 * @since 3.3
		 * @return array
		 */
		public function ksesAllowedHTML( $allowedtags, $context )
		{
			if( self::KSES_FRONTEND_CONTEXT === $context ) {
				global $allowedposttags;

				$allowedtags = array_merge( $allowedtags, $allowedposttags );

				if( !isset( $allowedtags['input'] ) ) {
					$allowedtags['input'] = [
						'id' => true,
						'class' => true,
						'name' => true,
						'type' => true,
						'tabindex' => true,
						'value' => true,
						'disabled' => true,
						'data-*' => true
					];
				}

			}

			return $allowedtags;
		}


		/**
		 * Callback for the wpca_registered_shortcodes hook
		 *
		 * Register the shortcodes for this module
		 *
		 * @param array $registered
		 *
		 * @since 3.0
		 *
		 * @return array
		 */
		public function registerShortcodes( $registered = array() )
		{
			$shordcodes = array(
					'wpca_btn_privacy'        => 'doShortcodeLinkCookiePolicy', // backword compat
					'wpca_cookie_policy_link' => 'doShortcodeLinkCookiePolicy',
					'wpca_btn_accept'         => 'doShortcodeBtnAccept',
			        'wpca_btn_accept_cc'      => 'doShortcodeBtnAcceptCc',
					'wpca_btn_decline'        => 'doShortcodeBtnDecline',
					'wpca_btn_reset_consent'  => 'doShortcodeBtnResetConsent',
					'wpca_btn_cc_settings'    => 'doShortcodeBtnCcSettings'
			);

			foreach ( $shordcodes as $shordcode => $callback ) {
				$registered[] = $shordcode;

				if( !defined( 'WPCA_ACTIVE' ) ) {
					$callback = array( 'WpieFw\Helpers\WpieMiscHelper', 'doShortcodeNot' );
				} else {
					$callback = array( $this, $callback );
				}

				add_filter( 'wpca_shortcode_' . $shordcode, $callback, 10, 3 );
			}

			return $registered;
		}


		/**
		 * Callback for the wpca_shortcode_wpca_cookie_policy_link hook
		 *
		 * Render the shortcode content
		 *
		 * @access public
		 *
		 * @param array $atts
		 * @param string $content
		 * @param string $tag
		 *
		 * @uses WpcaFrontend::renderbtnCookiePolicy()
		 *
		 * @since 2.3.3
		 *
		 * @return string
		 */
		public function doShortcodeLinkCookiePolicy( $atts = array(), $content = '', $tag = '' )
		{
		    if( !$this->hasCookiePolicyPage ) {
				return '';
		    }

			$atts = shortcode_atts(
					array(
					    'txt' => $this->settingsProcessor->get( 'content' )->get( 'content_cookie_policy_link_text' ),
					    'class' => $this->_cookiePolicyCssClass,
					    'target' => $this->settingsProcessor->get( 'content' )->get( 'content_cookie_policy_page_target' )
					), $atts, $tag );

			$atts['href'] = $this->cookiePolicyUrl;

			return $this->renderLinkPolicy( $atts );
		}


		/**
		 * Callback for the wpca_shortcode_wpca_btn_accept hook
		 *
		 * Render the shortcode content
		 *
		 * @access public
		 *
		 * @param array $atts
		 * @param string $content
		 * @param string $tag
		 *
		 * @uses WpcaFrontend::renderBtnAccept()
		 *
		 * @since 2.3.3
		 *
		 * @return string
		 */
		public function doShortcodeBtnAccept( $atts = array(), $content = '', $tag = '' )
		{
			$atts = shortcode_atts(
					array(
							'txt' => '',
							'txt_accepting' => '',
					), $atts, $tag );

			$txt = $atts['txt'];
			$txtAccepting = $atts['txt_accepting'];

			return $this->renderBtnAccept( $txt, $txtAccepting );
		}


		/**
		 * Callback for the wpca_shortcode_wpca_btn_accept_cc hook
		 *
		 * Render the shortcode content
		 *
		 * @access public
		 *
		 * @param array $atts
		 * @param string $content
		 * @param string $tag
		 *
		 * @uses WpcaFrontend::renderBtnAccept()
		 *
		 * @since 3.2.11
		 *
		 * @return string
		 */
		public function doShortcodeBtnAcceptCc( $atts = array(), $content = '', $tag = '' )
		{
		    $atts = shortcode_atts(
		        array(
		            'txt' => '',
		            'txt_accepting' => '',
		            'cc' => '%CC%'
		        ), $atts, $tag );

		    $txt = $atts['txt'];
		    $txtAccepting = $atts['txt_accepting'];
		    $cc = $atts['cc'];

			/** @var WpcaModuleAutomate $moduleAutomate */
			$moduleAutomate = $this->getModule( 'automate' );

		    // Check for valid Cookie Categories
		    if( '' !== $cc && '%CC%' !== $cc && '{CC}' !== $cc && !$moduleAutomate->isAllowedCookieCategory( $cc ) ) {
		        $cc = WpcaModuleAutomate::CC_IDX_DEFAULT;
		    }

		    return $this->renderBtnAccept( $txt, $txtAccepting, $cc );
		}


		/**
		 * Callback for the wpca_shortcode_wpca_btn_decline hook
		 *
		 * Render the shortcode content
		 *
		 * @access public
		 *
		 * @param array $atts
		 * @param string $content
		 * @param string $tag
		 *
		 * @uses WpcaFrontend::renderBtnDecline()
		 *
		 * @since 3.1
		 *
		 * @return string
		 */
		public function doShortcodeBtnDecline( $atts = array(), $content = '', $tag = '' )
		{
			$atts = shortcode_atts(
					array(
							'txt' => '',
							'txt_declining' => '',
					), $atts, $tag );

			$txt = $atts['txt'];
			$txtDeclining = $atts['txt_declining'];

			return $this->renderBtnDecline( $txt, $txtDeclining );
		}

		/**
		 * Callback for the wpca_shortcode_wpca_btn_reset_consent hook
		 *
		 * Render the shortcode content
		 *
		 * @access public
		 *
		 * @param array $atts
		 * @param string $content
		 * @param string $tag
		 *
		 * @uses WpcaFrontend::renderBtnResetConsent()
		 *
		 * @since 3.1
		 *
		 * @return string
		 */
		public function doShortcodeBtnResetConsent( $atts = array(), $content = '', $tag = '' )
		{
			$atts = shortcode_atts(
					array(
							'txt' => '',
							'txt_resetting' => ''
					), $atts, $tag );

			$txt = $atts['txt'];
			$txtResetting = $atts['txt_resetting'];

			return $this->renderBtnResetConsent( $txt, $txtResetting );
		}

		/**
		 * Callback for the wpca_shortcode_wpca_btn_cc_settings hook
		 *
		 * Render the shortcode content
		 *
		 * @access public
		 *
		 * @param array $atts
		 * @param string $content
		 * @param string $tag
		 *
		 * @uses WpcaFrontend::renderBtnCcSettings()
		 *
		 * @since 3.2.11
		 *
		 * @return string
		 */
		public function doShortcodeBtnCcSettings( $atts = array(), $content = '', $tag = '' )
		{
		    $atts = shortcode_atts(
		        array(
		            'txt' => ''
		        ), $atts, $tag );

		    $txt = $atts['txt'];

		    return $this->renderBtnCcSettings( $txt );
		}


		/**
		 * Determine if cookies should be blocked
		 *
		 * @param string $cc
		 *
		 * @since 3.0.2
		 *
		 * @return boolean
		 */
		public function doBlockCookies( $cc = '' )
		{
			/** @var WpcaModuleCore $moduleCore */
			$moduleCore = $this->getModule( 'core' );
			/** @var WpcaModuleAutomate $moduleAutomate */
			$moduleAutomate = $this->getModule( 'automate' );

			$clientHasAll = $moduleAutomate->hasClientCookieCategoriesAll();
			$clientHasCategories = $moduleAutomate->hasClientCookieCategories();
			$do3rdPartyCookiesBeforeConsent = $moduleCore->do3rdPartyCookiesBeforeConsent();

			// Does the client has the passed category set
			$cc = trim( $cc );
			$checkForClientCc = false;
			if( '' !== $cc && $moduleAutomate->isAllowedCookieCategory( $cc ) ) {
				$checkForClientCc = true;
				$clientHasCategory = $moduleAutomate->hasClientThisCookieCategory( $cc );
			}

			// block cookies by default
			$block = true;

			// @TODO: move this if statement up to begin of method
			// don't block cookies if plugin is not active
			if( !defined( 'WPCA_ACTIVE' ) ) {
				$block = false;

			// and don't block cookies if a cookie category is passed
			// and client has this category stored
			} elseif( $checkForClientCc && $clientHasCategory ) {
				$block = false;

			// and don't block cookies if
			// client has accepted ALL categories
			} elseif( $this->accepted && $clientHasAll ) {
				$block = false;

			// and don't block cookies if
			// client has accepted and hasn't got categories yet
			// This is the case when the user accepts without saving the settings
			} elseif( $this->accepted && !$clientHasCategories ) {
				$block = false;

			// and don't block cookies if
			// client has NOT accepted and declined yet (1st visit untill client makes a choice)
			// and ALL (3rd party) cookies are allowed before consent
			} elseif( $this->firstVisit && $do3rdPartyCookiesBeforeConsent ) {
				$block = false;
			}

			return $block;
		}


		/**
		 * Setup the parameters for the frontend logic
		 *
		 * @since 3.4
		 */
		public function initParams()
		{
			// return to prevent fatal errors
			// @see https://github.com/webRtistik/wp-cookie-allow/issues/79
			/** @var WpcaModuleCore $moduleCore */
			if( false === ( $moduleCore = $this->getModule( 'core' ) ) ) {
				return;
			}

			// set all the necessary cookie params
			$this->_setCookieParams();

			if( isset( $_REQUEST[self::QUERY_VAR_NO_CACHE_HEADERS] ) ) {
				$this->_noCacheHeaders = true;
			} elseif( $this->settingsProcessor->get( 'general' )->get( 'general_disable_browser_cache' ) ) {
				$this->_noCacheHeaders = true;
			}

			if( $this->_noCacheHeaders ) {
				$this->_setNoCacheHeaders();
			}

			// do not show the bar or box on the cookie policy page
			if( isset($_REQUEST[self::QUERY_VAR_BYPASS_COOKIE_CONSENT]) ) {
				$this->bypassingConsent = true;
			}

			// check if user initiated a cookie consent reset
			if( isset($_REQUEST[self::QUERY_VAR_RESET_COOKIE_CONSENT]) ) {
				WpieCookieHelper::delete( $moduleCore->getCookieName( 'consent' ), true, $this->_cookiePath, $this->_cookieDomain );
				// redirect to URL without reset consent
				wp_redirect( remove_query_arg( self::QUERY_VAR_RESET_COOKIE_CONSENT ) );
				exit;
			}

			// set the layout params
			$this->layout = $this->settingsProcessor->get( 'style' )->get( 'style_type' );

			// did consent (accept) or not
			$this->accepted = ( '1' === $moduleCore->cookieValueConsent );

			// declined or not
			$this->declined = ( '0' === $moduleCore->cookieValueConsent );

			// if not accepted and declined yet, assume it's a 1st visit
			$this->firstVisit = ( !$this->accepted && !$this->declined );

			// use the (X) a dismiss action
			$this->useXasDismiss = $this->_doXasDismiss();

			// the privacy page
			$this->_cookiePolicyPageId = (int)$this->settingsProcessor->get( 'content' )->get( 'content_cookie_policy_page' );
			$this->hasCookiePolicyPage = ( -1 !== $this->_cookiePolicyPageId );
			if( $this->hasCookiePolicyPage ) {
				$this->cookiePolicyIsPdf = ( wp_attachment_is( 'pdf', $this->_cookiePolicyPageId ) );
				if( !$this->cookiePolicyIsPdf ) {
					// show or hide the bar/box on the cookie policy page
					$this->showBarBoxOnCookiePolicyPage = $this->settingsProcessor
						->get( 'content' )
						->get( 'content_show_bar_box_cookie_policy_page' );

					// the cookie policy page URL
					$cookiePolicyUrl = get_page_link( $this->_cookiePolicyPageId );

					if( $this->showBarBoxOnCookiePolicyPage ) {
						$this->cookiePolicyUrl = $cookiePolicyUrl;
						$this->cookiePolicyUrlRel = wp_make_link_relative( $this->cookiePolicyUrl );
					} else {
						$this->cookiePolicyUrl = add_query_arg( array( self::QUERY_VAR_BYPASS_COOKIE_CONSENT => '1' ), $cookiePolicyUrl );
					}
				} else {
					$this->cookiePolicyUrl = wp_get_attachment_url( $this->_cookiePolicyPageId );
				}
			}
		}


		/**
		 * Callback for the wpca_init_frontend_only_hooks hook
		 *
		 * @since 2.3.3
		 */
		public function initHooksFrontend()
		{
			if( !defined( 'WPCA_ACTIVE' ) ) {
				return;
			}

			$this->initParams();

			add_action( 'wp_head', array( $this, 'setGoogleConsentModeDefaults'), 
				apply_filters( 'wpca_google_consent_mode_defaults_script_prio', 1 ) 
			);
			add_filter( 'wpca_scripts_frontend', array( $this, 'setScripts' ) );
			add_action( 'wpca_styles_frontend', array( $this, 'setStyles' ) );
			add_filter( 'wpca_script_frontend_vars_before', array( $this, 'setScriptVars' ) );
			add_filter( 'wpca_styles_frontend_inline', array( $this, 'setInlineStyles' ) );

			// hook at priority 2 so that  params are declared
			add_action( 'wp_loaded', array( $this, 'renderWrapper' ), 2 );
			add_action( 'wp_footer', array( $this, 'addHtml' ), 999999 );
		}


		/**
		 * {@inheritDoc}
		 * @see \WpieFw\Modules\Iterator\WpieModuleInterface::init()
		 *
		 * @since 2.3.3
		 */
		public function init()
		{
			// register shortcodes for both admin (ajax) requests and frontend
			add_filter( 'wpca_registered_shortcodes', array( $this, 'registerShortcodes' ) );

			// Ensure only allowed HTML is used
			add_filter( 'wp_kses_allowed_html', array( $this, 'ksesAllowedHTML' ), 10, 2 );

			// apply hooks for frontend only
			add_action( 'wpca_init_frontend_only_hooks', array( $this, 'initHooksFrontend') );
		}
		

		/**
		 * Set no caching headers
		 *
		 * @since 3.2.3
		 */
		private function _setNoCacheHeaders()
		{
			add_filter( 'wp_headers', function( $headers, $wp ) {
				$headers['Cache-Control'] = 'no-cache, no-store, must-revalidate';
				$headers['Pragma'] = 'no-cache';
				$headers['Expires'] = '0';

				return $headers;
			}, 999999, 2 );
		}


		/**
		 * Set cookie params
		 *
		 * Set cookie params: path, domain counting also for multisite situations
		 *
		 * @since 3.1.2
		 * @since 3.2.3 added check for domain mapping
		 */
		private function _setCookieParams()
		{
			$isMs = WpieMultisiteHelper::isMs();
			$isSubdomain = ( $isMs ) ? is_subdomain_install() : false;
			$isDomainMapping = WpieMultisiteHelper::isDomainMapping();

			$msGlobalConsent = $this
				->settingsProcessor
				->get( 'general' )
				->get( 'general_ms_enable_global_consent' );


			/**
			 * Wether to or not to let visitors give global consent on multisite installs
			 *
			 * @param bool $msGlobalConsent
			 *
			 * @since 3.2.3
			 *
			 * @return bool
			 */
			$msGlobalConsent = apply_filters( 'wpca_multisite_give_global_consent', $msGlobalConsent );

			// if domain mapping, force global consent to be false
			if( $isMs && $isDomainMapping ) {
				$this->_cookiePath = WpieMultisiteHelper::getBlogDetail( 'path' );
				$this->_cookieDomain = '';
			} elseif( $isMs && $msGlobalConsent && $isSubdomain ) {
				$this->_cookiePath = '/';
				$this->_cookieDomain = '.' . WpieMiscHelper::getCleanUri( network_home_url() );
			} elseif( $isMs && $msGlobalConsent && !$isSubdomain ) {
				$this->_cookiePath = '/';
				$this->_cookieDomain = '';
			} elseif( $isMs && !$msGlobalConsent && $isSubdomain ) {
				// subdomain install
				$this->_cookiePath = WpieMultisiteHelper::getBlogDetail( 'path' );
				$this->_cookieDomain = '';
			} elseif( $isMs && !$msGlobalConsent && !$isSubdomain ) {
				// subfolder install
				$this->_cookiePath = WpieMultisiteHelper::getBlogDetail( 'path' );
				$this->_cookieDomain = '';
			} else {
				// no multisite
				$this->_cookiePath = '/';
				$this->_cookieDomain = '';
			}
		}


		/**
		 * Determine if the complete bar/box logic or only scripts/styles/shortcodes should be loaded
		 *
		 * @since 3.1
		 *
		 * @return boolean
		 */
		private function _doConsentLayout()
		{
			if( $this->bypassingConsent ) {
				return false;
			} elseif( $this->declined ) {
				return false;
			} elseif( $this->firstVisit ) {
				return true;
			} else {
				return false;
			}
		}


		/**
		 * Determine if the closing (X) is realy a dismiss
		 *
		 * @uses WpcaCore::do3rdPartyCookiesBeforeConsent()
		 *
		 * @since 3.1.2
		 *
		 * @return boolean
		 */
		private function _doXasDismiss()
		{
			// extra check for useXasDismiss, if allowing 3rd party cookies before consent
			// the closing (X) should not work as a dismiss but as an accept

			/** @var WpcaModuleCore $moduleCore */
			$moduleCore = $this->getModule( 'core' );

			if( $moduleCore->do3rdPartyCookiesBeforeConsent() ) {
				return false;
			} else {
				return $this->settingsProcessor->get( 'general' )->get( 'general_use_x_as_dismiss' );
			}
		}


		/**
		 * Render the inline CSS
		 *
		 * @access private
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.4.5
		 *
		 * @return string the rendered CSS HTML
		 */
		private function _rendeInlineCSS() 
		{
            // fixes warnings when rgb setting does not exist
			$rgbWhite =  WpieMiscHelper::hex2rgb( '#ffffff' );
			
			$vars = [];				
			$vars['style_overlay_color']                           = $this->settingsProcessor->get('style')->get( 'style_overlay_color' );
			$vars['style_overlay_opacity']                         = $this->settingsProcessor->get('style')->get( 'style_overlay_opacity' );
			$vars['style_button_accept_color']                     = $this->settingsProcessor->get('style')->get( 'style_button_accept_color' );
			$vars['style_button_accept_text_color']                = $this->settingsProcessor->get('style')->get( 'style_button_accept_text_color' );
			$vars['style_button_accept_color_hover']               = $this->settingsProcessor->get('style')->get( 'style_button_accept_color_hover' );
			$vars['style_button_accept_text_color_hover']          = $this->settingsProcessor->get('style')->get( 'style_button_accept_text_color_hover' );
			$vars['style_button_decline_color']                    = $this->settingsProcessor->get('style')->get( 'style_button_decline_color' );
			$vars['style_button_decline_text_color']               = $this->settingsProcessor->get('style')->get( 'style_button_decline_text_color' );
			$vars['style_button_decline_color_hover']              = $this->settingsProcessor->get('style')->get( 'style_button_decline_color_hover' );
			$vars['style_button_decline_text_color_hover']         = $this->settingsProcessor->get('style')->get( 'style_button_decline_text_color_hover' );
			$vars['style_button_cc_settings_color']                = $this->settingsProcessor->get('style')->get( 'style_button_cc_settings_color' );
			$vars['style_button_cc_settings_text_color']           = $this->settingsProcessor->get('style')->get( 'style_button_cc_settings_text_color' );
			$vars['style_button_cc_settings_color_hover']          = $this->settingsProcessor->get('style')->get( 'style_button_cc_settings_color_hover' );
			$vars['style_button_cc_settings_text_color_hover']     = $this->settingsProcessor->get('style')->get( 'style_button_cc_settings_text_color_hover' );
			$vars['style_button_reset_consent_color']              = $this->settingsProcessor->get('style')->get( 'style_button_reset_consent_color' );
			$vars['style_button_reset_consent_text_color']         = $this->settingsProcessor->get('style')->get( 'style_button_reset_consent_text_color' );
			$vars['style_button_reset_consent_color_hover']        = $this->settingsProcessor->get('style')->get( 'style_button_reset_consent_color_hover' );
			$vars['style_button_reset_consent_text_color_hover']   = $this->settingsProcessor->get('style')->get( 'style_button_reset_consent_text_color_hover' );
			$vars['style_button_delete_consent_color']             = $this->settingsProcessor->get('style')->get( 'style_button_delete_consent_color' );
			$vars['style_button_delete_consent_text_color']        = $this->settingsProcessor->get('style')->get( 'style_button_delete_consent_text_color' );
			$vars['style_button_delete_consent_color_hover']       = $this->settingsProcessor->get('style')->get( 'style_button_delete_consent_color_hover' );
			$vars['style_button_delete_consent_text_color_hover']  = $this->settingsProcessor->get('style')->get( 'style_button_delete_consent_text_color_hover' );
			$vars['style_css']                                     = $this->settingsProcessor->get('style')->get( 'style_css' );

			// cookie category settings
			$vars['style_cc_border_radius']                        = $this->settingsProcessor->get('style')->get( 'style_cc_border_radius' );
			$vars['style_cc_box_padding']                          = $this->settingsProcessor->get('style')->get( 'style_cc_box_padding' );
			$vars['style_cc_popup_modal_padding']                  = $this->settingsProcessor->get('style')->get( 'style_cc_popup_modal_padding' );
			$vars['style_cc_switch_width']                         = $this->settingsProcessor->get('style')->get( 'style_cc_switch_width' );
			$vars['style_cc_switch_height']                        = $this->settingsProcessor->get('style')->get( 'style_cc_switch_height' );
			$vars['style_cc_switch_vertical_corr']                 = $this->settingsProcessor->get('style')->get( 'style_cc_switch_vertical_corr' );
			$vars['style_cc_switch_bgd_color_on']                  = $this->settingsProcessor->get('style')->get( 'style_cc_switch_bgd_color_on' );
			$vars['style_cc_switch_bgd_color_off']                 = $this->settingsProcessor->get('style')->get( 'style_cc_switch_bgd_color_off' );
			$vars['style_cc_switch_slider_color_on']               = $this->settingsProcessor->get('style')->get( 'style_cc_switch_slider_color_on' );
			$vars['style_cc_switch_slider_color_off']              = $this->settingsProcessor->get('style')->get( 'style_cc_switch_slider_color_off' );
			$vars['style_cc_switch_bgd_color_on_req']              = $this->settingsProcessor->get('style')->get( 'style_cc_switch_bgd_color_on_req' );
			$vars['style_cc_switch_slider_color_on_req']           = $this->settingsProcessor->get('style')->get( 'style_cc_switch_slider_color_on_req' );
			$vars['style_cc_bgd_color']                            = $this->settingsProcessor->get('style')->get( 'style_cc_bgd_color' );
			$vars['style_cc_text_color']                           = $this->settingsProcessor->get('style')->get( 'style_cc_text_color' );
			$vars['style_cc_link_color']                           = $this->settingsProcessor->get('style')->get( 'style_cc_link_color' );
			$vars['style_cc_link_color_hover']                     = $this->settingsProcessor->get('style')->get( 'style_cc_link_color_hover' );
			$vars['style_cc_button_accept_all_color']              = $this->settingsProcessor->get('style')->get( 'style_cc_button_accept_all_color' );
			$vars['style_cc_button_accept_all_color_hover']        = $this->settingsProcessor->get('style')->get( 'style_cc_button_accept_all_color_hover' );
			$vars['style_cc_button_accept_all_text_color']         = $this->settingsProcessor->get('style')->get( 'style_cc_button_accept_all_text_color' );
			$vars['style_cc_button_accept_all_text_color_hover']   = $this->settingsProcessor->get('style')->get( 'style_cc_button_accept_all_text_color_hover' );
			$vars['style_cc_button_save_color']                    = $this->settingsProcessor->get('style')->get( 'style_cc_button_save_color' );
			$vars['style_cc_button_save_color_hover']              = $this->settingsProcessor->get('style')->get( 'style_cc_button_save_color_hover' );
			$vars['style_cc_button_save_text_color']               = $this->settingsProcessor->get('style')->get( 'style_cc_button_save_text_color' );
			$vars['style_cc_button_save_text_color_hover']         = $this->settingsProcessor->get('style')->get( 'style_cc_button_save_text_color_hover' );

			// placeholder
			$vars['style_placeholder_min_w']                       = $this->settingsProcessor->get('style')->get( 'style_placeholder_min_w' );
			$vars['style_placeholder_min_h']                       = $this->settingsProcessor->get('style')->get( 'style_placeholder_min_h' );

			// reconsider icon
			$vars['style_reconsider_icon_size']                    = $this->settingsProcessor->get( 'style' )->get( 'style_reconsider_icon_size' );
			$vars['style_reconsider_icon_space']                   = $this->settingsProcessor->get( 'style' )->get( 'style_reconsider_icon_space' );
			
			// convert hexadecimal colors to rgb to prevent an opacity in the buttons
			$vars['style_button_accept_color_rgb']                 = !empty( $vars['style_button_accept_color'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_accept_color'] ) : $rgbWhite;
			$vars['style_button_accept_color_hover_rgb']           = !empty( $vars['style_button_accept_color_hover'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_accept_color_hover'] ) : $rgbWhite;
			$vars['style_button_decline_color_rgb']                = !empty( $vars['style_button_decline_color'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_decline_color'] ) : $rgbWhite;
			$vars['style_button_decline_color_hover_rgb']          = !empty( $vars['style_button_decline_color_hover'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_decline_color_hover'] ) : $rgbWhite;
			$vars['style_button_cc_settings_color_rgb']            = !empty( $vars['style_button_cc_settings_color'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_cc_settings_color'] ) : $rgbWhite;
			$vars['style_button_cc_settings_color_hover_rgb']      = !empty( $vars['style_button_cc_settings_color_hover'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_cc_settings_color_hover'] ) : $rgbWhite;
			$vars['style_button_reset_consent_color_rgb']          = !empty( $vars['style_button_reset_consent_color'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_reset_consent_color'] ) : $rgbWhite;
			$vars['style_button_reset_consent_color_hover_rgb']    = !empty( $vars['style_button_reset_consent_color_hover'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_reset_consent_color_hover'] ) : $rgbWhite;
			$vars['style_button_delete_consent_color_rgb']         = !empty( $vars['style_button_delete_consent_color'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_delete_consent_color'] ) : $rgbWhite;
			$vars['style_button_delete_consent_color_hover_rgb']   = !empty( $vars['style_button_delete_consent_color_hover'] ) ? WpieMiscHelper::hex2rgb( $vars['style_button_delete_consent_color_hover'] ) : $rgbWhite;
			$vars['style_cc_button_accept_all_color_rgb']          = !empty( $vars['style_cc_button_accept_all_color'] ) ? WpieMiscHelper::hex2rgb( $vars['style_cc_button_accept_all_color'] ) : $rgbWhite;
			$vars['style_cc_button_accept_all_color_hover_rgb']    = !empty( $vars['style_cc_button_accept_all_color_hover'] ) ? WpieMiscHelper::hex2rgb( $vars['style_cc_button_accept_all_color_hover'] ) : $rgbWhite;
			$vars['style_cc_button_save_color_rgb']                = !empty( $vars['style_cc_button_save_color'] ) ? WpieMiscHelper::hex2rgb( $vars['style_cc_button_save_color'] ) : $rgbWhite;
			$vars['style_cc_button_save_color_hover_rgb']          = !empty( $vars['style_cc_button_save_color_hover'] ) ? WpieMiscHelper::hex2rgb( $vars['style_cc_button_save_color_hover'] ) : $rgbWhite;

			$vars['style_box_width']                               = $this->settingsProcessor->get('style')->get( 'style_box_width' );
			$vars['style_box_height']                              = $this->settingsProcessor->get('style')->get( 'style_box_height' );
			$vars['style_border_radius']                           = $this->settingsProcessor->get('style')->get( 'style_box_border_radius' );
			$vars['style_window_space']                            = $this->settingsProcessor->get('style')->get( 'style_box_window_space' );
		
			$vars['style_color']                                   = $this->settingsProcessor->get('style')->get( 'style_color' );
			$vars['style_opacity']                                 = $this->settingsProcessor->get('style')->get( 'style_opacity' );
			$vars['style_text_color']                              = $this->settingsProcessor->get('style')->get( 'style_text_color' );
			$vars['style_link_color']                              = $this->settingsProcessor->get('style')->get( 'style_link_color' );
			$vars['style_link_color_hover']                        = $this->settingsProcessor->get('style')->get( 'style_link_color_hover' );
			// convert hexadecimal colors to rgb
			$vars['style_color_rgb']                               = !empty($vars['style_color']) ? WpieMiscHelper::hex2rgb( $vars['style_color'] ) : $rgbWhite;

			try {
				$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_INLINE_CSS, false );
				$template = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_INLINE_CSS );
				$template->setVars( $vars );
				$template->setVar( 'layout', $this->layout );
			} catch( WpieExceptionInterface $e ) {
				throw $e;
			}	

			return $template->render( false, true );			
		}


		/**
		 * Setup the template for layout bar
		 *
		 * @access	private
		 *
		 * @uses 	WpieTemplate
		 *
		 * @since 	2.3.3
		 *
		 * @return 	WpieTemplate
		 */
		private function _renderBar()
		{
			// create CSS classes based on settings defaults or user input
			$pos = $this->settingsProcessor->get( 'style' )->get( 'style_bar_pos' );
			$textAlign = $this->settingsProcessor->get( 'style' )->get( 'style_text_align' );

			$position = ( '0' == $pos ) ? 'top' : 'bottom';
			$textAlign = ( '0' == $textAlign ) ? 'left' : ( '1' == $textAlign ? 'center' : 'right' );
			$class = "wpca-$position wpca-align-$textAlign wpca-hide";

			// create new WpieTemplate instance
			$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_BAR, false );
			$template = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_BAR );
			$template->setVar( 'class', $class );

			return $template;
		}


		/**
		 * Setup the tenplate for layout box
		 *
		 * @access	private
		 *
		 * @uses 	WpieTemplate
		 *
		 * @since 	2.3.3
		 *
		 * @return 	WpieTemplate
		 */
		private function _renderBox()
		{
			// create CSS classes based on settings defaults or user input
			$position = $this->settingsProcessor->get( 'style' )->get( 'style_box_pos' );
			$textAlign = $this->settingsProcessor->get( 'style' )->get( 'style_text_align' );
			$btnAlign = $this->settingsProcessor->get( 'style' )->get( 'style_box_btn_align' );
			$space = $this->settingsProcessor->get('style')->get( 'style_box_window_space' );
			$width = $this->settingsProcessor->get('style')->get( 'style_box_width' );
			// force leading zero
			$width = zeroise( $width, 2 );

			$centered = ( 'ct' === $position ) ? true : false;
			$posTopBot = ( ( 'tl' === $position || 'tr' === $position ) ) ? 'wpca-top' : (( !$centered ) ? 'wpca-bottom' : '' );
			$textAlign = ( '0' == $textAlign ) ? 'left' : ( '1' == $textAlign ? 'center' : 'right' );
			$btnAlign = ( '0' == $btnAlign ) ? 'left' : ( '1' == $btnAlign ? 'center' : 'right' );
			$class = "wpca-$position $posTopBot wpca-align-$textAlign wpca-btn-align-$btnAlign wpca-hide wpca-box-width-$width";

			// create new WpieTemplate instance
			$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_BOX, false );
			$template = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_BOX );
			$template->setVar( 'class', $class );
			$template->setVar( 'window_space', $space );

			return $template;
		}


		/**
		 * Render the layer
		 *
		 * @access private
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.2.11
		 *
		 * @return string the rendered html
		 */
		private function _renderLayer()
		{
		    try {
		        $finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_LAYER, false );
		        $layer = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_LAYER );
		        $html = $layer->render( false, true );
		    } catch( \Throwable $exc ) {
		        // log only when logged in
		        WpieExceptionLogger::log( $exc, true );
		        // on frontend do not show message
		        $html = '';
		    }

		    return $html;
        }


		/**
		 * Render the reconsider icon
		 *
		 * @access private
		 *
		 * @param string $hoverTitle the title to show when hovering the icon
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.4
		 *
		 * @return string the rendered html
		 */
		private function _renderReconsiderIcon( $hoverTitle = '' )
		{
			$html = '';

		    try {
				$showReconsiderIcon = $this->settingsProcessor->get( 'general' )->get( 'general_show_reconsider_icon' );

				if( $showReconsiderIcon ) {
					$position = $this->settingsProcessor->get( 'style' )->get( 'style_reconsider_icon_position' );
					$color = $this->settingsProcessor->get( 'style' )->get( 'style_reconsider_icon_color' );
					$class = "wpca-btn-cc-settings wpca-rci-$position wpca-hide";

					$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_RECONSIDER_IMG, false );
					$icon = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_RECONSIDER_IMG );
					$icon->setVar( 'class' , $class );
					$icon->setVar( 'hover_title' , $hoverTitle );
					$icon->setVar( 'fill' , $color );
					$html = $icon->render( false, true );
				}
		    } catch( \Throwable $exc ) {
		        // log only when logged in
		        WpieExceptionLogger::log( $exc, true );
		        // on frontend do not show message
		        $html = '';
		    }

		    return $html;
        }


		/**
		 * Render the Google Consent Mode Default Script
		 *
		 * @access private
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.4.8
		 *
		 * @return string the rendered html
		 */
		private function _renderGoogleConsentModeDefaultScript()
		{
			$html = '';

		    try {
				/** @var WpcaModuleCore $moduleCore */
				$moduleCore = $this->getModule( 'core' );		
				$do3rdParty = $moduleCore->do3rdPartyCookiesBeforeConsent();
				$doAnalytics = $moduleCore->doGoogleAnalyticsCookiesBeforeConsent();

				// Advertising defaults
				$adUserData = $do3rdParty ? 'granted' : 'denied';
				$adPersonalization = $do3rdParty ? 'granted' : 'denied';
				$adStorage = $do3rdParty ? 'granted' : 'denied';
				// Analytics defaults
				$analyticsStorage = $do3rdParty || $doAnalytics ? 'granted' : 'denied';
				// Functional / Techninal defaults
				$functionalityStorage = 'denied';
				$personalizationStorage = 'denied';
				$securityStorage = 'denied';

				$gcmWaitForUpdate = apply_filters( 'wpca_frontend_gcmv2_wait_for_update', 500 );
				$gcmAdsDataRedaction = apply_filters( 'wpca_frontend_gcmv2_ads_data_redaction', 'true' );
				$gcmDefaults = apply_filters( 'wpca_frontend_gcmv2_defaults', [
					'ad_user_data' => $adUserData,
					'ad_personalization' => $adPersonalization,
					'ad_storage' => $adStorage,
					'analytics_storage' => $analyticsStorage,
					'functionality_storage' => $functionalityStorage,
					'personalization_storage' => $personalizationStorage,
					'security_storage' => $securityStorage
				] );

				$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_GCMV2, false );
				$script = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_GCMV2 );
				$script->setVar( 'defaults' , $gcmDefaults );
				$script->setVar( 'wait_for_update' , $gcmWaitForUpdate );
				$script->setVar( 'ads_data_redaction' , $gcmAdsDataRedaction );
				$script->setVar( 'local_storage_key', self::LOCAL_STORAGE_KEY_GCM );
				$html = $script->render( false, true );				
		    } catch( \Throwable $exc ) {
		        // log only when logged in
		        WpieExceptionLogger::log( $exc, true );
		    }

		    return $html;
        }


		/**
		 * Render the popup
		 *
		 * @access private
		 *
		 * @uses WpieTemplate
		 *
		 * @since 3.2.11
		 *
		 * @return string the rendered html
		 */
		private function _renderPopup()
		{
		    $html = '';

		    try {
		        /**
		         * Let others add content to the popup
		         *
		         * @var string $content
		         *
		         * @since 3.2.11
		         */
		        $content = apply_filters( 'wpca_popup_content', '' );

				// ensure allowed html
				$content = wp_kses( $content, wp_kses_allowed_html( self::KSES_FRONTEND_CONTEXT ) );

		        if( is_string( $content ) && '' !== $content ) {
					$closeXColor = $this->settingsProcessor->get( 'style' )->get( 'style_close_color' );
		            $finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_POPUP, false );
		            $popup = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_POPUP );
		            $popup->setVar( 'content', $content );
					$popup->setVar( 'color_close_x_icon', $closeXColor );
		            $html = $popup->render( false, true );
		        }
		    } catch( \Throwable $exc ) {
		        // log only when logged in
		        WpieExceptionLogger::log( $exc, true );
		        // on fronprocesstend do not show message
		        $html = '';
		    }

		    return $html;
		}
	}

	if( !function_exists( 'wpca_block_cookies' ) )
	{
		/**
		 * Block cookies if consent has not been given
		 *
		 * @param string $cc
		 *
		 * @uses WpcaFrontend::doBlockCookies()
		 *
		 * @since 3.0.2
		 *
		 * @return boolean
		 */
		function wpca_block_cookies( $cc = '' )
		{
			/** @var WpcaModuleFrontend $module */
			$module = \WpieFw\modules\WpieModuleProcessor::getModule( 'wpca', 'frontend' );

			if( false !== $module ) {
				$block = $module->doBlockCookies( $cc );
				unset( $module );

				return $block;
			}
		}
	}
}