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