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 +129 -2 3.0.4 → 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,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