$identifiers = Utils::array_flatten(\array_values($existing)); $templates = \array_column(TemplateConsumers::getCurrentBlockerConsumer()->retrieveBy('identifier', $identifiers), null, 'identifier'); $existing = \array_map(function ($existingTemplates) use($templates) { return \array_filter(\array_map(function ($template) use($templates) { return $templates[$template] ?? null; }, $existingTemplates)); }, $existing); $noLongerExisting = \array_diff_key($active, $existing); // Update the state when there are external URLs hosts which do no longer exist if (\count($noLongerExisting) > 0) { foreach ($noLongerExisting as $host => $templates) { unset($active[$host]); unset($noticeState[$host]); } $this->getStates()->set(self::NOTICE_SCANNER_EXPLICIT_EXTERNAL_URL_COVERAGE, $active); } // Check if for already scanned elements there is already a template found foreach ($active as $host => $state) { $existingTemplates = $existing[$host] ?? []; if ($state === self::SCANNER_EXPLICIT_EXTERNAL_URL_COVERAGE_STATE_SCANNED && \count($existingTemplates) === 0) { unset($active[$host]); } } // Short circuit if (\count($active) === 0) { return null; } $resultScanned = Utils::array_flatten(\array_values(\array_filter(\array_map(function ($host) use($existing) { $templates = \wp_list_pluck($existing[$host], 'headline'); return \count($templates) > 0 ? $templates : null; }, \array_keys($existing))))); $resultManualTrigger = Utils::array_flatten(\array_values(\array_filter(\array_map(function ($host) use($existing) { $templates = \wp_list_pluck($existing[$host], 'headline'); return \count($templates) > 0 ? null : $host; }, \array_keys($existing))))); $resultScannedJoined = UtilsUtils::joinWithAndSeparator($resultScanned, \__(' and ', 'real-cookie-banner')); $resultManualTriggerJoined = UtilsUtils::joinWithAndSeparator($resultManualTrigger, \__(' and ', 'real-cookie-banner')); $configPageUrl = Core::getInstance()->getConfigPage()->getUrl(); $startScannerLink = \add_query_arg(self::DISMISS_SCANNER_EXPLICIT_EXTERNAL_URL_COVERAGE_NOTICE_START_SCAN_QUERY_ARG, '1', $configPageUrl . '#/scanner'); $dismissBase = $configPageUrl; if (!UtilsUtils::isRest()) { $dismissBase = isset($_SERVER['REQUEST_URI']) ? \esc_url_raw(\wp_unslash($_SERVER['REQUEST_URI'])) : ''; } $dismissLink = \add_query_arg(self::DISMISS_SCANNER_EXPLICIT_EXTERNAL_URL_COVERAGE_NOTICE_QUERY_ARG, '1', $dismissBase); switch (\true) { case \count($resultManualTrigger) > 0 && \count($resultScanned) > 0: $text = \sprintf( // translators: \__('Real Cookie Banner offers new service templates for %1$s and for handling external URLs %2$s, which may be of interest to you.', 'real-cookie-banner'), \esc_html($resultScannedJoined), \esc_html($resultManualTriggerJoined) ); break; case \count($resultScanned) > 0: $text = \sprintf( // translators: \__('Real Cookie Banner offers new service templates for %s that may be of interest to you.', 'real-cookie-banner'), \esc_html($resultScannedJoined) ); break; case \count($resultManualTrigger) > 0: $text = \sprintf( // translators: \__('Real Cookie Banner offers new service templates for handling external URLs %s, which may be of interest to you.', 'real-cookie-banner'), \esc_html($resultManualTriggerJoined) ); break; default: $text = ''; } return \sprintf('

%s

%s

', \join(' ', [$text, \count($resultManualTrigger) > 0 ? \sprintf(\__('Start the scanner to check if the service template can be used for your case and will be suggested in the scanner!', 'real-cookie-banner')) : '']), \join(' • ', \array_filter([\count($resultManualTrigger) > 0 ? \sprintf('%s', \esc_url($startScannerLink), \__('Start scanner', 'real-cookie-banner')) : '', \sprintf('%s', \esc_url($configPageUrl . '#/scanner'), \__('Open scanner', 'real-cookie-banner')), \sprintf('%s', \esc_url($dismissLink), \__('Dismiss notice', 'real-cookie-banner'))]))); } /** * Checks if the notice about services which are processing data in unsafe countries should be shown, * and returns the titles of the services which process data in unsafe countries */ public function servicesDataProcessingInUnsafeCountriesNoticeHtml() { $noticeState = $this->getStates()->get(self::NOTICE_SERVICE_DATA_PROCESSING_IN_UNSAFE_COUNTRIES, null); // Should it be recalculated by metadata change or should it be initially checked? if (($noticeState === \true || $noticeState === null) && !Consent::getInstance()->isDataProcessingInUnsafeCountries()) { $iso3166OneAlpha2 = Iso3166OneAlpha2::getSortedCodes(); $servicesHtml = []; foreach (Core::getInstance()->getCookieConsentManagement()->getSettings()->getConsent()->calculateServicesWithDataProcessingInUnsafeCountries() as $candidate) { $servicesHtml[] = \sprintf( // translators:s \__('%1$s is processing data to %2$s', 'real-cookie-banner'), \esc_html($candidate['name']), \join(', ', \array_map(function ($country) use($iso3166OneAlpha2) { return $iso3166OneAlpha2[$country] ?? $country; }, $candidate['unsafeCountries'])) ); } if (\count($servicesHtml) > 0) { return \sprintf('

