PluginProbe
Extendify / 3.1.0
Extendify v3.1.0
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / app / Shared / Admin.php

Admin.php in Extendify 3.1.0, at app/Shared/Admin.php

382 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Help Center Script loader.
5 */
6
7 namespace Extendify\Shared;
8
9 defined('ABSPATH') || die('No direct access.');
10
11 use Extendify\Config;
12 use Extendify\PartnerData;
13 use Extendify\Shared\Controllers\UserSelectionController;
14 use Extendify\Shared\DataProvider\ResourceData;
15 use Extendify\Shared\Services\AdminMenuList;
16 use Extendify\Shared\Services\ApexDomain\ApexDomain;
17 use Extendify\Shared\Services\Escaper;
18 use Extendify\Shared\Services\PluginDependencies\SimplyBook;
19 use Extendify\SiteSettings;
20 use Extendify\Shared\Controllers\ImageGenerationController;
21 use Extendify\Shared\DataProvider\ProductsData;
22
23 /**
24 * This class handles any file loading for the admin area.
25 */
26
27 class Admin
28 {
29 /**
30 * Adds various actions to set up the page
31 *
32 * @return void
33 */
34 public function __construct()
35 {
36 \add_action('init', [$this, 'addExtraMetaFields']);
37 \add_action('admin_enqueue_scripts', [$this, 'loadGlobalScripts']);
38 \add_action('wp_enqueue_scripts', [$this, 'loadGlobalScripts']);
39 \add_action('wp_ajax_search-install-plugins', [$this, 'recordPluginsSearchTerms'], -1);
40 \add_action('rest_api_init', [$this, 'recordBlocksSearchTerms']);
41 \add_action('wp_ajax_query-themes', [$this, 'recordThemesSearchTerms'], -1);
42 AdminMenuList::init();
43 \add_action('simplybook_activation', [SimplyBook::class, 'getIndustryCode'], 10, 0);
44 }
45
46 // phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
47 /**
48 * Adds scripts to every page
49 *
50 * @return void
51 */
52 public function loadGlobalScripts()
53 {
54 \wp_enqueue_media();
55
56 $this->updateUserMeta();
57
58 $version = constant('EXTENDIFY_DEVMODE') ? uniqid() : Config::$version;
59
60 /**
61 * Enqueue shared JavaScript files if they exist in the asset manifest
62 * Ensures proper loading order: vendors -> common
63 */
64
65 // Enqueue the vendor chunk first.
66 if (isset(Config::$assetManifest['extendify-vendors.js'])) {
67 wp_enqueue_script(
68 'extendify-vendors',
69 EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-vendors.js'],
70 [],
71 $version,
72 true
73 // Load in footer.
74 );
75 }
76
77 // Enqueue the common chunk next, dependent on vendors.
78 if (isset(Config::$assetManifest['extendify-common.js'])) {
79 wp_enqueue_script(
80 'extendify-common',
81 EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-common.js'],
82 ['extendify-vendors'],
83 $version,
84 true
85 );
86 }
87
88 /*
89 * Loads a unique runtime generated by Webpack to manage all the other generated scripts.
90 * Without this runtime, the other Extendify scripts won't load.
91 */
92 $scriptAssetPath = EXTENDIFY_PATH . 'public/build/' . Config::$assetManifest['extendify-runtime.php'];
93 $fallback = [
94 'dependencies' => [],
95 'version' => $version,
96 ];
97 $scriptAsset = file_exists($scriptAssetPath) ? require $scriptAssetPath : $fallback;
98
99 foreach ($scriptAsset['dependencies'] as $style) {
100 \wp_enqueue_style($style);
101 }
102
103 \wp_enqueue_script(
104 Config::$slug . '-runtime-scripts',
105 EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-runtime.js'],
106 $scriptAsset['dependencies'],
107 $scriptAsset['version'],
108 true
109 );
110
111 $scriptAssetPath = EXTENDIFY_PATH . 'public/build/' . Config::$assetManifest['extendify-shared.php'];
112 $fallback = [
113 'dependencies' => [],
114 'version' => $version,
115 ];
116 $scriptAsset = file_exists($scriptAssetPath) ? require $scriptAssetPath : $fallback;
117
118 foreach ($scriptAsset['dependencies'] as $style) {
119 \wp_enqueue_style($style);
120 }
121
122 \wp_enqueue_script(
123 Config::$slug . '-shared-scripts',
124 EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-shared.js'],
125 $scriptAsset['dependencies'],
126 $scriptAsset['version'],
127 true
128 );
129
130 $partnerData = PartnerData::getPartnerData();
131 $userConsent = get_user_meta(get_current_user_id(), 'extendify_ai_consent', true);
132 $htmlAllowlist = [
133 'a' => [
134 'target' => [],
135 'href' => [],
136 'rel' => [],
137 ],
138 ];
139
140 if (!function_exists('get_plugins')) {
141 require_once ABSPATH . 'wp-admin/includes/plugin.php';
142 }
143
144 $activePlugins = array_values(\get_option('active_plugins', []));
145 $activePluginSlugs = array_map(function ($plugin) {
146 return dirname($plugin);
147 }, $activePlugins);
148 $productActivationPlugins = PartnerData::setting('showProductActivation') ?? [];
149
150 $productActivationPlugins = array_filter(
151 $productActivationPlugins,
152 function ($plugin) use ($activePluginSlugs) {
153 return in_array($plugin['slug'], $activePluginSlugs);
154 }
155 );
156
157 \wp_add_inline_script(
158 Config::$slug . '-shared-scripts',
159 'window.extSharedData = ' . \wp_json_encode([
160 'root' => \esc_url_raw(rest_url(Config::$slug . '/' . Config::$apiVersion)),
161 'homeUrl' => \esc_url_raw(\get_home_url()),
162 'adminUrl' => \esc_url_raw(\admin_url()),
163 'nonce' => \esc_attr(\wp_create_nonce('wp_rest')),
164 'devbuild' => (bool) constant('EXTENDIFY_DEVMODE'),
165 'assetPath' => \esc_url(EXTENDIFY_URL . 'public/assets'),
166 'siteId' => \esc_attr(\get_option('extendify_site_id', '')),
167 'siteCreatedAt' => \esc_attr(SiteSettings::getSiteCreatedAt()),
168 'themeSlug' => \esc_attr(\get_option('stylesheet')),
169 'version' => \esc_attr(Config::$version),
170 'siteTitle' => \esc_attr(\get_bloginfo('name')),
171 'siteProfile' => \get_option('extendify_site_profile', []),
172 'wpLanguage' => \esc_attr(\get_locale()),
173 'wpVersion' => \esc_attr(\get_bloginfo('version')),
174 'isBlockTheme' => function_exists('wp_is_block_theme') ? (bool) wp_is_block_theme() : false,
175 'userId' => \esc_attr(\get_current_user_id()),
176 // phpcs:ignore WordPress.Security.NonceVerification
177 'userEmail' => isset($_GET['extendify-launch-success'])
178 ? \esc_attr(\wp_get_current_user()->user_email)
179 : null,
180 'partnerLogo' => \esc_attr(PartnerData::$logo),
181 'partnerId' => \esc_attr(PartnerData::$id),
182 'partnerName' => \esc_attr(PartnerData::$name),
183 'launchDataLegacy' => \wp_json_encode((UserSelectionController::get()->get_data() ?? [])),
184 'resourceData' => \wp_json_encode((new ResourceData())->getData()),
185 'showAIConsent' => isset($partnerData['showAIConsent']) ? (bool) $partnerData['showAIConsent'] : false,
186 'showChat' => (bool) (PartnerData::setting('showChat') || constant('EXTENDIFY_DEVMODE')),
187 'useAgentOnboarding' => (bool) (
188 PartnerData::setting('useAgentOnboarding') ||
189 Config::preview('agent-onboarding') ||
190 constant('EXTENDIFY_DEVMODE')
191 ),
192 'showAIPageCreation' => (bool) (
193 PartnerData::setting('showAIPageCreation') || constant('EXTENDIFY_DEVMODE')
194 ),
195 'showAILogo' => (bool) PartnerData::setting('showAILogo'),
196 'showImprint' => array_map('esc_attr', (array) PartnerData::setting('showImprint')),
197 'showProductActivation' => array_values($productActivationPlugins),
198 'consentTermsCustom' => \wp_kses((html_entity_decode(($partnerData['consentTermsCustom'] ?? ''))
199 ?? ''), $htmlAllowlist),
200 'userGaveConsent' => $userConsent ? (bool) $userConsent : false,
201 'installedPlugins' => array_map('esc_attr', array_keys(\get_plugins())),
202 'activePlugins' => $activePlugins,
203 'installedPluginsSlugs' => array_values(array_filter(array_map(function ($p) {
204 return $p['TextDomain'] ?? '';
205 }, \get_plugins()))),
206 'frontPage' => \esc_attr(\get_option('page_on_front', 0)),
207 'globalStylesPostID' => \esc_attr(\WP_Theme_JSON_Resolver::get_user_global_styles_post_id()),
208 'showLocalizedCopy' => (bool) array_key_exists('showLocalizedCopy', $partnerData),
209 'activity' => \wp_json_encode(\get_option('extendify_shared_activity', null)),
210 'showDraft' => isset($partnerData['showDraft']) ? (bool) $partnerData['showDraft'] : false,
211 'showLaunch' => Config::$showLaunch,
212 'phpVersion' => \esc_attr(PHP_VERSION),
213 'apexDomain' => PartnerData::setting('enableApexDomain')
214 ? rawurlencode(ApexDomain::getApexDomain(\get_home_url()))
215 : null,
216 'launchCompletedAt' => \esc_attr(\get_option('extendify_onboarding_completed', false)),
217 'showSiteQuestions' => (bool) (
218 PartnerData::setting('showLaunchQuestions') || Config::preview('launch-questions')
219 ),
220 'products' => ProductsData::get(),
221 'showAIAgents' => (bool) (PartnerData::setting('showAIAgents') || Config::preview('ai-agent')),
222 'pluginGroupId' => Escaper::recursiveEscAttr(PartnerData::setting('pluginGroupId')),
223 'adminPagesMenuList' => get_option('_transient_extendify_admin_pages_menu', []),
224 'globalState' => ImageGenerationController::get()->get_data(),
225 ]),
226 'before'
227 );
228
229 \wp_set_script_translations('extendify-common', 'extendify-local', EXTENDIFY_PATH . 'languages/js');
230 \wp_set_script_translations(
231 Config::$slug . '-shared-scripts',
232 'extendify-local',
233 EXTENDIFY_PATH . 'languages/js'
234 );
235
236 \wp_enqueue_style(
237 Config::$slug . '-shared-common-styles',
238 EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-shared.css'],
239 [],
240 Config::$version,
241 'all'
242 );
243 $cssColorVars = PartnerData::cssVariableMapping();
244 $cssString = implode('; ', array_map(function ($k, $v) {
245 return "$k: $v";
246 }, array_keys($cssColorVars), $cssColorVars));
247 \wp_add_inline_style(
248 Config::$slug . '-shared-common-styles',
249 wp_strip_all_tags(":root { $cssString; }")
250 );
251 }
252
253 /**
254 * Adds additional meta fields to post types
255 *
256 * @return void
257 */
258 public function addExtraMetaFields()
259 {
260 // Add a tag to pages that were made with Launch.
261 register_post_meta('page', 'made_with_extendify_launch', [
262 'single' => true,
263 'type' => 'boolean',
264 'show_in_rest' => true,
265 ]);
266 register_post_meta('post', 'made_with_extendify_launch', [
267 'single' => true,
268 'type' => 'boolean',
269 'show_in_rest' => true,
270 ]);
271 }
272
273 /**
274 * Records plugin search terms from the WordPress plugin search page
275 * Stores terms in the 'extendify_plugin_search_terms' option
276 *
277 * @return void
278 */
279 public function recordPluginsSearchTerms()
280 {
281 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification.Missing
282 $searchTerm = isset($_POST['s']) ? \sanitize_text_field(\wp_unslash(urldecode($_POST['s']))) : '';
283 if (empty($searchTerm)) {
284 return;
285 }
286
287 $searchTerms = \get_option('extendify_plugin_search_terms', []);
288 $searchTerms[] = $searchTerm;
289 $searchTerms = array_unique($searchTerms);
290
291 \update_option('extendify_plugin_search_terms', $searchTerms);
292 }
293
294 /**
295 * Records block search terms from the WordPress the editor's block search
296 * Stores terms in the 'extendify_block_search_terms' option
297 *
298 * @return void
299 */
300 public function recordBlocksSearchTerms()
301 {
302 // Exits early if it is not a REST API request.
303 if (!\wp_is_serving_rest_request()) {
304 return;
305 }
306
307 // Exits early if it is not a GET request.
308 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'GET') {
309 return;
310 }
311
312 $wp = $GLOBALS['wp'];
313
314 $restRoute = ($wp->query_vars['rest_route'] ?? '');
315 // Exits early if it's not the blocks search route.
316 if ($restRoute !== '/wp/v2/block-directory/search') {
317 return;
318 }
319
320 $searchTerm = \sanitize_text_field(\wp_unslash(($wp->query_vars['term'] ?? '')));
321 // Exits early if the search term is empty or is not user input.
322 if (empty($searchTerm) || str_starts_with($searchTerm, 'block:')) {
323 return;
324 }
325
326 // Get current search term from the url and merge it with existing search terms.
327 $searchTerms = \get_option('extendify_block_search_terms', []);
328 \update_option('extendify_block_search_terms', array_merge($searchTerms, [$searchTerm]));
329 }
330
331 /**
332 * Updates the user meta to disable the welcome guide from the Gutenberg editor
333 * and close the pattern modal.
334 *
335 * @return void
336 */
337 public function updateUserMeta()
338 {
339 $currentPreferences = get_user_meta(get_current_user_id(), 'wp_persisted_preferences', true);
340 if (!$currentPreferences) {
341 $currentPreferences = [];
342 }
343
344 $postPreferences = array_key_exists('core/edit-post', $currentPreferences)
345 ? $currentPreferences['core/edit-post']
346 : [];
347 $corePreferences = array_key_exists('core', $currentPreferences) ? $currentPreferences['core'] : [];
348
349 $newPreferences = array_merge($currentPreferences, [
350 'core/edit-post' => array_merge($postPreferences, ['welcomeGuide' => false]),
351 'core' => array_merge($corePreferences, ['enableChoosePatternModal' => false]),
352 '_modified' => wp_date('Y-m-d\TH:i:s.v\Z'),
353 ]);
354
355 update_user_meta(get_current_user_id(), 'wp_persisted_preferences', $newPreferences);
356 }
357
358 /**
359 * Records search terms used when browsing themes in the admin interface.
360 *
361 * This method listens for the 'query-themes' AJAX action, extracts the search term
362 * from the request, and stores it in the 'extendify_theme_search_terms' option.
363 * Duplicate terms are filtered out.
364 *
365 * @return void
366 */
367 public function recordThemesSearchTerms()
368 {
369 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification.Missing
370 $searchTerm = \sanitize_text_field(\wp_unslash(urldecode(($_POST['request']['search'] ?? ''))));
371 if (empty($searchTerm)) {
372 return;
373 }
374
375 $searchTerms = \get_option('extendify_theme_search_terms', []);
376 $searchTerms[] = $searchTerm;
377 $searchTerms = array_unique($searchTerms);
378
379 \update_option('extendify_theme_search_terms', $searchTerms);
380 }
381 }
382