| @@ -9,14 +9,21 @@ | ||
| 9 | 9 | defined('ABSPATH') || die('No direct access.'); |
| 10 | 10 | |
| 11 | 11 | use Extendify\Config; |
| 12 | 12 | use Extendify\PartnerData; |
| 13 | +use Extendify\Notifications\Availability; | |
| 13 | 14 | use Extendify\Shared\Controllers\UserSelectionController; |
| 15 | +use Extendify\Shared\DataProvider\NotificationData; | |
| 14 | 16 | use Extendify\Shared\DataProvider\ResourceData; |
| 15 | 17 | use Extendify\Shared\Services\AdminMenuList; |
| 16 | 18 | use Extendify\Shared\Services\ApexDomain\ApexDomain; |
| 17 | 19 | use Extendify\Shared\Services\Escaper; |
| 18 | 20 | use Extendify\Shared\Services\PluginDependencies\SimplyBook; |
| 21 | +use Extendify\Shared\Services\PluginsActivation\Imagify as ImagifyActivation; | |
| 22 | +use Extendify\Shared\Services\PluginsActivation\Metricool as MetricoolActivation; | |
| 23 | +use Extendify\Shared\Services\PluginsActivation\SimplyBook as SimplyBookActivation; | |
| 24 | +use Extendify\Shared\Services\PluginsActivation\TranslatePress as TranslatePressActivation; | |
| 25 | +use Extendify\Shared\Services\SiteImages; | |
| 19 | 26 | use Extendify\SiteSettings; |
| 20 | 27 | use Extendify\Shared\Controllers\ImageGenerationController; |
| 21 | 28 | use Extendify\Shared\DataProvider\ProductsData; |
| 22 | 29 | |
| @@ -140,8 +147,67 @@ | ||
| 140 | 147 | if (!function_exists('get_plugins')) { |
| 141 | 148 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 142 | 149 | } |
| 143 | 150 | |
| 151 | + $activePlugins = array_values(\get_option('active_plugins', [])); | |
| 152 | + $activePluginSlugs = array_map(function ($plugin) { | |
| 153 | + return dirname($plugin); | |
| 154 | + }, $activePlugins); | |
| 155 | + $productActivationPlugins = PartnerData::setting('showProductActivation') ?? []; | |
| 156 | + | |
| 157 | + $productActivationPlugins = array_filter( | |
| 158 | + $productActivationPlugins, | |
| 159 | + function ($plugin) use ($activePluginSlugs) { | |
| 160 | + return in_array($plugin['slug'], $activePluginSlugs); | |
| 161 | + } | |
| 162 | + ); | |
| 163 | + | |
| 164 | + $activations = [ | |
| 165 | + ImagifyActivation::class, | |
| 166 | + MetricoolActivation::class, | |
| 167 | + SimplyBookActivation::class, | |
| 168 | + TranslatePressActivation::class, | |
| 169 | + ]; | |
| 170 | + | |
| 171 | + $productActivationPlugins = array_map(function ($plugin) use ($activations) { | |
| 172 | + foreach ($activations as $activation) { | |
| 173 | + if ($plugin['slug'] === $activation::slug()) { | |
| 174 | + return array_merge($plugin, [ | |
| 175 | + 'scriptData' => $activation::scriptData(), | |
| 176 | + 'eligible' => $activation::isEligible(), | |
| 177 | + 'endpoint' => Config::$slug . '/' . Config::$apiVersion | |
| 178 | + . $activation::createAccountRoute(), | |
| 179 | + ]); | |
| 180 | + } | |
| 181 | + } | |
| 182 | + | |
| 183 | + return $plugin; | |
| 184 | + }, $productActivationPlugins); | |
| 185 | + | |
| 186 | + // Visitors read the stamp and the alt text, so both resolve in the site's locale. | |
| 187 | + $switchedLocale = \switch_to_locale(\get_locale()); | |
| 188 | + // translators: Short label stamped onto an image marking it as AI-generated. | |
| 189 | + // Give the all-caps form your language uses. | |
| 190 | + $aiImageLabel = \_x('AI GENERATED', 'uppercase', 'extendify-local'); | |
| 191 | + // translators: %s is the image description. Alt text prefix marking an image as AI-generated. | |
| 192 | + $aiImageAltPattern = \__('AI Generated: %s', 'extendify-local'); | |
| 193 | + if ($switchedLocale) { | |
| 194 | + \restore_previous_locale(); | |
| 195 | + } | |
| 196 | + | |
| 197 | + $extendifyCodeData = (array) PartnerData::setting('extendifyCodeData'); | |
| 198 | + | |
| 199 | + // esc_url() strips the {DESCRIPTION} braces; shield the placeholder across it. | |
| 200 | + $extendifyCodeLink = str_replace( | |
| 201 | + '__EXTENDIFY_DESCRIPTION__', | |
| 202 | + '{DESCRIPTION}', | |
| 203 | + \esc_url_raw(str_replace( | |
| 204 | + '{DESCRIPTION}', | |
| 205 | + '__EXTENDIFY_DESCRIPTION__', | |
| 206 | + (string) ($extendifyCodeData['link'] ?? '') | |
| 207 | + )) | |
| 208 | + ); | |
| 209 | + | |
| 144 | 210 | \wp_add_inline_script( |
| 145 | 211 | Config::$slug . '-shared-scripts', |
| 146 | 212 | 'window.extSharedData = ' . \wp_json_encode([ |
| 147 | 213 | 'root' => \esc_url_raw(rest_url(Config::$slug . '/' . Config::$apiVersion)), |
| @@ -155,17 +221,33 @@ | ||
| 155 | 221 | 'themeSlug' => \esc_attr(\get_option('stylesheet')), |
| 156 | 222 | 'version' => \esc_attr(Config::$version), |
| 157 | 223 | 'siteTitle' => \esc_attr(\get_bloginfo('name')), |
| 158 | 224 | 'siteProfile' => \get_option('extendify_site_profile', []), |
| 225 | + // Empty when the launch-time image fetch failed. | |
| 226 | + 'siteImages' => SiteImages::normalize(\get_option('extendify_site_images', [])), | |
| 159 | 227 | 'wpLanguage' => \esc_attr(\get_locale()), |
| 228 | + 'aiImageLabel' => $aiImageLabel, | |
| 229 | + 'aiImageAltPattern' => $aiImageAltPattern, | |
| 160 | 230 | 'wpVersion' => \esc_attr(\get_bloginfo('version')), |
| 161 | 231 | 'isBlockTheme' => function_exists('wp_is_block_theme') ? (bool) wp_is_block_theme() : false, |
| 162 | 232 | 'userId' => \esc_attr(\get_current_user_id()), |
| 233 | + // phpcs:ignore WordPress.Security.NonceVerification | |
| 234 | + 'userEmail' => isset($_GET['extendify-launch-success']) | |
| 235 | + ? \esc_attr(\wp_get_current_user()->user_email) | |
| 236 | + : null, | |
| 163 | 237 | 'partnerLogo' => \esc_attr(PartnerData::$logo), |
| 164 | 238 | 'partnerId' => \esc_attr(PartnerData::$id), |
| 165 | 239 | 'partnerName' => \esc_attr(PartnerData::$name), |
| 166 | 240 | 'launchDataLegacy' => \wp_json_encode((UserSelectionController::get()->get_data() ?? [])), |
| 167 | 241 | 'resourceData' => \wp_json_encode((new ResourceData())->getData()), |
| 242 | + 'notifications' => \wp_json_encode( | |
| 243 | + Availability::available(NotificationData::get()) | |
| 244 | + ), | |
| 245 | + 'notificationState' => \get_user_meta( | |
| 246 | + \get_current_user_id(), | |
| 247 | + 'extendify_notification_state', | |
| 248 | + true | |
| 249 | + ) ?: ['cards' => []], | |
| 168 | 250 | 'showAIConsent' => isset($partnerData['showAIConsent']) ? (bool) $partnerData['showAIConsent'] : false, |
| 169 | 251 | 'showChat' => (bool) (PartnerData::setting('showChat') || constant('EXTENDIFY_DEVMODE')), |
| 170 | 252 | 'useAgentOnboarding' => (bool) ( |
| 171 | 253 | PartnerData::setting('useAgentOnboarding') || |
| @@ -176,16 +258,19 @@ | ||
| 176 | 258 | PartnerData::setting('showAIPageCreation') || constant('EXTENDIFY_DEVMODE') |
| 177 | 259 | ), |
| 178 | 260 | 'showAILogo' => (bool) PartnerData::setting('showAILogo'), |
| 179 | 261 | 'showImprint' => array_map('esc_attr', (array) PartnerData::setting('showImprint')), |
| 180 | - 'consentTermsCustom' => \wp_kses((html_entity_decode(($partnerData['consentTermsCustom'] ?? '')) | |
| 181 | - ?? ''), $htmlAllowlist), | |
| 262 | + 'showProductActivation' => array_values($productActivationPlugins), | |
| 263 | + 'consentTermsCustom' => \wp_kses((html_entity_decode( | |
| 264 | + ($partnerData['consentTermsCustom'] ?? ''), | |
| 265 | + ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 | |
| 266 | + ) ?? ''), $htmlAllowlist), | |
| 182 | 267 | 'userGaveConsent' => $userConsent ? (bool) $userConsent : false, |
| 183 | 268 | 'installedPlugins' => array_map('esc_attr', array_keys(\get_plugins())), |
| 269 | + 'activePlugins' => $activePlugins, | |
| 184 | 270 | 'installedPluginsSlugs' => array_values(array_filter(array_map(function ($p) { |
| 185 | 271 | return $p['TextDomain'] ?? ''; |
| 186 | 272 | }, \get_plugins()))), |
| 187 | - 'activePlugins' => array_map('esc_attr', array_values(\get_option('active_plugins', []))), | |
| 188 | 273 | 'frontPage' => \esc_attr(\get_option('page_on_front', 0)), |
| 189 | 274 | 'globalStylesPostID' => \esc_attr(\WP_Theme_JSON_Resolver::get_user_global_styles_post_id()), |
| 190 | 275 | 'showLocalizedCopy' => (bool) array_key_exists('showLocalizedCopy', $partnerData), |
| 191 | 276 | 'activity' => \wp_json_encode(\get_option('extendify_shared_activity', null)), |
| @@ -200,8 +285,15 @@ | ||
| 200 | 285 | PartnerData::setting('showLaunchQuestions') || Config::preview('launch-questions') |
| 201 | 286 | ), |
| 202 | 287 | 'products' => ProductsData::get(), |
| 203 | 288 | 'showAIAgents' => (bool) (PartnerData::setting('showAIAgents') || Config::preview('ai-agent')), |
| 289 | + 'showExtendifyCode' => (bool) PartnerData::setting('showExtendifyCode'), | |
| 290 | + 'extendifyCodeData' => [ | |
| 291 | + 'link' => $extendifyCodeLink, | |
| 292 | + 'title' => $extendifyCodeData['title'] ?? '', | |
| 293 | + 'message' => $extendifyCodeData['message'] ?? '', | |
| 294 | + 'ctaPrimary' => $extendifyCodeData['cta-primary'] ?? '', | |
| 295 | + ], | |
| 204 | 296 | 'pluginGroupId' => Escaper::recursiveEscAttr(PartnerData::setting('pluginGroupId')), |
| 205 | 297 | 'adminPagesMenuList' => get_option('_transient_extendify_admin_pages_menu', []), |
| 206 | 298 | 'globalState' => ImageGenerationController::get()->get_data(), |
| 207 | 299 | ]), |
| @@ -245,8 +337,15 @@ | ||
| 245 | 337 | 'type' => 'boolean', |
| 246 | 338 | 'show_in_rest' => true, |
| 247 | 339 | ]); |
| 248 | 340 | register_post_meta('post', 'made_with_extendify_launch', [ |
| 341 | + 'single' => true, | |
| 342 | + 'type' => 'boolean', | |
| 343 | + 'show_in_rest' => true, | |
| 344 | + ]); | |
| 345 | + | |
| 346 | + // Marks AI-generated images for EU AI Act disclosure. | |
| 347 | + register_post_meta('attachment', 'extendify_ai_generated', [ | |
| 249 | 348 | 'single' => true, |
| 250 | 349 | 'type' => 'boolean', |
| 251 | 350 | 'show_in_rest' => true, |
| 252 | 351 | ]); |