PluginProbe
ElasticPress / 5.3.5
ElasticPress v5.3.5
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
← All changes | includes/classes/Feature.php +330 -138 4.4.15.3.5 View file →
@@ -9,10 +9,10 @@
9 9 */
10 10
11 11 namespace ElasticPress;
12 12
13 -use ElasticPress\FeatureRequirementsStatus as FeatureRequirementsStatus;
14 -use ElasticPress\Utils as Utils;
13 +use ElasticPress\FeatureRequirementsStatus;
14 +use ElasticPress\Utils;
15 15
16 16 if ( ! defined( 'ABSPATH' ) ) {
17 17 exit; // Exit if accessed directly.
18 18 }
@@ -72,13 +72,21 @@
72 72 /**
73 73 * True if the feature requires content reindexing after activating
74 74 *
75 75 * @since 2.1
76 - * @var [type]
76 + * @var bool
77 77 */
78 - public $requires_install_reindex;
78 + public $requires_install_reindex = false;
79 79
80 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 + /**
81 89 * The order in the features screen
82 90 *
83 91 * @var int
84 92 * @since 3.6.0
@@ -102,33 +110,63 @@
102 110 */
103 111 public $available_during_installation = false;
104 112
105 113 /**
106 - * Run on every page load for feature to set itself up
114 + * Whether the feature should be always visible in the dashboard
107 115 *
108 - * @since 2.1
116 + * @since 4.5.0
117 + * @var boolean
109 118 */
110 - abstract public function setup();
119 + protected $is_visible = true;
111 120
112 121 /**
113 - * Output feature box summary
122 + * Settings description
114 123 *
115 - * @since 2.1
124 + * @since 5.0.0
125 + * @var array
116 126 */
117 - public function output_feature_box_summary() {
118 - if ( $this->summary ) {
119 - echo '<p>' . esc_html( $this->summary ) . '</p>';
120 - }
121 - }
127 + protected $settings_schema = [];
122 128
123 129 /**
124 - * Implement to output feature box long text
130 + * The slug, or array of slugs, of a feature that is required to be active.
125 131 *
126 - * @since 3.0
132 + * @since 5.0.0
133 + * @var false|string|array
127 134 */
128 - abstract public function output_feature_box_long();
135 + protected $requires_feature = false;
129 136
130 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 + * The name of a group that a feature may belong to.
147 + *
148 + * @since 5.3.0
149 + * @var false|string
150 + */
151 + public $group = false;
152 +
153 + /**
154 + * Field groups available to a feature
155 + *
156 + * @since 5.3.0
157 + * @var array
158 + */
159 + protected $field_group_map = [];
160 +
161 + /**
162 + * Run on every page load for feature to set itself up
163 + *
164 + * @since 2.1
165 + */
166 + abstract public function setup();
167 +
168 + /**
131 169 * Create feature
132 170 *
133 171 * @since 3.0
134 172 */
@@ -149,14 +187,14 @@
149 187 * @since 2.2
150 188 * @return FeatureRequirementsStatus
151 189 */
152 190 public function requirements_status() {
153 - $status = new FeatureRequirementsStatus( 0 );
191 + $status = new FeatureRequirementsStatus( 0, null, $this );
154 192
155 193 /**
156 194 * Filter feature requirement status
157 195 *
158 - * @hook ep_{indexable_slug}_index_kill
196 + * @hook ep_feature_requirements_status
159 197 * @param {FeatureRequirementStatus} $status Current feature requirement status
160 198 * @param {Feature} $feature Current feature
161 199 * @since 2.2
162 200 * @return {FeatureRequirementStatus} New status
@@ -166,18 +204,35 @@
166 204
167 205 /**
168 206 * Return feature settings
169 207 *
170 - * @since 2.2.1
171 - * @return array|bool
208 + * @since 2.2.1, 4.5.0 started using default settings
209 + * @return array
172 210 */
173 211 public function get_settings() {
174 - $feature_settings = Utils\get_option( 'ep_feature_settings', [] );
212 + $all_settings = Utils\get_option( 'ep_feature_settings', [] );
175 213
176 - return ( ! empty( $feature_settings[ $this->slug ] ) ) ? $feature_settings[ $this->slug ] : false;
214 + $feature_settings = ( ! empty( $all_settings[ $this->slug ] ) ) ? (array) $all_settings[ $this->slug ] : [];
215 +
216 + $feature_settings = wp_parse_args( $feature_settings, $this->default_settings );
217 +
218 + return $feature_settings;
177 219 }
178 220
179 221 /**
222 + * Return a specific setting of the feature
223 + *
224 + * @since 4.5.0
225 + * @param string $setting_name The setting name
226 + * @return mixed
227 + */
228 + public function get_setting( string $setting_name ) {
229 + $settings = $this->get_settings();
230 +
231 + return isset( $settings[ $setting_name ] ) ? $settings[ $setting_name ] : null;
232 + }
233 +
234 + /**
180 235 * Returns true if feature is active
181 236 *
182 237 * @since 2.2
183 238 * @return boolean
@@ -187,9 +242,13 @@
187 242
188 243 $active = false;
189 244
190 245 if ( ! empty( $feature_settings[ $this->slug ] ) && $feature_settings[ $this->slug ]['active'] ) {
191 - $active = true;
246 + $active = ! in_array(
247 + $this->requirements_status()->get_code(),
248 + [ FeatureRequirementsStatus::FORCE_DISABLED, FeatureRequirementsStatus::TEMPORARILY_DISABLED ],
249 + true
250 + );
192 251 }
193 252
194 253 /**
195 254 * Filter whether a feature is active or not
@@ -204,8 +263,23 @@
204 263 return apply_filters( 'ep_feature_active', $active, $feature_settings, $this );
205 264 }
206 265
207 266 /**
267 + * Get the value of the setting that requires a reindex, if it exists.
268 + *
269 + * @since 4.5.0
270 + * @return mixed
271 + */
272 + public function get_reindex_setting() {
273 + $settings = $this->get_settings();
274 + $setting = $this->setting_requires_install_reindex;
275 +
276 + return $settings && $setting && ! empty( $settings[ $setting ] )
277 + ? $settings[ $setting ]
278 + : '';
279 + }
280 +
281 + /**
208 282 * To be run after initial feature activation
209 283 *
210 284 * @since 2.1
211 285 */
@@ -221,169 +295,287 @@
221 295 do_action( 'ep_feature_post_activation', $this->slug, $this );
222 296 }
223 297
224 298 /**
225 - * Outputs feature box
299 + * To be run after feature deactivation
226 300 *
227 - * @since 2.1
301 + * @since 5.3.2
228 302 */
229 - public function output_feature_box() {
230 - $this->output_feature_box_summary();
231 -
303 + public function post_deactivation() {
232 304 /**
233 - * Fires before feature box summary is shown
305 + * Fires after feature is deactivated
234 306 *
235 - * @hook ep_feature_box_summary
236 - * @param {string} $slug Feature slug
307 + * @hook ep_feature_post_deactivation
308 + * @param {string} $slug Feature slug
237 309 * @param {Feature} $feature Current feature
238 - * @since 2.1
310 + * @since 5.3.2
239 311 */
240 - do_action( 'ep_feature_box_summary', $this->slug, $this );
241 - ?>
312 + do_action( 'ep_feature_post_deactivation', $this->slug, $this );
313 + }
242 314
243 - <button aria-expanded="false" class="learn-more button button-secondary button-small" type="button"><?php esc_html_e( 'Learn more', 'elasticpress' ); ?></button>
315 + /**
316 + * Returns the feature title.
317 + *
318 + * @since 4.4.1
319 + * @return string
320 + */
321 + public function get_title(): string {
322 + return $this->title;
323 + }
244 324
245 - <div class="long">
246 - <?php $this->output_feature_box_long(); ?>
325 + /**
326 + * Returns the feature short title.
327 + *
328 + * @since 4.4.1
329 + * @return string
330 + */
331 + public function get_short_title(): string {
332 + if ( ! empty( $this->short_title ) ) {
333 + return $this->short_title;
334 + }
247 335
248 - <p><button aria-expanded="true" class="collapse button button-secondary button-small" type="button"><?php esc_html_e( 'Collapse', 'elasticpress' ); ?></button></p>
336 + return $this->get_title();
337 + }
249 338
250 - <?php
251 - /**
252 - * Fires after feature long description
253 - *
254 - * @hook ep_feature_box_long
255 - * @param {string} $slug Feature slug
256 - * @param {Feature} $feature Current feature
257 - * @since 2.1
258 - */
259 - do_action( 'ep_feature_box_long', $this->slug, $this );
260 - ?>
261 -
262 - </div>
263 - <?php
339 + /**
340 + * Returns whether the feature is visible in the dashboard or not.
341 + *
342 + * By default, all active features are visible.
343 + *
344 + * @since 4.5.0
345 + * @return boolean
346 + */
347 + public function is_visible() {
348 + /**
349 + * Filter whether a feature is visible or not in the dashboard.
350 + *
351 + * Example:
352 + * ```
353 + * add_filter(
354 + * 'ep_feature_is_visible',
355 + * function ( $is_visible, $feature_slug ) {
356 + * return 'terms' === $feature_slug ? true : $is_visible;
357 + * },
358 + * 10,
359 + * 2
360 + * );
361 + * ```
362 + *
363 + * @hook ep_feature_is_visible
364 + * @param {bool} $is_visible True to display the feature
365 + * @param {string} $feature_slug Feature slug
366 + * @param {Feature} $feature Feature object
367 + * @since 4.5.0
368 + * @return {bool} New $is_visible value
369 + */
370 + return apply_filters( 'ep_feature_is_visible', $this->is_visible || $this->is_active(), $this->slug, $this );
264 371 }
265 372
266 373 /**
267 - * Output extra feature box settings.
374 + * Returns whether the feature is available or not.
268 375 *
269 - * By default this does nothing. Override to add additional settings.
270 - *
271 - * @since 3.0
376 + * @since 4.5.0
377 + * @return boolean
272 378 */
273 - public function output_feature_box_settings() {
379 + public function is_available(): bool {
380 + $requirements_status = $this->requirements_status();
274 381 /**
275 - * Optionally override
382 + * Filter whether a feature is available or not.
383 + *
384 + * Example:
385 + * ```
386 + * add_filter(
387 + * 'ep_feature_is_available',
388 + * function ( $is_available, $feature_slug ) {
389 + * return 'terms' === $feature_slug ? true : $is_available;
390 + * },
391 + * 10,
392 + * 2
393 + * );
394 + * ```
395 + *
396 + * @hook ep_feature_is_available
397 + * @param {bool} $is_available True if the feature is available
398 + * @param {string} $feature_slug Feature slug
399 + * @param {Feature} $feature Feature object
400 + * @since 4.5.0
401 + * @return {bool} New $is_available value
276 402 */
403 + return apply_filters(
404 + 'ep_feature_is_available',
405 + $this->is_visible()
406 + && ! in_array(
407 + $requirements_status->get_code(),
408 + [ FeatureRequirementsStatus::FORCE_DISABLED, FeatureRequirementsStatus::TEMPORARILY_DISABLED ],
409 + true
410 + ),
411 + $this->slug,
412 + $this
413 + );
277 414 }
278 415
279 416 /**
280 - * Output feature settings
417 + * Get a JSON representation of the feature
281 418 *
282 - * @since 3.0
419 + * @since 5.0.0
420 + * @return string
283 421 */
284 - public function output_settings_box() {
422 + public function get_json() {
285 423 $requirements_status = $this->requirements_status();
286 - $sync_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK )
287 - ? network_admin_url( 'admin.php?page=elasticpress-sync' )
288 - : admin_url( 'admin.php?page=elasticpress-sync' );
289 - ?>
290 424
291 - <form>
292 - <?php
293 - if ( ! empty( $requirements_status->message ) ) :
294 - $messages = (array) $requirements_status->message;
295 - ?>
296 - <?php foreach ( $messages as $message ) : ?>
297 - <div class="requirements-status-notice">
298 - <?php echo wp_kses_post( $message ); ?>
299 - </div>
300 - <?php endforeach; ?>
301 - <?php endif; ?>
425 + $feature_desc = [
426 + 'slug' => $this->slug,
427 + 'title' => $this->get_title(),
428 + 'shortTitle' => $this->get_short_title(),
429 + 'summary' => $this->summary,
430 + 'docsUrl' => $this->docs_url,
431 + 'defaultSettings' => $this->default_settings,
432 + 'order' => $this->order,
433 + 'isAvailable' => $this->is_available(),
434 + 'isPoweredByEpio' => $this->is_powered_by_epio,
435 + 'isVisible' => $this->is_visible(),
436 + 'reqStatusCode' => $requirements_status->get_code(),
437 + 'reqStatusMessages' => (array) $requirements_status->get_message(),
438 + 'settingsSchema' => $this->get_settings_schema(),
439 + 'group' => $this->group,
440 + 'requiredFeature' => $this->get_required_feature(),
441 + 'fieldGroups' => $this->get_field_group_map(),
442 + ];
302 443
303 - <?php if ( $this->requires_install_reindex ) : ?>
304 - <div class="requirements-status-notice requirements-status-notice--reindex" role="status">
305 - <?php esc_html_e( 'Enabling this feature will require re-indexing your content.', 'elasticpress' ); ?>
306 - </div>
307 - <?php endif; ?>
444 + return $feature_desc;
445 + }
308 446
309 - <div class="requirements-status-notice requirements-status-notice--syncing" role="alert">
310 - <?php
311 - printf(
312 - '%1$s <a href="%2$s">%3$s</a>',
313 - esc_html__( 'Settings not saved. Cannot save settings while a sync is in progress.', 'elasticpress' ),
314 - esc_url( $sync_url ),
315 - esc_html__( 'View sync status.', 'elasticpress' )
316 - );
317 - ?>
318 - </div>
447 + /**
448 + * Return the feature settings schema
449 + *
450 + * @since 5.0.0
451 + * @return array
452 + */
453 + public function get_settings_schema() {
454 + // Settings were not set yet.
455 + if ( [] === $this->settings_schema ) {
456 + $this->set_settings_schema();
457 + }
319 458
320 - <h3><?php esc_html_e( 'Settings', 'elasticpress' ); ?></h3>
459 + $active = [
460 + 'default' => false,
461 + 'key' => 'active',
462 + 'label' => __( 'Enable', 'elasticpress' ),
463 + 'requires_feature' => $this->get_required_feature(),
464 + 'requires_sync' => $this->requires_install_reindex,
465 + 'type' => 'toggle',
466 + ];
321 467
322 - <div class="feature-fields">
323 - <div class="field js-toggle-feature">
324 - <div class="field-name status"><?php esc_html_e( 'Status', 'elasticpress' ); ?></div>
325 - <div class="input-wrap <?php if ( 2 === $requirements_status->code ) : ?>disabled<?php endif; ?>">
326 - <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>
327 - <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>
328 - </div>
329 - </div>
468 + $settings_schema = [
469 + $active,
470 + ...$this->settings_schema,
471 + ];
330 472
331 - <?php
332 - $this->output_feature_box_settings();
333 - ?>
334 - </div>
473 + /**
474 + * Filter the settings schema of a feature
475 + *
476 + * @hook ep_feature_is_available
477 + * @since 5.0.0
478 + * @param {array} $settings_schema True if the feature is available
479 + * @param {string} $feature_slug Feature slug
480 + * @param {Feature} $feature Feature object
481 + * @return {array} New $settings_schema value
482 + */
483 + return apply_filters( 'ep_feature_settings_schema', $settings_schema, $this->slug, $this );
484 + }
335 485
336 - <div class="action-wrap">
337 - <span class="no-dash-sync">
338 - <?php esc_html_e( 'Setting adjustments to this feature require a re-sync. Use WP-CLI.', 'elasticpress' ); ?>
339 - </span>
486 + /**
487 + * Reset the cached settings schema so it is rebuilt on next access.
488 + *
489 + * @since 5.3.3
490 + */
491 + public function reset_settings_schema() {
492 + $this->settings_schema = [];
493 + }
340 494
341 - <input type="hidden" name="action" value="ep_save_feature">
342 - <input type="hidden" name="feature" value="<?php echo esc_attr( $this->slug ); ?>">
343 - <input type="hidden" name="requires_reindex" value="<?php echo $this->requires_install_reindex ? '1' : '0'; ?>">
344 - <input type="hidden" name="was_active" value="<?php echo $this->is_active() ? '1' : '0'; ?>">
345 - <?php wp_nonce_field( 'ep_dashboard_nonce', 'nonce' ); ?>
495 + /**
496 + * Default implementation of `set_settings_schema` based on the `default_settings` attribute
497 + *
498 + * @since 5.0.0
499 + */
500 + protected function set_settings_schema() {
501 + if ( [] === $this->default_settings ) {
502 + return;
503 + }
346 504
347 - <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">
348 - <?php esc_html_e( 'Save', 'elasticpress' ); ?>
349 - </button>
350 - </div>
351 - </form>
505 + foreach ( $this->default_settings as $key => $default_value ) {
506 + $type = 'text';
507 + if ( in_array( $default_value, [ '0', '1' ], true ) ) {
508 + $type = 'checkbox';
509 + }
510 + if ( is_bool( $default_value ) ) {
511 + $type = 'toggle';
512 + }
352 513
353 - <?php
514 + $this->settings_schema[] = [
515 + 'default' => $default_value,
516 + 'key' => $key,
517 + 'label' => $key,
518 + 'type' => $type,
519 + ];
520 + }
354 521 }
355 522
356 523 /**
357 - * Returns the ElasticPress.io logo.
524 + * Sets the i18n strings for the feature.
358 525 *
359 - * @since 4.4.1
360 - * @return string
526 + * @return void
527 + * @since 5.2.0
361 528 */
362 - public function get_epio_logo() : string {
363 - return sprintf( '<img class="feature-epio-logo" alt="ElasticPresss.io logo" src="%s" width="110" height="20">', esc_url( plugins_url( '/images/logo-elasticpress-io.svg', EP_FILE ) ) );
529 + public function set_i18n_strings(): void {
364 530 }
365 531
366 532 /**
367 - * Returns the feature title.
533 + * Get all features required by this feature
368 534 *
369 - * @since 4.4.1
370 - * @return string
535 + * @since 5.3.0
536 + * @return array List of required feature slugs
371 537 */
372 - public function get_title() : string {
373 - return $this->title;
538 + public function get_required_feature() {
539 + return $this->requires_feature ? array_unique( (array) $this->requires_feature ) : [];
374 540 }
375 541
376 542 /**
377 - * Returns the feature short title.
543 + * Get the field group map for the feature.
378 544 *
379 - * @since 4.4.1
380 - * @return string
545 + * @since 5.3.0
546 + * @return array
381 547 */
382 - public function get_short_title() : string {
383 - if ( ! empty( $this->short_title ) ) {
384 - return $this->short_title;
385 - }
548 + public function get_field_group_map(): array {
549 + /**
550 + * Filter available field groups.
551 + *
552 + * @hook ep_feature_field_groups
553 + * @since 5.3.0
554 + * @param {array} $field_groups Current field groups
555 + * @return {array} New field groups
556 + */
557 + return apply_filters( 'ep_feature_field_groups', $this->field_group_map );
558 + }
386 559
387 - return $this->get_title();
560 + /**
561 + * Get the feature slug.
562 + *
563 + * @since 5.3.0
564 + * @return string Feature slug.
565 + */
566 + public function get_feature_slug(): string {
567 + return $this->slug;
568 + }
569 +
570 + /**
571 + * Pre-handle feature activation
572 + *
573 + * This method is called before features are setup and is intended to be used
574 + * to modify features requirements status.
575 + *
576 + * @since 5.3.3
577 + * @return void
578 + */
579 + public function pre_handle_feature_activation() {
388 580 }
389 581 }