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 +84 -7 3.1.4 → trunk View file →
@@ -59,8 +59,9 @@
59 59 'showSecondaryDomainRecommendationAgent' => false,
60 60 'domainTLDs' => ['com', 'net'],
61 61 'priorityDomainTLDs' => [],
62 62 'stagingSites' => ['wordpress'],
63 + 'trialDomains' => [],
63 64 'domainSearchURL' => '',
64 65 'showDraft' => false,
65 66 'showChat' => false,
66 67 'showAIPageCreation' => false,
@@ -77,9 +78,8 @@
77 78 'showPartnerBranding' => false,
78 79 'disabledProducts' => [],
79 80 'customProducts' => [],
80 81 ],
81 - 'showPartnerNotifications' => false,
82 82 'license' => 'active',
83 83 'showAIAgents' => false,
84 84 'agentAbilitiesAllowlist' => [],
85 85 'showQuickEdit' => false,
@@ -98,8 +98,9 @@
98 98 'useAutoUpdate' => false,
99 99 'showLaunchUpdate' => false,
100 100 'activeTests' => [],
101 101 'showExtendifyCode' => false,
102 + 'useComingSoon' => false,
102 103 'extendifyCodeData' => [
103 104 'link' => '',
104 105 'title' => '',
105 106 'message' => '',
@@ -104,8 +105,10 @@
104 105 'title' => '',
105 106 'message' => '',
106 107 'cta-primary' => '',
107 108 ],
109 + 'customDesign' => null,
110 + 'strings' => [],
108 111 ];
109 112
110 113 // phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
111 114 /**
@@ -130,8 +133,9 @@
130 133 self::$config['domainTLDs'] = ($data['domainTLDs'] ?? self::$config['domainTLDs']);
131 134 self::$config['priorityDomainTLDs'] = ($data['priorityDomainTLDs']
132 135 ?? self::$config['priorityDomainTLDs']);
133 136 self::$config['stagingSites'] = array_map('trim', ($data['stagingSites'] ?? self::$config['stagingSites']));
137 + self::$config['trialDomains'] = array_map('trim', ($data['trialDomains'] ?? self::$config['trialDomains']));
134 138 self::$config['domainSearchURL'] = ($data['domainSearchURL'] ?? self::$config['domainSearchURL']);
135 139 self::$logo = isset($data['logo'][0]['thumbnails']['large']['url'])
136 140 ? $data['logo'][0]['thumbnails']['large']['url']
137 141 : self::$logo;
@@ -165,10 +169,8 @@
165 169 ?? self::$config['productRecommendations']['disabledProducts']),
166 170 'customProducts' => ($data['productRecommendationCustomSlugs']
167 171 ?? self::$config['productRecommendations']['customProducts']),
168 172 ];
169 - self::$config['showPartnerNotifications'] = ($data['showPartnerNotifications']
170 - ?? self::$config['showPartnerNotifications']);
171 173 self::$config['license'] = ($data['license'] ?? self::$config['license']);
172 174 self::$config['showImprint'] = ($data['showImprint'] ?? self::$config['showImprint']);
173 175 self::$config['showLaunchQuestions'] = ($data['showLaunchQuestions'] ?? self::$config['showLaunchQuestions']);
174 176 self::$config['showAIAgents'] = ($data['showAIAgents'] ?? self::$config['showAIAgents']);
@@ -188,9 +190,12 @@
188 190 self::$config['useAutoUpdate'] = ($data['useAutoUpdate'] ?? self::$config['useAutoUpdate']);
189 191 self::$config['showLaunchUpdate'] = ($data['showLaunchUpdate'] ?? self::$config['showLaunchUpdate']);
190 192 self::$config['activeTests'] = ($data['activeTests'] ?? self::$config['activeTests']);
191 193 self::$config['showExtendifyCode'] = ($data['showExtendifyCode'] ?? self::$config['showExtendifyCode']);
194 + self::$config['useComingSoon'] = ($data['useComingSoon'] ?? self::$config['useComingSoon']);
192 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']);
193 198
194 199 // Add the job hook to fetch the partner data.
195 200 \add_action('extendify_fetch_partner_data', [self::class, 'fetchPartnerData']);
196 201 }
@@ -274,12 +279,16 @@
274 279 }
275 280
276 281 $sanitizedData = array_merge(
277 282 Sanitizer::sanitizeUnknown($result['data']),
278 - ['consentTermsCustom' => \sanitize_text_field(htmlentities(
279 - ($result['data']['consentTermsCustom'] ?? ''),
280 - ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401
281 - ))]
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 + ]
282 291 );
283 292
284 293 // Merge before persisting as this data is accessed directly elsewhere.
285 294 $mergedData = array_merge(self::$config, $sanitizedData);
@@ -285,8 +294,76 @@
285 294 $mergedData = array_merge(self::$config, $sanitizedData);
286 295 \update_option('extendify_partner_data_v2', $mergedData);
287 296
288 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;
289 366 }
290 367
291 368 /**
292 369 * Return colors mapped as css variables