| 1 |
<?php |
| 2 |
|
| 3 |
namespace BulkWP\BulkDelete\Core\Base; |
| 4 |
|
| 5 |
use BulkWP\BulkDelete\Core\Base\Mixin\Renderer; |
| 6 |
|
| 7 |
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 |
public function __construct() { |
| 131 |
$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 |
public function register( $hook_suffix, $page_slug ) { |
| 141 |
$this->page_hook_suffix = $hook_suffix; |
| 142 |
$this->page_slug = $page_slug; |
| 143 |
|
| 144 |
add_action( "add_meta_boxes_{$this->page_hook_suffix}", array( $this, 'setup_metabox' ) ); |
| 145 |
|
| 146 |
add_filter( 'bd_javascript_array', array( $this, 'filter_js_array' ) ); |
| 147 |
|
| 148 |
if ( ! empty( $this->action ) ) { |
| 149 |
add_action( 'bd_' . $this->action, array( $this, 'process' ) ); |
| 150 |
} |
| 151 |
} |
| 152 |
|
| 153 |
/** |
| 154 |
* Setup the meta box. |
| 155 |
*/ |
| 156 |
public function setup_metabox() { |
| 157 |
add_meta_box( |
| 158 |
$this->meta_box_slug, |
| 159 |
$this->messages['box_label'], |
| 160 |
array( $this, 'render_box' ), |
| 161 |
$this->page_hook_suffix, |
| 162 |
'advanced' |
| 163 |
); |
| 164 |
} |
| 165 |
|
| 166 |
/** |
| 167 |
* Render the meta box. |
| 168 |
*/ |
| 169 |
public function render_box() { |
| 170 |
if ( $this->is_hidden() ) { |
| 171 |
bd_wp_kses_wf(sprintf( |
| 172 |
/* translators: 1 Module url */ |
| 173 |
__( 'This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), |
| 174 |
'admin.php?page=' . esc_attr( $this->page_slug ) |
| 175 |
)); |
| 176 |
|
| 177 |
return; |
| 178 |
} |
| 179 |
|
| 180 |
$this->render(); |
| 181 |
} |
| 182 |
|
| 183 |
/** |
| 184 |
* Is the current meta box hidden by user. |
| 185 |
* |
| 186 |
* @return bool True, if hidden. False, otherwise. |
| 187 |
*/ |
| 188 |
protected function is_hidden() { |
| 189 |
$current_user = wp_get_current_user(); |
| 190 |
$user_meta_field = $this->get_hidden_box_user_meta_field(); |
| 191 |
$hidden_boxes = get_user_meta( $current_user->ID, $user_meta_field, true ); |
| 192 |
|
| 193 |
return is_array( $hidden_boxes ) && in_array( $this->meta_box_slug, $hidden_boxes, true ); |
| 194 |
} |
| 195 |
|
| 196 |
/** |
| 197 |
* Get the user meta field that stores the status of the hidden meta boxes. |
| 198 |
* |
| 199 |
* @return string Name of the User Meta field. |
| 200 |
*/ |
| 201 |
protected function get_hidden_box_user_meta_field() { |
| 202 |
if ( 'posts' === $this->item_type ) { |
| 203 |
return 'metaboxhidden_toplevel_page_bulk-delete-posts'; |
| 204 |
} else { |
| 205 |
return 'metaboxhidden_bulk-wp_page_' . $this->page_slug; |
| 206 |
} |
| 207 |
} |
| 208 |
|
| 209 |
/** |
| 210 |
* Filter the js array. |
| 211 |
* |
| 212 |
* Use `append_to_js_array` function to append any module specific js options. |
| 213 |
* |
| 214 |
* @see $this->append_to_js_array |
| 215 |
* |
| 216 |
* @param array $js_array JavaScript Array. |
| 217 |
* |
| 218 |
* @return array Modified JavaScript Array |
| 219 |
*/ |
| 220 |
public function filter_js_array( $js_array ) { |
| 221 |
$js_array['dt_iterators'][] = '_' . $this->field_slug; |
| 222 |
|
| 223 |
$js_array['pre_delete_msg'][ $this->action ] = $this->action . '_confirm_deletion'; |
| 224 |
$js_array['error_msg'][ $this->action ] = $this->action . '_error'; |
| 225 |
|
| 226 |
$js_array['msg'][ $this->action . '_confirm_deletion' ] = __( 'Are you sure you want to delete all the posts based on the selected option?', 'bulk-delete' ); |
| 227 |
$js_array['msg'][ $this->action . '_error' ] = __( 'Please select posts from at least one option', 'bulk-delete' ); |
| 228 |
|
| 229 |
if ( ! empty( $this->messages['confirm_deletion'] ) ) { |
| 230 |
$js_array['msg'][ $this->action . '_confirm_deletion' ] = $this->messages['confirm_deletion']; |
| 231 |
} |
| 232 |
|
| 233 |
if ( ! empty( $this->messages['confirm_scheduled'] ) ) { |
| 234 |
$js_array['pre_schedule_msg'][ $this->action ] = $this->action . '_confirm_scheduled'; |
| 235 |
|
| 236 |
$js_array['msg'][ $this->action . '_confirm_scheduled' ] = $this->messages['confirm_scheduled']; |
| 237 |
} |
| 238 |
|
| 239 |
if ( ! empty( $this->messages['validation_error'] ) ) { |
| 240 |
$js_array['msg'][ $this->action . '_error' ] = $this->messages['validation_error']; |
| 241 |
} |
| 242 |
|
| 243 |
return $this->append_to_js_array( $js_array ); |
| 244 |
} |
| 245 |
|
| 246 |
/** |
| 247 |
* Append any module specific options to JS array. |
| 248 |
* |
| 249 |
* This function will be overridden by the child classes. |
| 250 |
* |
| 251 |
* @param array $js_array JavaScript Array. |
| 252 |
* |
| 253 |
* @return array Modified JavaScript Array |
| 254 |
*/ |
| 255 |
protected function append_to_js_array( $js_array ) { |
| 256 |
return $js_array; |
| 257 |
} |
| 258 |
|
| 259 |
/** |
| 260 |
* Helper function for processing deletion. |
| 261 |
* Setups up cron and invokes the actual delete method. |
| 262 |
* |
| 263 |
* @param array $request Request array. |
| 264 |
*/ |
| 265 |
public function process( $request ) { |
| 266 |
$options = $this->parse_common_filters( $request ); |
| 267 |
$options = $this->convert_user_input_to_options( $request, $options ); |
| 268 |
$cron_options = $this->parse_cron_filters( $request ); |
| 269 |
|
| 270 |
/** |
| 271 |
* Filter the processed delete options. |
| 272 |
* |
| 273 |
* @since 6.0.0 |
| 274 |
* |
| 275 |
* @param array $options Processed options. |
| 276 |
* @param array $request Request array. |
| 277 |
* @param \BulkWP\BulkDelete\Core\Base\BaseModule The delete module. |
| 278 |
*/ |
| 279 |
$options = apply_filters( 'bd_processed_delete_options', $options, $request, $this ); //phpcs:ignore |
| 280 |
|
| 281 |
if ( $this->is_scheduled( $cron_options ) ) { |
| 282 |
$msg = $this->schedule_deletion( $cron_options, $options ); |
| 283 |
} else { |
| 284 |
$items_deleted = $this->delete( $options ); |
| 285 |
$msg = sprintf( $this->get_success_message( $items_deleted ), $items_deleted ); |
| 286 |
} |
| 287 |
|
| 288 |
add_settings_error( |
| 289 |
$this->page_slug, |
| 290 |
$this->action, |
| 291 |
$msg, |
| 292 |
'updated' |
| 293 |
); |
| 294 |
} |
| 295 |
|
| 296 |
/** |
| 297 |
* Delete items based on delete options. |
| 298 |
* |
| 299 |
* @param array $options Delete Options. |
| 300 |
* |
| 301 |
* @return int Number of items deleted. |
| 302 |
*/ |
| 303 |
public function delete( $options ) { |
| 304 |
/** |
| 305 |
* Filter delete options before deleting items. |
| 306 |
* |
| 307 |
* @since 6.0.0 Added `Modules` parameter. |
| 308 |
* |
| 309 |
* @param array $options Delete options. |
| 310 |
* @param \BulkWP\BulkDelete\Core\Base\BaseModule Modules that is triggering deletion. |
| 311 |
*/ |
| 312 |
$options = apply_filters( 'bd_delete_options', $options, $this ); //phpcs:ignore |
| 313 |
|
| 314 |
return $this->do_delete( $options ); |
| 315 |
} |
| 316 |
|
| 317 |
/** |
| 318 |
* Get Success Message. |
| 319 |
* |
| 320 |
* @param int $items_deleted Number of items that were deleted. |
| 321 |
* |
| 322 |
* @return string Success message. |
| 323 |
*/ |
| 324 |
protected function get_success_message( $items_deleted ) { |
| 325 |
if ( 0 === $items_deleted ) { |
| 326 |
if ( ! empty( $this->messages['nothing_to_delete'] ) ) { |
| 327 |
return $this->messages['nothing_to_delete']; |
| 328 |
} |
| 329 |
} |
| 330 |
|
| 331 |
return _n( $this->messages['deleted_one'], $this->messages['deleted_multiple'], $items_deleted, 'bulk-delete' ); // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingle, WordPress.WP.I18n.NonSingularStringLiteralPlural |
| 332 |
} |
| 333 |
|
| 334 |
/** |
| 335 |
* Getter for cron_hook. |
| 336 |
* |
| 337 |
* @return string Cron Hook name. |
| 338 |
*/ |
| 339 |
public function get_cron_hook() { |
| 340 |
return $this->cron_hook; |
| 341 |
} |
| 342 |
|
| 343 |
/** |
| 344 |
* Getter for field slug. |
| 345 |
* |
| 346 |
* @return string Field Slug. |
| 347 |
*/ |
| 348 |
public function get_field_slug() { |
| 349 |
return $this->field_slug; |
| 350 |
} |
| 351 |
|
| 352 |
/** |
| 353 |
* Getter for action. |
| 354 |
* |
| 355 |
* @return string Modules action. |
| 356 |
*/ |
| 357 |
public function get_action() { |
| 358 |
return $this->action; |
| 359 |
} |
| 360 |
|
| 361 |
/** |
| 362 |
* Is the current deletion request a scheduled request? |
| 363 |
* |
| 364 |
* @param array $cron_options Request object. |
| 365 |
* |
| 366 |
* @return bool True if it is a scheduled request, False otherwise. |
| 367 |
*/ |
| 368 |
protected function is_scheduled( $cron_options ) { |
| 369 |
return $cron_options['is_scheduled']; |
| 370 |
} |
| 371 |
|
| 372 |
/** |
| 373 |
* Schedule Deletion of items. |
| 374 |
* |
| 375 |
* @param array $cron_options Cron options. |
| 376 |
* @param array $options Deletion option. |
| 377 |
* |
| 378 |
* @return string Message. |
| 379 |
*/ |
| 380 |
protected function schedule_deletion( $cron_options, $options ) { |
| 381 |
$options['cron_label'] = $cron_options['cron_label']; |
| 382 |
|
| 383 |
if ( '-1' === $cron_options['frequency'] ) { |
| 384 |
wp_schedule_single_event( $cron_options['start_time'], $this->cron_hook, array( $options ) ); |
| 385 |
} else { |
| 386 |
wp_schedule_event( $cron_options['start_time'], $cron_options['frequency'], $this->cron_hook, array( $options ) ); |
| 387 |
} |
| 388 |
|
| 389 |
return $this->messages['scheduled'] . ' ' . $this->get_task_list_link(); |
| 390 |
} |
| 391 |
|
| 392 |
/** |
| 393 |
* Get the link to the page that lists all the scheduled tasks. |
| 394 |
* |
| 395 |
* @return string Link to scheduled tasks page. |
| 396 |
*/ |
| 397 |
protected function get_task_list_link() { |
| 398 |
return sprintf( |
| 399 |
/* translators: 1 Cron page url */ |
| 400 |
__( 'See the full list of <a href = "%s">scheduled tasks</a>', 'bulk-delete' ), |
| 401 |
get_bloginfo( 'wpurl' ) . '/wp-admin/admin.php?page=' . \Bulk_Delete::CRON_PAGE_SLUG |
| 402 |
); |
| 403 |
} |
| 404 |
|
| 405 |
/** |
| 406 |
* Parse request and create cron options. |
| 407 |
* |
| 408 |
* @param array $request Request array. |
| 409 |
* |
| 410 |
* @return array Parsed cron option. |
| 411 |
*/ |
| 412 |
protected function parse_cron_filters( $request ) { |
| 413 |
$cron_options = array( |
| 414 |
'is_scheduled' => false, |
| 415 |
); |
| 416 |
|
| 417 |
$scheduled = bd_array_get_bool( $request, 'smbd_' . $this->field_slug . '_cron', false ); |
| 418 |
|
| 419 |
if ( $scheduled ) { |
| 420 |
$cron_options['is_scheduled'] = true; |
| 421 |
$cron_options['frequency'] = sanitize_text_field( $request[ 'smbd_' . $this->field_slug . '_cron_freq' ] ); |
| 422 |
$cron_options['start_time'] = bd_get_gmt_offseted_time( sanitize_text_field( $request[ 'smbd_' . $this->field_slug . '_cron_start' ] ) ); |
| 423 |
|
| 424 |
$cron_options['cron_label'] = $this->get_cron_label(); |
| 425 |
} |
| 426 |
|
| 427 |
return $cron_options; |
| 428 |
} |
| 429 |
|
| 430 |
/** |
| 431 |
* Get the human readable label for the Schedule job. |
| 432 |
* |
| 433 |
* @return string Human readable label for schedule job. |
| 434 |
*/ |
| 435 |
public function get_cron_label() { |
| 436 |
return $this->messages['cron_label']; |
| 437 |
} |
| 438 |
|
| 439 |
/** |
| 440 |
* Get the name of the module. |
| 441 |
* |
| 442 |
* This is used as the key to identify the module from page. |
| 443 |
* |
| 444 |
* @return string Module name. |
| 445 |
*/ |
| 446 |
public function get_name() { |
| 447 |
return bd_get_short_class_name( $this ); |
| 448 |
} |
| 449 |
|
| 450 |
/** |
| 451 |
* Get the page slug of the module. |
| 452 |
* |
| 453 |
* @since 6.0.1 |
| 454 |
* |
| 455 |
* @return string Page slug. |
| 456 |
*/ |
| 457 |
public function get_page_slug() { |
| 458 |
return $this->page_slug; |
| 459 |
} |
| 460 |
} |
| 461 |
|