Files
idpan.poznan.pl/plugins/system/n3tcookieconsent/n3tcookieconsent.php
2026-02-08 21:16:11 +01:00

1821 lines
54 KiB
PHP

<?php
/**
* @package n3t Cookie Consent
* @author Pavel Poles - n3t.cz
* @copyright (C) 2021 - Pavel Poles - n3t.cz
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
*
* @noinspection PhpMultipleClassDeclarationsInspection
**/
defined( '_JEXEC' ) or die( 'Restricted access' );
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Version;
use Joomla\Registry\Registry;
use Joomla\CMS\Uri\Uri;
class plgSystemN3tCookieConsent extends CMSPlugin
{
private const BLOCK_DESCRIPTION = 'description';
private const BLOCK_PRIVACY = 'privacy';
private const BLOCK_FUNCTIONAL = 'functional';
private const BLOCK_PREFERENCES = 'preferences';
private const BLOCK_ANALYTICS = 'analytics';
private const BLOCK_MARKETING = 'marketing';
private const BLOCK_UNKNOWN = 'unknown';
private const BLOCK_HIDDEN = 'hidden';
private const BLOCK_SYSTEM = 'system';
private const BLOCK_CUSTOM_DESCRIPTION = 'custom_description';
private const BLOCK_CUSTOM = 'custom';
private const BLOCK_CONSENT = 'consent';
private const SCAN_IGNORE = 'ignore';
private const LOG_FILE = 'n3t_cookie_consent.php';
/**
* Contains list of known Cookies
* @var ?array
* @since 4.0.0
*/
private $cookieList = null;
/**
* Is site set as multilingual?
* @var bool
* @since 4.0.0
*/
private $isMultiLanguage = false;
/**
* Is scan mode?
* @var bool
* @since 4.0.0
*/
private $isScanMode = false;
/**
* Scan mode just finished?
* @var bool
* @since 4.0.0
*/
private $isScanModeFinished = false;
/**
* Report cookie contents
* @var ?array
* @since 4.0.0
*/
private $reportCookie = null;
/**
* Array of newly registered Cookies
* @var array
* @since 4.0.0
*/
private $debuggerCollectedCookies = [];
/**
* Array of blocked PHP Cookies
* @var array
* @since 4.0.0
*/
private $debuggerBlockedCookies = [];
/**
* Array of discovered iframes
* @var array
* @since 4.0.2
*/
private $debuggerIFrames = [];
/**
* Constructor
* @param $subject
* @param $config
*/
public function __construct(&$subject, $config = array())
{
parent::__construct($subject, $config);
$app = Factory::getApplication();
$this->isMultiLanguage = Multilanguage::isEnabled();
if ($app->isClient('site'))
{
$this->isScanMode = !!Factory::getApplication()->getSession()->get('n3tcc_scan');
if (!$this->isScanMode && $this->getInput()->get('n3tcc_scan') === ApplicationHelper::getHash('plg_system_n3tcookiesconsent'))
{
$this->isScanMode = true;
$app->getSession()->set('n3tcc_scan', true);
}
elseif ($this->isScanMode && $this->getInput()->get('n3tcc_scan') === '0')
{
$this->isScanMode = false;
$this->isScanModeFinished = true;
$app->getSession()->set('n3tcc_scan', false);
}
if ($this->isScanMode) {
$this->loadLanguage();
$app->enqueueMessage(Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_MODE_RUNNING', Route::_('index.php?n3tcc_scan=0')), 'warning');
}
$reportCookie = $this->getInput()->cookie->get($this->params->get('cookie_name', 'n3t_cc') . '_report', '', 'string');
if ($reportCookie)
$this->reportCookie = explode(';', $reportCookie);
}
}
/**
* Helper function to get JInput based on Joomla version
*
* @return \Joomla\Input\Input
*
* @throws Exception
* @since 4.0.0
*/
private function getInput(): \Joomla\Input\Input
{
if (Version::MAJOR_VERSION < 4)
return Factory::getApplication()->input;
else
return Factory::getApplication()->getInput();
}
/**
* Loads language files
*
* @param $extension
* @param $basePath
*
* @return bool
*
* @throws Exception
* @since 4.0.0
*/
public function loadLanguage($extension = '', $basePath = JPATH_ADMINISTRATOR)
{
$langFile = 'plg_' . $this->_type . '_' . $this->_name . '.data';
$lang = Factory::getApplication()->getLanguage();
if (!$lang->getPaths($langFile))
$lang->load($langFile, JPATH_ADMINISTRATOR);
return parent::loadLanguage($extension, $basePath);
}
/**
* Returns true if plugin should be enabled
* @return bool
*
* @throws Exception
* @since 4.0.0
*/
private function isEnabled(): bool
{
$app = Factory::getApplication();
return
$app->isClient('site')
&& $app->getDocument()->getType() === 'html'
&& (!$this->params->get('hide_from_bots', true) || !Factory::getApplication()->client->robot);
}
/**
* Returns true if current page is privacy page
* @return bool
*
* @throws Exception
* @since 4.0.0
*/
private function isPrivacyPage(): bool
{
$input = $this->getInput();
if ($this->params->get('privacy_policy_type', 'menuitem') === 'menuitem' && $this->params->get('privacy_policy') === $input->get('Itemid'))
return true;
return false;
}
/**
* Returns true if plugin should use autorun
* @return bool
*
* @throws Exception
* @since 4.0.0
*/
private function isVisible(): bool
{
static $disabledTmplList = null;
if ($disabledTmplList === null) {
$tmplList = (array) $this->params->get('disable_tmpl', [['tmpl' => 'component'], ['tmpl' => 'raw']]);
$disabledTmplList = [];
foreach ($tmplList as $tmpl)
$disabledTmplList[] = ((array)$tmpl)['tmpl'];
}
if (in_array($this->getInput()->get('tmpl'), $disabledTmplList))
return false;
return true;
}
/**
* Returns true if trigger should be visible
* @return bool
*
* @throws Exception
* @since 4.0.0
*/
private function isTriggerVisible(): bool
{
return $this->params->get('show_trigger', 1)
&& $this->isVisible();
}
/**
* Returns current user consent
*
* @return Registry
*
* @throws Exception
* @since 4.0.0
*/
private function getConsent(): Registry
{
$consent = $this->getInput()->cookie->get($this->params->get('cookie_name', 'n3t_cc'), null, 'raw');
return new Registry($consent);
}
/**
* Returns currently allowed categories by user consent
*
* @return Registry
*
* @throws Exception
* @since 4.0.0
*/
private function allowedCategories(): array
{
$categories = [];
foreach ($this->params->get('blocks', []) as $block) {
if ($block->type == self::BLOCK_FUNCTIONAL || $block->type == self::BLOCK_SYSTEM || $block->type == self::BLOCK_CUSTOM && $block->readonly && $block->default_enabled)
$categories[] = $block->type;
}
$consent = $this->getConsent();
$categories = array_merge($categories, $consent->get('level', []));
return array_unique($categories);
}
/**
* Returns if cookie by given name is enabled by current user consent
* @return bool
*
* @throws Exception
* @since 4.0.0
*/
private function isCookieEnabled(string $cookieName): bool
{
$cookieName = trim($cookieName);
$cookies = $this->loadCookies();
$category = null;
foreach ($cookies as $cookie) {
if ($cookie['is_regex'] && preg_match('~' . $cookie['name'] . '~', $cookieName)) {
$category = $cookie['category'];
break;
}
if (!$cookie['is_regex'] && $cookie['name'] == $cookieName) {
$category = $cookie['category'];
break;
}
}
if ($category === self::BLOCK_HIDDEN)
$category = self::BLOCK_UNKNOWN;
if ($category === self::BLOCK_SYSTEM)
$category = self::BLOCK_FUNCTIONAL;
if ($category === null) {
switch ($this->params->get('allow_unknown_cookies', 'settings')) {
case 'allow': return true;
case 'block': return false;
default: $category = self::BLOCK_UNKNOWN; break;
}
}
return in_array($category, $this->allowedCategories());
}
/**
* Returns translation, if exists (avoids reporting unstranslated strings)
*
* @param $text
*
* @return string
*
* @throws Exception
* @since 4.0.0
*/
private function translateText(string $text, ?int $count = null): string
{
if ($text === 'SITENAME') {
$appConfig = Factory::getConfig();
return $appConfig->get('sitename');
}
static $lang = null;
if ($lang === null)
$lang = Factory::getApplication()->getLanguage();
if ($count === null)
return $lang->hasKey($text) ? Text::_($text) : $text;
else
return Text::plural($text, $count);
}
/**
* Returns translation of text constant, if site is multilanguage, returns the text constnt
*
* @param $text
*
* @return string
*
* @throws Exception
* @since 4.0.0
*/
private function multilang(string $text): string
{
return $this->isMultiLanguage ? $text : $this->translateText($text);
}
/**
* returns array of cookies used in script
* @return array
*
* @since 4.0.0
*/
private function loadCookies(): array
{
if ($this->cookieList == null) {
$cookies = [];
foreach ($this->params->get('blocks', []) as $block) {
if (isset($block->cookies) && !empty($block->cookies)) {
foreach ((array)$block->cookies as $cookie) {
$cookie = (object)$cookie;
if ($block->alias ?? '')
$category = $block->alias;
else {
$category = $block->type ?? '';
if (!$category)
$category = self::BLOCK_UNKNOWN;
if ($category == self::BLOCK_HIDDEN)
$category = self::BLOCK_UNKNOWN;
if ($category == self::BLOCK_SYSTEM)
$category = self::BLOCK_FUNCTIONAL;
}
$cookies[] = [
'name' => $cookie->name,
'is_regex' => !!($cookie->regex ?? false),
'category' => $category,
'required' => $block->type == self::BLOCK_FUNCTIONAL || $block->type == self::BLOCK_SYSTEM || $block->type == self::BLOCK_CUSTOM && $block->readonly && $block->default_enabled,
];
}
}
}
$this->cookieList = $cookies;
}
return $this->cookieList;
}
/**
* returns true, if cookie is registered, otherwise false
*
* @param string $cookieName
*
* @return bool
*
* @since 4.0.0
*/
private function isCookieRegistered(string $cookieName): bool
{
$cookies = $this->loadCookies();
foreach ($cookies as $cookie) {
if ($cookie['is_regex'] && preg_match('~' . $cookie['name'] . '~', $cookieName))
return true;
if (!$cookie['is_regex'] && $cookie['name'] == $cookieName)
return true;
}
return false;
}
/**
* returns array containing CookieConsent script settings
* @return array
*
* @throws Exception
* @since 4.0.0
*/
private function scriptOptions(): array
{
$appConfig = JFactory::getConfig();
$options = [];
if (!$this->params->get('autorun', true) || !$this->isVisible() || $this->isPrivacyPage())
$options['autorun'] = false;
if ((int)$this->params->get('delay', 0))
$options['delay'] = (int)$this->params->get('delay', 0);
$options['cookie_expiration'] = (int)$this->params->get('cookie_expiration', 395);
if ((int)$this->params->get('cookie_necessary_only_expiration'))
$options['cookie_necessary_only_expiration'] = (int)$this->params->get('cookie_necessary_only_expiration');
if ($path = $this->params->get('cookie_path', $appConfig->get('cookie_path')))
$options['cookie_path'] = $path;
if ($domain = $this->params->get('cookie_domain', $appConfig->get('cookie_domain')))
$options['cookie_domain'] = $domain;
if ($this->params->get('cookie_same_site', 'Lax') != 'Lax')
$options['cookie_same_site'] = $this->params->get('cookie_same_site');
$options['use_rfc_cookie'] = true;
if ($this->params->get('force_consent'))
$options['force_consent'] = true;
if ((int)$this->params->get('revision'))
$options['revision'] = (int)$this->params->get('revision');
$options['current_lang'] = 'default';
$options['autoclear_cookies'] = true;
if ($this->params->get('page_scripts'))
$options['page_scripts'] = true;
if ($this->params->get('mode', 'opt-in') != 'opt-in')
$options['mode'] = $this->params->get('mode');
if ($this->params->get('remove_cookie_tables'))
$options['remove_cookie_tables'] = true;
if ($this->params->get('hide_from_bots', true))
$options['hide_from_bots'] = true;
$guiOptions = [];
$guiOptions['consent_modal'] = [
'layout' => $this->params->get('consent_modal_layout', 'box'),
'position' => $this->params->get('consent_modal_position', 'bottom right'),
'transition' => $this->params->get('consent_modal_transition', 'slide'),
'swap_buttons' => !!$this->params->get('consent_modal_swap_buttons', true),
];
$guiOptions['settings_modal'] = [
'layout' => $this->params->get('settings_modal_layout', 'bar'),
'position' => $this->params->get('settings_modal_position', 'right'),
'transition' => $this->params->get('settings_modal_transition', 'slide'),
];
$options['gui_options'] = $guiOptions;
$language = [];
$privacyLink = '';
if ($this->params->get('privacy_policy_type', 'menuitem') === 'menuitem' && $this->params->get('privacy_policy'))
$privacyLink = Route::_('index.php?Itemid=' . $this->params->get('privacy_policy'));
elseif ($this->params->get('privacy_policy_type', 'menuitem') === 'url' && $this->params->get('privacy_policy_url'))
$privacyLink = $this->params->get('privacy_policy_url');
$consentText = Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_CONSENT_MODAL_DESCRIPTION');
if ($this->params->get('secondary_button_role', 'settings') !== 'settings' && $this->params->get('tertiary_button_role', 'none') !== 'settings')
$consentText .= ' ' . Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_SETTINGS_LINK');
if ($privacyLink)
$consentText .= ' ' . Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_PRIVACY_POLICY_LINK', $privacyLink);
if ((int)$this->params->get('revision'))
$consentText .= '{{revision_message}}';
$consentModallanguage = [
'title' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_CONSENT_MODAL_TITLE'),
'description' => $consentText,
'revision_message' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_CONSENT_MODAL_REVISION'),
'primary_btn' => [
'text' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BTN_' . $this->params->get('primary_button_role', 'accept_all')),
'role' => $this->params->get('primary_button_role', 'accept_all'),
]
];
if ($this->params->get('secondary_button_role', 'settings') != 'none') {
$consentModallanguage['secondary_btn'] = [
'text' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BTN_' . $this->params->get('secondary_button_role', 'settings')),
'role' => $this->params->get('secondary_button_role', 'settings'),
];
}
$language['consent_modal'] = $consentModallanguage;
$settingsModallanguage = [
'title' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_SETTINGS_MODAL_TITLE'),
'save_settings_btn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BTN_SAVE_SETTINGS'),
'accept_all_btn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BTN_ACCEPT_ALL_SETTINGS'),
'close_btn_label' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BTN_CLOSE_SETTINGS'),
];
if ($this->params->get('show_reject_all', true))
$settingsModallanguage['reject_all_btn'] = Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BTN_REJECT_ALL_SETTINGS');
$settingsModallanguage['cookie_table_headers'] = [
['name' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_TABLE_NAME')],
['description' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_TABLE_DESCRIPTION')],
];
if ($this->params->get('show_cookie_provider', 1))
$settingsModallanguage['cookie_table_headers'][] = ['provider' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_TABLE_PROVIDER')];
if ($this->params->get('show_cookie_expiration', 1))
$settingsModallanguage['cookie_table_headers'][] = ['expiration' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_TABLE_EXPIRATION')];
$settingsModallanguage['blocks'] = [];
if ($this->isScanMode) {
$blockOptions = [];
$blockOptions['title'] = Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BLOCK_SCAN_TITLE');
$blockOptions['description'] = Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_BLOCK_SCAN_DESCRIPTION', Route::_('index.php?n3tcc_scan=0'));
$settingsModallanguage['blocks'][] = $blockOptions;
}
foreach ($this->params->get('blocks', []) as $block) {
$blockOptions = [];
if ($block->type == self::BLOCK_HIDDEN)
continue;
if ($block->type == self::BLOCK_SYSTEM)
continue;
switch ($block->type) {
case self::BLOCK_CUSTOM:
case self::BLOCK_CUSTOM_DESCRIPTION:
$blockOptions['title'] = $this->translateText($block->title);
$blockOptions['description'] = $this->translateText($block->description);
if ($block->type == self::BLOCK_CUSTOM) {
$blockOptions['toggle'] = [
'value' => $block->alias,
'enabled' => !!$block->default_enabled,
'readonly' => !!$block->readonly,
];
}
break;
case self::BLOCK_PRIVACY:
if ($privacyLink) {
$blockOptions['title'] = Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BLOCK_' . $block->type . '_TITLE');
$blockOptions['description'] = Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_BLOCK_' . $block->type . '_DESCRIPTION', $privacyLink);
} else
continue 2;
break;
case self::BLOCK_CONSENT:
$blockOptions['title'] = Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BLOCK_' . $block->type . '_TITLE');
$blockOptions['description'] = '<div class="n3tcc-consent-info">' . Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BLOCK_' . $block->type . '_DESCRIPTION') . '</div>';
break;
default:
$blockOptions['title'] = Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BLOCK_' . $block->type . '_TITLE');
$blockOptions['description'] = Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_BLOCK_' . $block->type . '_DESCRIPTION');
break;
}
switch ($block->type) {
case self::BLOCK_CUSTOM:
$blockOptions['toggle'] = [
'value' => $block->alias,
'enabled' => !!$block->default_enabled,
'readonly' => !!$block->readonly,
];
break;
case self::BLOCK_FUNCTIONAL:
$blockOptions['toggle'] = [
'value' => $block->type,
'enabled' => true,
'readonly' => true,
];
break;
case self::BLOCK_PREFERENCES:
case self::BLOCK_ANALYTICS:
case self::BLOCK_MARKETING:
case self::BLOCK_UNKNOWN:
$blockOptions['toggle'] = [
'value' => $block->type,
'enabled' => false,
'readonly' => false,
];
break;
}
if (isset($block->cookies) && !empty($block->cookies)) {
$cookieTable = [];
foreach ((array)$block->cookies as $cookie) {
$cookie = (object)$cookie;
$cookieOptions = [
'name' => $cookie->name,
'description' => $this->translateText($cookie->description ?? '') ?: '&nbsp;',
'is_regex' => !!($cookie->regex ?? false),
];
if ($this->params->get('show_cookie_provider', 1))
$cookieOptions['provider'] = $this->translateText($cookie->provider ?? '') ?: '&nbsp;';
if ($this->params->get('show_cookie_expiration', 1)) {
if (isset($cookie->expiration_unit) && !empty($cookie->expiration_unit))
$cookieOptions['expiration'] = $this->translateText('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_' . strtoupper($cookie->expiration_unit), (int)($cookie->expiration ?? ''));
else
$cookieOptions['expiration'] = $this->translateText($cookie->expiration ?? '') ?: '&nbsp;';
}
$cookieTable[] = $cookieOptions;
}
if ($cookieTable)
$blockOptions['cookie_table'] = $cookieTable;
}
$settingsModallanguage['blocks'][] = $blockOptions;
}
$language['settings_modal'] = $settingsModallanguage;
$options['languages'] = [
'default' => $language,
];
return $options;
}
/**
* Returns array containing CSS variables with custom colors
* @return array
*
* @since 4.0.0
*/
private function styleOptions(): array
{
$colors = [];
foreach ($this->params as $name => $value) {
if (preg_match('~^color_~', $name) && $value) {
$name = '--cc-' . preg_replace('~^color_~', '', $name);
$name = str_replace('_', '-', $name);
$colors[$name] = $value;
}
}
return $colors;
}
/**
* returns array containing IFrameManager script settings
* @return array
*
* @throws Exception
* @since 4.0.0
*/
private function iframeManagerOptions(): array
{
$services = [];
$service = [
'embedUrl' => '{data-id}',
'cookie' => [
'name' => $this->params->get('cookie_name', 'n3t_cc') . '_ifm_unknown',
'expiration' => (int)$this->params->get('cookie_expiration', 395),
],
'languages' => [
'default' => [
'notice' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_UNKNOWN'),
'loadBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_LOAD'),
'loadAllBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_ALWAYS'),
]
],
];
if ($thumb = $this->params->get('ifm_thumbnail')) {
if (Version::MAJOR_VERSION >= 4)
$thumb = HTMLHelper::cleanImageURL($thumb)->url;
$service['thumbnailUrl'] = Uri::base() . $thumb;
}
$services['unknown'] = $service;
$service = [
'embedUrl' => 'https://www.youtube-nocookie.com/embed/{data-id}',
'thumbnailUrl' => 'https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg',
'cookie' => [
'name' => $this->params->get('cookie_name', 'n3t_cc') . '_ifm_youtube',
],
'languages' => [
'default' => [
'notice' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_YOUTUBE'),
'loadBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_LOAD'),
'loadAllBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_ALWAYS'),
]
],
];
$services['youtube'] = $service;
$service = [
'embedUrl' => 'https://player.vimeo.com/video/{data-id}',
'cookie' => [
'name' => $this->params->get('cookie_name', 'n3t_cc') . '_ifm_vimeo',
],
'languages' => [
'default' => [
'notice' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_VIMEO'),
'loadBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_LOAD'),
'loadAllBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_ALWAYS'),
]
],
];
$services['vimeo'] = $service;
$service = [
'embedUrl' => 'https://www.dailymotion.com/embed/video/{data-id}',
'cookie' => [
'name' => $this->params->get('cookie_name', 'n3t_cc') . '_ifm_dailymotion',
],
'languages' => [
'default' => [
'notice' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_DAILYMOTION'),
'loadBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_LOAD'),
'loadAllBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_ALWAYS'),
]
],
];
$services['dailymotion'] = $service;
$providers = (array)$this->params->get('ifm_services');
foreach ($providers as $provider) {
$provider = (array)$provider;
$service = [
'embedUrl' => '{data-id}',
'cookie' => [
'name' => $this->params->get('cookie_name', 'n3t_cc') . '_ifm_' . md5($provider['url']),
],
'languages' => [
'default' => [
'notice' => $provider['terms'] ? Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_GENERAL_TOS', $provider['terms'], $provider['name']) : Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_GENERAL', $provider['name']),
'loadBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_LOAD'),
'loadAllBtn' => Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_IFM_BTN_ALWAYS'),
]
],
];
if ($thumb = $provider['thumbnail']) {
if (Version::MAJOR_VERSION >= 4)
$thumb = HTMLHelper::cleanImageURL($thumb)->url;
$service['thumbnailUrl'] = Uri::base() . $thumb;
} elseif ($thumb = $this->params->get('ifm_thumbnail')) {
if (Version::MAJOR_VERSION >= 4)
$thumb = HTMLHelper::cleanImageURL($thumb)->url;
$service['thumbnailUrl'] = Uri::base() . $thumb;
}
$services[md5($provider['url'])] = $service;
}
$options = [
'currLang' => 'default',
'services' => $services,
];
return $options;
}
/**
* Resturns string with HTML code to insert after <head>
* @return string|null
*
* @throws Exception
* @since 4.0.0
*/
private function htmlOutput(): ?string
{
$html = "\n";
$path = HTMLHelper::script('plg_n3tcookieconsent/n3tconsentmanager.min.js', ['pathOnly' => true, 'relative' => true]);
$path .= '?' . Factory::getDocument()->getMediaVersion();
$html.= '<script src="' . $path. '"></script>' . "\n";
if ($this->isTriggerVisible()) {
$trigger = $this->renderLayout('trigger', [
'params' => $this->params,
]);
} else
$trigger = false;
$params = [
'options' => $this->scriptOptions(),
'cookies' => $this->loadCookies(),
'trigger' => $trigger,
'params' => $this->params,
'isScanMode' => $this->isScanMode,
];
if ($this->params->get('use_iframe_manager', false))
$params['iframeManagerOptions'] = $this->iframeManagerOptions();
$script = $this->renderLayout('script', $params);
$html.= '<script>' . $script . '</script>' . "\n";
return $html;
}
/**
* Updates plugin settings in database
* @return bool
*
* @since 4.0.0
*/
private function updateParams(): bool
{
$plugin = PluginHelper::getPlugin($this->_type, $this->_name);
if (strlen((string)$this->params) > pow(2,16) - 1)
return false;
$db = Factory::getDbo();
$query = $db->getQuery(true);
$query->update('#__extensions')
->set('params=' . $db->quote((string)$this->params))
->where('extension_id = ' . (int)$plugin->id);
try
{
$db->lockTable('#__extensions');
} catch (Exception $e) {
return false;
}
try {
$result = $db->setQuery($query)->execute();
} catch (Exception $e) {
$db->unlockTables();
return false;
}
$db->unlockTables();
$this->cookieList = null;
return $result;
}
/**
* Parse expiration string into expiration and expiration unit
*
* @param string $expirationStr
* @param string $expiration
* @param string $expirationUnit
*
* @throws Exception
* @since 4.0.0
*/
private function parseExpiration(string $expirationStr, ?string &$expiration, ?string &$expirationUnit)
{
$expiration = $expirationStr;
$expirationUnit = '';
if ($this->isMultiLanguage)
{
switch (true) {
case strpos(strtolower($expiration), 'session') !== false:
$expiration = $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_SESSION');
break;
case strpos(strtolower($expiration), 'various') !== false:
$expiration = $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_VARIOUS');
break;
case strpos(strtolower($expiration), 'year') !== false:
$expiration = (int)$expiration;
$expirationUnit = 'years';
break;
case strpos(strtolower($expiration), 'month') !== false:
$expiration = (int)$expiration;
$expirationUnit = 'months';
break;
case strpos(strtolower($expiration), 'day') !== false:
$expiration = (int)$expiration;
$expirationUnit = 'days';
break;
case strpos(strtolower($expiration), 'hour') !== false:
$expiration = (int)$expiration;
$expirationUnit = 'hours';
break;
case strpos(strtolower($expiration), 'minute') !== false:
$expiration = (int)$expiration;
$expirationUnit = 'minutes';
break;
case strpos(strtolower($expiration), 'second') !== false:
$expiration = (int)$expiration;
$expirationUnit = 'seconds';
break;
}
} else {
switch (true) {
case strpos(strtolower($expiration), 'session') !== false:
$expiration = $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_SESSION');
break;
case strpos(strtolower($expiration), 'various') !== false:
$expiration = $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_VARIOUS');
break;
case strpos(strtolower($expiration), 'year') !== false:
$expiration = Text::plural('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_YEARS', (int) $expiration);
break;
case strpos(strtolower($expiration), 'month') !== false:
$expiration = Text::plural('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_MONTHS', (int) $expiration);
break;
case strpos(strtolower($expiration), 'day') !== false:
$expiration = Text::plural('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_DAYS', (int) $expiration);
break;
case strpos(strtolower($expiration), 'hour') !== false:
$expiration = Text::plural('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_HOURS', (int) $expiration);
break;
case strpos(strtolower($expiration), 'minute') !== false:
$expiration = Text::plural('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_MINUTES', (int) $expiration);
break;
case strpos(strtolower($expiration), 'second') !== false:
$expiration = Text::plural('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_SECONDS', (int) $expiration);
break;
}
}
}
/**
* Search cookie databases for cookie and return its description
*
* @param string $cookieName
*
* @return ?array
*
* @throws Exception
* @since 4.0.0
*/
private function searchCookieDatabase(string $cookieName): ?array
{
$cookieData = null;
// Joomla cookie database
$lang = Factory::getApplication()->getLanguage();
if (($handle = fopen(__DIR__ . '/data/joomla.csv', "r")) !== false) {
while (($data = fgetcsv($handle)) !== false) {
if (count($data) < 7)
continue;
$name = $data[1];
$name = str_replace('(n3t_cc)', $this->params->get('cookie_name', 'n3t_cc'), $name);
if ((int)$data[3])
$name = ApplicationHelper::getHash($name);
if ((int)$data[4])
$name = md5($name);
if (!(int)$data[2] && $cookieName !== $name)
continue;
if ((int)$data[2] && !preg_match('~' . $name . '~', $cookieName))
continue;
$cookieData = [
'name' => $name,
'description' => $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_DATABASE_' . $data[5]),
'regex' => (int)$data[2],
'provider' => $this->multilang('SITENAME'),
'expiration' => $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_UNKNOWN'),
'category' => $data[6],
];
switch ($data[7]) {
case 'plugin':
$info = explode('|', $data[8]);
if (count($info) < 5)
break;
$plugin = PluginHelper::getPlugin($info[0], $info[1]);
if (!$plugin)
break;
$params = new Registry($plugin->params);
if ($this->isMultiLanguage) {
$cookieData['expiration'] = (int)$params->get($info[2], (int)$info[3]);
$cookieData['expiration_unit'] = $info[4];
} else
$cookieData['expiration'] = Text::plural('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_' . strtoupper($info[4]), (int)$params->get($info[2], (int)$info[3]));
break;
case 'session':
case 'various':
case 'unknown':
$cookieData['expiration'] = $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_' . strtoupper($data[7]));
break;
default:
if ($this->isMultiLanguage) {
$cookieData['expiration'] = $data[7];
$cookieData['expiration_unit'] = $data[8];
} else
$cookieData['expiration'] = Text::plural('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_' . strtoupper($data[7]), (int)$data[8]);
}
break;
}
fclose($handle);
}
// Open cookie database
if (!$cookieData && (($handle = fopen(__DIR__ . '/data/open-cookie-database.csv', "r")) !== false)) {
while (($data = fgetcsv($handle)) !== false) {
if (count($data) < 10)
continue;
if (!(int)$data[9] && $cookieName !== $data[3])
continue;
if ((int)$data[9] && strpos($cookieName, $data[3]) !== 0)
continue;
$description = $data[5];
if ($lang->hasKey('PLG_SYSTEM_N3TCOOKIECONSENT_DATABASE_' . $data[0]))
$description = $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_DATABASE_' . $data[0]);
$this->parseExpiration($data[6], $expiration, $expirationUnit);
$cookieData = [
'name' => (int)$data[9] ? '^' . $data[3] : $data[3],
'description' => $description,
'regex' => (int)$data[9],
'provider' => $data[1],
'expiration' => $expiration,
'expiration_unit' => $expirationUnit,
];
switch (strtolower($data[2])) {
case 'functional': $cookieData['category'] = self::BLOCK_FUNCTIONAL; break;
case 'analytics': $cookieData['category'] = self::BLOCK_ANALYTICS; break;
case 'marketing': $cookieData['category'] = self::BLOCK_MARKETING; break;
default: $cookieData['category'] = self::BLOCK_HIDDEN; break;
}
break;
}
fclose($handle);
}
// Unknown Cookie
if (!$cookieData) {
$cookieData = [
'name' => $cookieName,
'description' => $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_DESCRIPTION_UNKNOWN'),
'regex' => 0,
'provider' => $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_PROVIDER_UNKNOWN'),
'expiration' => $this->multilang('PLG_SYSTEM_N3TCOOKIECONSENT_SCAN_COOKIE_EXPIRATION_UNKNOWN'),
'category' => self::BLOCK_HIDDEN,
];
}
return $cookieData;
}
/**
* Adds new unknown Cookie to "Unknown" category
*
* @param string $cookieName
*
* @return bool
*
* @throws Exception
* @since 4.0.0
*/
private function registerUnknownCookie(string $cookieName): bool
{
$cookieName = trim($cookieName);
if (!$cookieName)
return false;
if ($this->isCookieRegistered($cookieName))
return false;
if ($this->isScanMode) {
$scannedList = Factory::getApplication()->getSession()->get('n3tcc_scan_list', []);
if (!in_array($cookieName, $scannedList)) {
$scannedList[] = $cookieName;
Factory::getApplication()->getSession()->set('n3tcc_scan_list', $scannedList);
}
}
$this->debuggerCollectedCookies[] = $cookieName;
$cookieData = $this->searchCookieDatabase($cookieName);
if ($cookieData['category'] === self::SCAN_IGNORE)
return false;
$blocks = (array)$this->params->get('blocks', []);
$blockIndex = null;
foreach ($blocks as $index => $block) {
if ($block->type == $cookieData['category'] || isset($block->alias) && $block->alias == $cookieData['category']) {
$blockIndex = $index;
break;
}
}
if ($blockIndex !== null)
$block = $blocks[$blockIndex];
else {
$block = new stdClass();
$block->type = $cookieData['category'];
$block->alias = '';
$block->title = '';
$block->description = '';
$block->default_enabled = 0;
$block->readonly = 0;
$block->cookies = [];
$blocks[] = $block;
}
unset($cookieData['category']);
if (isset($block->cookies) && !empty($block->cookies))
$block->cookies = (array)$block->cookies;
else
$block->cookies = [];
$block->cookies[] = (object)$cookieData;
$this->params->set('blocks', $blocks);
$this->cookieList = null;
return true;
}
/**
* Returns log file based on params
*
* @return string
*
* @since 4.0.0
*/
private function logFile(): string
{
return $this->params->get('log_consents_rotate') ? self::LOG_FILE : 'keep/' . self::LOG_FILE;
}
/**
* Joomla onBeforeRender Event
*
* @since 4.0.0
*/
public function onBeforeRender()
{
$app = Factory::getApplication();
if ($this->isScanModeFinished) {
$script = "window.parent.postMessage('n3t_cookie_consent_finish_scan', '*');";
if (Version::MAJOR_VERSION < 4)
Factory::getDocument()->addScriptDeclaration($script);
else
{
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->addInlineScript($script);
}
}
if (!$app->isClient('administrator'))
return;
$hasDefinition = false;
$blocks = (array)$this->params->get('blocks', []);
foreach ($blocks as $block) {
if (isset($block->cookies) && !empty($block->cookies)) {
$hasDefinition = true;
break;
}
}
if (!$hasDefinition) {
$this->loadLanguage();
$plugin = PluginHelper::getPlugin($this->_type, $this->_name);
$app->enqueueMessage(Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_WARNING_SETUP_COOKIES_FIRST',
Route::_('index.php?option=com_plugins&view=plugin&task=plugin.edit&extension_id=' . $plugin->id)), 'warning');
}
}
/**
* @param string $attrStr
*
* @return array
*
* @since 4.0.2
*/
private function parseHtmlAttributes(string $htmlCode, array &$debuggerData = []): array
{
$dom = new \DomDocument();
$dom->loadHTML($htmlCode);
$elem = $dom->getElementsByTagName('iframe')->item(0);
$attrs = [];
foreach ($elem->attributes as $name => $value)
$attrs[$name] = $value->textContent;
return $attrs;
}
/**
* Joomla onAfterRender Event
*
* @since 4.0.0
*/
public function onAfterRender()
{
$app = Factory::getApplication();
if (!$this->isEnabled())
return;
$buffer = $app->getBody();
if (strpos($buffer, '<html') === false)
return;
$this->loadLanguage();
$html = $this->htmlOutput();
if ($html)
$buffer = preg_replace('~(<head(\s[^>]*)?>)~', '$1' . $html, $buffer);
if (strpos($buffer, '{n3tcookieconsent ') !== false)
$buffer = preg_replace('~{n3tcookieconsent\ssettings}([^{]*){/n3tcookieconsent}~', '<a href="#" aria-label="$1" class="cc-link" data-cc="c-settings">$1</a>', $buffer);
if ($this->params->get('youtube_nocookie', true)) {
if (strpos($buffer, '<iframe') !== false) {
$buffer = preg_replace_callback('~<iframe\s[^>]*?>~', function($matches) {
return preg_replace('~http(s)?://(www\.)?youtube\.com/~', 'http$1://www.youtube-nocookie.com/', $matches[0]);
}, $buffer);
}
}
if ($this->params->get('use_iframe_manager', false)) {
if (strpos($buffer, '<iframe') !== false) {
$buffer = preg_replace_callback('~<iframe(\s[^>]*)?>~', function($matches) {
$debuggerIframe = [
'code' => $matches[0],
];
$attributes = $this->parseHtmlAttributes($matches[0], $debuggerIframe);
$debuggerIframe['attributes'] = $attributes;
$service = '';
$id = '';
if (isset($attributes['src'])) {
$id = $attributes['src'];
unset($attributes['src']);
}
$debuggerIframe['processed'] = !!$id;
if (!$id) {
$this->debuggerIFrames[] = $debuggerIframe;
return $matches[0];
}
$uri = new Uri($id);
$domain = $uri->getHost();
if (!$domain || $domain == (new Uri(Uri::base()))->getHost()) {
$debuggerIframe['processed'] = false;
$this->debuggerIFrames[] = $debuggerIframe;
return $matches[0];
}
$providers = (array)$this->params->get('ifm_whitelist', []);
foreach ($providers as $provider) {
$provider = (array)$provider;
if (stripos($id, $provider['url']) === 0) {
$debuggerIframe['processed'] = false;
$this->debuggerIFrames[] = $debuggerIframe;
return $matches[0];
}
}
$providers = (array)$this->params->get('ifm_services', []);
foreach ($providers as $provider) {
$provider = (array)$provider;
if (stripos($id, $provider['url']) === 0)
$service = md5($provider['url']);
}
if (!$service && preg_match('~^(www\.)(youtube(-nocookie)?\.com|youtu.be)$~i', $domain)) {
$service = 'youtube';
$id = $uri->getPath();
$id = explode('/', $id);
$id = array_pop($id);
}
if (!$service && preg_match('~^player\.vimeo\.com$~i', $domain)) {
$service = 'vimeo';
$id = $uri->getPath();
$id = explode('/', $id);
$id = array_pop($id);
}
if (!$service && preg_match('~^(api|www)\.dailymotion\.com$~i', $domain)) {
$service = 'dailymotion';
$id = $uri->getPath();
$id = explode('/', $id);
$id = array_pop($id);
}
$service = $service ?: 'unknown';
$this->debuggerIFrames[] = $debuggerIframe;
return '<div data-service="' . $service . '" data-id="' . $id . '" data-autoscale data-attributes="' . htmlspecialchars(json_encode($attributes), ENT_QUOTES) . '"></div>';
}, $buffer);
}
}
$app->setBody($buffer);
}
/**
* n3tDebug onN3tDebugAddPanel Event
*
* @throws Exception
* @since 4.0.0
*/
public function onN3tDebugAddPanel()
{
if (!Factory::getApplication()->isClient('site'))
return null;
if (Version::MAJOR_VERSION == 3)
JLoader::registerNamespace('n3tCookieConsent', __DIR__ . DIRECTORY_SEPARATOR);
else
JLoader::registerNamespace('n3tCookieConsent', __DIR__ . DIRECTORY_SEPARATOR . 'n3tCookieConsent');
return new \n3tCookieConsent\Debug\Panel($this);
}
/**
* Helper function for debugger panel
*
* @return array
*
* @throws Exception
* @since 4.0.0
*/
public function collectDebugData(): array
{
$data = [
'consent' => $this->getConsent()->toArray(),
'allowedCategories' => $this->allowedCategories(),
];
if ($this->debuggerBlockedCookies)
$data['blockedCookies'] = $this->debuggerBlockedCookies;
if ($this->isScanMode) {
$data['scanMode'] = true;
$data['reportCookie'] = $this->reportCookie;
$data['scannedList'] = Factory::getApplication()->getSession()->get('n3tcc_scan_list', []);
$data['collectedCookies'] = $this->debuggerCollectedCookies;
}
if ($this->debuggerIFrames)
$data['iframes'] = $this->debuggerIFrames;
return $data;
}
/**
* Joomla onBeforeCompileHead Event
*
* @throws Exception
* @since 4.0.0
*/
public function onBeforeCompileHead()
{
if (!$this->isEnabled())
return;
$this->loadLanguage();
HTMLHelper::script('plg_n3tcookieconsent/cookieconsent.min.js', ['version' => 'auto', 'relative' => true], ['defer' => true]);
HTMLHelper::stylesheet( 'plg_n3tcookieconsent/n3tconsentmanager.min.css', ['version' => 'auto', 'relative' => true]);
if ($this->params->get('use_iframe_manager', false)) {
HTMLHelper::script('plg_n3tcookieconsent/iframemanager.min.js', ['version' => 'auto', 'relative' => true], ['defer' => true]);
}
$style = $this->renderLayout('style', [
'styles' => $this->styleOptions(),
'params' => $this->params,
]);
$doc = Factory::getDocument();
$doc->addStyleDeclaration($style);
if ($this->isScanMode) {
$input = $this->getInput();
$needUpdate = false;
if ($this->reportCookie) {
foreach($this->reportCookie as $cookieName)
$needUpdate = $this->registerUnknownCookie($cookieName) || $needUpdate;
$appConfig = JFactory::getConfig();
$path = $this->params->get('cookie_path', $appConfig->get('cookie_path', '/'));
$domain = $this->params->get('cookie_domain', $appConfig->get('cookie_domain'));
if (!$domain)
$domain = '.' . $this->getInput()->server->get('SERVER_NAME');
$input->cookie->set($this->params->get('cookie_name', 'n3t_cc') . '_report', '', 1, $path, $domain);
}
$headers = headers_list();
foreach ($headers as $header) {
if (stripos($header, 'Set-Cookie:') === 0) {
$cookie = explode(';', trim(substr($header, 11)));
$cookie = explode('=', $cookie[0]);
if (count($cookie) === 2)
$needUpdate = $this->registerUnknownCookie($cookie[0]) || $needUpdate;
}
}
if ($needUpdate)
$this->updateParams();
}
// Block PHP cookies
$headers = headers_list();
$needUpdate = false;
foreach ($headers as $index => $header) {
if (stripos($header, 'Set-Cookie:') === 0) {
$cookie = explode(';', trim(substr($header, 11)));
foreach ($cookie as $value) {
$value = trim($value);
if (preg_match('~^expires=~i', $value)) {
$value = explode('=', trim($value));
$date = new \DateTime($value[1]);
$now = new DateTime();
if ($date < $now)
continue 2;
}
}
$cookie = explode('=', $cookie[0]);
if (count($cookie) === 2) {
$cookieName = trim($cookie[0]);
if (!$this->isCookieEnabled($cookieName)) {
$this->debuggerBlockedCookies[] = $cookieName;
unset($headers[$index]);
$needUpdate = true;
}
}
}
}
if ($needUpdate) {
header_remove();
foreach ($headers as $header)
header($header);
}
// TODO add scripts based on categories
}
/**
* Joomla Ajax Event
* index.php?option=com_ajax&group=system&format=raw&plugin=N3tCookieConsentExport
* Export plugin settings in JSON format
*
* @throws Exception
* @since 4.0.0
*/
public function onAjaxN3tCookieConsentExport()
{
$app = Factory::getApplication();
if (!$app->isClient('administrator'))
return;
$user = JFactory::getUser();
if (!$user->authorise('core.edit', 'com_plugins'))
return;
$app->setHeader('Content-disposition', 'attachment; filename="n3t-cookie-consent.json"', true);
$app->setHeader('Content-Type', 'application/json', true);
echo $this->params->toString();
}
/**
* Joomla Ajax Event
* index.php?option=com_ajax&group=system&format=raw&plugin=N3tCookieConsentImport
* Import plugin settings in JSON format. Supported formats
* - export from plugin settings
* - export from CookieBot.com scan
*
* @throws Exception
* @since 4.0.0
*/
public function onAjaxN3tCookieConsentImport()
{
$app = Factory::getApplication();
if (!$app->isClient('administrator'))
return;
$user = JFactory::getUser();
if (!$user->authorise('core.edit', 'com_plugins'))
return;
$this->loadLanguage();
$plugin = PluginHelper::getPlugin($this->_type, $this->_name);
$files = $this->getInput()->files->get('n3tcc');
if (!is_array($files) || !isset($files['import']) || $files['import']['error']) {
$app->enqueueMessage(Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_CFG_IMPORT_ERROR_FILE'));
$app->redirect(Route::_('index.php?option=com_plugins&view=plugin&layout=edit&extension_id=' . $plugin->id, false));
return;
}
// TODO detekce chyby
$params = new Joomla\Registry\Registry(file_get_contents($files['import']['tmp_name']));
if ($params->get('privacy_policy_type')) {
// Plugin export
$this->params = $params;
$this->updateParams();
$app->enqueueMessage(Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_CFG_IMPORT_SUCCESS'));
} elseif ($params->get('domain') && $params->get('cookies')) {
// CookieBot.com export
$blocks = [];
$blocks[0] = [
'type' => self::BLOCK_DESCRIPTION,
];
foreach ($params->get('cookies') as $cookie) {
if (!isset($blocks[(int)$cookie->Category])) {
$blocks[(int)$cookie->Category] = [
'type' => ['', self::BLOCK_FUNCTIONAL, self::BLOCK_PREFERENCES, self::BLOCK_ANALYTICS, self::BLOCK_MARKETING, self::BLOCK_UNKNOWN][(int)$cookie->Category],
'cookies' => [],
];
}
$this->parseExpiration($cookie->ExpireDescription, $expiration, $expirationUnit);
$blockCookie = [
'name' => $cookie->NamePattern ?: $cookie->Name,
'description' => $cookie->PurposeDescription,
'regex' => (int)!!$cookie->NamePattern,
'provider' => $cookie->Provider,
'expiration' => $expiration,
'expiration_unit' => $expirationUnit,
];
$blocks[(int)$cookie->Category]['cookies'][] = $blockCookie;
}
$blocks[1000] = [
'type' => self::BLOCK_PRIVACY,
];
$blocks[1001] = [
'type' => self::BLOCK_CONSENT,
];
$this->params->set('blocks', $blocks);
$this->updateParams();
$app->enqueueMessage(Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_CFG_IMPORT_SUCCESS_COOKIEBOT', $params->get('domain')));
} else {
$app->enqueueMessage(Text::_('PLG_SYSTEM_N3TCOOKIECONSENT_CFG_IMPORT_ERROR_FORMAT'));
}
$app->redirect(Route::_('index.php?option=com_plugins&view=plugin&layout=edit&extension_id=' . $plugin->id, false));
}
/**
* Joomla Ajax Event
* index.php?option=com_ajax&group=system&format=raw&plugin=N3tCookieConsentLogConsent
* Log user consent
*
* @throws Exception
* @since 4.0.0
*/
public function onAjaxN3tCookieConsentLogConsent()
{
if ($this->params->get('log_consents', 1)) {
$this->loadLanguage();
Log::addLogger(
[
'text_file' => $this->logFile(),
'text_entry_format' => "{DATETIME}\t{CLIENTIP}\t{MESSAGE}"
],
JLog::ALL,
['n3t_cookie_consent']
);
$consent = $this->getConsent();
$categories = implode(',', $consent->get('level', []));
$guid = $consent->get('data.guid');
Log::add(Text::sprintf('PLG_SYSTEM_N3TCOOKIECONSENT_LOG_CONSENT', $categories, (int) $this->params->get('revision'), $guid), Log::INFO, 'n3t_cookie_consent');
}
// Clear unallowed cookies
$input = $this->getInput();
$appConfig = JFactory::getConfig();
$path = $appConfig->get('cookie_path', '/');
$domain = $appConfig->get('cookie_domain');
foreach($input->cookie->getArray() as $cookieName => $value) {
if (!$this->isCookieEnabled($cookieName)) {
$this->debuggerBlockedCookies[] = $cookieName;
$input->cookie->set($cookieName, '', 1, $path, $domain);
}
}
}
/**
* Joomla Ajax Event
* index.php?option=com_ajax&group=system&format=raw&plugin=N3tCookieConsentDefaults
* Scan frontpage on Cookies
*
* @throws Exception
* @since 4.0.0
*/
public function onAjaxN3tCookieConsentDefaults()
{
$this->loadLanguage();
$app = Factory::getApplication();
if (!$app->isClient('administrator'))
return;
$user = JFactory::getUser();
if (!$user->authorise('core.edit', 'com_plugins'))
return;
$blocks = [];
$blocks[] = (object)['type' => self::BLOCK_DESCRIPTION];
$blocks[] = (object)['type' => self::BLOCK_FUNCTIONAL];
$blocks[] = (object)['type' => self::BLOCK_PREFERENCES];
$blocks[] = (object)['type' => self::BLOCK_ANALYTICS];
$blocks[] = (object)['type' => self::BLOCK_MARKETING];
$blocks[] = (object)['type' => self::BLOCK_UNKNOWN];
$blocks[] = (object)['type' => self::BLOCK_HIDDEN];
$blocks[] = (object)['type' => self::BLOCK_SYSTEM];
$blocks[] = (object)['type' => self::BLOCK_PRIVACY];
$blocks[] = (object)['type' => self::BLOCK_CONSENT];
$this->params->set('blocks', $blocks);
$this->updateParams();
// Register basic cookies
$this->registerUnknownCookie($this->params->get('cookie_name', 'n3t_cc'));
if ($this->params->get('use_iframe_manager', false))
$this->registerUnknownCookie($this->params->get('cookie_name', 'n3t_cc') . '_ifm_');
$this->registerUnknownCookie(md5(ApplicationHelper::getHash('site')));
$this->registerUnknownCookie(md5(ApplicationHelper::getHash('administrator')));
$this->registerUnknownCookie('joomla_user_state');
$this->registerUnknownCookie(ApplicationHelper::getHash('PlgSystemLogout'));
if (PluginHelper::isEnabled('system', 'remember'))
$this->registerUnknownCookie('joomla_remember_me_');
if (Multilanguage::isEnabled())
$this->registerUnknownCookie('language');
// Register some core extensions
if (PluginHelper::isEnabled('captcha', 'recaptcha'))
$this->registerUnknownCookie('_GRECAPTCHA');
if (PluginHelper::isEnabled('captcha', 'recaptcha_invisible'))
$this->registerUnknownCookie('_GRECAPTCHA');
// Register some other known extensions
if (PluginHelper::isEnabled('captcha', 'n3tmulticaptcha'))
$this->registerUnknownCookie(ApplicationHelper::getHash('n3t_multicaptcha'));
if (PluginHelper::isEnabled('captcha', 'n3tmulticaptcha'))
$this->registerUnknownCookie(ApplicationHelper::getHash('n3t_multicaptcha'));
$this->updateParams();
$plugin = PluginHelper::getPlugin($this->_type, $this->_name);
$app->redirect(Route::_('index.php?option=com_plugins&view=plugin&layout=edit&extension_id=' . $plugin->id, false));
}
/**
* Joomla onContentPrepareForm Event
* @param $form
* @param $data
*
* @throws Exception
* @since 4.0.0
*/
public function onContentPrepareForm($form, $data)
{
if (!($form instanceof Form))
return;
if ($form->getName() != 'com_plugins.plugin')
return;
if (!is_object($data))
return;
if (!isset($data->extension_id))
return;
$plugin = PluginHelper::getPlugin($this->_type, $this->_name);
if ($data->extension_id != $plugin->id)
return;
$blocksField = $form->getField('blocks', 'params');
$subform = new SimpleXMLElement($blocksField->formsource);
if (!$this->params->get('show_cookie_provider', 1)) {
$node = $subform->xpath(".//field[@name='provider']");
unset($node[0][0]);
}
if (!$this->params->get('show_cookie_expiration', 1)) {
$node = $subform->xpath(".//field[@name='expiration']");
unset($node[0][0]);
}
if (!$this->params->get('show_cookie_expiration', 1) || !$this->isMultiLanguage) {
$node = $subform->xpath(".//field[@name='expiration_unit']");
unset($node[0][0]);
}
$form->setFieldAttribute('blocks', 'formsource', $subform->asXML(), 'params');
}
/**
* Joomla Ajax Event
* index.php?option=com_ajax&group=system&format=raw&plugin=N3tCookieConsentSetCookie
* Sets cookie value
*
* @throws Exception
* @since 4.0.0
*/
public function onAjaxN3tCookieConsentSetCookie()
{
if (version_compare(phpversion(), '7.3.0', '<'))
return;
if (!$this->params->get('cookie_domains'))
return;
$app = Factory::getApplication();
$appConfig = JFactory::getConfig();
$value = $this->getInput()->get('val', '', 'string');
if (!$value)
return;
if (null === json_decode($value))
return;
$time = time() + (int)$this->params->get('cookie_expiration', '395') * 24 * 60 * 60;
$name = $this->params->get('cookie_name', 'n3t_cc');
$path = $this->params->get('cookie_path', $appConfig->get('cookie_path', '/'));
$domain = $this->params->get('cookie_domain', $appConfig->get('cookie_domain'));
if (!$domain)
$domain = '.' . $this->getInput()->server->get('SERVER_NAME');
setrawcookie($name, rawurlencode($value), [
'expires' => $time,
'path' => $path,
'domain' => $domain,
'secure' => $app->isSSLConnection(),
'httponly' => false,
'samesite' => 'None',
]);
$app->setHeader('Content-Type', 'image/svg+xml', true);
return '<svg xmlns="http://www.w3.org/2000/svg"/>';
}
/**
* Joomla Ajax Event
* index.php?option=com_ajax&group=system&format=raw&plugin=N3tCookieConsentExportLog
* Export consents log
*
* @throws Exception
* @since 4.0.0
*/
public function onAjaxN3tCookieConsentExportLog()
{
$app = Factory::getApplication();
if (!$app->isClient('administrator'))
return;
$user = JFactory::getUser();
if (!$user->authorise('core.edit', 'com_plugins'))
return;
$logFile = Factory::getApplication()->get('log_path', JPATH_ADMINISTRATOR . '/logs') . '/' . $this->logFile();
header('Content-disposition: attachment; filename="n3t-cookie-consent.log"', true);
header('Content-Type: text/plain; charset=UTF-8', true);
if (!file_exists($logFile)) {
header('Content-Length: 0', true);
return;
}
$size = filesize($logFile);
header('Content-Length: ' . $size, true);
readfile($logFile);
}
/**
* Renders Joomla Layout file with correct paths
*
* @param string $layoutFile
* @param array $displayData
*
* @since 4.1.5
*/
private function renderLayout(string $layoutFile, array $displayData = []): string {
ob_start();
require PluginHelper::getLayoutPath($this->_type, $this->_name, $layoutFile);
return ob_get_clean();
}
}