PluginProbe
Extendify / 3.2.0
Extendify v3.2.0
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 0.7.0 All 126 releases
extendify / bootstrap.php

bootstrap.php in Extendify 3.2.0, at bootstrap.php

297 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Bootstrap the application
5 */
6
7 defined('ABSPATH') || die('No direct access.');
8
9 use Extendify\AdminPageRouter;
10 use Extendify\Assist\Admin as AssistAdmin;
11 use Extendify\Agent\Admin as AgentAdmin;
12 use Extendify\Config;
13 use Extendify\Draft\Admin as DraftAdmin;
14 use Extendify\HelpCenter\Admin as HelpCenterAdmin;
15 use Extendify\Insights;
16 use Extendify\Launch\Admin as LaunchAdmin;
17 use Extendify\AutoLaunch\Admin as AutoLaunchAdmin;
18 use Extendify\Library\Admin as LibraryAdmin;
19 use Extendify\Library\Frontend as LibraryFrontend;
20 use Extendify\Agent\Frontend as AgentFrontend;
21 use Extendify\QuickEdit\Frontend as QuickEditFrontend;
22 use Extendify\PageCreator\Admin as PageCreatorAdmin;
23 use Extendify\PartnerData;
24 use Extendify\Notifications\Admin as NotificationsAdmin;
25 use Extendify\Notifications\Frontend as NotificationsFrontend;
26 use Extendify\Toolbar\Admin as ToolbarAdmin;
27 use Extendify\Toolbar\Frontend as ToolbarFrontend;
28 use Extendify\Recommendations\Admin as RecommendationsAdmin;
29 use Extendify\PluginNotifications\Admin as PluginNotificationsAdmin;
30 use Extendify\Shared\Admin as SharedAdmin;
31 use Extendify\Shared\DataProvider\NotificationData;
32 use Extendify\Shared\DataProvider\ResourceData;
33 use Extendify\Shared\Services\BlockStyleVariations;
34 use Extendify\Shared\Services\ForcePluginReinstall;
35 use Extendify\Shared\Services\Import\ImagesImporter;
36 use Extendify\Shared\Services\PluginRedirectDisabler;
37 use Extendify\Shared\Services\PluginsActivation\SimplyBook;
38 use Extendify\Shared\Services\VersionMigrator;
39
40 if (!defined('EXTENDIFY_REQUIRED_CAPABILITY')) {
41 define('EXTENDIFY_REQUIRED_CAPABILITY', 'manage_options');
42 }
43
44 if (!defined('EXTENDIFY_PATH')) {
45 define('EXTENDIFY_PATH', \plugin_dir_path(__FILE__));
46 }
47
48 if (!defined('EXTENDIFY_URL')) {
49 define('EXTENDIFY_URL', \plugin_dir_url(__FILE__));
50 }
51
52 if (is_readable(EXTENDIFY_PATH . 'vendor/autoload.php')) {
53 require EXTENDIFY_PATH . 'vendor/autoload.php';
54 }
55
56 // Registered after the autoloader — these callbacks reference classes it loads.
57 add_filter('http_request_args', function ($args, $url) {
58 $extendifyHosts = array_filter(array_map(function ($host) {
59 return wp_parse_url($host, PHP_URL_HOST);
60 }, \Extendify\Constants::serviceUrls()));
61
62 $host = wp_parse_url($url, PHP_URL_HOST);
63 if ($host && (str_contains($host, 'extendify') || in_array($host, $extendifyHosts, true))) {
64 $args['timeout'] = 45;
65 }
66
67 return $args;
68 }, 100, 2);
69
70 add_action('update_option', function ($option) {
71 if (in_array($option, ['WPLANG', 'blogname'], true)) {
72 \delete_transient('extendify_recommendations');
73 \delete_transient('extendify_domains');
74 \delete_transient('extendify_supportArticles');
75 }
76
77 // Delete the partner transient so we can fetch new data when the locale is switched.
78 if (($option === 'WPLANG') && get_transient('extendify_partner_data_cache_check')) {
79 delete_transient('extendify_partner_data_cache_check');
80 PartnerData::getPartnerData();
81 }
82 });
83
84 // Delete the partner transient so we can fetch new data when the locale is switched via WP-CLI.
85 add_action('cli_init', function () {
86 $command = sanitize_text_field(wp_unslash(($_SERVER['argv'][1] ?? '')));
87 if ($command === 'language' && get_transient('extendify_partner_data_cache_check')) {
88 delete_transient('extendify_partner_data_cache_check');
89 PartnerData::getPartnerData();
90 }
91 });
92
93 if (!defined('EXTENDIFY_IS_THEME_EXTENDABLE')) {
94 define('EXTENDIFY_IS_THEME_EXTENDABLE', get_option('stylesheet') === 'extendable');
95 }
96
97 (static function () {
98 // This file should have no dependencies and always load.
99 new LibraryFrontend();
100 // This file hooks into an external task and should always load.
101 new Insights();
102 // This class set up the image import check scheduler.
103 new ImagesImporter();
104
105 // Run various database updates depending on the plugin version.
106 new VersionMigrator();
107
108 // Force-reinstall support for /wp/v2/plugins (opt-in via request header).
109 ForcePluginReinstall::register();
110
111 // Keeps our design variation names registered so global styles can hold them.
112 BlockStyleVariations::register();
113
114 // Their registration callback comes in unauthenticated, so this cannot sit behind the capability gate.
115 SimplyBook::register();
116
117 // This class will fetch and cache partner data to be used
118 // throughout every class below. If opt in.
119 new PartnerData();
120
121 // Assign A/B test variants now that partner data is loaded.
122 Insights::setup((array) PartnerData::setting('activeTests'));
123
124 // Set up scheduled cache (if opt-in and active).
125 if (!PartnerData::setting('deactivated')) {
126 ResourceData::scheduleCache();
127 NotificationData::scheduleCache();
128 }
129
130 if (!current_user_can(EXTENDIFY_REQUIRED_CAPABILITY)) {
131 return;
132 }
133
134 // The config class will collect information about the
135 // partner and plugin, so it's easier to access.
136 new Config();
137 if (!defined('EXTENDIFY_DEVMODE')) {
138 define('EXTENDIFY_DEVMODE', Config::$environment === 'DEVELOPMENT');
139 }
140
141 // This is a global "loader" class that loads in any assets that are shared everywhere.
142 new SharedAdmin();
143 // This class will handle loading library assets.
144 new LibraryAdmin();
145 if (PartnerData::setting('hidePluginNotifications')) {
146 new PluginNotificationsAdmin();
147 }
148
149 // Only load these if the partner ID is set. These are all opt-in features.
150 if (!Config::$partnerId && !constant('EXTENDIFY_DEVMODE')) {
151 return;
152 }
153
154 if (PartnerData::setting('deactivated')) {
155 return;
156 }
157
158 // This class handles the admin pages required for the plugin.
159 new AdminPageRouter();
160 new PluginRedirectDisabler();
161
162 // This class will handle loading page creator assets.
163 if (PartnerData::setting('showAIPageCreation') || constant('EXTENDIFY_DEVMODE')) {
164 new PageCreatorAdmin();
165 }
166
167 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
168 $page = sanitize_text_field(wp_unslash($_GET['page'] ?? ''));
169
170 if ($page === 'extendify-launch') {
171 new LaunchAdmin();
172 return;
173 }
174
175 if ($page === 'extendify-auto-launch') {
176 new AutoLaunchAdmin();
177 return;
178 }
179
180 // The remaining classes handle loading assets for each individual products.
181 // They are essentially asset loading classes.
182 if ($page === 'extendify-assist') {
183 new AssistAdmin();
184 }
185
186 $extendifyShowAgent = constant('EXTENDIFY_IS_THEME_EXTENDABLE')
187 && Config::$launchCompleted
188 && (PartnerData::setting('showAIAgents') || Config::preview('ai-agent'));
189
190 // True when the AI Agent is actually loaded this request — the gate
191 // above, or dev mode (so it can be exercised locally). Quick Edit and
192 // the simple toolbar both build on the Agent, so they share this
193 // decision rather than gating themselves independently.
194 $extendifyLoadAgent = $extendifyShowAgent || constant('EXTENDIFY_DEVMODE');
195
196 if (!$extendifyShowAgent) {
197 new HelpCenterAdmin();
198 }
199
200 if (PartnerData::setting('showProductRecommendations') || constant('EXTENDIFY_DEVMODE')) {
201 new RecommendationsAdmin();
202 }
203
204 new NotificationsAdmin();
205 new NotificationsFrontend();
206
207 if (PartnerData::setting('showDraft') || constant('EXTENDIFY_DEVMODE')) {
208 new DraftAdmin();
209 }
210
211 if ($extendifyLoadAgent) {
212 new AgentAdmin();
213 new AgentFrontend();
214 }
215
216 if ($extendifyLoadAgent) {
217 new QuickEditFrontend();
218 }
219
220 // Simple Extendify toolbar — replaces the WordPress core admin
221 // bar on the front end with a minimal AI Agent / Quick Edit /
222 // Edit / WP Admin bar. The user-profile "Toolbar Style" setting
223 // controls per-user override; the default is Launch-aware
224 // ('simple' post-Launch, 'full' pre-Launch). Gated on the Agent
225 // too: the toolbar's "AI Agent" button drives the Agent's mounted
226 // admin-bar button, so without the Agent it would present a dead
227 // button — never ship the toolbar unless the Agent is there.
228 if ($extendifyLoadAgent && (PartnerData::setting('showSimpleToolbar') || Config::preview('simple-toolbar'))) {
229 new ToolbarAdmin();
230 new ToolbarFrontend();
231 }
232 })();
233
234 (static function () {
235 if (!current_user_can(EXTENDIFY_REQUIRED_CAPABILITY)) {
236 return;
237 }
238
239 // This loads in all the REST API routes used by the plugin.
240 require EXTENDIFY_PATH . 'routes/api.php';
241 })();
242
243 // This file is used to update the plugin and removed before w.org release.
244 if (is_readable(EXTENDIFY_PATH . '/updater.php')) {
245 require EXTENDIFY_PATH . 'updater.php';
246 }
247
248 add_action('init', function () {
249 load_plugin_textdomain('extendify-local', false, dirname(plugin_basename(__FILE__)) . '/languages/php');
250 });
251
252 // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
253 add_filter('load_script_translation_file', function ($file, $handle, $domain) {
254 if ($domain !== 'extendify-local') {
255 return $file;
256 }
257
258 return extendifyResolveFallbackLocaleFile($file);
259 }, 10, 3);
260
261 add_filter('load_textdomain_mofile', function ($mofile, $domain) {
262 if ($domain !== 'extendify-local') {
263 return $mofile;
264 }
265
266 return extendifyResolveFallbackLocaleFile($mofile);
267 }, 10, 2);
268
269 /**
270 * Resolves a fallback translation file path if the requested locale is unsupported.
271 *
272 * This function checks if the current file path includes an unsupported locale
273 * and replaces it with a supported fallback (e.g. es_AR → es_ES),
274 * as long as the fallback file exists on disk.
275 *
276 * @param string $filepath The original .mo or .json translation file path.
277 * @return string The fallback file path if it exists, or the original one.
278 */
279
280 function extendifyResolveFallbackLocaleFile($filepath)
281 {
282 if (str_contains($filepath, '-es_') && !file_exists($filepath)) {
283 $fallbackFile = preg_replace('/-es_[A-Z]{2}/', '-es_ES', $filepath);
284 if ($fallbackFile && file_exists($fallbackFile)) {
285 return $fallbackFile;
286 }
287 }
288
289 return $filepath;
290 }
291
292 // To cover legacy conflicts.
293 // phpcs:ignore
294 class ExtendifySdk
295 {
296 }
297