PluginProbe ʕ •ᴥ•ʔ
Gallery : FooGallery / 3.2.6
Gallery : FooGallery v3.2.6
3.3.2 3.3.3 3.3.0 3.1.31 3.1.32 3.1.34 3.1.36 3.2.0 3.2.6 trunk 1.10.3 2.0.24 2.1.34 2.2.44 2.3.3 2.4.32 3.0.6 3.1.11 3.1.12 3.1.13 3.1.20 3.1.25 3.1.26 3.1.26.1 3.1.26.2
foogallery / includes / extensions / class-extensions-api.php
foogallery / includes / extensions Last commit date
class-extension.php 4 weeks ago class-extensions-api.php 4 weeks ago class-extensions-loader.php 4 weeks ago
class-extensions-api.php
706 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * @TODO
9 */
10
11 if ( ! class_exists( 'FooGallery_Extensions_API' ) ) {
12
13 define( 'FOOGALLERY_EXTENSIONS_MESSAGE_TRANSIENT_KEY', 'foogallery_extensions_message' );
14 define( 'FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY', 'foogallery_extensions_activated' );
15 define( 'FOOGALLERY_EXTENSIONS_ERRORS_OPTIONS_KEY', 'foogallery_extensions_errors' );
16 define( 'FOOGALLERY_EXTENSIONS_AUTO_ACTIVATED_OPTIONS_KEY', 'foogallery_extensions_auto_activated' );
17 define( 'FOOGALLERY_EXTENSIONS_OVERRIDES_OPTIONS_KEY', 'foogallery_extensions_overrides' );
18
19 /**
20 * FooGallery Extensions Manager Class
21 * Class FooGallery_Extensions_API
22 */
23 class FooGallery_Extensions_API {
24
25 /**
26 * Internal list of all extensions
27 * @var array
28 */
29 private $extensions = false;
30
31 /**
32 * Internal list of all extension slugs
33 * @var array
34 */
35 private $extension_slugs = false;
36
37 /**
38 * Extension API constructor
39 * @param bool $load
40 */
41 function __construct( $load = false ) {
42 if ( $load ) {
43 $this->load_available_extensions();
44 }
45 }
46
47 /**
48 * Reset all previous errors
49 */
50 public function reset_errors() {
51 //delete_option( FOOGALLERY_EXTENSIONS_LOADING_ERRORS );
52 //delete_option( FOOGALLERY_EXTENSIONS_LOADING_ERRORS_RESPONSE );
53 }
54
55 /**
56 * Load all available extensions
57 */
58 private function load_available_extensions() {
59 $this->extensions = array();
60
61 $this->extensions[] = array(
62 'slug' => 'albums',
63 'class' => 'FooGallery_Albums_Extension',
64 'title' => 'Albums',
65 'categories' => array( 'Featured', 'Free' ),
66 'description' => foogallery__( 'Group your galleries into albums. Albums comes with 2 unique album templates to showcase your galleries.', 'foogallery' ),
67 'external_link_text' => foogallery__( 'Read documentation', 'foogallery' ),
68 'external_link_url' => 'https://fooplugins.com/documentation/foogallery/getting-started-foogallery/adding-albums/',
69 'dashicon' => 'dashicons-book-alt',
70 'tags' => array( 'functionality', 'free', ),
71 'source' => 'bundled'
72 );
73
74 // The FooGallery Migrate feature.
75 $this->extensions[] = array(
76 'slug' => 'foogallery-migrate',
77 'class' => 'FooGallery_Migrate_Dummy',
78 'categories' => array( 'Free' ),
79 'title' => 'FooGallery Migrate',
80 'file' => 'migrate.php',
81 'description' => foogallery__( 'Migrate to FooGallery from other gallery plugins', 'foogallery' ),
82 'external_link_text' => foogallery__( 'Read about FooGallery Migrate', 'foogallery' ),
83 'external_link_url' => 'https://fooplugins.com/foogallery-migrate-for-wordpress-galleries/',
84 'dashicon' => 'dashicons-migrate',
85 'tags' => array( 'tools', 'free', ),
86 'source' => 'repo',
87 'download_link' => 'https://downloads.wordpress.org/plugin/foogallery-migrate.latest-stable.zip',
88 );
89 }
90
91 /**
92 * Get all loaded extensions
93 * @return array
94 */
95 function get_all() {
96
97 //check if we need to load
98 if ( false === $this->extensions ) {
99 $this->load_available_extensions();
100 }
101
102 //get any extra extensions from plugins
103 $extra_extensions = apply_filters( 'foogallery_available_extensions', array() );
104
105 if ( count( $extra_extensions ) > 0 ) {
106 //get a list of slugs so we can determine duplicates!
107 $this->extension_slugs = array();
108 foreach ( $this->extensions as $extension ) {
109 $this->extension_slugs[] = $extension['slug'];
110 }
111
112 //only add if not a duplicate
113 foreach ( $extra_extensions as $extension ) {
114 if ( ! in_array( $extension['slug'], $this->extension_slugs ) ) {
115 $this->extensions[] = $extension;
116 $this->extension_slugs[] = $extension['slug'];
117 }
118 }
119 }
120
121 return $this->extensions;
122 }
123
124
125 /**
126 * return a list of all extensions for the extension view.
127 * This list could be changed based on other plugin
128 */
129 function get_all_for_view() {
130 $extensions = array();
131
132 //add all extensions to an array using the slug as the array key
133 foreach ( $this->get_all() as &$extension ) {
134 $active = $this->is_active( $extension['slug'], true );
135 $extension['downloaded'] = $active || $this->is_downloaded( $extension );
136 $extension['is_active'] = $active;
137 $extension['has_errors'] = $this->has_errors( $extension['slug'] );
138
139 $extensions[ $extension['slug'] ] = $extension;
140 }
141
142 $extensions = apply_filters( 'foogallery_extensions_for_view', $extensions );
143
144 return $extensions;
145 }
146 /**
147 * Get all loaded extensions slugs
148 * @return array
149 */
150 function get_all_slugs() {
151 //load all extensions first!
152 $this->get_all();
153
154 return $this->extension_slugs;
155 }
156
157 /**
158 * @TODO
159 * @param $slug
160 *
161 * @return bool
162 */
163 public function get_extension( $slug ) {
164 foreach ( $this->get_all() as $extension ) {
165 if ( $extension['slug'] === $slug ) {
166 return $extension;
167 }
168 }
169 return false;
170 }
171
172 /**
173 * @TODO
174 * @param $file
175 *
176 * @return bool
177 */
178 public function get_extension_by_file( $file ) {
179 $file = basename( $file ); //normalize to just the filename
180
181 foreach ( $this->get_all() as $extension ) {
182 if ( foo_safe_get( $extension, 'file' ) === $file ) {
183 return $extension;
184 }
185 }
186 return false;
187 }
188
189 /**
190 * @TODO
191 * @param $slug
192 *
193 * @return bool
194 */
195 public function is_active( $slug ) {
196 $overrides = $this->get_overrides();
197 if ( array_key_exists( $slug, $overrides ) ) {
198 return $overrides[$slug] === 'active';
199 }
200
201 $active_extensions = $this->get_active_extensions();
202 if ( array_key_exists( $slug, $active_extensions ) ) {
203 //it has been previously activated through the extensions page
204 return true;
205 }
206
207 $extension = $this->get_extension( $slug );
208
209 //if we have an 'plugin_active_class' attribute and that class exists, it means our plugin must be active
210 if ( isset( $extension['plugin_active_class'] ) ) {
211 if ( class_exists( $extension['plugin_active_class'] ) ) {
212 return true;
213 }
214 }
215
216 //if we have an 'activated_by_default' attribute and it is true, it means the extension is active
217 if ( isset( $extension['activated_by_default'] ) && $extension['activated_by_default'] ) {
218 return true;
219 }
220
221 //if we cannot find the extension class in memory, then check to see if the extension plugin is activated
222 if ( isset( $extension['perform_plugin_active_check'] ) && true === $extension['perform_plugin_active_check'] &&
223 isset( $extension['file'] ) ) {
224 $plugin = $this->find_active_wordpress_plugin( $extension );
225
226 return $plugin !== false;
227 }
228
229 return false;
230 }
231
232 /**
233 * @TODO
234 *
235 * @param bool $extension
236 *
237 * @param bool $slug
238 *
239 * @return bool
240 */
241 public function is_downloaded( $extension = false, $slug = false ) {
242 //allow you to pass in a slug rather
243 if ( ! $extension && $slug !== false ) {
244 $extension = $this->get_extension( $slug );
245 }
246 if ( $extension ) {
247 //first check if the class exists
248 if ( class_exists( $extension['class'] ) ) {
249 return true;
250 }
251
252 //next fallback to see if a plugin exists that has the same file name
253 $plugin = $this->find_wordpress_plugin( $extension );
254 return false !== $plugin;
255 }
256 return false;
257 }
258
259 /**
260 * @TODO
261 * @param $slug
262 *
263 * @return bool
264 */
265 public function has_errors( $slug ) {
266 $error_extensions = $this->get_error_extensions();
267
268 if ( $error_extensions ) {
269 return array_key_exists( $slug, $error_extensions );
270 }
271 return false;
272 }
273
274 /**
275 * @TODO
276 * @param $plugin
277 */
278 public function handle_wordpress_plugin_deactivation( $plugin ) {
279 $extension = $this->get_extension_by_file( $plugin );
280 if ( $extension ) {
281 //we have found a matching extension
282 $this->deactivate( $extension['slug'], false );
283 }
284 }
285
286 /**
287 * @TODO
288 * @param $plugin
289 */
290 public function handle_wordpress_plugin_activation( $plugin ) {
291 $extension = $this->get_extension_by_file( $plugin );
292 if ( $extension ) {
293 //we have found a matching extension
294 $this->activate( $extension['slug'], false );
295 }
296 }
297
298 /**
299 * @TODO
300 * @param $slug
301 * @param bool $deactivate_wordpress_plugin
302 * @param bool $error_loading
303 *
304 * @return array|mixed|void
305 */
306 public function deactivate( $slug, $deactivate_wordpress_plugin = true, $error_loading = false ) {
307 $extension = $this->get_extension( $slug );
308 if ( $extension ) {
309 if ( $deactivate_wordpress_plugin && 'bundled' !== foo_safe_get( $extension, 'source', false ) ) {
310 $plugin = $this->find_wordpress_plugin( $extension );
311 if ( $plugin ) {
312 $failure = deactivate_plugins( $plugin['file'], true, false );
313 if ( null !== $failure ) {
314 return array(
315 /* translators: %s: Value inserted at runtime. */
316 'message' => sprintf( __( 'The feature %s could NOT be deactivated!', 'foogallery' ), "<strong>{$extension['title']}</strong>" ),
317 'type' => 'error'
318 );
319 }
320 }
321 }
322
323 $active_extensions = $this->get_active_extensions();
324 if ( array_key_exists( $slug, $active_extensions ) ) {
325 unset( $active_extensions[ $slug ] );
326 if ( empty($active_extensions) ) {
327 delete_option( FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY );
328 } else {
329 update_option( FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY, $active_extensions );
330 }
331 }
332
333 $this->set_override( $slug, 'deactivated' );
334
335 if ( $error_loading ) {
336 $this->add_to_error_extensions( $slug );
337 }
338
339 //we are done, allow for extensions to do something after an extension is activated
340 do_action( 'foogallery_extension_deactivated-' . $slug );
341
342 return apply_filters( 'foogallery_extensions_deactivated_message-' . $slug, array(
343 /* translators: %s: Value inserted at runtime. */
344 'message' => sprintf( __( 'The feature %s was successfully deactivated', 'foogallery' ), "<strong>{$extension['title']}</strong>" ),
345 'type' => 'success',
346 ) );
347 }
348 return array(
349 /* translators: %s: Value inserted at runtime. */
350 'message' => sprintf( __( 'Unknown feature : %s', 'foogallery' ), $slug ),
351 'type' => 'error',
352 );
353 }
354
355 private function set_override( $slug, $status ) {
356 $overrides = $this->get_overrides();
357 $overrides[$slug] = $status;
358 update_option( FOOGALLERY_EXTENSIONS_OVERRIDES_OPTIONS_KEY, $overrides );
359 }
360
361 /**
362 * @TODO
363 *
364 * @param $slug
365 * @param bool $activate_wordpress_plugin
366 *
367 * @return array|mixed|void
368 */
369 public function activate( $slug, $activate_wordpress_plugin = true ) {
370 $extension = $this->get_extension( $slug );
371 if ( $extension ) {
372 //first remove it from our error list (if it was there before)
373 $this->remove_from_error_extensions( $slug );
374
375 if ( $activate_wordpress_plugin && 'bundled' !== foo_safe_get( $extension, 'source', false ) ) {
376 //activate the plugin, WordPress style!
377 $plugin = $this->find_wordpress_plugin( $extension );
378
379 if ( $plugin ) {
380
381 //check min version
382 $minimum_version = foo_safe_get( $extension, 'minimum_version' );
383 if ( !empty($minimum_version) ) {
384 $actual_version = $plugin['plugin']['Version'];
385 if ( version_compare( $actual_version, $minimum_version ) < 0 ) {
386 /* translators: %s: Value inserted at runtime. */
387 $this->add_to_error_extensions( $slug, sprintf( __( 'Requires %s version %s','foogallery' ), $extension['title'], $minimum_version ) );
388 return array(
389 /* translators: %s: Value inserted at runtime. */
390 'message' => sprintf( __( 'The feature %s could not be activated, because you are using an outdated version! Please update %s to at least version %s.', 'foogallery' ), $extension['title'], $extension['title'], $minimum_version ),
391 'type' => 'error',
392 );
393 }
394 }
395
396 //try to activate the plugin
397 $failure = activate_plugin( $plugin['file'], '', false, false );
398 if ( null !== $failure ) {
399 return array(
400 /* translators: %s: Value inserted at runtime. */
401 'message' => sprintf( __( 'The feature %s could NOT be activated!', 'foogallery' ), "<strong>{$extension['title']}</strong>" ),
402 'type' => 'error',
403 );
404 }
405 }
406 }
407 //load an instance of the extension class into memory
408 $loader = new FooGallery_Extensions_Loader();
409 $loader->load_extension( $slug, foo_safe_get( $extension, 'class', false ) );
410
411 //then add the extension to our saved option so that it can be loaded on startup
412 $this->add_to_activated_extensions( $extension );
413
414 $this->set_override( $slug, 'active' );
415
416 //we are done, allow for extensions to do something after an extension is activated
417 do_action( 'foogallery_extension_activated-' . $slug );
418
419 //return our result
420 return apply_filters( 'foogallery_extension_activated_message-' . $slug, array(
421 /* translators: %s: Value inserted at runtime. */
422 'message' => sprintf( __( 'The feature %s was successfully activated', 'foogallery' ), "<strong>{$extension['title']}</strong>" ),
423 'type' => 'success',
424 ) );
425 }
426 return array(
427 /* translators: %s: Value inserted at runtime. */
428 'message' => sprintf( __( 'Unknown feature : %s', 'foogallery' ), $slug ),
429 'type' => 'error',
430 );
431 }
432
433 /**
434 * @TODO
435 * @param boolean $extension
436 *
437 * @return array|bool
438 */
439 private function find_wordpress_plugin( $extension ) {
440 $this->load_wordpress_plugin_functions();
441
442 $plugins = get_plugins();
443 foreach ( $plugins as $plugin_file => $plugin ) {
444 if ( isset($extension['file']) && foo_ends_with( $plugin_file, $extension['file'] ) ) {
445 return array(
446 'file' => $plugin_file,
447 'plugin' => $plugin,
448 'active' => is_plugin_active( $plugin_file ),
449 );
450 }
451 }
452 return false;
453 }
454
455 /**
456 * @TODO
457 * @param boolean $extension
458 *
459 * @return array|bool
460 */
461 private function find_active_wordpress_plugin( $extension ) {
462 $this->load_wordpress_plugin_functions();
463
464 $plugins = get_plugins();
465 foreach ( $plugins as $plugin_file => $plugin ) {
466 if ( is_plugin_active( $plugin_file ) && isset($extension['file']) && foo_ends_with( $plugin_file, $extension['file'] ) ) {
467 return array(
468 'file' => $plugin_file,
469 'plugin' => $plugin
470 );
471 }
472 }
473 return false;
474 }
475
476 /**
477 * Load plugin administration functions when extension checks run on the frontend.
478 *
479 * WordPress 5.3 does not load these functions during a normal frontend bootstrap.
480 *
481 * @return void
482 */
483 private function load_wordpress_plugin_functions() {
484 if ( ! function_exists( 'get_plugins' ) || ! function_exists( 'is_plugin_active' ) ) {
485 require_once ABSPATH . 'wp-admin/includes/plugin.php';
486 }
487 }
488
489 /**
490 * @TODO
491 * @param $slug
492 *
493 * @return array|mixed|void
494 */
495 public function download( $slug ) {
496 $extension = $this->get_extension( $slug );
497 if ( $extension ) {
498
499 //we need some files!
500 require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // plugins_api calls
501 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Plugin_Upgrader class
502 require_once FOOGALLERY_PATH . 'includes/admin/class-silent-installer-skin.php'; //our silent installer skin
503
504 $download_link = isset( $extension['download_link'] ) ? $extension['download_link'] : false;
505
506 if ( 'repo' === $extension['source'] ) {
507 $plugins_api = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) );
508
509 if ( is_wp_error( $plugins_api ) ) {
510 return array(
511 /* translators: %s: Value inserted at runtime. */
512 'message' => sprintf( __( 'Unable to connect to the WordPress.org plugin API to download %s. Full error log: %s', 'foogallery' ), $slug, '<br />' . var_export( $plugins_api, true ) ),
513 'type' => 'error',
514 );
515 }
516
517 //get the download link from the API call
518 if ( isset( $plugins_api->download_link ) ) {
519 $download_link = $plugins_api->download_link;
520 }
521 }
522
523 //check we have something to download
524 if ( empty( $download_link ) ) {
525 return array(
526 /* translators: %s: Value inserted at runtime. */
527 'message' => sprintf( __( 'The feature %s has no download link!', 'foogallery' ), $slug ),
528 'type' => 'error',
529 );
530 }
531
532 $skin = new FooGallery_Silent_Installer_Skin();
533
534 //instantiate Plugin_Upgrader
535 $upgrader = new Plugin_Upgrader( $skin );
536
537 $upgrader->install( $download_link );
538
539 if ( 'process_failed' === $skin->feedback ) {
540 $error_message = is_wp_error( $skin->result ) ? $skin->result->get_error_message() : __( 'Unknown!', 'foogallery' );
541
542 //save the error message for the extension
543 /* translators: %s: Value inserted at runtime. */
544 $this->add_to_error_extensions( $slug, sprintf( __('Could not be downloaded! Error : %s', 'foogallery' ), $error_message ) );
545
546 //we had an error along the way
547 return apply_filters( 'foogallery_extensions_download_failure-' . $slug, array(
548 /* translators: %s: Value inserted at runtime. */
549 'message' => sprintf( __( 'The feature %s could NOT be downloaded! Error : %s', 'foogallery' ), "<strong>{$extension['title']}</strong>", $error_message ),
550 'type' => 'error'
551 ) );
552 }
553
554 //return our result
555 return apply_filters( 'foogallery_extensions_download_success-' . $slug, array(
556 /* translators: %s: Value inserted at runtime. */
557 'message' => sprintf( __( 'The feature %s was successfully downloaded and can now be activated. %s', 'foogallery' ),
558 "<strong>{$extension['title']}</strong>",
559 '<a href="' . esc_url( add_query_arg( array(
560 'action' => 'activate',
561 'extension' => $slug, ) ) ) . '">' . __( 'Activate immediately', 'foogallery' ) . '</a>'
562 ),
563 'type' => 'success',
564 ) );
565 }
566 return array(
567 /* translators: %s: Value inserted at runtime. */
568 'message' => sprintf( __( 'Unknown feature : %s', 'foogallery' ), $slug ),
569 'type' => 'error',
570 );
571 }
572
573 /**
574 * @TODO
575 * @return mixed|void
576 */
577 public function get_active_extensions() {
578 return get_option( FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY, array() );
579 }
580
581 /**
582 * Return extensions that should be loaded on startup.
583 *
584 * This mirrors is_active() so explicit active/deactivated overrides and
585 * default/plugin-backed extensions cannot drift from the runtime loader.
586 *
587 * @return array<string,string>
588 */
589 public function get_loadable_extensions() {
590 $loadable_extensions = $this->get_active_extensions();
591 if ( ! is_array( $loadable_extensions ) ) {
592 $loadable_extensions = array();
593 }
594
595 $overrides = $this->get_overrides();
596 if ( ! is_array( $overrides ) ) {
597 $overrides = array();
598 }
599
600 foreach ( $overrides as $slug => $status ) {
601 if ( 'deactivated' === $status ) {
602 unset( $loadable_extensions[ $slug ] );
603 }
604 }
605
606 foreach ( $this->get_all() as $extension ) {
607 $slug = foo_safe_get( $extension, 'slug' );
608 $class = foo_safe_get( $extension, 'class' );
609
610 if ( empty( $slug ) || empty( $class ) ) {
611 continue;
612 }
613
614 if ( $this->is_active( $slug ) && $this->is_downloaded( $extension ) ) {
615 $loadable_extensions[ $slug ] = $class;
616 }
617 }
618
619 return $loadable_extensions;
620 }
621
622 /**
623 * Returns the extension overrides.
624 * @return mixed|void
625 */
626 public function get_overrides() {
627 return get_option( FOOGALLERY_EXTENSIONS_OVERRIDES_OPTIONS_KEY, array() );
628 }
629
630 /**
631 * @TODO
632 * @return mixed|void
633 */
634 public function get_error_extensions() {
635 return get_option( FOOGALLERY_EXTENSIONS_ERRORS_OPTIONS_KEY, array() );
636 }
637
638 public function get_error_message( $slug ) {
639 $error_extensions = $this->get_error_extensions();
640 if ( array_key_exists( $slug, $error_extensions ) ) {
641 return $error_extensions[ $slug ];
642 }
643 return '';
644 }
645
646 /**
647 * @TODO
648 * @param $extension
649 */
650 private function add_to_activated_extensions( $extension ) {
651 $slug = $extension['slug'];
652 $active_extensions = $this->get_active_extensions();
653 if ( !array_key_exists( $slug, $active_extensions ) ) {
654 $active_extensions[ $slug ] = $extension['class'];
655 update_option( FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY, $active_extensions );
656 }
657 }
658
659 /**
660 * @TODO
661 * @param $slug
662 */
663 public function add_to_error_extensions( $slug, $error_message = '' ) {
664 $error_extensions = $this->get_error_extensions();
665
666 if ( empty($error_message) ) {
667 $error_message = __( 'Error loading feature!', 'foogallery' );
668 }
669
670 if ( array_key_exists( $slug, $error_extensions ) &&
671 $error_message === $error_extensions[$slug]) {
672 //do nothing!
673 } else {
674 $error_extensions[$slug] = $error_message;
675 update_option( FOOGALLERY_EXTENSIONS_ERRORS_OPTIONS_KEY, $error_extensions );
676 }
677 }
678
679 /**
680 * @TODO
681 * @param $slug
682 */
683 private function remove_from_error_extensions( $slug ) {
684 $error_extensions = $this->get_error_extensions();
685 if ( array_key_exists( $slug, $error_extensions ) ) {
686 unset( $error_extensions[ $slug ] );
687 update_option( FOOGALLERY_EXTENSIONS_ERRORS_OPTIONS_KEY, $error_extensions );
688 }
689 }
690
691 /**
692 * @TODO
693 */
694 public function auto_activate_extensions() {
695 foreach ( $this->get_all() as $extension ) {
696 if ( true === foo_safe_get( $extension, 'activated_by_default' ) ) {
697 //check to see if the extension is downloaded
698 if ( $this->is_downloaded( $extension ) ) {
699 $this->add_to_activated_extensions( $extension );
700 }
701 }
702 }
703 }
704 }
705 }
706