%s

', \__('Some services perform data processing in unsafe third countries within the meaning of data protection regulations. In the service configurations, you have stated that no security mechanisms are in place for this data processing. You should obtain special consent for this or only use services with data processing in secure countries or with appropriate security mechanisms.', 'real-cookie-banner'), \join('
  • ', $servicesHtml)); } } return null; } /** * Checks if the notice about service and blocker templates which got deleted should be shown, * and returns the titles of the services with some special instructions. */ public function admin_notice_service_using_template_which_got_deleted() { $state = $this->getStates()->get(self::NOTICE_USING_TEMPLATES_WHICH_GOT_DELETED, \true); if ($state) { $posts = \get_posts(Core::getInstance()->queryArguments(['post_type' => [Cookie::CPT_NAME, Blocker::CPT_NAME], 'fields' => 'ids', 'numberposts' => -1, 'nopaging' => \true, 'meta_query' => [['key' => Blocker::META_NAME_PRESET_ID, 'compare' => 'IN', 'value' => ['google-adsense']]], 'post_status' => ['publish', 'private', 'draft']], 'admin_notice_service_using_template_which_got_deleted')); if (\count($posts) > 0) { echo \sprintf('

    %s

    %s

    %s

    %s
    ', \wp_kses_post(\__('[ACTION REQUIRED] As of January 16, 2024, Google AdSense will only display advertising on your website if you obtain consent in accordance with the TCF standard. You must act now to continue earning advertising revenue!', 'real-cookie-banner')), \wp_kses_post(\__('You are currently obtaining non-standard compliant consent for Google Adsense via Real Cookie Banner. Please delete the Google AdSense service and Google AdSense content blocker in the Real Cookie Banner settings and then configure the TCF integration.', 'real-cookie-banner')), \esc_url(\__('https://devowl.io/knowledge-base/google-adsense-tcf-consent-wordpress/', 'real-cookie-banner')), \esc_html__('Read instructions for Google AdSense configuration with TCF consents', 'real-cookie-banner'), \sprintf('', \esc_js($this->getStates()->noticeDismissOnClickHandler(self::NOTICE_USING_TEMPLATES_WHICH_GOT_DELETED, 'false')))); } else { $this->getStates()->set(self::NOTICE_USING_TEMPLATES_WHICH_GOT_DELETED, \false); } } return null; } /** * Check if the Pro notice should be shown in config page. See `proHeadlineButton.tsx`. */ public function isConfigProNoticeVisible() { $next = $this->getStates()->get(self::NOTICE_GET_PRO_MAIN_BUTTON, 0); if ($next === 0) { $this->getStates()->set(self::NOTICE_GET_PRO_MAIN_BUTTON, \true); return \false; } return \time() > $next; } /** * Create an notice that the scanner should be rerun after any plugin got toggled. */ public function admin_notice_scanner_rerun_after_plugin_toggle() { if ($this->getStates()->get(self::NOTICE_SCANNER_RERUN_AFTER_PLUGIN_TOGGLE, \false) && \current_user_can(Core::MANAGE_MIN_CAPABILITY)) { echo \sprintf('

    %s • %s

    %s
    ', \esc_html__('You have enabled or disabled plugins on your website, which may require your cookie banner to be adjusted. Please scan your website again as soon as you have finished the changes!', 'real-cookie-banner'), \esc_js($this->getStates()->noticeDismissOnClickHandler(self::NOTICE_SCANNER_RERUN_AFTER_PLUGIN_TOGGLE, 'false', Core::getInstance()->getConfigPage()->getUrl() . '#/scanner?start=1')), \esc_html__('Scan website again', 'real-cookie-banner'), \sprintf('', \esc_js($this->getStates()->noticeDismissOnClickHandler(self::NOTICE_SCANNER_RERUN_AFTER_PLUGIN_TOGGLE, 'false')))); } } /** * Create an notice and inform the user about too many TCF vendors. */ public function admin_notice_tcf_too_much_vendors() { if ($this->isPro() && TCF::getInstance()->isActive() && $this->getStates()->get(self::NOTICE_TCF_TOO_MUCH_VENDORS, \false) && TcfVendorConfiguration::getInstance()->getAllCount() > self::TCF_TOO_MUCH_VENDORS) { echo \sprintf('

    %s • %s

    %s
    ', \wp_kses_post(\__('Are you really embedding ads and content from all created TCF vendors on your website? Asking for consent from vendors you don\'t use could be an abuse of rights and could make the entire consent ineffective. You make it difficult for your website visitors to make an informed choice by using too many vendors. We therefore recommend you to create only TCF vendors that you actually use!', 'real-cookie-banner')), \esc_url(Core::getInstance()->getConfigPage()->getUrl() . '#/cookies/tcf-vendors'), \esc_html__('Configure TCF vendors', 'real-cookie-banner'), \sprintf('', \esc_js($this->getStates()->noticeDismissOnClickHandler(self::NOTICE_TCF_TOO_MUCH_VENDORS, 'false')))); } } /** * Set a scan result as ignored/unignored. * * @param string $type Can be `template` or `host` * @param string $value * @param boolean $state */ public function setScannerIgnored($type, $value, $state) { $useKey = $type === 'template' ? self::SCANNER_IGNORED_TEMPALTES : self::SCANNER_IGNORED_EXTERNAL_HOSTS; $ignored = $this->getStates()->get($useKey, []); if ($state) { $ignored[] = $value; } else { $searchIdx = \array_search($value, $ignored, \true); if ($searchIdx !== \false) { unset($ignored[$searchIdx]); } } return $this->getStates()->set($useKey, \array_values(\array_unique($ignored))); } /** * Get a list of ignored scanner results mapped by template and external host. */ public function getScannerIgnored() { return ['templates' => $this->getStates()->get(self::SCANNER_IGNORED_TEMPALTES, []), 'hosts' => $this->getStates()->get(self::SCANNER_IGNORED_EXTERNAL_HOSTS, [])]; } /** * Set checklist item as open/closed. * * @param string $id * @param boolean $state */ public function setChecklistItem($id, $state) { return $this->getStates()->set(\DevOwl\RealCookieBanner\view\Notices::CHECKLIST_PREFIX . $id, $state); } /** * Get checklist item state. * * @param string $id * @param boolean $state */ public function isChecklistItem($id) { return $this->getStates()->get(\DevOwl\RealCookieBanner\view\Notices::CHECKLIST_PREFIX . $id); } /** * Check if a non-blocking request has started. */ public function isNonBlockingRequestStarted() { return $this->nonBlockingRequestStarted; } /** * Get list of clicked modal hints. * * @return string[] */ public function getClickedModalHints() { return \array_keys($this->getStates()->getKeysStartingWith(self::MODAL_HINT_PREFIX)); } /** * Dismiss a scanner explicit external URL coverage notice by type. * * @param string $state */ public function dismissScannerExplicitExternalUrlCoverageNotice($state) { // Remove explicit notices of external URLs which do not require a manual scan $noticeState = $this->getStates()->get(\DevOwl\RealCookieBanner\view\Notices::NOTICE_SCANNER_EXPLICIT_EXTERNAL_URL_COVERAGE, []); $doUpdate = \false; $dismissed = []; foreach ($noticeState as $host => $oldState) { if ($oldState === $state) { $noticeState[$host] = \DevOwl\RealCookieBanner\view\Notices::SCANNER_EXPLICIT_EXTERNAL_URL_COVERAGE_STATE_DISMISSED; $dismissed[] = $host; $doUpdate = \true; } } if ($doUpdate) { $this->getStates()->set(\DevOwl\RealCookieBanner\view\Notices::NOTICE_SCANNER_EXPLICIT_EXTERNAL_URL_COVERAGE, $noticeState); } return $dismissed; } /** * Reset states of notices which needs to get recalculated when a service / content blocker got updated / deleted. */ public function recalculatePostDependingNotices() { $states = $this->getStates(); $states->set(self::NOTICE_SERVICES_WITH_EMPTY_PRIVACY_POLICY, null); $states->set(self::NOTICE_SERVICES_WITH_UPDATED_TEMPLATES, null); $states->set(self::NOTICE_SERVICES_WITH_SUCCESSOR_TEMPLATES, null); $states->set(self::NOTICE_SERVICES_WITH_GOOGLE_CONSENT_MODE_ADJUSTMENTS, null); } /** * Getter. * * @codeCoverageIgnore */ public function getStates() { return $this->states; } }