PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / admin / satellite-plugins.php

satellite-plugins.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/admin/satellite-plugins.php

414 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Satellite (free companion) plugin teasers.
4 *
5 * StoreEngine keeps the frameworks (payment-gateway registry, courier/dropship
6 * frameworks) in core while the concrete providers ship in free companion
7 * plugins — StoreEngine Payments and StoreEngine Connectors. When a companion
8 * plugin is NOT active, its provider filters never run, so the admin has no way
9 * to discover what those plugins unlock. This class supplies a small, filterable
10 * catalog of "what you'd get" plus the plugin's install/download state, which
11 * the React admin renders as an install-only teaser card. Once the companion
12 * plugin is active, the teaser disappears and the real provider UI takes over.
13 *
14 * @package StoreEngine\Admin
15 */
16
17 namespace StoreEngine\Admin;
18
19 use StoreEngine\Utils\Helper;
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit;
23 }
24
25 class SatellitePlugins {
26
27 /**
28 * StoreEngine Payments — plugin basename + store product id + free download URL.
29 */
30 const PAYMENTS_BASENAME = 'storeengine-payments/storeengine-payments.php';
31 const PAYMENTS_PRODUCT_ID = 369;
32 const PAYMENTS_DOWNLOAD_URL = 'https://store.kodezen.com/se-download/free/storeengine-payments/latest/';
33
34 /**
35 * StoreEngine Connectors — plugin basename + store product id + free download URL.
36 */
37 const CONNECTORS_BASENAME = 'storeengine-connectors/storeengine-connectors.php';
38 const CONNECTORS_PRODUCT_ID = 386;
39 const CONNECTORS_DOWNLOAD_URL = 'https://store.kodezen.com/se-download/free/storeengine-connectors/latest/';
40
41 /**
42 * StoreEngine Bricks Addons — page-builder companion (no provider catalog).
43 */
44 const BRICKS_BASENAME = 'storeengine-bricks-addons/storeengine-bricks-addons.php';
45 const BRICKS_PRODUCT_ID = 171;
46 const BRICKS_DOWNLOAD_URL = 'https://store.kodezen.com/se-download/free/storeengine-bricks-addons/latest/';
47
48 /**
49 * StoreEngine Elementor Addons — page-builder companion (no provider catalog).
50 */
51 const ELEMENTOR_BASENAME = 'storeengine-elementor-addons/storeengine-elementor-addons.php';
52 const ELEMENTOR_PRODUCT_ID = 160;
53 const ELEMENTOR_DOWNLOAD_URL = 'https://store.kodezen.com/se-download/free/storeengine-elementor-addons/latest/';
54
55 /**
56 * Static metadata for every external ("satellite") companion plugin that the
57 * Add-ons screen can offer to download / one-click install. Live install
58 * state (active/installed) is layered on in get_teaser_data().
59 *
60 * `kind`:
61 * 'satellite' — registers providers into StoreEngine core (Payments,
62 * Connectors); ships an `items` catalog of what it unlocks
63 * and a `settings` route to manage it once active.
64 * 'plugin' — self-contained page-builder companion (Bricks, Elementor);
65 * no in-core catalog, links out to its docs instead.
66 *
67 * @return array<string, array>
68 */
69 protected static function definitions(): array {
70 return [
71 'payments' => [
72 'kind' => 'satellite',
73 'basename' => self::PAYMENTS_BASENAME,
74 'name' => __( 'StoreEngine Payments', 'storeengine' ),
75 'product_id' => self::PAYMENTS_PRODUCT_ID,
76 'download_url' => self::payments_download_url(),
77 'details' => __( 'Extra payment gateways — Paystack, Square and more — behind one lightweight companion plugin.', 'storeengine' ),
78 'icon' => 'money-receive',
79 'color' => '#5a6ff0',
80 'category' => 'payments',
81 'docs_url' => '',
82 'requires' => '',
83 // Where the "Manage" action jumps once the plugin is active.
84 'settings' => [ 'page' => 'storeengine-settings', 'path' => 'payment-method' ],
85 'items' => [
86 [
87 'group' => __( 'Payment gateways', 'storeengine' ),
88 'list' => self::payment_methods_catalog(),
89 ],
90 ],
91 ],
92 'connectors' => [
93 'kind' => 'satellite',
94 'basename' => self::CONNECTORS_BASENAME,
95 'name' => __( 'StoreEngine Connectors', 'storeengine' ),
96 'product_id' => self::CONNECTORS_PRODUCT_ID,
97 'download_url' => self::connectors_download_url(),
98 'details' => __( 'Courier / shipping partners and dropshipping suppliers, ready to connect.', 'storeengine' ),
99 'icon' => 'shipping',
100 'color' => '#0ea5a3',
101 'category' => 'shipping',
102 'docs_url' => '',
103 'requires' => '',
104 'settings' => [ 'page' => 'storeengine-settings', 'path' => 'couriers' ],
105 'items' => [
106 [
107 'group' => __( 'Courier & shipping partners', 'storeengine' ),
108 'list' => self::courier_catalog(),
109 ],
110 [
111 'group' => __( 'Dropshipping suppliers', 'storeengine' ),
112 'list' => self::dropship_catalog(),
113 ],
114 ],
115 ],
116 'bricks-addons' => [
117 'kind' => 'plugin',
118 'basename' => self::BRICKS_BASENAME,
119 'name' => __( 'StoreEngine Bricks Addons', 'storeengine' ),
120 'product_id' => self::BRICKS_PRODUCT_ID,
121 'download_url' => self::bricks_download_url(),
122 'details' => __( 'StoreEngine elements for the Bricks site builder.', 'storeengine' ),
123 'icon' => 'store',
124 'color' => '#f0663a',
125 'category' => 'tools',
126 'docs_url' => 'https://store.kodezen.com/product/storeengine-bricks-addons/',
127 'requires' => __( 'Bricks Builder', 'storeengine' ),
128 'settings' => null,
129 'items' => [],
130 ],
131 'elementor-addons' => [
132 'kind' => 'plugin',
133 'basename' => self::ELEMENTOR_BASENAME,
134 'name' => __( 'StoreEngine Elementor Addons', 'storeengine' ),
135 'product_id' => self::ELEMENTOR_PRODUCT_ID,
136 'download_url' => self::elementor_download_url(),
137 'details' => __( 'StoreEngine widgets for the Elementor page builder.', 'storeengine' ),
138 'icon' => 'store',
139 'color' => '#e0295a',
140 'category' => 'tools',
141 'docs_url' => 'https://store.kodezen.com/product/storeengine-elementor-addons/',
142 'requires' => __( 'Elementor', 'storeengine' ),
143 'settings' => null,
144 'items' => [],
145 ],
146 ];
147 }
148
149 /**
150 * Companion plugin keys accepted by install_and_activate() → their install
151 * metadata (basename, download URL, display name). Central so the ajax
152 * endpoint never trusts a client-supplied URL — the key is the only input.
153 *
154 * @return array<string, array{basename:string, url:string, name:string}>
155 */
156 protected static function registry(): array {
157 $registry = [];
158 foreach ( self::definitions() as $key => $def ) {
159 $registry[ $key ] = [
160 'basename' => $def['basename'],
161 'url' => $def['download_url'],
162 'name' => $def['name'],
163 ];
164 }
165
166 return $registry;
167 }
168
169 /**
170 * Valid companion keys — the only accepted input to the install endpoint.
171 *
172 * @return string[]
173 */
174 public static function get_keys(): array {
175 return array_keys( self::definitions() );
176 }
177
178 /**
179 * Where the "Install StoreEngine Payments" teaser downloads from. Filterable
180 * so a site can repoint it (e.g. to a future WordPress.org listing).
181 */
182 public static function payments_download_url(): string {
183 return apply_filters( 'storeengine/satellite/payments_download_url', self::PAYMENTS_DOWNLOAD_URL );
184 }
185
186 /**
187 * Where the "Install StoreEngine Connectors" teaser downloads from. Filterable.
188 */
189 public static function connectors_download_url(): string {
190 return apply_filters( 'storeengine/satellite/connectors_download_url', self::CONNECTORS_DOWNLOAD_URL );
191 }
192
193 /**
194 * Where the "Install StoreEngine Bricks Addons" teaser downloads from. Filterable.
195 */
196 public static function bricks_download_url(): string {
197 return apply_filters( 'storeengine/satellite/bricks_download_url', self::BRICKS_DOWNLOAD_URL );
198 }
199
200 /**
201 * Where the "Install StoreEngine Elementor Addons" teaser downloads from. Filterable.
202 */
203 public static function elementor_download_url(): string {
204 return apply_filters( 'storeengine/satellite/elementor_download_url', self::ELEMENTOR_DOWNLOAD_URL );
205 }
206
207 /**
208 * Teaser payload for every companion plugin, localized into
209 * `StoreEngineGlobal.satellite_plugins` for the React admin. Each entry is
210 * the static definition plus live `active` / `installed` state and its `key`.
211 *
212 * @return array<string, array>
213 */
214 public static function get_teaser_data(): array {
215 $data = [];
216 foreach ( self::definitions() as $key => $def ) {
217 $data[ $key ] = array_merge(
218 $def,
219 [
220 'key' => $key,
221 'active' => self::is_plugin_active( $def['basename'] ),
222 'installed' => self::is_plugin_installed( $def['basename'] ),
223 ]
224 );
225 }
226
227 /**
228 * Filter the full satellite-plugin teaser payload.
229 *
230 * @param array $data Teaser data keyed by plugin.
231 */
232 return apply_filters( 'storeengine/admin/satellite_plugins', $data );
233 }
234
235 /**
236 * Download, install, and activate a companion plugin from the StoreEngine
237 * store — the one-click path behind the teaser button.
238 *
239 * The plugin is resolved from $key against the server-side registry(), so the
240 * package URL is never taken from the client. Idempotent: a plugin already
241 * active short-circuits; installed-but-inactive is just activated; otherwise
242 * it is downloaded from the free store URL and activated.
243 *
244 * @param string $key 'payments' | 'connectors'.
245 *
246 * @return array{status:string, message:string, plugin:string}|\WP_Error
247 */
248 public static function install_and_activate( string $key ) {
249 $registry = self::registry();
250 if ( ! isset( $registry[ $key ] ) ) {
251 return new \WP_Error( 'storeengine_unknown_satellite', __( 'Unknown plugin.', 'storeengine' ), [ 'status' => 400 ] );
252 }
253
254 if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
255 return new \WP_Error( 'storeengine_cannot_install', __( 'You do not have permission to install plugins.', 'storeengine' ), [ 'status' => 403 ] );
256 }
257
258 $basename = $registry[ $key ]['basename'];
259 $url = $registry[ $key ]['url'];
260 $name = $registry[ $key ]['name'];
261
262 require_once ABSPATH . 'wp-admin/includes/file.php';
263 require_once ABSPATH . 'wp-admin/includes/misc.php';
264 require_once ABSPATH . 'wp-admin/includes/plugin.php';
265 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
266
267 // Already active — nothing to do.
268 if ( self::is_plugin_active( $basename ) ) {
269 return [ 'status' => 'active', 'plugin' => $key, 'message' => sprintf( /* translators: %s: plugin name. */ __( '%s is already active.', 'storeengine' ), $name ) ];
270 }
271
272 // Not installed yet → download + install from the free store URL.
273 if ( ! self::is_plugin_installed( $basename ) ) {
274 if ( ! WP_Filesystem() ) {
275 return new \WP_Error( 'storeengine_fs_unavailable', __( 'WordPress could not access the filesystem to install the plugin. Please download and install it manually.', 'storeengine' ), [ 'status' => 500 ] );
276 }
277
278 $skin = new \Automatic_Upgrader_Skin();
279 $upgrader = new \Plugin_Upgrader( $skin );
280 $result = $upgrader->install( $url );
281
282 if ( is_wp_error( $result ) ) {
283 return $result;
284 }
285
286 if ( true !== $result ) {
287 $messages = method_exists( $skin, 'get_errors' ) && is_wp_error( $skin->get_errors() ) ? $skin->get_errors()->get_error_message() : '';
288 return new \WP_Error(
289 'storeengine_install_failed',
290 $messages ? $messages : __( 'The plugin could not be installed. Please download and install it manually.', 'storeengine' ),
291 [ 'status' => 500 ]
292 );
293 }
294
295 // The upgrader wrote new files — refresh the plugin list before activating.
296 wp_clean_plugins_cache();
297 }
298
299 if ( ! self::is_plugin_installed( $basename ) ) {
300 return new \WP_Error( 'storeengine_install_missing', __( 'The plugin was downloaded but its main file was not found. Please install it manually.', 'storeengine' ), [ 'status' => 500 ] );
301 }
302
303 $activated = activate_plugin( $basename );
304 if ( is_wp_error( $activated ) ) {
305 return $activated;
306 }
307
308 return [
309 'status' => 'installed',
310 'plugin' => $key,
311 'message' => sprintf( /* translators: %s: plugin name. */ __( '%s installed and activated.', 'storeengine' ), $name ),
312 ];
313 }
314
315 /**
316 * Providers unlocked by StoreEngine Payments. Kept in sync with the plugin's
317 * `src/providers.php`; filterable so the plugin (or a site) can extend it.
318 *
319 * @return array<int, array{label:string, details:string}>
320 */
321 protected static function payment_methods_catalog(): array {
322 return apply_filters( 'storeengine/admin/satellite_payments_catalog', [
323 [
324 'label' => 'Paystack',
325 'details' => __( 'Card, bank transfer, USSD and mobile-money payments across Africa.', 'storeengine' ),
326 ],
327 [
328 'label' => 'Square',
329 'details' => __( 'Card payments via the Square Web Payments SDK — card data never touches your server.', 'storeengine' ),
330 ],
331 ] );
332 }
333
334 /**
335 * Courier / shipping partners unlocked by StoreEngine Connectors.
336 *
337 * @return array<int, array{label:string, details:string}>
338 */
339 protected static function courier_catalog(): array {
340 return apply_filters( 'storeengine/admin/satellite_courier_catalog', [
341 [
342 'label' => 'Pathao',
343 'details' => __( 'Push orders and auto-poll delivery status.', 'storeengine' ),
344 ],
345 [
346 'label' => 'Steadfast',
347 'details' => __( 'One-click consignment creation and tracking sync.', 'storeengine' ),
348 ],
349 [
350 'label' => 'Shiprocket',
351 'details' => __( 'Multi-carrier shipping and tracking.', 'storeengine' ),
352 ],
353 ] );
354 }
355
356 /**
357 * Dropshipping suppliers unlocked by StoreEngine Connectors.
358 *
359 * @return array<int, array{label:string, details:string}>
360 */
361 protected static function dropship_catalog(): array {
362 return apply_filters( 'storeengine/admin/satellite_dropship_catalog', [
363 [
364 'label' => 'AliExpress',
365 'details' => __( 'Import products and sync inventory from the marketplace.', 'storeengine' ),
366 ],
367 [
368 'label' => 'CJ Dropshipping',
369 'details' => __( 'Source and fulfil from the CJ supplier network.', 'storeengine' ),
370 ],
371 [
372 'label' => 'Spocket',
373 'details' => __( 'Curated US/EU suppliers with fast shipping.', 'storeengine' ),
374 ],
375 [
376 'label' => 'Zendrop',
377 'details' => __( 'Curated US suppliers and auto-fulfilment.', 'storeengine' ),
378 ],
379 [
380 'label' => 'Printful',
381 'details' => __( 'Print-on-demand products and fulfilment.', 'storeengine' ),
382 ],
383 [
384 'label' => 'Printify',
385 'details' => __( 'Print-on-demand catalog across multiple providers.', 'storeengine' ),
386 ],
387 ] );
388 }
389
390 protected static function is_plugin_active( string $basename ): bool {
391 if ( method_exists( Helper::class, 'is_plugin_active' ) ) {
392 return Helper::is_plugin_active( $basename );
393 }
394
395 if ( ! function_exists( 'is_plugin_active' ) ) {
396 include_once ABSPATH . 'wp-admin/includes/plugin.php';
397 }
398
399 return is_plugin_active( $basename );
400 }
401
402 protected static function is_plugin_installed( string $basename ): bool {
403 if ( method_exists( Helper::class, 'is_plugin_installed' ) ) {
404 return (bool) Helper::is_plugin_installed( $basename );
405 }
406
407 if ( ! function_exists( 'get_plugins' ) ) {
408 include_once ABSPATH . 'wp-admin/includes/plugin.php';
409 }
410
411 return array_key_exists( $basename, get_plugins() );
412 }
413 }
414