|
Server : Apache System : Linux s1230 5.15.0-139-generic #149~20.04.1 SMP Tue Jul 14 11:21:49 UTC 2026 x86_64 User : p141464 ( 418825) PHP Version : 7.4.33.12 Disable Function : NONE Directory : /html/relaunch-kmu/wp-content/plugins/real-cookie-banner/inc/comp/ |
Upload File : |
<?php
namespace DevOwl\RealCookieBanner\comp;
use DevOwl\RealCookieBanner\Vendor\DevOwl\CookieConsentManagement\settings\BannerLink as SettingsBannerLink;
use DevOwl\RealCookieBanner\Vendor\DevOwl\Multilingual\AbstractSyncPlugin;
use DevOwl\RealCookieBanner\base\UtilsProvider;
use DevOwl\RealCookieBanner\comp\migration\DbConsentV2;
use DevOwl\RealCookieBanner\Core;
use DevOwl\RealCookieBanner\lite\settings\TcfVendorConfiguration;
use DevOwl\RealCookieBanner\scanner\Persist;
use DevOwl\RealCookieBanner\settings\BannerLink;
use DevOwl\RealCookieBanner\settings\Blocker;
use DevOwl\RealCookieBanner\settings\Consent;
use DevOwl\RealCookieBanner\settings\Cookie;
use DevOwl\RealCookieBanner\settings\CookieGroup;
use DevOwl\RealCookieBanner\settings\CountryBypass;
use DevOwl\RealCookieBanner\settings\General;
use DevOwl\RealCookieBanner\settings\Revision;
use DevOwl\RealCookieBanner\settings\TCF;
use DevOwl\RealCookieBanner\Stats;
use DevOwl\RealCookieBanner\templates\TemplateConsumers;
use DevOwl\RealCookieBanner\UserConsent;
use DevOwl\RealCookieBanner\Utils;
use DevOwl\RealCookieBanner\view\Checklist;
use DevOwl\RealCookieBanner\view\checklist\PrivacyPolicy;
use DevOwl\RealCookieBanner\view\checklist\PrivacyPolicyMentionUsage;
use DevOwl\RealCookieBanner\view\customize\banner\BasicLayout;
use DevOwl\RealCookieBanner\view\customize\banner\BodyDesign;
use DevOwl\RealCookieBanner\view\customize\banner\Decision;
use DevOwl\RealCookieBanner\view\customize\banner\individual\Group;
use DevOwl\RealCookieBanner\view\customize\banner\individual\SaveButton;
use DevOwl\RealCookieBanner\view\customize\banner\Texts;
use DevOwl\RealCookieBanner\view\Notices;
// @codeCoverageIgnoreStart
\defined('ABSPATH') or die('No script kiddies please!');
// Avoid direct file request
// @codeCoverageIgnoreEnd
/**
* Apply database migrations when we update the plugin.
* @internal
*/
class DatabaseUpgrades
{
use UtilsProvider;
/**
* Installed version.
*
* @var string|false
*/
private $installed;
/**
* C'tor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
$this->installed = '';
}
/**
* A new version got installed, run all our migrations if not yet migrated.
*
* @param string|false $installed
*/
public function apply($installed)
{
$this->installed = $installed;
$result = [];
$result[] = $this->migration_we5cq1();
$result[] = $this->migration_1td2xu0();
$result[] = $this->migration_1xwnv8m();
$result[] = $this->migration_1ydq6ff();
$result[] = $this->migration_20chay0();
$result[] = $this->migration_2rb441c();
$result[] = $this->migration_2d8dedh();
$result[] = $this->migration_2z4e99b();
$result[] = $this->migration_2unhn5x();
$result[] = $this->migration_2undj42();
$result[] = $this->migration_2vqpmwj();
$result[] = $this->migration_861m47jgm();
$result[] = $this->migration_861n6fudh();
$result[] = $this->migration_863h7nj72();
$result[] = $this->migration_cawgkp();
$result[] = $this->migration_866ay8jeb();
$result[] = $this->migration_866awy2fr();
$result[] = $this->migration_863gt04va();
$result[] = $this->migration_apv5uu();
$result[] = $this->migration_86940n0a0();
$result[] = $this->migration_86951yt9g();
$result[] = $this->migration_22wkegu();
// This should always be the last migration
$result[] = $this->updateRealCookieBannerTechnicalDefinitions();
(new DbConsentV2())->probablyCreateJob();
}
/**
* Update the technical definitions of the Real Cookie Banner service for some version updates.
*
* @see https://app.clickup.com/2088/v/dc/218-357/218-14272?block=block-6bdbdc5a-026f-435b-a87a-69b621614a8e
*/
protected function updateRealCookieBannerTechnicalDefinitions()
{
if (Core::versionCompareOlderThan($this->installed, '4.8.4', ['4.8.5', '4.9.0', '5.0.0'])) {
// Lazy it, to be compatible with other plugins like WPML or PolyLang...
\add_action('init', function () {
$realCookieBannerService = Cookie::getInstance()->getServiceByIdentifier('real-cookie-banner');
if ($realCookieBannerService !== null) {
$template = TemplateConsumers::getCurrentServiceConsumer()->retrieveBy('identifier', 'real-cookie-banner', \true);
if (\count($template) > 0) {
TemplateConsumers::getInstance()->createFromTemplate($template[0], null, $realCookieBannerService->ID, [Cookie::META_NAME_TECHNICAL_DEFINITIONS], \false);
}
}
}, 20);
return \true;
}
return \false;
}
/**
* Delete the already known, randomly selected powered-by text and regenerate it.
*
* @see https://app.clickup.com/t/we5cq1
*/
protected function migration_we5cq1()
{
if ($this->installed && \version_compare($this->installed, '2.6.5', '<=')) {
\delete_option(Texts::SETTING_POWERED_BY_TEXT);
return \true;
}
return \false;
}
/**
* We have updated our Real Cookie Banner template and we need to automatically apply the patch to the
* Real Cookie Banner service.
*
* @see https://app.clickup.com/t/1td2xu0
*/
protected function migration_1td2xu0()
{
if ($this->installed && \version_compare($this->installed, '2.11.0', '<=')) {
// Lazy it, to be compatible with other plugins like WPML or PolyLang...
\add_action('init', function () {
$realCookieBannerService = Cookie::getInstance()->getServiceByIdentifier('real-cookie-banner');
if ($realCookieBannerService !== null) {
$template = TemplateConsumers::getCurrentServiceConsumer()->retrieveBy('identifier', 'real-cookie-banner', \true);
if (\count($template) > 0) {
TemplateConsumers::getInstance()->createFromTemplate($template[0], null, $realCookieBannerService->ID);
}
}
}, 20);
return \true;
}
return \false;
}
/**
* Reset "Animation only on mobile devices" default to `false` as it should not be activated
* automatically for already existing users.
*
* @see https://app.clickup.com/t/1xwnv8m
*/
protected function migration_1xwnv8m()
{
if (Core::versionCompareOlderThan($this->installed, '2.15.0', ['2.16.0', '2.15.1'])) {
\update_option(BasicLayout::SETTING_ANIMATION_IN_ONLY_MOBILE, \false);
\update_option(BasicLayout::SETTING_ANIMATION_OUT_ONLY_MOBILE, \false);
return \true;
}
return \false;
}
/**
* Move all found markups to the respective new database table and drop the known markup column.
*
* @see https://app.clickup.com/t/1ydq6ff
*/
protected function migration_1ydq6ff()
{
global $wpdb;
$table_name = Core::getInstance()->getTableName(Persist::TABLE_NAME);
$table_name_markup = Core::getInstance()->getTableName(Persist::TABLE_NAME_MARKUP);
if (Core::versionCompareOlderThan($this->installed, '2.15.0', ['2.16.0', '2.15.1'], function () use($wpdb, $table_name_markup) {
// phpcs:disable WordPress.DB.PreparedSQL
$exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name_markup}'") === $table_name_markup;
// phpcs:enable WordPress.DB.PreparedSQL
return 0 !== $exists;
})) {
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->query("INSERT IGNORE INTO {$table_name_markup} (markup, markup_hash) SELECT markup, markup_hash FROM {$table_name} WHERE markup IS NOT NULL");
// phpcs:enable WordPress.DB.PreparedSQL
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->query("ALTER TABLE {$table_name} DROP COLUMN markup");
// phpcs:enable WordPress.DB.PreparedSQL
return \true;
}
return \false;
}
/**
* Deactivate "Use same stylings as 'Accept all'" as it should not be activated automatically for already existing users.
*
* @see https://app.clickup.com/t/20chay0
*/
protected function migration_20chay0()
{
if (Core::versionCompareOlderThan($this->installed, '2.17.3', ['2.17.4', '2.18.0'])) {
\update_option(Decision::SETTING_SHOW_GROUPS, '1');
\update_option(BodyDesign::SETTING_BUTTON_ACCEPT_ESSENTIALS_USE_ACCEPT_ALL, '');
\update_option(SaveButton::SETTING_USE_ACCEPT_ALL, '');
\update_option(Consent::SETTING_LIST_SERVICES_NOTICE, '');
return \true;
}
return \false;
}
/**
* Revert to cookie version 1 for users already using RCB.
*
* @see https://app.clickup.com/t/2rb441c
*/
protected function migration_2rb441c()
{
if (Core::versionCompareOlderThan($this->installed, '3.0.1', ['3.0.2', '3.1.0'])) {
\update_option(Consent::SETTING_COOKIE_VERSION, Consent::COOKIE_VERSION_1);
return \true;
}
return \false;
}
/**
* Multiple metadata rename migrations.
*
* @see https://app.clickup.com/t/2d8dedh
*/
protected function migration_2d8dedh()
{
global $wpdb;
if (Core::versionCompareOlderThan($this->installed, '3.0.2', ['3.0.3', '3.1.0'])) {
// Get posts which hold post meta which needs to be renamed so we can clear the post cache for them
$setCookiesViaManager = General::getInstance()->getSetCookiesViaManager();
$affectedPostIds = $wpdb->get_col(
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->prepare("SELECT p.ID FROM {$wpdb->postmeta} pm\n INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID\n WHERE pm.meta_key IN ('" . \join("','", \array_merge(['providerPrivacyPolicy', 'codeOptOutDelete', 'noTechnicalDefinitions', 'technicalDefinitions'], ($setCookiesViaManager === 'none' ? [] : $setCookiesViaManager === 'googleTagManager') ? ['googleTagManagerInEventName', 'googleTagManagerOutEventName', 'codeOptInNoGoogleTagManager', 'codeOptOutNoGoogleTagManager'] : ['matomoTagManagerInEventName', 'matomoTagManagerOutEventName', 'codeOptInNoMatomoTagManager', 'codeOptOutNoMatomoTagManager'])) . "') AND p.post_type = %s\n GROUP BY p.ID", Cookie::CPT_NAME)
);
if (\count($affectedPostIds) > 0) {
// Rename the metadata directly through a plain SQL query so hooks like `update_post_meta` are not called
// This avoids issues with WPML or PolyLang and their syncing process
$wpdb->query(
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->prepare("UPDATE {$wpdb->postmeta} pm\n INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID\n SET pm.meta_key = CASE\n WHEN pm.meta_key = 'providerPrivacyPolicy' THEN 'providerPrivacyPolicyUrl'\n WHEN pm.meta_key = 'codeOptOutDelete' THEN 'deleteTechnicalDefinitionsAfterOptOut'\n WHEN pm.meta_key = 'noTechnicalDefinitions' THEN 'isEmbeddingOnlyExternalResources'\n " . \join(' ', $setCookiesViaManager === 'googleTagManager' ? ["WHEN pm.meta_key = 'googleTagManagerInEventName' THEN 'tagManagerOptInEventName'", "WHEN pm.meta_key = 'googleTagManagerOutEventName' THEN 'tagManagerOptOutEventName'", "WHEN pm.meta_key = 'codeOptInNoGoogleTagManager' THEN 'executeCodeOptInWhenNoTagManagerConsentIsGiven'", "WHEN pm.meta_key = 'codeOptOutNoGoogleTagManager' THEN 'executeCodeOptOutWhenNoTagManagerConsentIsGiven'"] : ["WHEN pm.meta_key = 'matomoTagManagerInEventName' THEN 'tagManagerOptInEventName'", "WHEN pm.meta_key = 'matomoTagManagerOutEventName' THEN 'tagManagerOptOutEventName'", "WHEN pm.meta_key = 'codeOptInNoMatomoTagManager' THEN 'executeCodeOptInWhenNoTagManagerConsentIsGiven'", "WHEN pm.meta_key = 'codeOptOutNoMatomoTagManager' THEN 'executeCodeOptOutWhenNoTagManagerConsentIsGiven'"]) . "\n ELSE pm.meta_key\n END,\n pm.meta_value = CASE\n WHEN pm.meta_key = 'technicalDefinitions' THEN REPLACE(`meta_value`, '\"sessionDuration\"', '\"isSessionDuration\"')\n ELSE pm.meta_value\n END\n WHERE p.post_type = %s", Cookie::CPT_NAME)
);
foreach ($affectedPostIds as $affectedPostId) {
\clean_post_cache(\intval($affectedPostId));
}
}
// Get posts which hold post meta which needs to be renamed so we can clear the post cache for them
$affectedPostIds = $wpdb->get_col($wpdb->prepare("SELECT p.ID FROM {$wpdb->postmeta} pm\n INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID\n WHERE pm.meta_key IN (\n 'criteria', 'visual', 'visualDarkMode', 'forceHidden', 'hosts', 'cookies'\n ) AND p.post_type = %s\n GROUP BY p.ID", Blocker::CPT_NAME));
if (\count($affectedPostIds) > 0) {
// Rename the metadata directly through a plain SQL query so hooks like `update_post_meta` are not called
// This avoids issues with WPML or PolyLang and their syncing process
$wpdb->query($wpdb->prepare("UPDATE {$wpdb->postmeta} pm\n INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID\n SET pm.meta_key = CASE\n WHEN pm.meta_key = 'visual' THEN 'isVisual'\n WHEN pm.meta_key = 'visualDarkMode' THEN 'isVisualDarkMode'\n WHEN pm.meta_key = 'forceHidden' THEN 'shouldForceToShowVisual'\n WHEN pm.meta_key = 'hosts' THEN 'rules'\n WHEN pm.meta_key = 'cookies' THEN 'services'\n ELSE pm.meta_key\n END,\n pm.meta_value = CASE\n WHEN pm.meta_key = 'criteria' AND pm.meta_value = 'cookies' THEN 'services'\n ELSE pm.meta_value\n END\n WHERE p.post_type = %s", Blocker::CPT_NAME));
foreach ($affectedPostIds as $affectedPostId) {
\clean_post_cache(\intval($affectedPostId));
}
}
return \true;
}
return \false;
}
/**
* Create direct aggregates in database instead of calculating from consents.
*
* @see https://app.clickup.com/t/2z4e99b
*/
protected function migration_2z4e99b()
{
global $wpdb;
$table_name_stats_buttons_clicked = $this->getTableName(Stats::TABLE_NAME_BUTTONS_CLICKED);
$table_name_stats_custom_bypass = $this->getTableName(Stats::TABLE_NAME_CUSTOM_BYPASS);
$table_name_consent = $this->getTableName(UserConsent::TABLE_NAME_DEPRECATED);
if (Core::versionCompareOlderThan($this->installed, '3.4.13', ['3.4.14', '3.5.0'], function () use($wpdb, $table_name_stats_buttons_clicked) {
// phpcs:disable WordPress.DB.PreparedSQL
$exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name_stats_buttons_clicked}'") === $table_name_stats_buttons_clicked;
// phpcs:enable WordPress.DB.PreparedSQL
if ($exists) {
// phpcs:disable WordPress.DB.PreparedSQL
$countRows = \intval($wpdb->get_var("SELECT COUNT(*) AS cnt FROM {$table_name_stats_buttons_clicked}"));
// phpcs:enable WordPress.DB.PreparedSQL
// Only execute the migration once
return $countRows === 0;
}
return \false;
})) {
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->query("INSERT IGNORE INTO {$table_name_stats_buttons_clicked} (context, `day`, button_clicked, `count`)\n SELECT context, CONVERT(created, DATE) AS `day`, button_clicked, COUNT(DISTINCT uuid) AS cnt\n FROM {$table_name_consent}\n WHERE forwarded IS NULL\n GROUP BY 1, 2, 3");
$wpdb->query("INSERT IGNORE INTO {$table_name_stats_custom_bypass} (context, `day`, custom_bypass, `count`)\n SELECT context, CONVERT(created, DATE) AS `day`, IFNULL(custom_bypass, CASE WHEN dnt = 1 THEN 'dnt' ELSE 'none' END) AS custom_bypass, COUNT(DISTINCT uuid) AS cnt\n FROM {$table_name_consent}\n WHERE forwarded IS NULL\n GROUP BY 1, 2, 3");
// phpcs:enable WordPress.DB.PreparedSQL
return \true;
}
return \false;
}
/**
* Rename `consentForwardingUniqueName` to `uniqueName` and create one if missing.
*
* @see https://app.clickup.com/t/2unhn5x
*/
protected function migration_2unhn5x()
{
global $wpdb;
if (Core::versionCompareOlderThan($this->installed, '3.4.13', ['3.4.14', '3.5.0'])) {
// Get posts which hold post meta which needs to be renamed so we can clear the post cache for them
// If `consentForwardingUniqueNameMetaId` is `NULL` you need to insert into that database table
$result = $wpdb->get_results(
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->prepare("SELECT\n p.ID, p.post_name,\n pmConsentForwardingUniqueName.meta_id AS consentForwardingUniqueNameMetaId,\n pmConsentForwardingUniqueName.meta_value AS consentForwardingUniqueName,\n pmUniqueName.meta_value AS uniqueName,\n pmTemplateId.meta_value AS presetId,\n IFNULL(pmTemplateId.meta_value, p.post_name) AS potentialUniqueName\n FROM {$wpdb->posts} p\n LEFT JOIN {$wpdb->postmeta} pmConsentForwardingUniqueName\n ON pmConsentForwardingUniqueName.post_id = p.ID AND pmConsentForwardingUniqueName.meta_key = 'consentForwardingUniqueName'\n LEFT JOIN {$wpdb->postmeta} pmUniqueName\n ON pmUniqueName.post_id = p.ID AND pmUniqueName.meta_key = 'uniqueName'\n LEFT JOIN {$wpdb->postmeta} pmTemplateId\n ON pmTemplateId.post_id = p.ID AND pmTemplateId.meta_key = 'presetId'\n WHERE post_type = %s", Cookie::CPT_NAME),
ARRAY_A
);
$affectedPostIds = \array_map(function ($row) {
return $row['ID'];
}, $result);
if (\count($affectedPostIds) > 0) {
// Rename or create the metadata directly through a plain SQL query so hooks like `update_post_meta` are not called
// This avoids issues with WPML or PolyLang and their syncing process
foreach ($result as $row) {
if (!empty($row['consentForwardingUniqueNameMetaId']) && !empty($row['consentForwardingUniqueName'])) {
// Rename meta_key to `uniqueName`ยด
$wpdb->query(
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_key = 'uniqueName', meta_value = %s WHERE meta_id = %d", $row['potentialUniqueName'], $row['consentForwardingUniqueNameMetaId']),
ARRAY_A
);
} elseif (empty($row['consentForwardingUniqueNameMetaId']) && empty($row['uniqueName'])) {
// Insert new meta `uniqueName`
$wpdb->query(
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->prepare("INSERT IGNORE INTO {$wpdb->postmeta} (`post_id`, `meta_key`, `meta_value`) VALUES (%d, 'uniqueName', %s)", $row['ID'], $row['potentialUniqueName']),
ARRAY_A
);
}
}
foreach ($affectedPostIds as $affectedPostId) {
\clean_post_cache(\intval($affectedPostId));
}
}
return \true;
}
return \false;
}
/**
* Fill the `ui_view` database column.
*
* @see https://app.clickup.com/t/2undj42
*/
protected function migration_2undj42()
{
global $wpdb;
$table_name = $this->getTableName(UserConsent::TABLE_NAME_DEPRECATED);
if (Core::versionCompareOlderThan($this->installed, '3.4.13', ['3.4.14', '3.5.0'])) {
// phpcs:disable WordPress.DB
$wpdb->query("UPDATE {$table_name} SET ui_view = 'initial' WHERE dnt = 0 AND custom_bypass IS NULL");
// phpcs:enable WordPress.DB
return \true;
}
return \false;
}
/**
* Automatically check the checklist item if "Real Cookie Banner" is already mentioned in current
* privacy policy.
*
* @see https://app.clickup.com/t/2vqpmwj
*/
protected function migration_2vqpmwj()
{
if (Core::versionCompareOlderThan($this->installed, '3.4.13', ['3.4.14', '3.5.0'])) {
PrivacyPolicyMentionUsage::recalculate(BannerLink::getInstance()->getLegalLink(SettingsBannerLink::PAGE_TYPE_PRIVACY_POLICY, 'id'));
return \true;
}
return \false;
}
/**
* Automatically convert ePrivacy USA flag to data processing in unsafe countries.
*
* @see https://app.clickup.com/t/861m47jgm
*/
protected function migration_861m47jgm()
{
global $wpdb;
if (Core::versionCompareOlderThan($this->installed, '3.7.2', ['3.7.3', '3.8.0'])) {
// Enable new feature
$legacyOptionName = RCB_OPT_PREFIX . '-eprivacy-usa';
$option = \get_option($legacyOptionName);
if ($option) {
\update_option(Consent::SETTING_DATA_PROCESSING_IN_UNSAFE_COUNTRIES, \true);
}
//delete_option($legacyOptionName);
// Get posts which hold post meta which needs to be renamed so we can clear the post cache for them
$affectedPostIds = $wpdb->get_col($wpdb->prepare("SELECT p.ID FROM {$wpdb->postmeta} pm\n INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID\n WHERE pm.meta_key IN (\n 'ePrivacyUSA'\n ) AND p.post_type IN (%s, %s)\n GROUP BY p.ID", Cookie::CPT_NAME, 'rcb-tcf-vendor-conf'));
if (\count($affectedPostIds) > 0) {
// Rename the metadata directly through a plain SQL query so hooks like `update_post_meta` are not called
// This avoids issues with WPML or PolyLang and their syncing process
$wpdb->query($wpdb->prepare("UPDATE {$wpdb->postmeta} pm\n INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID\n SET pm.meta_key = CASE\n WHEN pm.meta_key = 'ePrivacyUSA' THEN 'dataProcessingInCountries'\n ELSE pm.meta_key\n END,\n pm.meta_value = CASE\n WHEN pm.meta_key = 'ePrivacyUSA' AND pm.meta_value = '1' THEN '[\"US\"]'\n WHEN pm.meta_key = 'ePrivacyUSA' AND pm.meta_value <> '1' THEN '[]'\n ELSE pm.meta_value\n END\n WHERE p.post_type IN (%s, %s)", Cookie::CPT_NAME, 'rcb-tcf-vendor-conf'));
foreach ($affectedPostIds as $affectedPostId) {
\clean_post_cache(\intval($affectedPostId));
}
}
return \true;
}
return \false;
}
/**
* Update the MD5 hash of truncated JSON strings in the revision database tables.
*
* @see https://app.clickup.com/t/861n6fudh
*/
public function migration_861n6fudh()
{
global $wpdb;
$table_name = $this->getTableName(Revision::TABLE_NAME);
$table_name_independent = $this->getTableName(Revision::TABLE_NAME_INDEPENDENT);
if (Core::versionCompareOlderThan($this->installed, '3.9.5', ['3.9.6', '3.10.0'])) {
// phpcs:disable WordPress.DB
$wpdb->query("UPDATE {$table_name} SET `hash` = MD5(json_revision)");
$wpdb->query("UPDATE {$table_name_independent} SET `hash` = MD5(json_revision)");
// phpcs:enable WordPress.DB
return \true;
}
return \false;
}
/**
* With the introduction of the legal basis in Settings > General, we now need to output the
* selected legal basis in the cookie banner for the "Data processing in unsafe countries".
* For this, we update the configured texts and replace the legal basis with the new `{{legalBasis}}` variable.
*
* - Add Switzerland (`CH`) to the country bypass list.
* - Set the TCF publisher country as operator country when given from older installations, and, calculate the `isProviderCurrentWebsite` for all services.
*
* @see https://app.clickup.com/t/863h7nj72
*/
protected function migration_863h7nj72()
{
global $wpdb;
if (Core::versionCompareOlderThan($this->installed, '3.11.5', ['3.12.0', '3.11.6'])) {
// phpcs:disable WordPress.DB
$sql = \sprintf("UPDATE {$wpdb->options}\n SET option_value = REPLACE(REPLACE(option_value, 'Art. 49 Abs. 1 (a) DSGVO', '{{legalBasis}}'), 'Art. 49 (1) (a) GDPR', '{{legalBasis}}')\n WHERE `option_name` LIKE '%s%%'", Texts::SETTING_DATA_PROCESSING_IN_UNSAFE_COUNTRIES);
$count = $wpdb->query($sql);
// phpcs:enable WordPress.DB
if ($count > 0) {
\wp_cache_delete('alloptions', 'options');
}
// Add Switzerland (`CH`) to the country bypass list.
// We do not use `getCountries()` as we need the non-expanded country list
$countries = \get_option(CountryBypass::SETTING_COUNTRY_BYPASS_COUNTRIES);
if (!empty($countries)) {
$countries = \explode(',', $countries);
if (!\in_array('CH', $countries, \true)) {
$countries[] = 'CH';
\update_option(CountryBypass::SETTING_COUNTRY_BYPASS_COUNTRIES, \join(',', $countries));
}
}
$deleteCountryForExistingUsers = \true;
if ($this->isPro()) {
$tcfPublisherCc = \get_option(TCF::SETTING_TCF_PUBLISHER_CC);
if (!empty($tcfPublisherCc)) {
// We need to call this after `enableOptionAutoload`
\add_action('init', function () use($tcfPublisherCc) {
\update_option(General::SETTING_OPERATOR_COUNTRY, $tcfPublisherCc);
}, 11);
\delete_option(TCF::SETTING_TCF_PUBLISHER_CC);
$deleteCountryForExistingUsers = \false;
}
}
if ($deleteCountryForExistingUsers) {
// We need to call this after `enableOptionAutoload`
\add_action('init', function () {
\update_option(General::SETTING_OPERATOR_COUNTRY, '');
}, 11);
}
// Get posts which hold post meta which needs to be renamed so we can clear the post cache for them
$affectedPostIds = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT(p.ID)\n FROM {$wpdb->posts} p\n LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'isProviderCurrentWebsite'\n WHERE p.post_type = %s AND pm.meta_value IS NULL", Cookie::CPT_NAME));
if (\count($affectedPostIds) > 0) {
// Insert the metadata directly through a plain SQL query so hooks like `update_post_meta` are not called
// This avoids issues with WPML or PolyLang and their syncing process
$wpdb->query($wpdb->prepare(
"INSERT INTO {$wpdb->postmeta} (post_id, meta_key, meta_value) SELECT p.ID, 'isProviderCurrentWebsite',\n CASE\n WHEN pmPrivacy.meta_value LIKE %s THEN '1'\n WHEN pmLegal.meta_value LIKE %s THEN '1'\n WHEN pmProvider.meta_value LIKE %s THEN '1'\n WHEN pmProvider.meta_value LIKE %s THEN '1'\n ELSE ''\n END AS isProviderCurrentWebsite\n FROM {$wpdb->posts} p\n LEFT JOIN {$wpdb->postmeta} pmProvider\n ON p.ID = pmProvider.post_id AND pmProvider.meta_key = 'provider'\n LEFT JOIN {$wpdb->postmeta} pmPrivacy\n ON p.ID = pmPrivacy.post_id AND pmPrivacy.meta_key = 'providerPrivacyPolicyUrl'\n LEFT JOIN {$wpdb->postmeta} pmLegal\n ON p.ID = pmLegal.post_id AND pmLegal.meta_key = 'providerLegalNoticeUrl'\n WHERE p.post_type = %s\n AND NOT EXISTS (\n SELECT 1 FROM {$wpdb->postmeta} pm\n WHERE pm.post_id = p.ID\n AND pm.meta_key = 'isProviderCurrentWebsite'\n )",
'%' . Utils::host(Utils::HOST_TYPE_MAIN) . '%',
'%' . Utils::host(Utils::HOST_TYPE_MAIN) . '%',
\get_bloginfo('name'),
// needed for backwards-compatibility due to a bug in older Real Cookie Banner versions
\html_entity_decode(\get_bloginfo('name')),
Cookie::CPT_NAME
));
foreach ($affectedPostIds as $affectedPostId) {
\clean_post_cache(\intval($affectedPostId));
}
}
return \true;
}
return \false;
}
/**
* With the introduction of the footer links in Cookies > Settings, we also removed the "Legal" section
* in the customizer. The links are now completely customizable via a custom post type.
*
* @see https://app.clickup.com/t/cawgkp
*/
protected function migration_cawgkp()
{
if (Core::versionCompareOlderThan($this->installed, '3.11.5', ['3.12.0', '3.11.6'])) {
BannerLink::getInstance()->createDefaults(\true);
return \true;
}
return \false;
}
/**
* With the introduction of the footer links in Cookies > Settings, we had a bug that migrated banner links
* did not update the checklist item accordingly.
*
* @see https://app.clickup.com/t/866ay8jeb
*/
protected function migration_866ay8jeb()
{
if (Core::versionCompareOlderThan($this->installed, '3.12.0', ['3.13.0', '3.12.1'])) {
\add_action('init', function () {
$privacyPolicyId = BannerLink::getInstance()->getLegalLink(SettingsBannerLink::PAGE_TYPE_PRIVACY_POLICY, 'id');
if ($privacyPolicyId > 0) {
if (!Checklist::getInstance()->isChecked(PrivacyPolicy::IDENTIFIER)) {
PrivacyPolicy::recalculate($privacyPolicyId);
}
if (!Checklist::getInstance()->isChecked(PrivacyPolicyMentionUsage::IDENTIFIER)) {
PrivacyPolicyMentionUsage::recalculate($privacyPolicyId);
}
}
}, 8);
return \true;
}
return \false;
}
/**
* With the introduction of the age notice age limit in Settings > Consent, we now need to output the
* age limit in the cookie banner. For this, we update the configured texts and replace the "16 years"
* with the new `{{minAge}} years` variable.
*
* @see https://app.clickup.com/t/866awy2fr
*/
protected function migration_866awy2fr()
{
global $wpdb;
if (Core::versionCompareOlderThan($this->installed, '3.12.0', ['3.13.0', '3.12.1'])) {
// phpcs:disable WordPress.DB
$sql = \sprintf("UPDATE {$wpdb->options}\n SET option_value = REPLACE(REPLACE(option_value, '16 years', '{{minAge}} years'), '16 Jahre', '{{minAge}} Jahre')\n WHERE `option_name` LIKE '%s%%' OR `option_name` LIKE '%s%%'", Texts::SETTING_AGE_NOTICE, Texts::SETTING_AGE_NOTICE_BLOCKER);
$count = $wpdb->query($sql);
// phpcs:enable WordPress.DB
if ($count > 0) {
\wp_cache_delete('alloptions', 'options');
}
return \true;
}
return \false;
}
/**
* With the introduction of TCF 2.2, IAB Europe recommends to only use TCF vendors which are really used
* while data processing. When there are more than x vendors created previsouly, show a notice.
*
* @see https://app.clickup.com/t/863gt04va
*/
protected function migration_863gt04va()
{
if (Core::versionCompareOlderThan($this->installed, '4.0.0', ['4.1.0', '4.0.1']) && $this->isPro()) {
\add_action('init', function () {
if (TCF::getInstance()->isActive()) {
$count = TcfVendorConfiguration::getInstance()->getAllCount();
if ($count > Notices::TCF_TOO_MUCH_VENDORS) {
Core::getInstance()->getNotices()->getStates()->set(Notices::NOTICE_TCF_TOO_MUCH_VENDORS, \true);
}
}
}, 8);
return \true;
}
return \false;
}
/**
* We have renamed the TCF Stacks in customizer to "Accordion" so it is not restricted to TCF-only.
*
* @see https://app.clickup.com/t/apv5uu
* @see https://app.clickup.com/2088/v/dc/218-357/218-12212?block=block-fc8ad147-87f1-4c73-bdb1-2f8faa1dd462
*/
protected function migration_apv5uu()
{
global $wpdb;
if (Core::versionCompareOlderThan($this->installed, '4.3.7', ['4.3.8', '4.4.0']) || Core::versionCompareOlderThan($this->installed, '4.4.0', ['4.4.1', '4.5.0'])) {
// phpcs:disable WordPress.DB.PreparedSQL
$wpdb->query("INSERT INTO {$wpdb->options} (`option_name`, `option_value`, `autoload`)\n SELECT\n REPLACE(option_name, 'tcf-stacks', 'accordion') AS option_name,\n option_value,\n 'yes' as autoload\n FROM {$wpdb->options}\n WHERE option_name LIKE 'rcb-banner-body-design-tcf-stacks%'\n ON DUPLICATE KEY UPDATE option_value = VALUES(option_value)");
// phpcs:enable WordPress.DB.PreparedSQL
\wp_cache_delete('alloptions', 'options');
return \true;
}
return \false;
}
/**
* Disable maximum height customize setting for the cookie banner for already existing users.
*
* @see https://app.clickup.com/t/86940n0a0
*/
protected function migration_86940n0a0()
{
if (Core::versionCompareOlderThan($this->installed, '4.5.4', ['4.5.5', '4.6.0'])) {
\update_option(BasicLayout::SETTING_MAX_HEIGHT_ENABLED, '');
return \true;
}
return \false;
}
/**
* We have introduced a new `button_clicked` for implicit consents (e.g. Country Bypass): `implicit_all`
* and `implicit_essential`. With a SQL query we can update older consents to reflect the correct `button_clicked`
* for Country Bypass by reading the configuration from the revisions.
*
* @see https://app.clickup.com/t/86951yt9g
*/
protected function migration_86951yt9g()
{
global $wpdb;
$table_name = Core::getInstance()->getTableName(UserConsent::TABLE_NAME_DEPRECATED);
$table_name_revision = Core::getInstance()->getTableName(Revision::TABLE_NAME);
if (Core::versionCompareOlderThan($this->installed, '4.7.11', ['4.7.12', '4.8.0'])) {
// phpcs:disable WordPress.DB
$sql = "UPDATE {$table_name} c\nINNER JOIN {$table_name_revision} r\nON c.revision = r.hash\nSET c.button_clicked = CASE\n WHEN r.json_revision LIKE '%\"isCountryBypass\":true%' AND r.json_revision LIKE '%\"countryBypassType\":\"all\"%' THEN 'implicit_all'\n WHEN r.json_revision LIKE '%\"isCountryBypass\":true%' AND r.json_revision NOT LIKE '%\"countryBypassType\":\"all\"%' THEN 'implicit_essential'\n ELSE 'none'\n END\nWHERE c.custom_bypass = 'geolocation'\nAND c.button_clicked NOT LIKE 'implicit%'";
$wpdb->query($sql);
// phpcs:enable WordPress.DB
}
return \false;
}
/**
* Create a `isDefault` meta key for all service groups (terms) so they can be differentiated in the cookie policy editor.
*
* Additionally, add the revision hash to the option which holds the current revision hash.
*
* Reset "Hide less relevant service details" default to `false` as it should not be activated automatically for already existing users.
*
* @see https://app.clickup.com/t/22wkegu
* @see https://app.clickup.com/t/8694wynf7
*/
public function migration_22wkegu()
{
global $wpdb;
if (Core::versionCompareOlderThan($this->installed, '4.7.15', ['4.7.16', '4.8.0'])) {
$fnMigrate = function () {
$defaultSlugs = \array_map('sanitize_title', \array_keys(CookieGroup::getInstance()->getDefaultDescriptions(\true)));
foreach (CookieGroup::getInstance()->getOrdered(\true, \true) as $term) {
$isDefault = \false;
foreach ($defaultSlugs as $defaultSlug) {
if (Utils::startsWith($term->slug, $defaultSlug)) {
$isDefault = \true;
break;
}
}
\update_term_meta($term->term_id, CookieGroup::META_NAME_IS_DEFAULT, $isDefault);
}
};
// Lazy it, to be compatible with other plugins like WPML or PolyLang...
\add_action('init', function () use($fnMigrate) {
$compLanguage = Core::getInstance()->getCompLanguage();
if ($compLanguage instanceof AbstractSyncPlugin) {
$compLanguage->iterateAllLanguagesContext($fnMigrate);
} else {
$fnMigrate();
}
}, 20);
$table_name = $this->getTableName(Revision::TABLE_NAME);
// phpcs:disable WordPress.DB
$sql = \sprintf("UPDATE {$wpdb->options} wpo\n INNER JOIN {$table_name} r ON wpo.option_value = r.hash\n SET wpo.option_value = CONCAT(wpo.option_value, ':', UNIX_TIMESTAMP(r.created))\n WHERE wpo.option_name LIKE '%s%%' AND wpo.option_value NOT LIKE '%%:%%'", Revision::OPTION_NAME_CURRENT_HASH_PREFIX);
$count = $wpdb->query($sql);
// phpcs:enable WordPress.DB
\update_option(Group::SETTING_DETIALS_HIDE_LESS_RELEVANT, \false);
if ($count > 0) {
\wp_cache_delete('alloptions', 'options');
}
}
return \false;
}
}