Completed
Push — 677-feature/add-wp-cli-support ( a6bca4...1a74d2 )
by Sudar
03:32
created

BaseModule::prepare_cli_input()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
ccs 0
cts 4
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BulkWP\BulkDelete\Core\Base;
4
5
use BulkWP\BulkDelete\Core\Base\Mixin\Renderer;
6
7 1
defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
8
9
/**
10
 * Encapsulates the Bulk Delete Meta box Module Logic.
11
 *
12
 * All Bulk Delete Meta box Modules should extend this class.
13
 * This class extends Renderer Mixin class since Bulk Delete still supports PHP 5.3.
14
 * Once PHP 5.3 support is dropped, Renderer will be implemented as a Trait and this class will `use` it.
15
 *
16
 * @since 6.0.0
17
 */
18
abstract class BaseModule extends Renderer {
19
	/**
20
	 * Item Type. Possible values 'posts', 'pages', 'users' etc.
21
	 *
22
	 * @var string
23
	 */
24
	protected $item_type;
25
26
	/**
27
	 * The hook suffix of the screen where this meta box would be shown.
28
	 *
29
	 * @var string
30
	 */
31
	protected $page_hook_suffix;
32
33
	/**
34
	 * Slug of the page where this module will be shown.
35
	 *
36
	 * @var string
37
	 */
38
	protected $page_slug;
39
40
	/**
41
	 * Slug of the meta box.
42
	 *
43
	 * @var string
44
	 */
45
	protected $meta_box_slug;
46
47
	/**
48
	 * Action in which the delete operation should be performed.
49
	 *
50
	 * @var string
51
	 */
52
	protected $action = '';
53
54
	/**
55
	 * Hook for scheduler.
56
	 *
57
	 * @var string
58
	 */
59
	protected $cron_hook;
60
61
	/**
62
	 * Url of the scheduler addon.
63
	 *
64
	 * @var string
65
	 */
66
	protected $scheduler_url;
67
68
	/**
69
	 * Messages shown to the user.
70
	 *
71
	 * @var array
72
	 */
73
	protected $messages = array(
74
		'box_label'         => '',
75
		'cron_label'        => '',
76
		'validation_error'  => '',
77
		'confirm_deletion'  => '',
78
		'confirm_scheduled' => '',
79
		'scheduled'         => '',
80
		'nothing_to_delete' => '',
81
		'deleted_one'       => '',
82
		'deleted_multiple'  => '',
83
	);
84
85
	/**
86
	 * Initialize and setup variables.
87
	 *
88
	 * @return void
89
	 */
90
	abstract protected function initialize();
91
92
	/**
93
	 * Render the Modules.
94
	 *
95
	 * @return void
96
	 */
97
	abstract public function render();
98
99
	/**
100
	 * Process common filters.
101
	 *
102
	 * @param array $request Request array.
103
	 *
104
	 * @return array User options.
105
	 */
106
	abstract protected function parse_common_filters( $request );
107
108
	/**
109
	 * Process user input and create metabox options.
110
	 *
111
	 * @param array $request Request array.
112
	 * @param array $options User options.
113
	 *
114
	 * @return array User options.
115
	 */
116
	abstract protected function convert_user_input_to_options( $request, $options );
117
118
	/**
119
	 * Perform the deletion.
120
	 *
121
	 * @param array $options Array of Delete options.
122
	 *
123
	 * @return int Number of items that were deleted.
124
	 */
125
	abstract protected function do_delete( $options );
126
127
	/**
128
	 * Create new instances of Modules.
129
	 */
130 392
	public function __construct() {
131 392
		$this->initialize();
132
	}
133
134
	/**
135
	 * Register.
136
	 *
137
	 * @param string $hook_suffix Page Hook Suffix.
138
	 * @param string $page_slug   Page slug.
139
	 */
140 9
	public function register( $hook_suffix, $page_slug ) {
141 9
		$this->page_hook_suffix = $hook_suffix;
142 9
		$this->page_slug        = $page_slug;
143
144 9
		add_action( "add_meta_boxes_{$this->page_hook_suffix}", array( $this, 'setup_metabox' ) );
145
146 9
		add_filter( 'bd_javascript_array', array( $this, 'filter_js_array' ) );
147
148 9
		if ( ! empty( $this->action ) ) {
149 9
			add_action( 'bd_' . $this->action, array( $this, 'process' ) );
150
		}
151
	}
152
153
	/**
154
	 * Setup the meta box.
155
	 */
156
	public function setup_metabox() {
157
		if ( array_key_exists( 'box_label', $this->messages ) ) {
158
			add_meta_box(
159
				$this->meta_box_slug,
160
				$this->messages['box_label'],
161
				array( $this, 'render_box' ),
162
				$this->page_hook_suffix,
163
				'advanced'
164
			);
165
		}
166
	}
167
168
	/**
169
	 * Render the meta box.
170
	 */
171 1
	public function render_box() {
172 1
		if ( $this->is_hidden() ) {
173
			printf(
174
				/* translators: 1 Module url */
175
				__( 'This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ),
176
				'admin.php?page=' . esc_attr( $this->page_slug )
177
			);
178
179
			return;
180
		}
181
182 1
		$this->render();
183
	}
184
185
	/**
186
	 * Is the current meta box hidden by user.
187
	 *
188
	 * @return bool True, if hidden. False, otherwise.
189
	 */
190 1
	protected function is_hidden() {
191 1
		$current_user    = wp_get_current_user();
192 1
		$user_meta_field = $this->get_hidden_box_user_meta_field();
193 1
		$hidden_boxes    = get_user_meta( $current_user->ID, $user_meta_field, true );
194
195 1
		return is_array( $hidden_boxes ) && in_array( $this->meta_box_slug, $hidden_boxes, true );
196
	}
197
198
	/**
199
	 * Get the user meta field that stores the status of the hidden meta boxes.
200
	 *
201
	 * @return string Name of the User Meta field.
202
	 */
203 1
	protected function get_hidden_box_user_meta_field() {
204 1
		if ( 'posts' === $this->item_type ) {
205
			return 'metaboxhidden_toplevel_page_bulk-delete-posts';
206
		} else {
207 1
			return 'metaboxhidden_bulk-wp_page_' . $this->page_slug;
208
		}
209
	}
210
211
	/**
212
	 * Filter the js array.
213
	 *
214
	 * Use `append_to_js_array` function to append any module specific js options.
215
	 *
216
	 * @see $this->append_to_js_array
217
	 *
218
	 * @param array $js_array JavaScript Array.
219
	 *
220
	 * @return array Modified JavaScript Array
221
	 */
222
	public function filter_js_array( $js_array ) {
223
		$js_array['dt_iterators'][] = '_' . $this->field_slug;
224
225
		$js_array['pre_delete_msg'][ $this->action ] = $this->action . '_confirm_deletion';
226
		$js_array['error_msg'][ $this->action ]      = $this->action . '_error';
227
228
		$js_array['msg'][ $this->action . '_confirm_deletion' ] = __( 'Are you sure you want to delete all the posts based on the selected option?', 'bulk-delete' );
229
		$js_array['msg'][ $this->action . '_error' ]            = __( 'Please select posts from at least one option', 'bulk-delete' );
230
231
		if ( ! empty( $this->messages['confirm_deletion'] ) ) {
232
			$js_array['msg'][ $this->action . '_confirm_deletion' ] = $this->messages['confirm_deletion'];
233
		}
234
235
		if ( ! empty( $this->messages['confirm_scheduled'] ) ) {
236
			$js_array['pre_schedule_msg'][ $this->action ] = $this->action . '_confirm_scheduled';
237
238
			$js_array['msg'][ $this->action . '_confirm_scheduled' ] = $this->messages['confirm_scheduled'];
239
		}
240
241
		if ( ! empty( $this->messages['validation_error'] ) ) {
242
			$js_array['msg'][ $this->action . '_error' ] = $this->messages['validation_error'];
243
		}
244
245
		return $this->append_to_js_array( $js_array );
246
	}
247
248
	/**
249
	 * Append any module specific options to JS array.
250
	 *
251
	 * This function will be overridden by the child classes.
252
	 *
253
	 * @param array $js_array JavaScript Array.
254
	 *
255
	 * @return array Modified JavaScript Array
256
	 */
257
	protected function append_to_js_array( $js_array ) {
258
		return $js_array;
259
	}
260
261
	/**
262
	 * Helper function for processing deletion.
263
	 * Setups up cron and invokes the actual delete method.
264
	 *
265
	 * @param array $request Request array.
266
	 */
267 1
	public function process( $request ) {
268 1
		$msg = $this->process_request( $request );
269
270 1
		add_settings_error(
271 1
			$this->page_slug,
272 1
			$this->action,
273
			$msg,
274 1
			'updated'
275
		);
276
	}
277
278
	/**
279
	 * Process CLI Request.
280
	 *
281
	 * @since 6.1.0
282
	 *
283
	 * @param array $request Request input.
284
	 *
285
	 * @return string Message to be shown to the user.
286
	 */
287
	public function process_cli_request( $request ) {
288
		$request = $this->prepare_cli_input( $request );
289
290
		return $this->process_request( $request );
291
	}
292
293
	/**
294
	 * Process the user request.
295
	 *
296
	 * @since 6.1.0
297
	 *
298
	 * @param array $request Request array. For UI it will be `$_POST`. For CLI it will be the `$assoc_arg`.
299
	 *
300
	 * @return string Message to be shown to the user.
301
	 */
302 1
	protected function process_request( $request ) {
303 1
		$options      = $this->parse_common_filters( $request );
304 1
		$options      = $this->convert_user_input_to_options( $request, $options );
305 1
		$cron_options = $this->parse_cron_filters( $request );
306
307
		/**
308
		 * Filter the processed delete options.
309
		 *
310
		 * @param array $options Processed options.
311
		 * @param array $request Request array.
312
		 * @param \BulkWP\BulkDelete\Core\Base\BaseModule The delete module.
313
		 *
314
		 * @since 6.0.0
315
		 */
316 1
		$options = apply_filters( 'bd_processed_delete_options', $options, $request, $this );
317
318 1
		if ( $this->is_scheduled( $cron_options ) ) {
319
			$msg = $this->schedule_deletion( $cron_options, $options );
320
		} else {
321 1
			$items_deleted = $this->delete( $options );
322 1
			$msg           = sprintf( $this->get_success_message( $items_deleted ), $items_deleted );
323
		}
324
325 1
		return $msg;
326
	}
327
328
	/**
329
	 * Delete items based on delete options.
330
	 *
331
	 * @param array $options Delete Options.
332
	 *
333
	 * @return int Number of items deleted.
334
	 */
335 311
	public function delete( $options ) {
336
		/**
337
		 * Filter delete options before deleting items.
338
		 *
339
		 * @since 6.0.0 Added `Modules` parameter.
340
		 *
341
		 * @param array $options Delete options.
342
		 * @param \BulkWP\BulkDelete\Core\Base\BaseModule Modules that is triggering deletion.
343
		 */
344 311
		$options = apply_filters( 'bd_delete_options', $options, $this );
345
346 311
		return $this->do_delete( $options );
347
	}
348
349
	/**
350
	 * Get Success Message.
351
	 *
352
	 * @param int $items_deleted Number of items that were deleted.
353
	 *
354
	 * @return string Success message.
355
	 */
356 1
	protected function get_success_message( $items_deleted ) {
357 1
		if ( 0 === $items_deleted ) {
358
			if ( ! empty( $this->messages['nothing_to_delete'] ) ) {
359
				return $this->messages['nothing_to_delete'];
360
			}
361
		}
362
363 1
		return _n( $this->messages['deleted_one'], $this->messages['deleted_multiple'], $items_deleted, 'bulk-delete' ); // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingle, WordPress.WP.I18n.NonSingularStringLiteralPlural
364
	}
365
366
	/**
367
	 * Getter for cron_hook.
368
	 *
369
	 * @return string Cron Hook name.
370
	 */
371
	public function get_cron_hook() {
372
		return $this->cron_hook;
373
	}
374
375
	/**
376
	 * Getter for field slug.
377
	 *
378
	 * @return string Field Slug.
379
	 */
380
	public function get_field_slug() {
381
		return $this->field_slug;
382
	}
383
384
	/**
385
	 * Getter for action.
386
	 *
387
	 * @return string Modules action.
388
	 */
389 321
	public function get_action() {
390 321
		return $this->action;
391
	}
392
393
	/**
394
	 * Is the current deletion request a scheduled request?
395
	 *
396
	 * @param array $cron_options Request object.
397
	 *
398
	 * @return bool True if it is a scheduled request, False otherwise.
399
	 */
400 3
	protected function is_scheduled( $cron_options ) {
401 3
		return $cron_options['is_scheduled'];
402
	}
403
404
	/**
405
	 * Schedule Deletion of items.
406
	 *
407
	 * @param array $cron_options Cron options.
408
	 * @param array $options      Deletion option.
409
	 *
410
	 * @return string Message.
411
	 */
412 32
	protected function schedule_deletion( $cron_options, $options ) {
413 32
		$options['cron_label'] = $cron_options['cron_label'];
414
415 32
		if ( '-1' === $cron_options['frequency'] ) {
416 8
			wp_schedule_single_event( $cron_options['start_time'], $this->cron_hook, array( $options ) );
417
		} else {
418 24
			wp_schedule_event( $cron_options['start_time'], $cron_options['frequency'], $this->cron_hook, array( $options ) );
419
		}
420
421 32
		return $this->messages['scheduled'] . ' ' . $this->get_task_list_link();
422
	}
423
424
	/**
425
	 * Get the link to the page that lists all the scheduled tasks.
426
	 *
427
	 * @return string Link to scheduled tasks page.
428
	 */
429 32
	protected function get_task_list_link() {
430 32
		return sprintf(
431
			/* translators: 1 Cron page url */
432 32
			__( 'See the full list of <a href = "%s">scheduled tasks</a>', 'bulk-delete' ),
433 32
			get_bloginfo( 'wpurl' ) . '/wp-admin/admin.php?page=' . \Bulk_Delete::CRON_PAGE_SLUG
434
		);
435
	}
436
437
	/**
438
	 * Parse request and create cron options.
439
	 *
440
	 * @param array $request Request array.
441
	 *
442
	 * @return array Parsed cron option.
443
	 */
444 31
	protected function parse_cron_filters( $request ) {
445
		$cron_options = array(
446 31
			'is_scheduled' => false,
447
		);
448
449 31
		$scheduled = bd_array_get_bool( $request, 'smbd_' . $this->field_slug . '_cron', false );
450
451 31
		if ( $scheduled ) {
452 28
			$cron_options['is_scheduled'] = true;
453 28
			$cron_options['frequency']    = sanitize_text_field( $request[ 'smbd_' . $this->field_slug . '_cron_freq' ] );
454 28
			$cron_options['start_time']   = bd_get_gmt_offseted_time( sanitize_text_field( $request[ 'smbd_' . $this->field_slug . '_cron_start' ] ) );
455
456 28
			$cron_options['cron_label'] = $this->get_cron_label();
457
		}
458
459 31
		return $cron_options;
460
	}
461
462
	/**
463
	 * Get the human readable label for the Schedule job.
464
	 *
465
	 * @return string Human readable label for schedule job.
466
	 */
467 28
	public function get_cron_label() {
468 28
		return $this->messages['cron_label'];
469
	}
470
471
	/**
472
	 * Get the name of the module.
473
	 *
474
	 * This is used as the key to identify the module from page.
475
	 *
476
	 * @return string Module name.
477
	 */
478
	public function get_name() {
479
		return bd_get_short_class_name( $this );
480
	}
481
482
	/**
483
	 * Get the page slug of the module.
484
	 *
485
	 * @since 6.0.1
486
	 *
487
	 * @return string Page slug.
488
	 */
489
	public function get_page_slug() {
490
		return $this->page_slug;
491
	}
492
493
	/**
494
	 * Get the prefix that is added to the user input fields in the UI.
495
	 *
496
	 * @return string UI input prefix.
497
	 * @since 6.1.0
498
	 *
499
	 */
500
	protected function get_ui_input_prefix() {
501
		return 'smbd_' . $this->field_slug . '_';
502
	}
503
504
	/**
505
	 * Get the map of non standard keys used in user input.
506
	 *
507
	 * Before v6.1.0, each module has non-standard names for some input keys.
508
	 * This became a problem when WP CLI was introduced.
509
	 * This function provides a way for each module to specify a map of these non-standard keys that it expects.
510
	 * Eventually the non-standard keys will be rectified and after that this function will no longer be needed.
511
	 *
512
	 * @return array UI map.
513
	 * @since 6.1.0
514
	 *
515
	 */
516
	protected function get_non_standard_input_key_map() {
517
		return [];
518
	}
519
520
	/**
521
	 * Prepare the input for a module.
522
	 *
523
	 * @since 6.1.0
524
	 *
525
	 * @param array $input Input array.
526
	 *
527
	 * @return array Prepared input.
528
	 */
529
	protected function prepare_cli_input( $input ) {
530
		$input = $this->prefix_input( $input, $this->get_ui_input_prefix() );
531
		$input = $this->handle_non_standard_keys( $input, $this->get_non_standard_input_key_map() );
532
533
		return $input;
534
	}
535
536
	/**
537
	 * Prefix user request array keys.
538
	 *
539
	 * @since 6.1.0
540
	 *
541
	 * @param array  $input  User input.
542
	 * @param string $prefix Prefix string.
543
	 *
544
	 * @return array Prefixed user input.
545
	 */
546
	protected function prefix_input( $input, $prefix ) {
547
		$prefixed_input = [];
548
549
		foreach ( $input as $key => $value ) {
550
			$prefixed_input[ $prefix . $key ] = $value;
551
		}
552
553
		return $prefixed_input;
554
	}
555
556
	/**
557
	 * Handle non standard keys that are used in the module.
558
	 *
559
	 * @since 6.1.0
560
	 *
561
	 * @param array $input User input.
562
	 * @param array $map Map of non standard and standard keys.
563
	 *
564
	 * @return array Processed input.
565
	 */
566
	protected function handle_non_standard_keys( $input, $map ) {
567
		foreach ( $map as $non_standard_key => $standard_key ) {
568
			if ( isset( $input[ $standard_key ] ) ) {
569
				$input[ $non_standard_key ] = $input[ $standard_key ];
570
			}
571
		}
572
573
		return $input;
574
	}
575
}
576