PluginProbe
ElasticPress / 5.0.1
ElasticPress v5.0.1
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / includes / classes / Feature.php

Feature.php in ElasticPress 5.0.1, at includes/classes/Feature.php

626 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Feature class to be initiated for all features.
4 *
5 * All features extend this class.
6 *
7 * @since 2.1
8 * @package elasticpress
9 */
10
11 namespace ElasticPress;
12
13 use ElasticPress\FeatureRequirementsStatus;
14 use ElasticPress\Utils;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // Exit if accessed directly.
18 }
19
20 /**
21 * Feature abstract class
22 */
23 abstract class Feature {
24 /**
25 * Feature slug
26 *
27 * @var string
28 * @since 2.1
29 */
30 public $slug;
31
32 /**
33 * Feature pretty title
34 *
35 * @var string
36 * @since 2.1
37 */
38 public $title;
39
40 /**
41 * Short title
42 *
43 * @var string
44 * @since 4.4.1
45 */
46 public $short_title;
47
48 /**
49 * Feature summary
50 *
51 * @var string
52 * @since 4.0.0
53 */
54 public $summary;
55
56 /**
57 * URL to feature documentation.
58 *
59 * @var string
60 * @since 4.0.0
61 */
62 public $docs_url;
63
64 /**
65 * Optional feature default settings
66 *
67 * @since 2.2
68 * @var array
69 */
70 public $default_settings = [];
71
72 /**
73 * True if the feature requires content reindexing after activating
74 *
75 * @since 2.1
76 * @var bool
77 */
78 public $requires_install_reindex = false;
79
80 /**
81 * The slug of a setting that requires content reindexing after activating.
82 *
83 * @since 4.5.0
84 * @var string
85 */
86 public $setting_requires_install_reindex = '';
87
88 /**
89 * The order in the features screen
90 *
91 * @var int
92 * @since 3.6.0
93 */
94 public $order;
95
96 /**
97 * Set if a feature should be on the left or right side
98 *
99 * @var string
100 * @since 3.6.0
101 */
102 public $group_order;
103
104 /**
105 * True if activation of this feature should be available during
106 * installation.
107 *
108 * @since 4.0.0
109 * @var boolean
110 */
111 public $available_during_installation = false;
112
113 /**
114 * Whether the feature should be always visible in the dashboard
115 *
116 * @since 4.5.0
117 * @var boolean
118 */
119 protected $is_visible = true;
120
121 /**
122 * Settings description
123 *
124 * @since 5.0.0
125 * @var array
126 */
127 protected $settings_schema = [];
128
129 /**
130 * The slug of a feature that is required to be active.
131 *
132 * @since 5.0.0
133 * @var false|string
134 */
135 protected $requires_feature = false;
136
137 /**
138 * Whether the feature is using ElasticPress.io.
139 *
140 * @since 5.0.0
141 * @var boolean
142 */
143 protected $is_powered_by_epio = false;
144
145 /**
146 * Run on every page load for feature to set itself up
147 *
148 * @since 2.1
149 */
150 abstract public function setup();
151
152 /**
153 * Output feature box summary
154 *
155 * @since 2.1
156 */
157 public function output_feature_box_summary() {
158 if ( $this->summary ) {
159 echo wp_kses_post( $this->summary );
160 }
161 }
162
163 /**
164 * Implement to output feature box long text
165 *
166 * @since 3.0
167 */
168 public function output_feature_box_long() {}
169
170 /**
171 * Create feature
172 *
173 * @since 3.0
174 */
175 public function __construct() {
176 /**
177 * Fires when Feature object is created
178 *
179 * @hook ep_feature_create
180 * @param {Feature} $feature Current feature
181 * @since 3.0
182 */
183 do_action( 'ep_feature_create', $this );
184 }
185
186 /**
187 * Returns requirements status of feature
188 *
189 * @since 2.2
190 * @return FeatureRequirementsStatus
191 */
192 public function requirements_status() {
193 $status = new FeatureRequirementsStatus( 0 );
194
195 /**
196 * Filter feature requirement status
197 *
198 * @hook ep_{indexable_slug}_index_kill
199 * @param {FeatureRequirementStatus} $status Current feature requirement status
200 * @param {Feature} $feature Current feature
201 * @since 2.2
202 * @return {FeatureRequirementStatus} New status
203 */
204 return apply_filters( 'ep_feature_requirements_status', $status, $this );
205 }
206
207 /**
208 * Return feature settings
209 *
210 * @since 2.2.1, 4.5.0 started using default settings
211 * @return array
212 */
213 public function get_settings() {
214 $all_settings = Utils\get_option( 'ep_feature_settings', [] );
215
216 $feature_settings = ( ! empty( $all_settings[ $this->slug ] ) ) ? (array) $all_settings[ $this->slug ] : [];
217
218 $feature_settings = wp_parse_args( $feature_settings, $this->default_settings );
219
220 return $feature_settings;
221 }
222
223 /**
224 * Return a specific setting of the feature
225 *
226 * @since 4.5.0
227 * @param string $setting_name The setting name
228 * @return mixed
229 */
230 public function get_setting( string $setting_name ) {
231 $settings = $this->get_settings();
232
233 return isset( $settings[ $setting_name ] ) ? $settings[ $setting_name ] : null;
234 }
235
236 /**
237 * Returns true if feature is active
238 *
239 * @since 2.2
240 * @return boolean
241 */
242 public function is_active() {
243 $feature_settings = Utils\get_option( 'ep_feature_settings', [] );
244
245 $active = false;
246
247 if ( ! empty( $feature_settings[ $this->slug ] ) && $feature_settings[ $this->slug ]['active'] ) {
248 $active = true;
249 }
250
251 /**
252 * Filter whether a feature is active or not
253 *
254 * @hook ep_feature_active
255 * @param {bool} $active Whether feature is active or not
256 * @param {array} $feature_settings Current feature settings
257 * @param {Feature} $feature Current feature
258 * @since 2.2
259 * @return {bool} New active value
260 */
261 return apply_filters( 'ep_feature_active', $active, $feature_settings, $this );
262 }
263
264 /**
265 * Get the value of the setting that requires a reindex, if it exists.
266 *
267 * @since 4.5.0
268 * @return mixed
269 */
270 public function get_reindex_setting() {
271 $settings = $this->get_settings();
272 $setting = $this->setting_requires_install_reindex;
273
274 return $settings && $setting && ! empty( $settings[ $setting ] )
275 ? $settings[ $setting ]
276 : '';
277 }
278
279 /**
280 * To be run after initial feature activation
281 *
282 * @since 2.1
283 */
284 public function post_activation() {
285 /**
286 * Fires after feature is activated
287 *
288 * @hook ep_feature_post_activation
289 * @param {string} $slug Feature slug
290 * @param {Feature} $feature Current feature
291 * @since 2.1
292 */
293 do_action( 'ep_feature_post_activation', $this->slug, $this );
294 }
295
296 /**
297 * Outputs feature box
298 *
299 * @since 2.1
300 */
301 public function output_feature_box() {
302 $this->output_feature_box_summary();
303
304 /**
305 * Fires before feature box summary is shown
306 *
307 * @hook ep_feature_box_summary
308 * @param {string} $slug Feature slug
309 * @param {Feature} $feature Current feature
310 * @since 2.1
311 */
312 do_action( 'ep_feature_box_summary', $this->slug, $this );
313 ?>
314
315 <button aria-expanded="false" class="learn-more button button-secondary button-small" type="button"><?php esc_html_e( 'Learn more', 'elasticpress' ); ?></button>
316
317 <div class="long">
318 <?php $this->output_feature_box_long(); ?>
319
320 <p><button aria-expanded="true" class="collapse button button-secondary button-small" type="button"><?php esc_html_e( 'Collapse', 'elasticpress' ); ?></button></p>
321
322 <?php
323 /**
324 * Fires after feature long description
325 *
326 * @hook ep_feature_box_long
327 * @param {string} $slug Feature slug
328 * @param {Feature} $feature Current feature
329 * @since 2.1
330 */
331 do_action( 'ep_feature_box_long', $this->slug, $this );
332 ?>
333
334 </div>
335 <?php
336 }
337
338 /**
339 * Output extra feature box settings.
340 *
341 * By default this does nothing. Override to add additional settings.
342 *
343 * @since 3.0
344 */
345 public function output_feature_box_settings() {
346 /**
347 * Optionally override
348 */
349 }
350
351 /**
352 * Output feature settings
353 *
354 * @since 3.0
355 */
356 public function output_settings_box() {
357 $requirements_status = $this->requirements_status();
358 $sync_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK )
359 ? network_admin_url( 'admin.php?page=elasticpress-sync' )
360 : admin_url( 'admin.php?page=elasticpress-sync' );
361 ?>
362
363 <form>
364 <?php
365 if ( ! empty( $requirements_status->message ) ) :
366 $messages = (array) $requirements_status->message;
367 ?>
368 <?php foreach ( $messages as $message ) : ?>
369 <div class="requirements-status-notice">
370 <?php echo wp_kses_post( $message ); ?>
371 </div>
372 <?php endforeach; ?>
373 <?php endif; ?>
374
375 <?php if ( $this->requires_install_reindex || $this->setting_requires_install_reindex ) : ?>
376 <div class="requirements-status-notice requirements-status-notice--reindex" role="status">
377 <?php esc_html_e( 'Enabling this feature will require re-syncing your content.', 'elasticpress' ); ?>
378 </div>
379 <?php endif; ?>
380
381 <div class="requirements-status-notice requirements-status-notice--syncing" role="alert">
382 <?php
383 printf(
384 '%1$s <a href="%2$s">%3$s</a>',
385 esc_html__( 'Settings not saved. Cannot save settings while a sync is in progress.', 'elasticpress' ),
386 esc_url( $sync_url ),
387 esc_html__( 'View sync status.', 'elasticpress' )
388 );
389 ?>
390 </div>
391
392 <h3><?php esc_html_e( 'Settings', 'elasticpress' ); ?></h3>
393
394 <div class="feature-fields">
395 <div class="field js-toggle-feature">
396 <div class="field-name status"><?php esc_html_e( 'Status', 'elasticpress' ); ?></div>
397 <div class="input-wrap <?php if ( 2 === $requirements_status->code ) : ?>disabled<?php endif; ?>">
398 <label><input name="settings[active]" <?php disabled( 2 === $requirements_status->code ); ?> type="radio" <?php checked( $this->is_active() ); ?> value="1"><?php esc_html_e( 'Enabled', 'elasticpress' ); ?></label><br>
399 <label><input name="settings[active]" <?php disabled( 2 === $requirements_status->code ); ?> type="radio" <?php checked( ! $this->is_active() ); ?> value="0"><?php esc_html_e( 'Disabled', 'elasticpress' ); ?></label>
400 </div>
401 </div>
402
403 <?php
404 $this->output_feature_box_settings();
405 ?>
406 </div>
407
408 <div class="action-wrap">
409 <span class="no-dash-sync">
410 <?php esc_html_e( 'Setting adjustments to this feature require a re-sync. Use WP-CLI.', 'elasticpress' ); ?>
411 </span>
412
413 <input type="hidden" name="action" value="ep_save_feature">
414 <input type="hidden" name="feature" value="<?php echo esc_attr( $this->slug ); ?>">
415 <input type="hidden" name="requires_reindex" value="<?php echo $this->requires_install_reindex ? '1' : '0'; ?>">
416 <input type="hidden" name="was_active" value="<?php echo $this->is_active() ? '1' : '0'; ?>">
417 <input type="hidden" name="setting_requires_reindex" value="<?php echo esc_attr( $this->setting_requires_install_reindex ); ?>">
418 <input type="hidden" name="setting_requires_reindex_was" value="<?php echo esc_attr( $this->get_reindex_setting() ); ?>">
419 <?php wp_nonce_field( 'ep_dashboard_nonce', 'nonce' ); ?>
420
421 <button name="submit" <?php disabled( 2 === $requirements_status->code || ( $this->requires_install_reindex && defined( 'EP_DASHBOARD_SYNC' ) && ! EP_DASHBOARD_SYNC ) ); ?> class="button button-primary" type="submit">
422 <?php esc_html_e( 'Save', 'elasticpress' ); ?>
423 </button>
424 </div>
425 </form>
426
427 <?php
428 }
429
430 /**
431 * Returns the ElasticPress.io logo.
432 *
433 * @since 4.4.1
434 * @return string
435 */
436 public function get_epio_logo() : string {
437 return sprintf( '<img class="feature-epio-logo" alt="ElasticPress.io logo" src="%s" width="110" height="20">', esc_url( plugins_url( '/images/logo-elasticpress-io.svg', EP_FILE ) ) );
438 }
439
440 /**
441 * Returns the feature title.
442 *
443 * @since 4.4.1
444 * @return string
445 */
446 public function get_title() : string {
447 return $this->title;
448 }
449
450 /**
451 * Returns the feature short title.
452 *
453 * @since 4.4.1
454 * @return string
455 */
456 public function get_short_title() : string {
457 if ( ! empty( $this->short_title ) ) {
458 return $this->short_title;
459 }
460
461 return $this->get_title();
462 }
463
464 /**
465 * Returns whether the feature is visible in the dashboard or not.
466 *
467 * By default, all active features are visible.
468 *
469 * @since 4.5.0
470 * @return boolean
471 */
472 public function is_visible() {
473 /**
474 * Filter whether a feature is visible or not in the dashboard.
475 *
476 * Example:
477 * ```
478 * add_filter(
479 * 'ep_feature_is_visible',
480 * function ( $is_visible, $feature_slug ) {
481 * return 'terms' === $feature_slug ? true : $is_visible;
482 * },
483 * 10,
484 * 2
485 * );
486 * ```
487 *
488 * @hook ep_feature_is_visible
489 * @param {bool} $is_visible True to display the feature
490 * @param {string} $feature_slug Feature slug
491 * @param {Feature} $feature Feature object
492 * @since 4.5.0
493 * @return {bool} New $is_visible value
494 */
495 return apply_filters( 'ep_feature_is_visible', $this->is_visible || $this->is_active(), $this->slug, $this );
496 }
497
498 /**
499 * Returns whether the feature is available or not.
500 *
501 * @since 4.5.0
502 * @return boolean
503 */
504 public function is_available() : bool {
505 $requirements_status = $this->requirements_status();
506 /**
507 * Filter whether a feature is available or not.
508 *
509 * Example:
510 * ```
511 * add_filter(
512 * 'ep_feature_is_available',
513 * function ( $is_available, $feature_slug ) {
514 * return 'terms' === $feature_slug ? true : $is_available;
515 * },
516 * 10,
517 * 2
518 * );
519 * ```
520 *
521 * @hook ep_feature_is_available
522 * @param {bool} $is_available True if the feature is available
523 * @param {string} $feature_slug Feature slug
524 * @param {Feature} $feature Feature object
525 * @since 4.5.0
526 * @return {bool} New $is_available value
527 */
528 return apply_filters( 'ep_feature_is_available', $this->is_visible() && 2 !== $requirements_status->code, $this->slug, $this );
529 }
530
531 /**
532 * Get a JSON representation of the feature
533 *
534 * @since 5.0.0
535 * @return string
536 */
537 public function get_json() {
538 $requirements_status = $this->requirements_status();
539
540 $feature_desc = [
541 'slug' => $this->slug,
542 'title' => $this->get_title(),
543 'shortTitle' => $this->get_short_title(),
544 'summary' => $this->summary,
545 'docsUrl' => $this->docs_url,
546 'defaultSettings' => $this->default_settings,
547 'order' => $this->order,
548 'isAvailable' => $this->is_available(),
549 'isPoweredByEpio' => $this->is_powered_by_epio,
550 'isVisible' => $this->is_visible(),
551 'reqStatusCode' => $requirements_status->code,
552 'reqStatusMessages' => (array) $requirements_status->message,
553 'settingsSchema' => $this->get_settings_schema(),
554 ];
555
556 return $feature_desc;
557 }
558
559 /**
560 * Return the feature settings schema
561 *
562 * @since 5.0.0
563 * @return array
564 */
565 public function get_settings_schema() {
566 // Settings were not set yet.
567 if ( [] === $this->settings_schema ) {
568 $this->set_settings_schema();
569 }
570
571 $active = [
572 'default' => false,
573 'key' => 'active',
574 'label' => __( 'Enable', 'elasticpress' ),
575 'requires_feature' => $this->requires_feature,
576 'requires_sync' => $this->requires_install_reindex,
577 'type' => 'toggle',
578 ];
579
580 $settings_schema = [
581 $active,
582 ...$this->settings_schema,
583 ];
584
585 /**
586 * Filter the settings schema of a feature
587 *
588 * @hook ep_feature_is_available
589 * @since 5.0.0
590 * @param {array} $settings_schema True if the feature is available
591 * @param {string} $feature_slug Feature slug
592 * @param {Feature} $feature Feature object
593 * @return {array} New $settings_schema value
594 */
595 return apply_filters( 'ep_feature_settings_schema', $settings_schema, $this->slug, $this );
596 }
597
598 /**
599 * Default implementation of `set_settings_schema` based on the `default_settings` attribute
600 *
601 * @since 5.0.0
602 */
603 protected function set_settings_schema() {
604 if ( [] === $this->default_settings ) {
605 return;
606 }
607
608 foreach ( $this->default_settings as $key => $default_value ) {
609 $type = 'text';
610 if ( in_array( $default_value, [ '0', '1' ], true ) ) {
611 $type = 'checkbox';
612 }
613 if ( is_bool( $default_value ) ) {
614 $type = 'toggle';
615 }
616
617 $this->settings_schema[] = [
618 'default' => $default_value,
619 'key' => $key,
620 'label' => $key,
621 'type' => $type,
622 ];
623 }
624 }
625 }
626