PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
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
← All changes | app/PartnerData.php +129 -2 3.0.43.2.1 View file →
@@ -7,8 +7,9 @@
7 7 namespace Extendify;
8 8
9 9 defined('ABSPATH') || die('No direct access.');
10 10
11 +use Extendify\Constants;
11 12 use Extendify\Shared\Services\Sanitizer;
12 13
13 14 /**
14 15 * Controller for handling partner settings
@@ -53,10 +54,14 @@
53 54 'showDomainBanner' => false,
54 55 'showDomainTask' => false,
55 56 'showSecondaryDomainBanner' => false,
56 57 'showSecondaryDomainTask' => false,
58 + 'showPrimaryDomainRecommendationAgent' => false,
59 + 'showSecondaryDomainRecommendationAgent' => false,
57 60 'domainTLDs' => ['com', 'net'],
61 + 'priorityDomainTLDs' => [],
58 62 'stagingSites' => ['wordpress'],
63 + 'trialDomains' => [],
59 64 'domainSearchURL' => '',
60 65 'showDraft' => false,
61 66 'showChat' => false,
62 67 'showAIPageCreation' => false,
@@ -63,8 +68,9 @@
63 68 'enableImageImports-1-14-6' => false,
64 69 'disableLibraryAutoOpen' => false,
65 70 'enableApexDomain' => false,
66 71 'showLaunch' => false,
72 + 'showLaunchTitle' => false,
67 73 'deactivated' => true,
68 74 'launchRedirectWebsite' => false,
69 75 'showAILogo' => false,
70 76 'showProductRecommendations' => false,
@@ -74,14 +80,35 @@
74 80 'customProducts' => [],
75 81 ],
76 82 'license' => 'active',
77 83 'showAIAgents' => false,
84 + 'agentAbilitiesAllowlist' => [],
85 + 'showQuickEdit' => false,
86 + // Simple front-end Extendify toolbar (replaces WP core admin
87 + // bar for editors who prefer it). Default style is Launch-aware
88 + // (simple post-Launch, full before).
89 + 'showSimpleToolbar' => false,
78 90 'showImprint' => [],
79 91 'showLaunchQuestions' => false,
80 92 'pluginGroupId' => null,
81 93 'requiredPlugins' => null,
94 + 'showProductActivation' => [],
82 95 'useAgentOnboarding' => false,
83 96 'hidePluginNotifications' => false,
97 + 'hideLaunchExitLink' => false,
98 + 'useAutoUpdate' => false,
99 + 'showLaunchUpdate' => false,
100 + 'activeTests' => [],
101 + 'showExtendifyCode' => false,
102 + 'useComingSoon' => false,
103 + 'extendifyCodeData' => [
104 + 'link' => '',
105 + 'title' => '',
106 + 'message' => '',
107 + 'cta-primary' => '',
108 + ],
109 + 'customDesign' => null,
110 + 'strings' => [],
84 111 ];
85 112
86 113 // phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
87 114 /**
@@ -98,10 +125,17 @@
98 125 self::$config['showSecondaryDomainTask'] = ($data['showSecondaryDomainTask']
99 126 ?? self::$config['showSecondaryDomainTask']);
100 127 self::$config['showSecondaryDomainBanner'] = ($data['showSecondaryDomainBanner']
101 128 ?? self::$config['showSecondaryDomainBanner']);
129 + self::$config['showPrimaryDomainRecommendationAgent'] = ($data['showPrimaryDomainRecommendationAgent']
130 + ?? self::$config['showPrimaryDomainRecommendationAgent']);
131 + self::$config['showSecondaryDomainRecommendationAgent'] = ($data['showSecondaryDomainRecommendationAgent']
132 + ?? self::$config['showSecondaryDomainRecommendationAgent']);
102 133 self::$config['domainTLDs'] = ($data['domainTLDs'] ?? self::$config['domainTLDs']);
134 + self::$config['priorityDomainTLDs'] = ($data['priorityDomainTLDs']
135 + ?? self::$config['priorityDomainTLDs']);
103 136 self::$config['stagingSites'] = array_map('trim', ($data['stagingSites'] ?? self::$config['stagingSites']));
137 + self::$config['trialDomains'] = array_map('trim', ($data['trialDomains'] ?? self::$config['trialDomains']));
104 138 self::$config['domainSearchURL'] = ($data['domainSearchURL'] ?? self::$config['domainSearchURL']);
105 139 self::$logo = isset($data['logo'][0]['thumbnails']['large']['url'])
106 140 ? $data['logo'][0]['thumbnails']['large']['url']
107 141 : self::$logo;
@@ -120,8 +154,9 @@
120 154 'secondaryColorText' => '#ffffff',
121 155 ];
122 156 self::$config['showAIPageCreation'] = ($data['showAIPageCreation'] ?? self::$config['showAIPageCreation']);
123 157 self::$config['showLaunch'] = ($data['showLaunch'] ?? self::$config['showLaunch']);
158 + self::$config['showLaunchTitle'] = ($data['showLaunchTitle'] ?? self::$config['showLaunchTitle']);
124 159 self::$config['deactivated'] = ($data['deactivated'] ?? self::$config['deactivated']);
125 160 self::$config['launchRedirectWebsite'] = ($data['launchRedirectWebsite']
126 161 ?? self::$config['launchRedirectWebsite']);
127 162 self::$config['showAILogo'] = ($data['showAILogo'] ?? self::$config['showAILogo']);
@@ -138,13 +173,29 @@
138 173 self::$config['license'] = ($data['license'] ?? self::$config['license']);
139 174 self::$config['showImprint'] = ($data['showImprint'] ?? self::$config['showImprint']);
140 175 self::$config['showLaunchQuestions'] = ($data['showLaunchQuestions'] ?? self::$config['showLaunchQuestions']);
141 176 self::$config['showAIAgents'] = ($data['showAIAgents'] ?? self::$config['showAIAgents']);
177 + self::$config['agentAbilitiesAllowlist'] = ($data['agentAbilitiesAllowlist']
178 + ?? self::$config['agentAbilitiesAllowlist']);
179 + self::$config['showQuickEdit'] = ($data['showQuickEdit'] ?? self::$config['showQuickEdit']);
180 + self::$config['showSimpleToolbar'] = ($data['showSimpleToolbar']
181 + ?? self::$config['showSimpleToolbar']);
142 182 self::$config['pluginGroupId'] = ($data['pluginGroup'] ?? self::$config['pluginGroupId']);
143 183 self::$config['requiredPlugins'] = ($data['requiredPlugins'] ?? self::$config['requiredPlugins']);
184 + self::$config['showProductActivation'] = ($data['showProductActivation']
185 + ?? self::$config['showProductActivation']);
144 186 self::$config['useAgentOnboarding'] = ($data['useAgentOnboarding'] ?? self::$config['useAgentOnboarding']);
145 187 self::$config['hidePluginNotifications'] = ($data['hidePluginNotifications']
146 188 ?? self::$config['hidePluginNotifications']);
189 + self::$config['hideLaunchExitLink'] = ($data['hideLaunchExitLink'] ?? self::$config['hideLaunchExitLink']);
190 + self::$config['useAutoUpdate'] = ($data['useAutoUpdate'] ?? self::$config['useAutoUpdate']);
191 + self::$config['showLaunchUpdate'] = ($data['showLaunchUpdate'] ?? self::$config['showLaunchUpdate']);
192 + self::$config['activeTests'] = ($data['activeTests'] ?? self::$config['activeTests']);
193 + self::$config['showExtendifyCode'] = ($data['showExtendifyCode'] ?? self::$config['showExtendifyCode']);
194 + self::$config['useComingSoon'] = ($data['useComingSoon'] ?? self::$config['useComingSoon']);
195 + self::$config['extendifyCodeData'] = ($data['extendifyCodeData'] ?? self::$config['extendifyCodeData']);
196 + self::$config['customDesign'] = ($data['customDesign'] ?? self::$config['customDesign']);
197 + self::$config['strings'] = ($data['strings'] ?? self::$config['strings']);
147 198
148 199 // Add the job hook to fetch the partner data.
149 200 \add_action('extendify_fetch_partner_data', [self::class, 'fetchPartnerData']);
150 201 }
@@ -202,10 +253,11 @@
202 253 [
203 254 'partner' => self::$id,
204 255 'wp_language' => \get_locale(),
205 256 'site_url' => \home_url(),
257 + 'site_id' => \get_option('extendify_site_id', ''),
206 258 ],
207 - 'https://dashboard.extendify.com/api/onboarding/partner-data/'
259 + Constants::DASHBOARD_HOST . '/api/onboarding/partner-data/'
208 260 );
209 261
210 262 $response = \wp_safe_remote_get($url, ['headers' => ['Accept' => 'application/json']]);
211 263
@@ -227,9 +279,16 @@
227 279 }
228 280
229 281 $sanitizedData = array_merge(
230 282 Sanitizer::sanitizeUnknown($result['data']),
231 - ['consentTermsCustom' => \sanitize_text_field(htmlentities(($result['data']['consentTermsCustom'] ?? '')))]
283 + [
284 + 'consentTermsCustom' => \sanitize_text_field(htmlentities(
285 + ($result['data']['consentTermsCustom'] ?? ''),
286 + ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401
287 + )),
288 + 'customDesign' => self::sanitizeDesign($result['data']['customDesign'] ?? null),
289 + 'strings' => self::sanitizeStrings($result['data']['strings'] ?? null),
290 + ]
232 291 );
233 292
234 293 // Merge before persisting as this data is accessed directly elsewhere.
235 294 $mergedData = array_merge(self::$config, $sanitizedData);
@@ -235,8 +294,76 @@
235 294 $mergedData = array_merge(self::$config, $sanitizedData);
236 295 \update_option('extendify_partner_data_v2', $mergedData);
237 296
238 297 return $mergedData;
298 + }
299 +
300 + /**
301 + * Partner copy overriding the shipped strings.
302 + *
303 + * Which keys exist is the flow's to know, so only shape and text are checked here.
304 + *
305 + * @param mixed $strings The map as the partner-data response carried it.
306 + * @return array
307 + */
308 + public static function sanitizeStrings($strings)
309 + {
310 + return array_map(
311 + 'sanitize_text_field',
312 + self::designEntries($strings, '/^[a-zA-Z][a-zA-Z0-9]*$/', 'is_string')
313 + );
314 + }
315 +
316 + /**
317 + * A design carries GLSL, which the text sanitizers break, so only its shape is checked.
318 + *
319 + * Empty members are left out rather than kept: json_encode writes an empty
320 + * PHP array as [], and the page reads the design as an object.
321 + *
322 + * @param mixed $design The design as the partner-data response carried it.
323 + * @return array|null
324 + */
325 + private static function sanitizeDesign($design)
326 + {
327 + if (!is_array($design)) {
328 + return null;
329 + }
330 +
331 + $shader = ($design['shader'] ?? null);
332 + $logo = ($design['logo'] ?? null);
333 + $kept = array_filter([
334 + 'vars' => self::designEntries(
335 + ($design['vars'] ?? null),
336 + '/^--ext-(ui|tpl)-[a-z0-9-]+$/',
337 + function ($value) {
338 + // A CSS value is a string, but 0.88 is a natural way to write one.
339 + return is_string($value) || is_int($value) || is_float($value);
340 + }
341 + ),
342 + 'templates' => self::designEntries(($design['templates'] ?? null), '/^[A-Za-z0-9_-]+$/', 'is_string'),
343 + 'shader' => is_string($shader) ? \wp_check_invalid_utf8($shader) : '',
344 + 'logo' => is_string($logo) ? \esc_url_raw($logo) : '',
345 + ]);
346 +
347 + return $kept ?: null;
348 + }
349 +
350 + private static function designEntries($entries, $keyPattern, callable $accepts)
351 + {
352 + if (!is_array($entries)) {
353 + return [];
354 + }
355 +
356 + $kept = [];
357 + foreach ($entries as $key => $value) {
358 + if (!is_string($key) || !preg_match($keyPattern, $key) || !$accepts($value)) {
359 + continue;
360 + }
361 +
362 + $kept[$key] = is_string($value) ? \wp_check_invalid_utf8($value) : $value;
363 + }
364 +
365 + return $kept;
239 366 }
240 367
241 368 /**
242 369 * Return colors mapped as css variables