PluginProbe
Extendify / trunk
Extendify vtrunk
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 +124 -2 3.0.5 → trunk 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,11 +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'],
58 61 'priorityDomainTLDs' => [],
59 62 'stagingSites' => ['wordpress'],
63 + 'trialDomains' => [],
60 64 'domainSearchURL' => '',
61 65 'showDraft' => false,
62 66 'showChat' => false,
63 67 'showAIPageCreation' => false,
@@ -64,8 +68,9 @@
64 68 'enableImageImports-1-14-6' => false,
65 69 'disableLibraryAutoOpen' => false,
66 70 'enableApexDomain' => false,
67 71 'showLaunch' => false,
72 + 'showLaunchTitle' => false,
68 73 'deactivated' => true,
69 74 'launchRedirectWebsite' => false,
70 75 'showAILogo' => false,
71 76 'showProductRecommendations' => false,
@@ -75,15 +80,35 @@
75 80 'customProducts' => [],
76 81 ],
77 82 'license' => 'active',
78 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,
79 90 'showImprint' => [],
80 91 'showLaunchQuestions' => false,
81 92 'pluginGroupId' => null,
82 93 'requiredPlugins' => null,
94 + 'showProductActivation' => [],
83 95 'useAgentOnboarding' => false,
84 96 'hidePluginNotifications' => false,
85 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' => [],
86 111 ];
87 112
88 113 // phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
89 114 /**
@@ -100,12 +125,17 @@
100 125 self::$config['showSecondaryDomainTask'] = ($data['showSecondaryDomainTask']
101 126 ?? self::$config['showSecondaryDomainTask']);
102 127 self::$config['showSecondaryDomainBanner'] = ($data['showSecondaryDomainBanner']
103 128 ?? self::$config['showSecondaryDomainBanner']);
129 + self::$config['showPrimaryDomainRecommendationAgent'] = ($data['showPrimaryDomainRecommendationAgent']
130 + ?? self::$config['showPrimaryDomainRecommendationAgent']);
131 + self::$config['showSecondaryDomainRecommendationAgent'] = ($data['showSecondaryDomainRecommendationAgent']
132 + ?? self::$config['showSecondaryDomainRecommendationAgent']);
104 133 self::$config['domainTLDs'] = ($data['domainTLDs'] ?? self::$config['domainTLDs']);
105 134 self::$config['priorityDomainTLDs'] = ($data['priorityDomainTLDs']
106 135 ?? self::$config['priorityDomainTLDs']);
107 136 self::$config['stagingSites'] = array_map('trim', ($data['stagingSites'] ?? self::$config['stagingSites']));
137 + self::$config['trialDomains'] = array_map('trim', ($data['trialDomains'] ?? self::$config['trialDomains']));
108 138 self::$config['domainSearchURL'] = ($data['domainSearchURL'] ?? self::$config['domainSearchURL']);
109 139 self::$logo = isset($data['logo'][0]['thumbnails']['large']['url'])
110 140 ? $data['logo'][0]['thumbnails']['large']['url']
111 141 : self::$logo;
@@ -124,8 +154,9 @@
124 154 'secondaryColorText' => '#ffffff',
125 155 ];
126 156 self::$config['showAIPageCreation'] = ($data['showAIPageCreation'] ?? self::$config['showAIPageCreation']);
127 157 self::$config['showLaunch'] = ($data['showLaunch'] ?? self::$config['showLaunch']);
158 + self::$config['showLaunchTitle'] = ($data['showLaunchTitle'] ?? self::$config['showLaunchTitle']);
128 159 self::$config['deactivated'] = ($data['deactivated'] ?? self::$config['deactivated']);
129 160 self::$config['launchRedirectWebsite'] = ($data['launchRedirectWebsite']
130 161 ?? self::$config['launchRedirectWebsite']);
131 162 self::$config['showAILogo'] = ($data['showAILogo'] ?? self::$config['showAILogo']);
@@ -142,14 +173,29 @@
142 173 self::$config['license'] = ($data['license'] ?? self::$config['license']);
143 174 self::$config['showImprint'] = ($data['showImprint'] ?? self::$config['showImprint']);
144 175 self::$config['showLaunchQuestions'] = ($data['showLaunchQuestions'] ?? self::$config['showLaunchQuestions']);
145 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']);
146 182 self::$config['pluginGroupId'] = ($data['pluginGroup'] ?? self::$config['pluginGroupId']);
147 183 self::$config['requiredPlugins'] = ($data['requiredPlugins'] ?? self::$config['requiredPlugins']);
184 + self::$config['showProductActivation'] = ($data['showProductActivation']
185 + ?? self::$config['showProductActivation']);
148 186 self::$config['useAgentOnboarding'] = ($data['useAgentOnboarding'] ?? self::$config['useAgentOnboarding']);
149 187 self::$config['hidePluginNotifications'] = ($data['hidePluginNotifications']
150 188 ?? self::$config['hidePluginNotifications']);
151 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']);
152 198
153 199 // Add the job hook to fetch the partner data.
154 200 \add_action('extendify_fetch_partner_data', [self::class, 'fetchPartnerData']);
155 201 }
@@ -207,10 +253,11 @@
207 253 [
208 254 'partner' => self::$id,
209 255 'wp_language' => \get_locale(),
210 256 'site_url' => \home_url(),
257 + 'site_id' => \get_option('extendify_site_id', ''),
211 258 ],
212 - 'https://dashboard.extendify.com/api/onboarding/partner-data/'
259 + Constants::DASHBOARD_HOST . '/api/onboarding/partner-data/'
213 260 );
214 261
215 262 $response = \wp_safe_remote_get($url, ['headers' => ['Accept' => 'application/json']]);
216 263
@@ -232,9 +279,16 @@
232 279 }
233 280
234 281 $sanitizedData = array_merge(
235 282 Sanitizer::sanitizeUnknown($result['data']),
236 - ['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 + ]
237 291 );
238 292
239 293 // Merge before persisting as this data is accessed directly elsewhere.
240 294 $mergedData = array_merge(self::$config, $sanitizedData);
@@ -240,8 +294,76 @@
240 294 $mergedData = array_merge(self::$config, $sanitizedData);
241 295 \update_option('extendify_partner_data_v2', $mergedData);
242 296
243 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;
244 366 }
245 367
246 368 /**
247 369 * Return colors mapped as css variables