common */ // Enqueue the vendor chunk first. if (isset(Config::$assetManifest['extendify-vendors.js'])) { wp_enqueue_script( 'extendify-vendors', EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-vendors.js'], [], $version, true // Load in footer. ); } // Enqueue the common chunk next, dependent on vendors. if (isset(Config::$assetManifest['extendify-common.js'])) { wp_enqueue_script( 'extendify-common', EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-common.js'], ['extendify-vendors'], $version, true ); } /* * Loads a unique runtime generated by Webpack to manage all the other generated scripts. * Without this runtime, the other Extendify scripts won't load. */ $scriptAssetPath = EXTENDIFY_PATH . 'public/build/' . Config::$assetManifest['extendify-runtime.php']; $fallback = [ 'dependencies' => [], 'version' => $version, ]; $scriptAsset = file_exists($scriptAssetPath) ? require $scriptAssetPath : $fallback; foreach ($scriptAsset['dependencies'] as $style) { \wp_enqueue_style($style); } \wp_enqueue_script( Config::$slug . '-runtime-scripts', EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-runtime.js'], $scriptAsset['dependencies'], $scriptAsset['version'], true ); $scriptAssetPath = EXTENDIFY_PATH . 'public/build/' . Config::$assetManifest['extendify-shared.php']; $fallback = [ 'dependencies' => [], 'version' => $version, ]; $scriptAsset = file_exists($scriptAssetPath) ? require $scriptAssetPath : $fallback; foreach ($scriptAsset['dependencies'] as $style) { \wp_enqueue_style($style); } \wp_enqueue_script( Config::$slug . '-shared-scripts', EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-shared.js'], $scriptAsset['dependencies'], $scriptAsset['version'], true ); $siteProfile = \get_option('extendify_site_profile', [ 'aiSiteType' => '', 'aiSiteCategory' => '', 'aiDescription' => '', 'aiKeywords' => [], ]); $partnerData = PartnerData::getPartnerData(); $userConsent = get_user_meta(get_current_user_id(), 'extendify_ai_consent', true); $htmlAllowlist = [ 'a' => [ 'target' => [], 'href' => [], 'rel' => [], ], ]; if (!function_exists('get_plugins')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } \wp_add_inline_script( Config::$slug . '-shared-scripts', 'window.extSharedData = ' . \wp_json_encode([ 'root' => \esc_url_raw(rest_url(Config::$slug . '/' . Config::$apiVersion)), 'homeUrl' => \esc_url_raw(\get_home_url()), 'adminUrl' => \esc_url_raw(\admin_url()), 'nonce' => \esc_attr(\wp_create_nonce('wp_rest')), 'devbuild' => (bool) constant('EXTENDIFY_DEVMODE'), 'assetPath' => \esc_url(EXTENDIFY_URL . 'public/assets'), 'siteId' => \esc_attr(\get_option('extendify_site_id', '')), 'siteCreatedAt' => \esc_attr(SiteSettings::getSiteCreatedAt()), 'themeSlug' => \esc_attr(\get_option('stylesheet')), 'version' => \esc_attr(Config::$version), 'siteTitle' => \esc_attr(\get_bloginfo('name')), 'siteType' => Escaper::recursiveEscAttr(\get_option('extendify_siteType', [])), 'siteProfile' => Escaper::recursiveEscAttr((array) $siteProfile), 'wpLanguage' => \esc_attr(\get_locale()), 'wpVersion' => \esc_attr(\get_bloginfo('version')), 'isBlockTheme' => function_exists('wp_is_block_theme') ? (bool) wp_is_block_theme() : false, 'userId' => \esc_attr(\get_current_user_id()), 'partnerLogo' => \esc_attr(PartnerData::$logo), 'partnerId' => \esc_attr(PartnerData::$id), 'partnerName' => \esc_attr(PartnerData::$name), 'allowedPlugins' => array_map('esc_attr', PartnerData::setting('allowedPluginsSlugs')), 'requiredPlugins' => Escaper::recursiveEscAttr(PartnerData::setting('requiredPlugins')), 'userData' => [ 'userSelectionData' => \wp_json_encode((UserSelectionController::get()->get_data() ?? [])), ], 'resourceData' => \wp_json_encode((new ResourceData())->getData()), 'showAIConsent' => isset($partnerData['showAIConsent']) ? (bool) $partnerData['showAIConsent'] : false, 'aiChatEnabled' => (bool) (PartnerData::setting('aiChatEnabled') || constant('EXTENDIFY_DEVMODE')), 'consentTermsHTML' => \wp_kses((html_entity_decode(($partnerData['consentTermsHTML'] ?? '')) ?? ''), $htmlAllowlist), 'userGaveConsent' => $userConsent ? (bool) $userConsent : false, 'installedPlugins' => array_map('esc_attr', array_keys(\get_plugins())), 'activePlugins' => array_map('esc_attr', array_values(\get_option('active_plugins', []))), 'frontPage' => \esc_attr(\get_option('page_on_front', 0)), 'globalStylesPostID' => \esc_attr(\WP_Theme_JSON_Resolver::get_user_global_styles_post_id()), 'showLocalizedCopy' => (bool) array_key_exists('showLocalizedCopy', $partnerData), 'activity' => \wp_json_encode(\get_option('extendify_shared_activity', null)), 'showDraft' => isset($partnerData['showDraft']) ? (bool) $partnerData['showDraft'] : false, 'apexDomain' => PartnerData::setting('enableApexDomain') ? rawurlencode(ApexDomain::getApexDomain(\get_home_url())) : null, ]), 'before' ); $cssColorVars = PartnerData::cssVariableMapping(); $cssString = implode('; ', array_map(function ($k, $v) { return "$k: $v"; }, array_keys($cssColorVars), $cssColorVars)); \wp_register_style(Config::$slug . '-shared-styles', '', [], $version, 'all'); \wp_enqueue_style(Config::$slug . '-shared-styles'); \wp_add_inline_style(Config::$slug . '-shared-styles', wp_strip_all_tags("body { $cssString; }")); } /** * Adds additional meta fields to post types * * @return void */ public function addExtraMetaFields() { // Add a tag to pages that were made with Launch. register_post_meta('page', 'made_with_extendify_launch', [ 'single' => true, 'type' => 'boolean', 'show_in_rest' => true, ]); register_post_meta('post', 'made_with_extendify_launch', [ 'single' => true, 'type' => 'boolean', 'show_in_rest' => true, ]); } /** * Records plugin search terms from the WordPress plugin search page * Stores terms in the 'extendify_plugin_search_terms' option * * @return void */ public function recordPluginsSearchTerms() { // Exits early if it is not an Ajax request, has no payload or invalid nonce. if (!\wp_doing_ajax() || empty($_POST)) { return; } if (array_key_exists( 'action', $_POST) && $_POST['action'] === 'updates' && !\check_ajax_referer('updates', '_ajax_nonce')) { return; } $action = isset($_POST['action']) ? \sanitize_text_field(\wp_unslash($_POST['action'])) : ''; $searchTerm = isset($_POST['s']) ? \sanitize_text_field(\wp_unslash($_POST['s'])) : ''; // Exits early if it is not the right action or search is empty. if (empty($action) || $action !== 'search-install-plugins' || empty($searchTerm)) { return; } // Merges search term with existing search terms and stores it in the database. $searchTerms = \get_option('extendify_plugin_search_terms', []); \update_option('extendify_plugin_search_terms', array_merge($searchTerms, [$searchTerm])); } /** * Records block search terms from the WordPress the editor's block search * Stores terms in the 'extendify_block_search_terms' option * * @return void */ public function recordBlocksSearchTerms() { // Exits early if it is not a REST API request. if (!\wp_is_serving_rest_request()) { return; } $wp = $GLOBALS['wp']; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $restRoute = ($wp->query_vars['rest_route'] ?? ''); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $searchTerm = \sanitize_text_field(\wp_unslash(($wp->query_vars['term'] ?? ''))); // Exits early if is not the blocks search route or if the search query is empty. if ($restRoute !== '/wp/v2/block-directory/search' || empty($searchTerm)) { return; } // Get current search term from the url and merge it with existing search terms. $searchTerms = \get_option('extendify_block_search_terms', []); \update_option('extendify_block_search_terms', array_merge($searchTerms, [$searchTerm])); } }