Changeset 2622865
- Timestamp:
- 11/01/2021 03:21:13 PM (4 years ago)
- Location:
- action-scheduler
- Files:
-
- 24 edited
- 1 copied
-
tags/3.4.0 (copied) (copied from action-scheduler/trunk)
-
tags/3.4.0/action-scheduler.php (modified) (4 diffs)
-
tags/3.4.0/changelog.txt (modified) (1 diff)
-
tags/3.4.0/classes/ActionScheduler_Compatibility.php (modified) (2 diffs)
-
tags/3.4.0/classes/ActionScheduler_ListTable.php (modified) (3 diffs)
-
tags/3.4.0/classes/ActionScheduler_wcSystemStatus.php (modified) (5 diffs)
-
tags/3.4.0/classes/abstracts/ActionScheduler_Abstract_ListTable.php (modified) (44 diffs)
-
tags/3.4.0/classes/data-stores/ActionScheduler_DBLogger.php (modified) (7 diffs)
-
tags/3.4.0/classes/data-stores/ActionScheduler_DBStore.php (modified) (47 diffs)
-
tags/3.4.0/classes/data-stores/ActionScheduler_wpPostStore.php (modified) (49 diffs)
-
tags/3.4.0/classes/schema/ActionScheduler_StoreSchema.php (modified) (2 diffs)
-
tags/3.4.0/deprecated/ActionScheduler_Schedule_Deprecated.php (modified) (2 diffs)
-
tags/3.4.0/readme.txt (modified) (2 diffs)
-
trunk/action-scheduler.php (modified) (4 diffs)
-
trunk/changelog.txt (modified) (1 diff)
-
trunk/classes/ActionScheduler_Compatibility.php (modified) (2 diffs)
-
trunk/classes/ActionScheduler_ListTable.php (modified) (3 diffs)
-
trunk/classes/ActionScheduler_wcSystemStatus.php (modified) (5 diffs)
-
trunk/classes/abstracts/ActionScheduler_Abstract_ListTable.php (modified) (44 diffs)
-
trunk/classes/data-stores/ActionScheduler_DBLogger.php (modified) (7 diffs)
-
trunk/classes/data-stores/ActionScheduler_DBStore.php (modified) (47 diffs)
-
trunk/classes/data-stores/ActionScheduler_wpPostStore.php (modified) (49 diffs)
-
trunk/classes/schema/ActionScheduler_StoreSchema.php (modified) (2 diffs)
-
trunk/deprecated/ActionScheduler_Schedule_Deprecated.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
action-scheduler/tags/3.4.0/action-scheduler.php
r2599552 r2622865 6 6 * Author: Automattic 7 7 * Author URI: https://automattic.com/ 8 * Version: 3. 3.08 * Version: 3.4.0 9 9 * License: GPLv3 10 10 * … … 27 27 */ 28 28 29 if ( ! function_exists( 'action_scheduler_register_3_dot_ 3_dot_0' ) && function_exists( 'add_action' ) ) {29 if ( ! function_exists( 'action_scheduler_register_3_dot_4_dot_0' ) && function_exists( 'add_action' ) ) { 30 30 31 if ( ! class_exists( 'ActionScheduler_Versions' ) ) {31 if ( ! class_exists( 'ActionScheduler_Versions', false ) ) { 32 32 require_once __DIR__ . '/classes/ActionScheduler_Versions.php'; 33 33 add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 ); 34 34 } 35 35 36 add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_ 3_dot_0', 0, 0 );36 add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_4_dot_0', 0, 0 ); 37 37 38 38 /** 39 39 * Registers this version of Action Scheduler. 40 40 */ 41 function action_scheduler_register_3_dot_ 3_dot_0() {41 function action_scheduler_register_3_dot_4_dot_0() { 42 42 $versions = ActionScheduler_Versions::instance(); 43 $versions->register( '3. 3.0', 'action_scheduler_initialize_3_dot_3_dot_0' );43 $versions->register( '3.4.0', 'action_scheduler_initialize_3_dot_4_dot_0' ); 44 44 } 45 45 … … 47 47 * Initializes this version of Action Scheduler. 48 48 */ 49 function action_scheduler_initialize_3_dot_ 3_dot_0() {49 function action_scheduler_initialize_3_dot_4_dot_0() { 50 50 // A final safety check is required even here, because historic versions of Action Scheduler 51 51 // followed a different pattern (in some unusual cases, we could reach this point and the 52 52 // ActionScheduler class is already defined—so we need to guard against that). 53 if ( ! class_exists( 'ActionScheduler' ) ) {53 if ( ! class_exists( 'ActionScheduler', false ) ) { 54 54 require_once __DIR__ . '/classes/abstracts/ActionScheduler.php'; 55 55 ActionScheduler::init( __FILE__ ); … … 58 58 59 59 // Support usage in themes - load this version if no plugin has loaded a version yet. 60 if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler' ) ) {61 action_scheduler_initialize_3_dot_ 3_dot_0();60 if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) { 61 action_scheduler_initialize_3_dot_4_dot_0(); 62 62 do_action( 'action_scheduler_pre_theme_init' ); 63 63 ActionScheduler_Versions::initialize_latest_version(); -
action-scheduler/tags/3.4.0/changelog.txt
r2599552 r2622865 1 1 *** Changelog *** 2 3 = 3.4.0 - 2021-10-29 = 4 * Enhancement - Number of items per page can now be set for the Scheduled Actions view (props @ovidiul). #771 5 * Fix - Do not lower the max_execution_time if it is already set to 0 (unlimited) (props @barryhughes). #755 6 * Fix - Avoid triggering autoloaders during the version resolution process (props @olegabr). #731 & #776 7 * Dev - ActionScheduler_wcSystemStatus PHPCS fixes (props @ovidiul). #761 8 * Dev - ActionScheduler_DBLogger.php PHPCS fixes (props @ovidiul). #768 9 * Dev - Fixed phpcs for ActionScheduler_Schedule_Deprecated (props @ovidiul). #762 10 * Dev - Improve actions table indicies (props @glagonikas). #774 & #777 11 * Dev - PHPCS fixes for ActionScheduler_DBStore.php (props @ovidiul). #769 & #778 12 * Dev - PHPCS Fixes for ActionScheduler_Abstract_ListTable (props @ovidiul). #763 & #779 13 * Dev - Adds new filter action_scheduler_claim_actions_order_by to allow tuning of the claim query (props @glagonikas). #773 14 * Dev - PHPCS fixes for ActionScheduler_WpPostStore class (props @ovidiul). #780 2 15 3 16 = 3.3.0 - 2021-09-15 = -
action-scheduler/tags/3.4.0/classes/ActionScheduler_Compatibility.php
r2599552 r2622865 84 84 * Only allows raising the existing limit and prevents lowering it. Wrapper for wc_set_time_limit(), when available. 85 85 * 86 * @param int The time limit in seconds.86 * @param int $limit The time limit in seconds. 87 87 */ 88 88 public static function raise_time_limit( $limit = 0 ) { 89 if ( $limit < ini_get( 'max_execution_time' ) ) { 89 $limit = (int) $limit; 90 $max_execution_time = (int) ini_get( 'max_execution_time' ); 91 92 /* 93 * If the max execution time is already unlimited (zero), or if it exceeds or is equal to the proposed 94 * limit, there is no reason for us to make further changes (we never want to lower it). 95 */ 96 if ( 97 0 === $max_execution_time 98 || ( $max_execution_time >= $limit && $limit !== 0 ) 99 ) { 90 100 return; 91 101 } … … 94 104 wc_set_time_limit( $limit ); 95 105 } elseif ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved 96 @set_time_limit( $limit ); 106 @set_time_limit( $limit ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged 97 107 } 98 108 } -
action-scheduler/tags/3.4.0/classes/ActionScheduler_ListTable.php
r2340383 r2622865 176 176 ); 177 177 178 parent::__construct( array( 179 'singular' => 'action-scheduler', 180 'plural' => 'action-scheduler', 181 'ajax' => false, 182 ) ); 183 } 184 178 parent::__construct( 179 array( 180 'singular' => 'action-scheduler', 181 'plural' => 'action-scheduler', 182 'ajax' => false, 183 ) 184 ); 185 186 add_screen_option( 187 'per_page', 188 array( 189 'default' => $this->items_per_page, 190 ) 191 ); 192 193 add_filter( 'set_screen_option_' . $this->get_per_page_option_name(), array( $this, 'set_items_per_page_option' ), 10, 3 ); 194 set_screen_options(); 195 } 196 197 /** 198 * Handles setting the items_per_page option for this screen. 199 * 200 * @param mixed $status Default false (to skip saving the current option). 201 * @param string $option Screen option name. 202 * @param int $value Screen option value. 203 * @return int 204 */ 205 public function set_items_per_page_option( $status, $option, $value ) { 206 return $value; 207 } 185 208 /** 186 209 * Convert an interval of seconds into a two part human friendly string. … … 550 573 $this->prepare_column_headers(); 551 574 552 $per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page ); 575 $per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page ); 576 553 577 $query = array( 554 578 'per_page' => $per_page, … … 610 634 return __( 'Search hook, args and claim ID', 'action-scheduler' ); 611 635 } 636 637 /** 638 * {@inheritDoc} 639 */ 640 protected function get_per_page_option_name() { 641 return str_replace( '-', '_', $this->screen->id ) . '_per_page'; 642 } 612 643 } -
action-scheduler/tags/3.4.0/classes/ActionScheduler_wcSystemStatus.php
r2542151 r2622865 13 13 protected $store; 14 14 15 function __construct( $store ) { 15 /** 16 * Constructor method for ActionScheduler_wcSystemStatus. 17 * 18 * @param ActionScheduler_Store $store Active store object. 19 * 20 * @return void 21 */ 22 public function __construct( $store ) { 16 23 $this->store = $store; 17 24 } … … 68 75 $order = 'oldest' === $date_type ? 'ASC' : 'DESC'; 69 76 70 $action = $this->store->query_actions( array( 71 'claimed' => false, 72 'status' => $status, 73 'per_page' => 1, 74 'order' => $order, 75 ) ); 77 $action = $this->store->query_actions( 78 array( 79 'claimed' => false, 80 'status' => $status, 81 'per_page' => 1, 82 'order' => $order, 83 ) 84 ); 76 85 77 86 if ( ! empty( $action ) ) { … … 93 102 */ 94 103 protected function get_template( $status_labels, $action_counts, $oldest_and_newest ) { 95 $as_version = ActionScheduler_Versions::instance()->latest_version();104 $as_version = ActionScheduler_Versions::instance()->latest_version(); 96 105 $as_datastore = get_class( ActionScheduler_Store::instance() ); 97 106 ?> … … 125 134 '<tr><td>%1$s</td><td> </td><td>%2$s<span style="display: none;">, Oldest: %3$s, Newest: %4$s</span></td><td>%3$s</td><td>%4$s</td></tr>', 126 135 esc_html( $status_labels[ $status ] ), 127 number_format_i18n( $count),128 $oldest_and_newest[ $status ]['oldest'],129 $oldest_and_newest[ $status ]['newest']136 esc_html( number_format_i18n( $count ) ), 137 esc_html( $oldest_and_newest[ $status ]['oldest'] ), 138 esc_html( $oldest_and_newest[ $status ]['newest'] ) 130 139 ); 131 140 } … … 138 147 139 148 /** 140 * is triggered when invoking inaccessible methods in an object context.149 * Is triggered when invoking inaccessible methods in an object context. 141 150 * 142 * @param string $name 143 * @param array $arguments 151 * @param string $name Name of method called. 152 * @param array $arguments Parameters to invoke the method with. 144 153 * 145 154 * @return mixed -
action-scheduler/tags/3.4.0/classes/abstracts/ActionScheduler_Abstract_ListTable.php
r2551612 r2622865 2 2 3 3 if ( ! class_exists( 'WP_List_Table' ) ) { 4 require_once ( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );4 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; 5 5 } 6 6 … … 14 14 * 15 15 * This class supports: 16 * - Bulk actions17 * - Search16 * - Bulk actions 17 * - Search 18 18 * - Sortable columns 19 19 * - Automatic translations of the columns … … 26 26 /** 27 27 * The table name 28 * 29 * @var string 28 30 */ 29 31 protected $table_name; … … 31 33 /** 32 34 * Package name, used to get options from WP_List_Table::get_items_per_page. 35 * 36 * @var string 33 37 */ 34 38 protected $package; … … 36 40 /** 37 41 * How many items do we render per page? 42 * 43 * @var int 38 44 */ 39 45 protected $items_per_page = 10; … … 42 48 * Enables search in this table listing. If this array 43 49 * is empty it means the listing is not searchable. 50 * 51 * @var array 44 52 */ 45 53 protected $search_by = array(); … … 49 57 * key must much the table column name and the value is the label, which is 50 58 * automatically translated. 59 * 60 * @var array 51 61 */ 52 62 protected $columns = array(); … … 59 69 * (with the prefix row_action_<key>) and the value is the label 60 70 * and title. 71 * 72 * @var array 61 73 */ 62 74 protected $row_actions = array(); … … 64 76 /** 65 77 * The Primary key of our table 78 * 79 * @var string 66 80 */ 67 81 protected $ID = 'ID'; … … 70 84 * Enables sorting, it expects an array 71 85 * of columns (the column names are the values) 86 * 87 * @var array 72 88 */ 73 89 protected $sort_by = array(); 74 90 91 /** 92 * The default sort order 93 * 94 * @var string 95 */ 75 96 protected $filter_by = array(); 76 97 77 98 /** 78 * @var array The status name => count combinations for this table's items. Used to display status filters. 99 * The status name => count combinations for this table's items. Used to display status filters. 100 * 101 * @var array 79 102 */ 80 103 protected $status_counts = array(); 81 104 82 105 /** 83 * @var array Notices to display when loading the table. Array of arrays of form array( 'class' => {updated|error}, 'message' => 'This is the notice text display.' ). 106 * Notices to display when loading the table. Array of arrays of form array( 'class' => {updated|error}, 'message' => 'This is the notice text display.' ). 107 * 108 * @var array 84 109 */ 85 110 protected $admin_notices = array(); 86 111 87 112 /** 88 * @var string Localised string displayed in the <h1> element above the able. 113 * Localised string displayed in the <h1> element above the able. 114 * 115 * @var string 89 116 */ 90 117 protected $table_header; … … 100 127 * is the array with primary keys, the second argument is a string with a list of the primary keys, 101 128 * escaped and ready to use (with `IN`). 129 * 130 * @var array 102 131 */ 103 132 protected $bulk_actions = array(); … … 107 136 * `_x` with some default (the package name). 108 137 * 109 * @deprecated 3.0.0 138 * @param string $text The new text to translate. 139 * @param string $context The context of the text. 140 * @return string|void The translated text. 141 * 142 * @deprecated 3.0.0 Use `_x()` instead. 110 143 */ 111 144 protected function translate( $text, $context = '' ) { … … 117 150 * also validates that the bulk method handler exists. It throws an exception because 118 151 * this is a library meant for developers and missing a bulk method is a development-time error. 152 * 153 * @return array 154 * 155 * @throws RuntimeException Throws RuntimeException when the bulk action does not have a callback method. 119 156 */ 120 157 protected function get_bulk_actions() { … … 147 184 check_admin_referer( 'bulk-' . $this->_args['plural'] ); 148 185 149 $method = 'bulk_' . $action;186 $method = 'bulk_' . $action; 150 187 if ( array_key_exists( $action, $this->bulk_actions ) && is_callable( array( $this, $method ) ) && ! empty( $_GET['ID'] ) && is_array( $_GET['ID'] ) ) { 151 188 $ids_sql = '(' . implode( ',', array_fill( 0, count( $_GET['ID'] ), '%s' ) ) . ')'; 152 $this->$method( $_GET['ID'], $wpdb->prepare( $ids_sql, $_GET['ID'] ) ); 153 } 154 155 wp_redirect( remove_query_arg( 156 array( '_wp_http_referer', '_wpnonce', 'ID', 'action', 'action2' ), 157 wp_unslash( $_SERVER['REQUEST_URI'] ) 158 ) ); 159 exit; 189 $id = array_map( 'absint', $_GET['ID'] ); 190 $this->$method( $id, $wpdb->prepare( $ids_sql, $id ) ); //phpcs:ignore WordPress.DB.PreparedSQL 191 } 192 193 if ( isset( $_SERVER['REQUEST_URI'] ) ) { 194 wp_safe_redirect( 195 remove_query_arg( 196 array( '_wp_http_referer', '_wpnonce', 'ID', 'action', 'action2' ), 197 esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) 198 ) 199 ); 200 exit; 201 } 160 202 } 161 203 … … 163 205 * Default code for deleting entries. 164 206 * validated already by process_bulk_action() 207 * 208 * @param array $ids ids of the items to delete. 209 * @param string $ids_sql the sql for the ids. 210 * @return void 165 211 */ 166 212 protected function bulk_delete( array $ids, $ids_sql ) { … … 218 264 global $wpdb; 219 265 220 $per_page = $this->get_items_per_page( $this-> package . '_items_per_page', $this->items_per_page );266 $per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page ); 221 267 return $wpdb->prepare( 'LIMIT %d', $per_page ); 222 268 } … … 228 274 */ 229 275 protected function get_items_offset() { 230 $per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page );276 $per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page ); 231 277 $current_page = $this->get_pagenum(); 232 278 if ( 1 < $current_page ) { … … 277 323 $valid_sortable_columns = array_values( $this->sort_by ); 278 324 279 if ( ! empty( $_GET['orderby'] ) && in_array( $_GET['orderby'], $valid_sortable_columns ) ) {280 $orderby = sanitize_text_field( $_GET['orderby'] );325 if ( ! empty( $_GET['orderby'] ) && in_array( $_GET['orderby'], $valid_sortable_columns, true ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 326 $orderby = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended 281 327 } else { 282 328 $orderby = $valid_sortable_columns[0]; … … 293 339 protected function get_request_order() { 294 340 295 if ( ! empty( $_GET['order'] ) && 'desc' === strtolower( $_GET['order'] ) ) {341 if ( ! empty( $_GET['order'] ) && 'desc' === strtolower( sanitize_text_field( wp_unslash( $_GET['order'] ) ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 296 342 $order = 'DESC'; 297 343 } else { … … 308 354 */ 309 355 protected function get_request_status() { 310 $status = ( ! empty( $_GET['status'] ) ) ? $_GET['status'] : '';356 $status = ( ! empty( $_GET['status'] ) ) ? sanitize_text_field( wp_unslash( $_GET['status'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended 311 357 return $status; 312 358 } … … 318 364 */ 319 365 protected function get_request_search_query() { 320 $search_query = ( ! empty( $_GET['s'] ) ) ? $_GET['s'] : '';366 $search_query = ( ! empty( $_GET['s'] ) ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended 321 367 return $search_query; 322 368 } … … 330 376 protected function get_table_columns() { 331 377 $columns = array_keys( $this->columns ); 332 if ( ! in_array( $this->ID, $columns ) ) {378 if ( ! in_array( $this->ID, $columns, true ) ) { 333 379 $columns[] = $this->ID; 334 380 } … … 344 390 * that feature it will return an empty string. 345 391 * 346 * TODO:347 * - Improve search doing LIKE by word rather than by phrases.348 *349 392 * @return string 350 393 */ … … 352 395 global $wpdb; 353 396 354 if ( empty( $_GET['s'] ) || empty( $this->search_by ) ) { 397 if ( empty( $_GET['s'] ) || empty( $this->search_by ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 355 398 return ''; 356 399 } 357 400 358 $filter = array(); 401 $search_string = sanitize_text_field( wp_unslash( $_GET['s'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended 402 403 $filter = array(); 359 404 foreach ( $this->search_by as $column ) { 360 $filter[] = $wpdb->prepare('`' . $column . '` like "%%s%"', $wpdb->esc_like( $_GET['s'] )); 405 $wild = '%'; 406 $sql_like = $wild . $wpdb->esc_like( $search_string ) . $wild; 407 $filter[] = $wpdb->prepare( '`' . $column . '` LIKE %s', $sql_like ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.DB.PreparedSQL.NotPrepared 361 408 } 362 409 return implode( ' OR ', $filter ); … … 370 417 global $wpdb; 371 418 372 if ( ! $this->filter_by || empty( $_GET['filter_by'] ) || ! is_array( $_GET['filter_by'] ) ) { 419 if ( ! $this->filter_by || empty( $_GET['filter_by'] ) || ! is_array( $_GET['filter_by'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 373 420 return ''; 374 421 } … … 377 424 378 425 foreach ( $this->filter_by as $column => $options ) { 379 if ( empty( $_GET['filter_by'][ $column ] ) || empty( $options[ $_GET['filter_by'][ $column ] ] ) ) { 426 if ( empty( $_GET['filter_by'][ $column ] ) || empty( $options[ $_GET['filter_by'][ $column ] ] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 380 427 continue; 381 428 } 382 429 383 $filter[] = $wpdb->prepare( "`$column` = %s", $_GET['filter_by'][ $column ] );430 $filter[] = $wpdb->prepare( "`$column` = %s", sanitize_text_field( wp_unslash( $_GET['filter_by'][ $column ] ) ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.DB.PreparedSQL.InterpolatedNotPrepared 384 431 } 385 432 … … 404 451 $this->process_row_actions(); 405 452 406 if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {453 if ( ! empty( $_REQUEST['_wp_http_referer'] && ! empty( $_SERVER['REQUEST_URI'] ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 407 454 // _wp_http_referer is used only on bulk actions, we remove it to keep the $_GET shorter 408 wp_ redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI']) ) );455 wp_safe_redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ); 409 456 exit; 410 457 } … … 415 462 $offset = $this->get_items_query_offset(); 416 463 $order = $this->get_items_query_order(); 417 $where = array_filter(array( 418 $this->get_items_query_search(), 419 $this->get_items_query_filters(), 420 )); 464 $where = array_filter( 465 array( 466 $this->get_items_query_search(), 467 $this->get_items_query_filters(), 468 ) 469 ); 421 470 $columns = '`' . implode( '`, `', $this->get_table_columns() ) . '`'; 422 471 423 472 if ( ! empty( $where ) ) { 424 $where = 'WHERE (' . implode( ') AND (', $where ) . ')';473 $where = 'WHERE (' . implode( ') AND (', $where ) . ')'; 425 474 } else { 426 475 $where = ''; … … 429 478 $sql = "SELECT $columns FROM {$this->table_name} {$where} {$order} {$limit} {$offset}"; 430 479 431 $this->set_items( $wpdb->get_results( $sql, ARRAY_A ) ); 480 $this->set_items( $wpdb->get_results( $sql, ARRAY_A ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 432 481 433 482 $query_count = "SELECT COUNT({$this->ID}) FROM {$this->table_name} {$where}"; 434 $total_items = $wpdb->get_var( $query_count ); 435 $per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page ); 436 $this->set_pagination_args( array( 437 'total_items' => $total_items, 438 'per_page' => $per_page, 439 'total_pages' => ceil( $total_items / $per_page ), 440 ) ); 441 } 442 483 $total_items = $wpdb->get_var( $query_count ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 484 $per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page ); 485 $this->set_pagination_args( 486 array( 487 'total_items' => $total_items, 488 'per_page' => $per_page, 489 'total_pages' => ceil( $total_items / $per_page ), 490 ) 491 ); 492 } 493 494 /** 495 * Display the table. 496 * 497 * @param string $which The name of the table. 498 */ 443 499 public function extra_tablenav( $which ) { 444 500 if ( ! $this->filter_by || 'top' !== $which ) { … … 449 505 450 506 foreach ( $this->filter_by as $id => $options ) { 451 $default = ! empty( $_GET['filter_by'][ $id ] ) ? $_GET['filter_by'][ $id ] : '';507 $default = ! empty( $_GET['filter_by'][ $id ] ) ? sanitize_text_field( wp_unslash( $_GET['filter_by'][ $id ] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended 452 508 if ( empty( $options[ $default ] ) ) { 453 509 $default = ''; … … 457 513 458 514 foreach ( $options as $value => $label ) { 459 echo '<option value="' . esc_attr( $value ) . '" ' . esc_html( $value == $default ? 'selected' : '' ) .'>'515 echo '<option value="' . esc_attr( $value ) . '" ' . esc_html( $value === $default ? 'selected' : '' ) . '>' 460 516 . esc_html( $label ) 461 517 . '</option>'; … … 472 528 * Set the data for displaying. It will attempt to unserialize (There is a chance that some columns 473 529 * are serialized). This can be override in child classes for futher data transformation. 530 * 531 * @param array $items Items array. 474 532 */ 475 533 protected function set_items( array $items ) { … … 484 542 * of how the primary key is named (to keep the code simpler). The bulk actions will do the proper 485 543 * name transformation though using `$this->ID`. 544 * 545 * @param array $row The row to render. 486 546 */ 487 547 public function column_cb( $row ) { 488 return '<input name="ID[]" type="checkbox" value="' . esc_attr( $row[ $this->ID ] ) . '" />';548 return '<input name="ID[]" type="checkbox" value="' . esc_attr( $row[ $this->ID ] ) . '" />'; 489 549 } 490 550 … … 495 555 * and it checks that the row action method exists before rendering it. 496 556 * 497 * @param array $row Row to render498 * @param $column_name Current row499 * @return 557 * @param array $row Row to be rendered. 558 * @param string $column_name Column name. 559 * @return string 500 560 */ 501 561 protected function maybe_render_actions( $row, $column_name ) { … … 506 566 $row_id = $row[ $this->ID ]; 507 567 508 $actions = '<div class="row-actions">';568 $actions = '<div class="row-actions">'; 509 569 $action_count = 0; 510 570 foreach ( $this->row_actions[ $column_name ] as $action_key => $action ) { … … 516 576 } 517 577 518 $action_link = ! empty( $action['link'] ) ? $action['link'] : add_query_arg( array( 'row_action' => $action_key, 'row_id' => $row_id, 'nonce' => wp_create_nonce( $action_key . '::' . $row_id ) ) ); 578 $action_link = ! empty( $action['link'] ) ? $action['link'] : add_query_arg( 579 array( 580 'row_action' => $action_key, 581 'row_id' => $row_id, 582 'nonce' => wp_create_nonce( $action_key . '::' . $row_id ), 583 ) 584 ); 519 585 $span_class = ! empty( $action['class'] ) ? $action['class'] : $action_key; 520 586 $separator = ( $action_count < count( $this->row_actions[ $column_name ] ) ) ? ' | ' : ''; … … 528 594 } 529 595 596 /** 597 * Process the bulk actions. 598 * 599 * @return void 600 */ 530 601 protected function process_row_actions() { 531 602 $parameters = array( 'row_action', 'row_id', 'nonce' ); 532 603 foreach ( $parameters as $parameter ) { 533 if ( empty( $_REQUEST[ $parameter ] ) ) { 604 if ( empty( $_REQUEST[ $parameter ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 534 605 return; 535 606 } 536 607 } 537 608 538 $method = 'row_action_' . $_REQUEST['row_action']; 539 540 if ( $_REQUEST['nonce'] === wp_create_nonce( $_REQUEST[ 'row_action' ] . '::' . $_REQUEST[ 'row_id' ] ) && method_exists( $this, $method ) ) { 541 $this->$method( $_REQUEST['row_id'] ); 542 } 543 544 wp_redirect( remove_query_arg( 545 array( 'row_id', 'row_action', 'nonce' ), 546 wp_unslash( $_SERVER['REQUEST_URI'] ) 547 ) ); 548 exit; 609 $action = sanitize_text_field( wp_unslash( $_REQUEST['row_action'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated 610 $row_id = sanitize_text_field( wp_unslash( $_REQUEST['row_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated 611 $nonce = sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated 612 $method = 'row_action_' . $action; // phpcs:ignore WordPress.Security.NonceVerification.Recommended 613 614 if ( wp_verify_nonce( $nonce, $action . '::' . $row_id ) && method_exists( $this, $method ) ) { 615 $this->$method( sanitize_text_field( wp_unslash( $row_id ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended 616 } 617 618 if ( isset( $_SERVER['REQUEST_URI'] ) ) { 619 wp_safe_redirect( 620 remove_query_arg( 621 array( 'row_id', 'row_action', 'nonce' ), 622 esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) 623 ) 624 ); 625 exit; 626 } 549 627 } 550 628 551 629 /** 552 630 * Default column formatting, it will escape everythig for security. 631 * 632 * @param array $item The item array. 633 * @param string $column_name Column name to display. 634 * 635 * @return string 553 636 */ 554 637 public function column_default( $item, $column_name ) { 555 $column_html = esc_html( $item[ $column_name ] );638 $column_html = esc_html( $item[ $column_name ] ); 556 639 $column_html .= $this->maybe_render_actions( $item, $column_name ); 557 640 return $column_html; … … 575 658 protected function display_admin_notices() { 576 659 foreach ( $this->admin_notices as $notice ) { 577 echo '<div id="message" class="' . $notice['class']. '">';660 echo '<div id="message" class="' . esc_attr( $notice['class'] ) . '">'; 578 661 echo ' <p>' . wp_kses_post( $notice['message'] ) . '</p>'; 579 662 echo '</div>'; … … 589 672 $request_status = $this->get_request_status(); 590 673 591 // Helper to set 'all' filter when not set on status counts passed in 674 // Helper to set 'all' filter when not set on status counts passed in. 592 675 if ( ! isset( $this->status_counts['all'] ) ) { 593 676 $this->status_counts = array( 'all' => array_sum( $this->status_counts ) ) + $this->status_counts; … … 613 696 if ( $status_list_items ) { 614 697 echo '<ul class="subsubsub">'; 615 echo implode( " | \n", $status_list_items ); 698 echo implode( " | \n", $status_list_items ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 616 699 echo '</ul>'; 617 700 } … … 625 708 protected function display_table() { 626 709 echo '<form id="' . esc_attr( $this->_args['plural'] ) . '-filter" method="get">'; 627 foreach ( $_GET as $key => $value ) { 710 foreach ( $_GET as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 628 711 if ( '_' === $key[0] || 'paged' === $key || 'ID' === $key ) { 629 712 continue; … … 632 715 } 633 716 if ( ! empty( $this->search_by ) ) { 634 echo $this->search_box( $this->get_search_box_button_text(), 'plugin' ); // WPCS: XSS OK717 echo $this->search_box( $this->get_search_box_button_text(), 'plugin' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 635 718 } 636 719 parent::display(); … … 645 728 $this->process_row_actions(); 646 729 647 if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {730 if ( ! empty( $_REQUEST['_wp_http_referer'] ) && ! empty( $_SERVER['REQUEST_URI'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 648 731 // _wp_http_referer is used only on bulk actions, we remove it to keep the $_GET shorter 649 wp_ redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI']) ) );732 wp_safe_redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ); 650 733 exit; 651 734 } … … 672 755 return esc_html__( 'Search', 'action-scheduler' ); 673 756 } 757 758 /** 759 * Gets the screen per_page option name. 760 * 761 * @return string 762 */ 763 protected function get_per_page_option_name() { 764 return $this->package . '_items_per_page'; 765 } 674 766 } -
action-scheduler/tags/3.4.0/classes/data-stores/ActionScheduler_DBLogger.php
r2599552 r2622865 30 30 $date_local = $date->format( 'Y-m-d H:i:s' ); 31 31 32 /** @var \wpdb $wpdb */ 32 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 33 33 global $wpdb; 34 $wpdb->insert( $wpdb->actionscheduler_logs, [ 35 'action_id' => $action_id, 36 'message' => $message, 37 'log_date_gmt' => $date_gmt, 38 'log_date_local' => $date_local, 39 ], [ '%d', '%s', '%s', '%s' ] ); 34 $wpdb->insert( 35 $wpdb->actionscheduler_logs, 36 array( 37 'action_id' => $action_id, 38 'message' => $message, 39 'log_date_gmt' => $date_gmt, 40 'log_date_local' => $date_local, 41 ), 42 array( '%d', '%s', '%s', '%s' ) 43 ); 40 44 41 45 return $wpdb->insert_id; … … 50 54 */ 51 55 public function get_entry( $entry_id ) { 52 /** @var \wpdb $wpdb */ 56 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 53 57 global $wpdb; 54 58 $entry = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->actionscheduler_logs} WHERE log_id=%d", $entry_id ) ); … … 86 90 */ 87 91 public function get_logs( $action_id ) { 88 /** @var \wpdb $wpdb */ 92 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 89 93 global $wpdb; 90 94 91 95 $records = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->actionscheduler_logs} WHERE action_id=%d", $action_id ) ); 92 96 93 return array_map( [ $this, 'create_entry_from_db_record' ], $records );97 return array_map( array( $this, 'create_entry_from_db_record' ), $records ); 94 98 } 95 99 … … 106 110 parent::init(); 107 111 108 add_action( 'action_scheduler_deleted_action', [ $this, 'clear_deleted_action_logs' ], 10, 1 );112 add_action( 'action_scheduler_deleted_action', array( $this, 'clear_deleted_action_logs' ), 10, 1 ); 109 113 } 110 114 … … 115 119 */ 116 120 public function clear_deleted_action_logs( $action_id ) { 117 /** @var \wpdb $wpdb */ 121 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 118 122 global $wpdb; 119 $wpdb->delete( $wpdb->actionscheduler_logs, [ 'action_id' => $action_id, ], [ '%d' ]);123 $wpdb->delete( $wpdb->actionscheduler_logs, array( 'action_id' => $action_id ), array( '%d' ) ); 120 124 } 121 125 … … 130 134 } 131 135 132 /** @var \wpdb $wpdb */ 136 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 133 137 global $wpdb; 134 138 $date = as_get_datetime_object(); … … 139 143 $format = '(%d, ' . $wpdb->prepare( '%s, %s, %s', $message, $date_gmt, $date_local ) . ')'; 140 144 $sql_query = "INSERT {$wpdb->actionscheduler_logs} (action_id, message, log_date_gmt, log_date_local) VALUES "; 141 $value_rows = [];145 $value_rows = array(); 142 146 143 147 foreach ( $action_ids as $action_id ) { 144 $value_rows[] = $wpdb->prepare( $format, $action_id ); 148 $value_rows[] = $wpdb->prepare( $format, $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 145 149 } 146 150 $sql_query .= implode( ',', $value_rows ); 147 151 148 $wpdb->query( $sql_query ); 152 $wpdb->query( $sql_query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 149 153 } 150 154 } -
action-scheduler/tags/3.4.0/classes/data-stores/ActionScheduler_DBStore.php
r2599552 r2622865 41 41 * 42 42 * @param ActionScheduler_Action $action Action object. 43 * @param DateTime $date Optional schedule date. Default null.43 * @param DateTime $date Optional schedule date. Default null. 44 44 * 45 45 * @return int Action ID. 46 * @throws RuntimeException Throws exception when saving the action fails. 46 47 */ 47 48 public function save_action( ActionScheduler_Action $action, \DateTime $date = null ) { … … 52 53 /** @var \wpdb $wpdb */ 53 54 global $wpdb; 54 $data = [55 $data = array( 55 56 'hook' => $action->get_hook(), 56 57 'status' => ( $action->is_finished() ? self::STATUS_COMPLETE : self::STATUS_PENDING ), 57 58 'scheduled_date_gmt' => $this->get_scheduled_date_string( $action, $date ), 58 59 'scheduled_date_local' => $this->get_scheduled_date_string_local( $action, $date ), 59 'schedule' => serialize( $action->get_schedule() ), 60 'schedule' => serialize( $action->get_schedule() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize 60 61 'group_id' => $this->get_group_id( $action->get_group() ), 61 ];62 ); 62 63 $args = wp_json_encode( $action->get_args() ); 63 64 if ( strlen( $args ) <= static::$max_index_length ) { … … 73 74 74 75 if ( is_wp_error( $action_id ) ) { 75 throw new RuntimeException( $action_id->get_error_message() ); 76 } 77 elseif ( empty( $action_id ) ) { 78 throw new RuntimeException( $wpdb->last_error ? $wpdb->last_error : __( 'Database error.', 'action-scheduler' ) ); 76 throw new \RuntimeException( $action_id->get_error_message() ); 77 } elseif ( empty( $action_id ) ) { 78 throw new \RuntimeException( $wpdb->last_error ? $wpdb->last_error : __( 'Database error.', 'action-scheduler' ) ); 79 79 } 80 80 … … 115 115 * 116 116 * @param string $slug The string name of a group. 117 * @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group.117 * @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group. 118 118 * 119 119 * @return int The group's ID, if it exists or is created, or 0 if it does not exist and is not created. … … 143 143 /** @var \wpdb $wpdb */ 144 144 global $wpdb; 145 $wpdb->insert( $wpdb->actionscheduler_groups, [ 'slug' => $slug ]);145 $wpdb->insert( $wpdb->actionscheduler_groups, array( 'slug' => $slug ) ); 146 146 147 147 return (int) $wpdb->insert_id; … … 158 158 /** @var \wpdb $wpdb */ 159 159 global $wpdb; 160 $data = $wpdb->get_row( $wpdb->prepare( 161 "SELECT a.*, g.slug AS `group` FROM {$wpdb->actionscheduler_actions} a LEFT JOIN {$wpdb->actionscheduler_groups} g ON a.group_id=g.group_id WHERE a.action_id=%d", 162 $action_id 163 ) ); 160 $data = $wpdb->get_row( 161 $wpdb->prepare( 162 "SELECT a.*, g.slug AS `group` FROM {$wpdb->actionscheduler_actions} a LEFT JOIN {$wpdb->actionscheduler_groups} g ON a.group_id=g.group_id WHERE a.action_id=%d", 163 $action_id 164 ) 165 ); 164 166 165 167 if ( empty( $data ) ) { … … 173 175 174 176 // Convert NULL dates to zero dates. 175 $date_fields = [177 $date_fields = array( 176 178 'scheduled_date_gmt', 177 179 'scheduled_date_local', 178 180 'last_attempt_gmt', 179 'last_attempt_gmt' 180 ];181 foreach ( $date_fields as $date_field ) {181 'last_attempt_gmt', 182 ); 183 foreach ( $date_fields as $date_field ) { 182 184 if ( is_null( $data->$date_field ) ) { 183 185 $data->$date_field = ActionScheduler_StoreSchema::DEFAULT_DATE; … … 215 217 $hook = $data->hook; 216 218 $args = json_decode( $data->args, true ); 217 $schedule = unserialize( $data->schedule ); 219 $schedule = unserialize( $data->schedule ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize 218 220 219 221 $this->validate_args( $args, $data->action_id ); … … 237 239 * 238 240 * @return string SQL statement already properly escaped. 241 * @throws InvalidArgumentException If the query is invalid. 239 242 */ 240 243 protected function get_query_actions_sql( array $query, $select_or_count = 'select' ) { 241 244 242 if ( ! in_array( $select_or_count, array( 'select', 'count' ) ) ) {245 if ( ! in_array( $select_or_count, array( 'select', 'count' ), true ) ) { 243 246 throw new InvalidArgumentException( __( 'Invalid value for select or count parameter. Cannot query actions.', 'action-scheduler' ) ); 244 247 } 245 248 246 $query = wp_parse_args( $query, [ 247 'hook' => '', 248 'args' => null, 249 'date' => null, 250 'date_compare' => '<=', 251 'modified' => null, 252 'modified_compare' => '<=', 253 'group' => '', 254 'status' => '', 255 'claimed' => null, 256 'per_page' => 5, 257 'offset' => 0, 258 'orderby' => 'date', 259 'order' => 'ASC', 260 ] ); 261 262 /** @var \wpdb $wpdb */ 263 global $wpdb; 264 $sql = ( 'count' === $select_or_count ) ? 'SELECT count(a.action_id)' : 'SELECT a.action_id'; 265 $sql .= " FROM {$wpdb->actionscheduler_actions} a"; 266 $sql_params = []; 267 268 if ( ! empty( $query[ 'group' ] ) || 'group' === $query[ 'orderby' ] ) { 249 $query = wp_parse_args( 250 $query, 251 array( 252 'hook' => '', 253 'args' => null, 254 'date' => null, 255 'date_compare' => '<=', 256 'modified' => null, 257 'modified_compare' => '<=', 258 'group' => '', 259 'status' => '', 260 'claimed' => null, 261 'per_page' => 5, 262 'offset' => 0, 263 'orderby' => 'date', 264 'order' => 'ASC', 265 ) 266 ); 267 268 /** @var \wpdb $wpdb */ 269 global $wpdb; 270 $sql = ( 'count' === $select_or_count ) ? 'SELECT count(a.action_id)' : 'SELECT a.action_id'; 271 $sql .= " FROM {$wpdb->actionscheduler_actions} a"; 272 $sql_params = array(); 273 274 if ( ! empty( $query['group'] ) || 'group' === $query['orderby'] ) { 269 275 $sql .= " LEFT JOIN {$wpdb->actionscheduler_groups} g ON g.group_id=a.group_id"; 270 276 } 271 277 272 $sql .= " WHERE 1=1";273 274 if ( ! empty( $query[ 'group'] ) ) {275 $sql .= " AND g.slug=%s";276 $sql_params[] = $query[ 'group'];277 } 278 279 if ( $query[ 'hook'] ) {280 $sql .= " AND a.hook=%s";281 $sql_params[] = $query[ 'hook'];282 } 283 if ( ! is_null( $query[ 'args'] ) ) {284 $sql .= " AND a.args=%s";285 $sql_params[] = $this->get_args_for_query( $query[ 'args'] );278 $sql .= ' WHERE 1=1'; 279 280 if ( ! empty( $query['group'] ) ) { 281 $sql .= ' AND g.slug=%s'; 282 $sql_params[] = $query['group']; 283 } 284 285 if ( $query['hook'] ) { 286 $sql .= ' AND a.hook=%s'; 287 $sql_params[] = $query['hook']; 288 } 289 if ( ! is_null( $query['args'] ) ) { 290 $sql .= ' AND a.args=%s'; 291 $sql_params[] = $this->get_args_for_query( $query['args'] ); 286 292 } 287 293 … … 293 299 } 294 300 295 if ( $query[ 'date'] instanceof \DateTime ) {296 $date = clone $query[ 'date'];301 if ( $query['date'] instanceof \DateTime ) { 302 $date = clone $query['date']; 297 303 $date->setTimezone( new \DateTimeZone( 'UTC' ) ); 298 304 $date_string = $date->format( 'Y-m-d H:i:s' ); 299 $comparator = $this->validate_sql_comparator( $query[ 'date_compare'] );300 $sql .= " AND a.scheduled_date_gmt $comparator %s";305 $comparator = $this->validate_sql_comparator( $query['date_compare'] ); 306 $sql .= " AND a.scheduled_date_gmt $comparator %s"; 301 307 $sql_params[] = $date_string; 302 308 } 303 309 304 if ( $query[ 'modified'] instanceof \DateTime ) {305 $modified = clone $query[ 'modified'];310 if ( $query['modified'] instanceof \DateTime ) { 311 $modified = clone $query['modified']; 306 312 $modified->setTimezone( new \DateTimeZone( 'UTC' ) ); 307 313 $date_string = $modified->format( 'Y-m-d H:i:s' ); 308 $comparator = $this->validate_sql_comparator( $query[ 'modified_compare'] );309 $sql .= " AND a.last_attempt_gmt $comparator %s";314 $comparator = $this->validate_sql_comparator( $query['modified_compare'] ); 315 $sql .= " AND a.last_attempt_gmt $comparator %s"; 310 316 $sql_params[] = $date_string; 311 317 } 312 318 313 if ( $query[ 'claimed' ] === true) {314 $sql .= " AND a.claim_id != 0";315 } elseif ( $query[ 'claimed' ] === false) {316 $sql .= " AND a.claim_id = 0";317 } elseif ( ! is_null( $query[ 'claimed'] ) ) {318 $sql .= " AND a.claim_id = %d";319 $sql_params[] = $query[ 'claimed'];319 if ( true === $query['claimed'] ) { 320 $sql .= ' AND a.claim_id != 0'; 321 } elseif ( false === $query['claimed'] ) { 322 $sql .= ' AND a.claim_id = 0'; 323 } elseif ( ! is_null( $query['claimed'] ) ) { 324 $sql .= ' AND a.claim_id = %d'; 325 $sql_params[] = $query['claimed']; 320 326 } 321 327 322 328 if ( ! empty( $query['search'] ) ) { 323 $sql .= " AND (a.hook LIKE %s OR (a.extended_args IS NULL AND a.args LIKE %s) OR a.extended_args LIKE %s";324 for ( $i = 0; $i < 3; $i++ ) {329 $sql .= ' AND (a.hook LIKE %s OR (a.extended_args IS NULL AND a.args LIKE %s) OR a.extended_args LIKE %s'; 330 for ( $i = 0; $i < 3; $i++ ) { 325 331 $sql_params[] = sprintf( '%%%s%%', $query['search'] ); 326 332 } … … 328 334 $search_claim_id = (int) $query['search']; 329 335 if ( $search_claim_id ) { 330 $sql .= ' OR a.claim_id = %d';336 $sql .= ' OR a.claim_id = %d'; 331 337 $sql_params[] = $search_claim_id; 332 338 } … … 362 368 } 363 369 364 if ( $query[ 'per_page'] > 0 ) {365 $sql .= " LIMIT %d, %d";366 $sql_params[] = $query[ 'offset'];367 $sql_params[] = $query[ 'per_page'];370 if ( $query['per_page'] > 0 ) { 371 $sql .= ' LIMIT %d, %d'; 372 $sql_params[] = $query['offset']; 373 $sql_params[] = $query['per_page']; 368 374 } 369 375 } 370 376 371 377 if ( ! empty( $sql_params ) ) { 372 $sql = $wpdb->prepare( $sql, $sql_params ); 378 $sql = $wpdb->prepare( $sql, $sql_params ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 373 379 } 374 380 … … 388 394 * @return string|array|null The IDs of actions matching the query. Null on failure. 389 395 */ 390 public function query_actions( $query = [], $query_type = 'select' ) {396 public function query_actions( $query = array(), $query_type = 'select' ) { 391 397 /** @var wpdb $wpdb */ 392 398 global $wpdb; … … 394 400 $sql = $this->get_query_actions_sql( $query, $query_type ); 395 401 396 return ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); 402 return ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoSql, WordPress.DB.DirectDatabaseQuery.NoCaching 397 403 } 398 404 … … 407 413 $sql = "SELECT a.status, count(a.status) as 'count'"; 408 414 $sql .= " FROM {$wpdb->actionscheduler_actions} a"; 409 $sql .= " GROUP BY a.status";415 $sql .= ' GROUP BY a.status'; 410 416 411 417 $actions_count_by_status = array(); 412 418 $action_stati_and_labels = $this->get_status_labels(); 413 419 414 foreach ( $wpdb->get_results( $sql ) as $action_data ) { 415 // Ignore any actions with invalid status 420 foreach ( $wpdb->get_results( $sql ) as $action_data ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 421 // Ignore any actions with invalid status. 416 422 if ( array_key_exists( $action_data->status, $action_stati_and_labels ) ) { 417 423 $actions_count_by_status[ $action_data->status ] = $action_data->count; … … 428 434 * 429 435 * @return void 436 * @throws \InvalidArgumentException If the action update failed. 430 437 */ 431 438 public function cancel_action( $action_id ) { … … 435 442 $updated = $wpdb->update( 436 443 $wpdb->actionscheduler_actions, 437 [ 'status' => self::STATUS_CANCELED ],438 [ 'action_id' => $action_id ],439 [ '%s' ],440 [ '%d' ]444 array( 'status' => self::STATUS_CANCELED ), 445 array( 'action_id' => $action_id ), 446 array( '%s' ), 447 array( '%d' ) 441 448 ); 442 449 if ( empty( $updated ) ) { … … 457 464 */ 458 465 public function cancel_actions_by_hook( $hook ) { 459 $this->bulk_cancel_actions( [ 'hook' => $hook ]);466 $this->bulk_cancel_actions( array( 'hook' => $hook ) ); 460 467 } 461 468 … … 468 475 */ 469 476 public function cancel_actions_by_group( $group ) { 470 $this->bulk_cancel_actions( [ 'group' => $group ]);477 $this->bulk_cancel_actions( array( 'group' => $group ) ); 471 478 } 472 479 … … 487 494 488 495 // Don't cancel actions that are already canceled. 489 if ( isset( $query_args['status'] ) && $query_args['status'] == self::STATUS_CANCELED) {496 if ( isset( $query_args['status'] ) && self::STATUS_CANCELED === $query_args['status'] ) { 490 497 return; 491 498 } … … 494 501 $query_args = wp_parse_args( 495 502 $query_args, 496 [503 array( 497 504 'per_page' => 1000, 498 505 'status' => self::STATUS_PENDING, 499 506 'orderby' => 'action_id', 500 ]507 ) 501 508 ); 502 509 … … 513 520 514 521 $wpdb->query( 515 $wpdb->prepare( // wpcs: PreparedSQLPlaceholders replacement count ok.516 "UPDATE {$wpdb->actionscheduler_actions} SET status = %s WHERE action_id IN {$query_in}", 522 $wpdb->prepare( 523 "UPDATE {$wpdb->actionscheduler_actions} SET status = %s WHERE action_id IN {$query_in}", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 517 524 $parameters 518 525 ) … … 527 534 * 528 535 * @param int $action_id Action ID. 536 * @throws \InvalidArgumentException If the action deletion failed. 529 537 */ 530 538 public function delete_action( $action_id ) { 531 539 /** @var \wpdb $wpdb */ 532 540 global $wpdb; 533 $deleted = $wpdb->delete( $wpdb->actionscheduler_actions, [ 'action_id' => $action_id ], [ '%d' ]);541 $deleted = $wpdb->delete( $wpdb->actionscheduler_actions, array( 'action_id' => $action_id ), array( '%d' ) ); 534 542 if ( empty( $deleted ) ) { 535 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 543 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment 536 544 } 537 545 do_action( 'action_scheduler_deleted_action', $action_id ); … … 543 551 * @param string $action_id Action ID. 544 552 * 545 * @throws \InvalidArgumentException546 553 * @return \DateTime The local date the action is scheduled to run, or the date that it ran. 547 554 */ … … 557 564 * @param int $action_id Action ID. 558 565 * 559 * @throws \InvalidArgumentException 566 * @throws \InvalidArgumentException If action cannot be identified. 560 567 * @return \DateTime The GMT date the action is scheduled to run, or the date that it ran. 561 568 */ … … 565 572 $record = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d", $action_id ) ); 566 573 if ( empty( $record ) ) { 567 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 568 } 569 if ( $record->status == self::STATUS_PENDING) {574 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment 575 } 576 if ( self::STATUS_PENDING === $record->status ) { 570 577 return as_get_datetime_object( $record->scheduled_date_gmt ); 571 578 } else { … … 579 586 * @param int $max_actions Maximum number of action to include in claim. 580 587 * @param \DateTime $before_date Jobs must be schedule before this date. Defaults to now. 588 * @param array $hooks Hooks to filter for. 589 * @param string $group Group to filter for. 581 590 * 582 591 * @return ActionScheduler_ActionClaim … … 587 596 $this->claim_before_date = $before_date; 588 597 $this->claim_actions( $claim_id, $max_actions, $before_date, $hooks, $group ); 589 $action_ids = $this->find_actions_by_claim_id( $claim_id );598 $action_ids = $this->find_actions_by_claim_id( $claim_id ); 590 599 $this->claim_before_date = null; 591 600 … … 602 611 global $wpdb; 603 612 $now = as_get_datetime_object(); 604 $wpdb->insert( $wpdb->actionscheduler_claims, [ 'date_created_gmt' => $now->format( 'Y-m-d H:i:s' ) ]);613 $wpdb->insert( $wpdb->actionscheduler_claims, array( 'date_created_gmt' => $now->format( 'Y-m-d H:i:s' ) ) ); 605 614 606 615 return $wpdb->insert_id; … … 613 622 * @param int $limit Number of action to include in claim. 614 623 * @param \DateTime $before_date Should use UTC timezone. 624 * @param array $hooks Hooks to filter for. 625 * @param string $group Group to filter for. 615 626 * 616 627 * @return int The number of actions that were claimed. 617 * @throws \RuntimeException 628 * @throws \InvalidArgumentException Throws InvalidArgumentException if group doesn't exist. 629 * @throws \RuntimeException Throws RuntimeException if unable to claim action. 618 630 */ 619 631 protected function claim_actions( $claim_id, $limit, \DateTime $before_date = null, $hooks = array(), $group = '' ) { … … 624 636 $date = is_null( $before_date ) ? $now : clone $before_date; 625 637 626 // can't use $wpdb->update() because of the <= condition 638 // can't use $wpdb->update() because of the <= condition. 627 639 $update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s"; 628 640 $params = array( … … 632 644 ); 633 645 634 $where = "WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s";646 $where = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s'; 635 647 $params[] = $date->format( 'Y-m-d H:i:s' ); 636 648 $params[] = self::STATUS_PENDING; … … 646 658 $group_id = $this->get_group_id( $group, false ); 647 659 648 // throw exception if no matching group found, this matches ActionScheduler_wpPostStore's behaviour 660 // throw exception if no matching group found, this matches ActionScheduler_wpPostStore's behaviour. 649 661 if ( empty( $group_id ) ) { 650 662 /* translators: %s: group name */ … … 656 668 } 657 669 658 $order = "ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC LIMIT %d"; 670 /** 671 * Sets the order-by clause used in the action claim query. 672 * 673 * @since x.x.x 674 * 675 * @param string $order_by_sql 676 */ 677 $order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC' ); 659 678 $params[] = $limit; 660 679 661 $sql = $wpdb->prepare( "{$update} {$where} {$order}", $params ); 662 663 $rows_affected = $wpdb->query( $sql ); 664 if ( $rows_affected === false ) { 680 $sql = $wpdb->prepare( "{$update} {$where} {$order} LIMIT %d", $params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders 681 $rows_affected = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 682 if ( false === $rows_affected ) { 665 683 throw new \RuntimeException( __( 'Unable to claim actions. Database error.', 'action-scheduler' ) ); 666 684 } … … 678 696 679 697 $sql = "SELECT COUNT(DISTINCT claim_id) FROM {$wpdb->actionscheduler_actions} WHERE claim_id != 0 AND status IN ( %s, %s)"; 680 $sql = $wpdb->prepare( $sql, [ self::STATUS_PENDING, self::STATUS_RUNNING ] );681 682 return (int) $wpdb->get_var( $sql ); 698 $sql = $wpdb->prepare( $sql, array( self::STATUS_PENDING, self::STATUS_RUNNING ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 699 700 return (int) $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 683 701 } 684 702 … … 694 712 695 713 $sql = "SELECT claim_id FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d"; 696 $sql = $wpdb->prepare( $sql, $action_id ); 697 698 return (int) $wpdb->get_var( $sql ); 714 $sql = $wpdb->prepare( $sql, $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 715 716 return (int) $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 699 717 } 700 718 … … 702 720 * Retrieve the action IDs of action in a claim. 703 721 * 722 * @param int $claim_id Claim ID. 704 723 * @return int[] 705 724 */ … … 719 738 // Verify that the scheduled date for each action is within the expected bounds (in some unusual 720 739 // cases, we cannot depend on MySQL to honor all of the WHERE conditions we specify). 721 foreach ( $wpdb->get_results( $sql ) as $claimed_action ) { 740 foreach ( $wpdb->get_results( $sql ) as $claimed_action ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 722 741 if ( $claimed_action->scheduled_date_gmt <= $cut_off ) { 723 742 $action_ids[] = absint( $claimed_action->action_id ); … … 736 755 /** @var \wpdb $wpdb */ 737 756 global $wpdb; 738 $wpdb->update( $wpdb->actionscheduler_actions, [ 'claim_id' => 0 ], [ 'claim_id' => $claim->get_id() ], [ '%d' ], [ '%d' ]);739 $wpdb->delete( $wpdb->actionscheduler_claims, [ 'claim_id' => $claim->get_id() ], [ '%d' ]);757 $wpdb->update( $wpdb->actionscheduler_actions, array( 'claim_id' => 0 ), array( 'claim_id' => $claim->get_id() ), array( '%d' ), array( '%d' ) ); 758 $wpdb->delete( $wpdb->actionscheduler_claims, array( 'claim_id' => $claim->get_id() ), array( '%d' ) ); 740 759 } 741 760 … … 752 771 $wpdb->update( 753 772 $wpdb->actionscheduler_actions, 754 [ 'claim_id' => 0 ],755 [ 'action_id' => $action_id ],756 [ '%s' ],757 [ '%d' ]773 array( 'claim_id' => 0 ), 774 array( 'action_id' => $action_id ), 775 array( '%s' ), 776 array( '%d' ) 758 777 ); 759 778 } … … 763 782 * 764 783 * @param int $action_id Action ID. 784 * @throws \InvalidArgumentException Throw an exception if action was not updated. 765 785 */ 766 786 public function mark_failure( $action_id ) { … … 769 789 $updated = $wpdb->update( 770 790 $wpdb->actionscheduler_actions, 771 [ 'status' => self::STATUS_FAILED ],772 [ 'action_id' => $action_id ],773 [ '%s' ],774 [ '%d' ]791 array( 'status' => self::STATUS_FAILED ), 792 array( 'action_id' => $action_id ), 793 array( '%s' ), 794 array( '%d' ) 775 795 ); 776 796 if ( empty( $updated ) ) { 777 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 797 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment 778 798 } 779 799 } … … 791 811 792 812 $sql = "UPDATE {$wpdb->actionscheduler_actions} SET attempts = attempts+1, status=%s, last_attempt_gmt = %s, last_attempt_local = %s WHERE action_id = %d"; 793 $sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time( 'mysql', true ), current_time( 'mysql' ), $action_id ); 794 $wpdb->query( $sql ); 813 $sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time( 'mysql', true ), current_time( 'mysql' ), $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 814 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 795 815 } 796 816 … … 801 821 * 802 822 * @return void 823 * @throws \InvalidArgumentException Throw an exception if action was not updated. 803 824 */ 804 825 public function mark_complete( $action_id ) { … … 807 828 $updated = $wpdb->update( 808 829 $wpdb->actionscheduler_actions, 809 [830 array( 810 831 'status' => self::STATUS_COMPLETE, 811 832 'last_attempt_gmt' => current_time( 'mysql', true ), 812 833 'last_attempt_local' => current_time( 'mysql' ), 813 ],814 [ 'action_id' => $action_id ],815 [ '%s' ],816 [ '%d' ]834 ), 835 array( 'action_id' => $action_id ), 836 array( '%s' ), 837 array( '%d' ) 817 838 ); 818 839 if ( empty( $updated ) ) { 819 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 840 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment 820 841 } 821 842 } … … 827 848 * 828 849 * @return string 850 * @throws \InvalidArgumentException Throw an exception if not status was found for action_id. 851 * @throws \RuntimeException Throw an exception if action status could not be retrieved. 829 852 */ 830 853 public function get_status( $action_id ) { … … 832 855 global $wpdb; 833 856 $sql = "SELECT status FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d"; 834 $sql = $wpdb->prepare( $sql, $action_id ); 835 $status = $wpdb->get_var( $sql ); 836 837 if ( $status === null) {857 $sql = $wpdb->prepare( $sql, $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 858 $status = $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 859 860 if ( null === $status ) { 838 861 throw new \InvalidArgumentException( __( 'Invalid action ID. No status found.', 'action-scheduler' ) ); 839 862 } elseif ( empty( $status ) ) { -
action-scheduler/tags/3.4.0/classes/data-stores/ActionScheduler_wpPostStore.php
r2599552 r2622865 5 5 */ 6 6 class ActionScheduler_wpPostStore extends ActionScheduler_Store { 7 const POST_TYPE = 'scheduled-action';8 const GROUP_TAXONOMY = 'action-group';7 const POST_TYPE = 'scheduled-action'; 8 const GROUP_TAXONOMY = 'action-group'; 9 9 const SCHEDULE_META_KEY = '_action_manager_schedule'; 10 const DEPENDENCIES_MET = 'as-post-store-dependencies-met';10 const DEPENDENCIES_MET = 'as-post-store-dependencies-met'; 11 11 12 12 /** … … 20 20 private $claim_before_date = null; 21 21 22 /** @var DateTimeZone */ 23 protected $local_timezone = NULL; 24 25 public function save_action( ActionScheduler_Action $action, DateTime $scheduled_date = NULL ){ 22 /** 23 * Local Timezone. 24 * 25 * @var DateTimeZone 26 */ 27 protected $local_timezone = null; 28 29 /** 30 * Save action. 31 * 32 * @param ActionScheduler_Action $action Scheduled Action. 33 * @param DateTime $scheduled_date Scheduled Date. 34 * 35 * @throws RuntimeException Throws an exception if the action could not be saved. 36 * @return int 37 */ 38 public function save_action( ActionScheduler_Action $action, DateTime $scheduled_date = null ) { 26 39 try { 27 40 $this->validate_action( $action ); 28 41 $post_array = $this->create_post_array( $action, $scheduled_date ); 29 $post_id = $this->save_post_array( $post_array );42 $post_id = $this->save_post_array( $post_array ); 30 43 $this->save_post_schedule( $post_id, $action->get_schedule() ); 31 44 $this->save_action_group( $post_id, $action->get_group() ); … … 33 46 return $post_id; 34 47 } catch ( Exception $e ) { 48 /* translators: %s: action error message */ 35 49 throw new RuntimeException( sprintf( __( 'Error saving action: %s', 'action-scheduler' ), $e->getMessage() ), 0 ); 36 50 } 37 51 } 38 52 39 protected function create_post_array( ActionScheduler_Action $action, DateTime $scheduled_date = NULL ) { 53 /** 54 * Create post array. 55 * 56 * @param ActionScheduler_Action $action Scheduled Action. 57 * @param DateTime $scheduled_date Scheduled Date. 58 * 59 * @return array Returns an array of post data. 60 */ 61 protected function create_post_array( ActionScheduler_Action $action, DateTime $scheduled_date = null ) { 40 62 $post = array( 41 'post_type' => self::POST_TYPE,42 'post_title' => $action->get_hook(),43 'post_content' => json_encode($action->get_args()),44 'post_status' => ( $action->is_finished() ? 'publish' : 'pending' ),63 'post_type' => self::POST_TYPE, 64 'post_title' => $action->get_hook(), 65 'post_content' => wp_json_encode( $action->get_args() ), 66 'post_status' => ( $action->is_finished() ? 'publish' : 'pending' ), 45 67 'post_date_gmt' => $this->get_scheduled_date_string( $action, $scheduled_date ), 46 68 'post_date' => $this->get_scheduled_date_string_local( $action, $scheduled_date ), … … 49 71 } 50 72 73 /** 74 * Save post array. 75 * 76 * @param array $post_array Post array. 77 * @return int Returns the post ID. 78 * @throws RuntimeException Throws an exception if the action could not be saved. 79 */ 51 80 protected function save_post_array( $post_array ) { 52 81 add_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10, 1 ); … … 60 89 } 61 90 62 $post_id = wp_insert_post( $post_array);91 $post_id = wp_insert_post( $post_array ); 63 92 64 93 if ( $has_kses ) { … … 69 98 remove_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10 ); 70 99 71 if ( is_wp_error( $post_id) || empty($post_id) ) {100 if ( is_wp_error( $post_id ) || empty( $post_id ) ) { 72 101 throw new RuntimeException( __( 'Unable to save action.', 'action-scheduler' ) ); 73 102 } … … 75 104 } 76 105 106 /** 107 * Filter insert post data. 108 * 109 * @param array $postdata Post data to filter. 110 * 111 * @return array 112 */ 77 113 public function filter_insert_post_data( $postdata ) { 78 if ( $postdata['post_type'] == self::POST_TYPE) {114 if ( self::POST_TYPE === $postdata['post_type'] ) { 79 115 $postdata['post_author'] = 0; 80 if ( $postdata['post_status'] == 'future') {116 if ( 'future' === $postdata['post_status'] ) { 81 117 $postdata['post_status'] = 'publish'; 82 118 } … … 114 150 */ 115 151 public function set_unique_post_slug( $override_slug, $slug, $post_ID, $post_status, $post_type ) { 116 if ( self::POST_TYPE == $post_type ) {152 if ( self::POST_TYPE === $post_type ) { 117 153 $override_slug = uniqid( self::POST_TYPE . '-', true ) . '-' . wp_generate_password( 32, false ); 118 154 } … … 120 156 } 121 157 158 /** 159 * Save post schedule. 160 * 161 * @param int $post_id Post ID of the scheduled action. 162 * @param string $schedule Schedule to save. 163 * 164 * @return void 165 */ 122 166 protected function save_post_schedule( $post_id, $schedule ) { 123 167 update_post_meta( $post_id, self::SCHEDULE_META_KEY, $schedule ); 124 168 } 125 169 170 /** 171 * Save action group. 172 * 173 * @param int $post_id Post ID. 174 * @param string $group Group to save. 175 * @return void 176 */ 126 177 protected function save_action_group( $post_id, $group ) { 127 if ( empty( $group) ) {128 wp_set_object_terms( $post_id, array(), self::GROUP_TAXONOMY, FALSE);178 if ( empty( $group ) ) { 179 wp_set_object_terms( $post_id, array(), self::GROUP_TAXONOMY, false ); 129 180 } else { 130 wp_set_object_terms( $post_id, array($group), self::GROUP_TAXONOMY, FALSE ); 131 } 132 } 133 181 wp_set_object_terms( $post_id, array( $group ), self::GROUP_TAXONOMY, false ); 182 } 183 } 184 185 /** 186 * Fetch actions. 187 * 188 * @param int $action_id Action ID. 189 * @return object 190 */ 134 191 public function fetch_action( $action_id ) { 135 192 $post = $this->get_post( $action_id ); 136 if ( empty( $post) || $post->post_type != self::POST_TYPE) {193 if ( empty( $post ) || self::POST_TYPE !== $post->post_type ) { 137 194 return $this->get_null_action(); 138 195 } … … 148 205 } 149 206 207 /** 208 * Get post. 209 * 210 * @param string $action_id - Action ID. 211 * @return WP_Post|null 212 */ 150 213 protected function get_post( $action_id ) { 151 if ( empty($action_id) ) { 152 return NULL; 153 } 154 return get_post($action_id); 155 } 156 214 if ( empty( $action_id ) ) { 215 return null; 216 } 217 return get_post( $action_id ); 218 } 219 220 /** 221 * Get NULL action. 222 * 223 * @return ActionScheduler_NullAction 224 */ 157 225 protected function get_null_action() { 158 226 return new ActionScheduler_NullAction(); 159 227 } 160 228 229 /** 230 * Make action from post. 231 * 232 * @param WP_Post $post Post object. 233 * @return WP_Post 234 */ 161 235 protected function make_action_from_post( $post ) { 162 236 $hook = $post->post_title; … … 168 242 $this->validate_schedule( $schedule, $post->ID ); 169 243 170 $group = wp_get_object_terms( $post->ID, self::GROUP_TAXONOMY, array( 'fields' => 'names') );171 $group = empty( $group ) ? '' : reset( $group);244 $group = wp_get_object_terms( $post->ID, self::GROUP_TAXONOMY, array( 'fields' => 'names' ) ); 245 $group = empty( $group ) ? '' : reset( $group ); 172 246 173 247 return ActionScheduler::factory()->get_stored_action( $this->get_action_status_by_post_status( $post->post_status ), $hook, $args, $schedule, $group ); … … 175 249 176 250 /** 177 * @param string $post_status 178 * 179 * @throws InvalidArgumentException if $post_status not in known status fields returned by $this->get_status_labels() 251 * Get action status by post status. 252 * 253 * @param string $post_status Post status. 254 * 255 * @throws InvalidArgumentException Throw InvalidArgumentException if $post_status not in known status fields returned by $this->get_status_labels(). 180 256 * @return string 181 257 */ … … 183 259 184 260 switch ( $post_status ) { 185 case 'publish' :261 case 'publish': 186 262 $action_status = self::STATUS_COMPLETE; 187 263 break; 188 case 'trash' :264 case 'trash': 189 265 $action_status = self::STATUS_CANCELED; 190 266 break; 191 default :267 default: 192 268 if ( ! array_key_exists( $post_status, $this->get_status_labels() ) ) { 193 269 throw new InvalidArgumentException( sprintf( 'Invalid post status: "%s". No matching action status available.', $post_status ) ); … … 201 277 202 278 /** 203 * @param string $action_status 204 * @throws InvalidArgumentException if $post_status not in known status fields returned by $this->get_status_labels() 279 * Get post status by action status. 280 * 281 * @param string $action_status Action status. 282 * 283 * @throws InvalidArgumentException Throws InvalidArgumentException if $post_status not in known status fields returned by $this->get_status_labels(). 205 284 * @return string 206 285 */ … … 208 287 209 288 switch ( $action_status ) { 210 case self::STATUS_COMPLETE :289 case self::STATUS_COMPLETE: 211 290 $post_status = 'publish'; 212 291 break; 213 case self::STATUS_CANCELED :292 case self::STATUS_CANCELED: 214 293 $post_status = 'trash'; 215 294 break; 216 default :295 default: 217 296 if ( ! array_key_exists( $action_status, $this->get_status_labels() ) ) { 218 297 throw new InvalidArgumentException( sprintf( 'Invalid action status: "%s".', $action_status ) ); … … 228 307 * Returns the SQL statement to query (or count) actions. 229 308 * 230 * @param array $query Filtering options 231 * @param string $select_or_count Whether the SQL should select and return the IDs or just the row count 232 * @throws InvalidArgumentException if $select_or_count not count or select 309 * @param array $query - Filtering options. 310 * @param string $select_or_count - Whether the SQL should select and return the IDs or just the row count. 311 * 312 * @throws InvalidArgumentException - Throw InvalidArgumentException if $select_or_count not count or select. 233 313 * @return string SQL statement. The returned SQL is already properly escaped. 234 314 */ 235 315 protected function get_query_actions_sql( array $query, $select_or_count = 'select' ) { 236 316 237 if ( ! in_array( $select_or_count, array( 'select', 'count' ) ) ) {317 if ( ! in_array( $select_or_count, array( 'select', 'count' ), true ) ) { 238 318 throw new InvalidArgumentException( __( 'Invalid schedule. Cannot save action.', 'action-scheduler' ) ); 239 319 } 240 320 241 $query = wp_parse_args( $query, array( 242 'hook' => '', 243 'args' => NULL, 244 'date' => NULL, 245 'date_compare' => '<=', 246 'modified' => NULL, 247 'modified_compare' => '<=', 248 'group' => '', 249 'status' => '', 250 'claimed' => NULL, 251 'per_page' => 5, 252 'offset' => 0, 253 'orderby' => 'date', 254 'order' => 'ASC', 255 'search' => '', 256 ) ); 257 258 /** @var wpdb $wpdb */ 321 $query = wp_parse_args( 322 $query, 323 array( 324 'hook' => '', 325 'args' => null, 326 'date' => null, 327 'date_compare' => '<=', 328 'modified' => null, 329 'modified_compare' => '<=', 330 'group' => '', 331 'status' => '', 332 'claimed' => null, 333 'per_page' => 5, 334 'offset' => 0, 335 'orderby' => 'date', 336 'order' => 'ASC', 337 'search' => '', 338 ) 339 ); 340 341 /** 342 * Global wpdb object. 343 * 344 * @var wpdb $wpdb 345 */ 259 346 global $wpdb; 260 $sql = ( 'count' === $select_or_count ) ? 'SELECT count(p.ID)' : 'SELECT p.ID ';261 $sql .= "FROM {$wpdb->posts} p";347 $sql = ( 'count' === $select_or_count ) ? 'SELECT count(p.ID)' : 'SELECT p.ID '; 348 $sql .= "FROM {$wpdb->posts} p"; 262 349 $sql_params = array(); 263 350 if ( empty( $query['group'] ) && 'group' === $query['orderby'] ) { … … 266 353 $sql .= " LEFT JOIN {$wpdb->terms} t ON tt.term_id=t.term_id"; 267 354 } elseif ( ! empty( $query['group'] ) ) { 268 $sql .= " INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id=p.ID";269 $sql .= " INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id=tt.term_taxonomy_id";270 $sql .= " INNER JOIN {$wpdb->terms} t ON tt.term_id=t.term_id";271 $sql .= " AND t.slug=%s";355 $sql .= " INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id=p.ID"; 356 $sql .= " INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id=tt.term_taxonomy_id"; 357 $sql .= " INNER JOIN {$wpdb->terms} t ON tt.term_id=t.term_id"; 358 $sql .= ' AND t.slug=%s'; 272 359 $sql_params[] = $query['group']; 273 360 } 274 $sql .= " WHERE post_type=%s";361 $sql .= ' WHERE post_type=%s'; 275 362 $sql_params[] = self::POST_TYPE; 276 363 if ( $query['hook'] ) { 277 $sql .= " AND p.post_title=%s";364 $sql .= ' AND p.post_title=%s'; 278 365 $sql_params[] = $query['hook']; 279 366 } 280 if ( ! is_null($query['args']) ) {281 $sql .= " AND p.post_content=%s";282 $sql_params[] = json_encode($query['args']);367 if ( ! is_null( $query['args'] ) ) { 368 $sql .= ' AND p.post_content=%s'; 369 $sql_params[] = wp_json_encode( $query['args'] ); 283 370 } 284 371 … … 292 379 if ( $query['date'] instanceof DateTime ) { 293 380 $date = clone $query['date']; 294 $date->setTimezone( new DateTimeZone( 'UTC') );295 $date_string = $date->format('Y-m-d H:i:s');296 $comparator = $this->validate_sql_comparator($query['date_compare']);297 $sql .= " AND p.post_date_gmt $comparator %s";381 $date->setTimezone( new DateTimeZone( 'UTC' ) ); 382 $date_string = $date->format( 'Y-m-d H:i:s' ); 383 $comparator = $this->validate_sql_comparator( $query['date_compare'] ); 384 $sql .= " AND p.post_date_gmt $comparator %s"; 298 385 $sql_params[] = $date_string; 299 386 } … … 301 388 if ( $query['modified'] instanceof DateTime ) { 302 389 $modified = clone $query['modified']; 303 $modified->setTimezone( new DateTimeZone( 'UTC') );304 $date_string = $modified->format('Y-m-d H:i:s');305 $comparator = $this->validate_sql_comparator($query['modified_compare']);306 $sql .= " AND p.post_modified_gmt $comparator %s";390 $modified->setTimezone( new DateTimeZone( 'UTC' ) ); 391 $date_string = $modified->format( 'Y-m-d H:i:s' ); 392 $comparator = $this->validate_sql_comparator( $query['modified_compare'] ); 393 $sql .= " AND p.post_modified_gmt $comparator %s"; 307 394 $sql_params[] = $date_string; 308 395 } 309 396 310 if ( $query['claimed'] === TRUE) {397 if ( true === $query['claimed'] ) { 311 398 $sql .= " AND p.post_password != ''"; 312 } elseif ( $query['claimed'] === FALSE) {399 } elseif ( false === $query['claimed'] ) { 313 400 $sql .= " AND p.post_password = ''"; 314 } elseif ( ! is_null($query['claimed']) ) {315 $sql .= " AND p.post_password = %s";401 } elseif ( ! is_null( $query['claimed'] ) ) { 402 $sql .= ' AND p.post_password = %s'; 316 403 $sql_params[] = $query['claimed']; 317 404 } 318 405 319 406 if ( ! empty( $query['search'] ) ) { 320 $sql .= " AND (p.post_title LIKE %s OR p.post_content LIKE %s OR p.post_password LIKE %s)";321 for ( $i = 0; $i < 3; $i++ ) {407 $sql .= ' AND (p.post_title LIKE %s OR p.post_content LIKE %s OR p.post_password LIKE %s)'; 408 for ( $i = 0; $i < 3; $i++ ) { 322 409 $sql_params[] = sprintf( '%%%s%%', $query['search'] ); 323 410 } … … 354 441 $sql .= " ORDER BY $orderby $order"; 355 442 if ( $query['per_page'] > 0 ) { 356 $sql .= " LIMIT %d, %d";443 $sql .= ' LIMIT %d, %d'; 357 444 $sql_params[] = $query['offset']; 358 445 $sql_params[] = $query['per_page']; … … 360 447 } 361 448 362 return $wpdb->prepare( $sql, $sql_params ); 449 return $wpdb->prepare( $sql, $sql_params ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 363 450 } 364 451 … … 376 463 */ 377 464 public function query_actions( $query = array(), $query_type = 'select' ) { 378 /** @var wpdb $wpdb */ 465 /** 466 * Global $wpdb object. 467 * 468 * @var wpdb $wpdb 469 */ 379 470 global $wpdb; 380 471 381 472 $sql = $this->get_query_actions_sql( $query, $query_type ); 382 473 383 return ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); 474 return ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared 384 475 } 385 476 … … 400 491 $action_status_name = $this->get_action_status_by_post_status( $post_status_name ); 401 492 } catch ( Exception $e ) { 402 // Ignore any post statuses that aren't for actions 493 // Ignore any post statuses that aren't for actions. 403 494 continue; 404 495 } … … 412 503 413 504 /** 414 * @param string $action_id 415 * 416 * @throws InvalidArgumentException 505 * Cancel action. 506 * 507 * @param int $action_id Action ID. 508 * 509 * @throws InvalidArgumentException If $action_id is not identified. 417 510 */ 418 511 public function cancel_action( $action_id ) { 419 512 $post = get_post( $action_id ); 420 if ( empty( $post ) || ( $post->post_type != self::POST_TYPE ) ) { 513 if ( empty( $post ) || ( self::POST_TYPE !== $post->post_type ) ) { 514 /* translators: %s is the action ID */ 421 515 throw new InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 422 516 } … … 427 521 } 428 522 523 /** 524 * Delete action. 525 * 526 * @param int $action_id Action ID. 527 * @return void 528 * @throws InvalidArgumentException If action is not identified. 529 */ 429 530 public function delete_action( $action_id ) { 430 531 $post = get_post( $action_id ); 431 if ( empty( $post ) || ( $post->post_type != self::POST_TYPE ) ) { 532 if ( empty( $post ) || ( self::POST_TYPE !== $post->post_type ) ) { 533 /* translators: %s is the action ID */ 432 534 throw new InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 433 535 } 434 536 do_action( 'action_scheduler_deleted_action', $action_id ); 435 537 436 wp_delete_post( $action_id, TRUE);437 } 438 439 /** 440 * @param string $action_id441 * 442 * @ throws InvalidArgumentException538 wp_delete_post( $action_id, true ); 539 } 540 541 /** 542 * Get date for claim id. 543 * 544 * @param int $action_id Action ID. 443 545 * @return ActionScheduler_DateTime The date the action is schedule to run, or the date that it ran. 444 546 */ … … 449 551 450 552 /** 451 * @param string $action_id 452 * 453 * @throws InvalidArgumentException 553 * Get Date GMT. 554 * 555 * @param int $action_id Action ID. 556 * 557 * @throws InvalidArgumentException If $action_id is not identified. 454 558 * @return ActionScheduler_DateTime The date the action is schedule to run, or the date that it ran. 455 559 */ 456 560 public function get_date_gmt( $action_id ) { 457 561 $post = get_post( $action_id ); 458 if ( empty( $post ) || ( $post->post_type != self::POST_TYPE ) ) { 562 if ( empty( $post ) || ( self::POST_TYPE !== $post->post_type ) ) { 563 /* translators: %s is the action ID */ 459 564 throw new InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 460 565 } 461 if ( $post->post_status == 'publish') {566 if ( 'publish' === $post->post_status ) { 462 567 return as_get_datetime_object( $post->post_modified_gmt ); 463 568 } else { … … 467 572 468 573 /** 469 * @param int $max_actions 574 * Stake claim. 575 * 576 * @param int $max_actions Maximum number of actions. 470 577 * @param DateTime $before_date Jobs must be schedule before this date. Defaults to now. 471 578 * @param array $hooks Claim only actions with a hook or hooks. … … 478 585 public function stake_claim( $max_actions = 10, DateTime $before_date = null, $hooks = array(), $group = '' ) { 479 586 $this->claim_before_date = $before_date; 480 $claim_id = $this->generate_claim_id();587 $claim_id = $this->generate_claim_id(); 481 588 $this->claim_actions( $claim_id, $max_actions, $before_date, $hooks, $group ); 482 $action_ids = $this->find_actions_by_claim_id( $claim_id );589 $action_ids = $this->find_actions_by_claim_id( $claim_id ); 483 590 $this->claim_before_date = null; 484 591 … … 487 594 488 595 /** 596 * Get claim count. 597 * 489 598 * @return int 490 599 */ 491 public function get_claim_count() {600 public function get_claim_count() { 492 601 global $wpdb; 493 602 494 $sql = "SELECT COUNT(DISTINCT post_password) FROM {$wpdb->posts} WHERE post_password != '' AND post_type = %s AND post_status IN ('in-progress','pending')"; 495 $sql = $wpdb->prepare( $sql, array( self::POST_TYPE ) ); 496 497 return $wpdb->get_var( $sql ); 498 } 499 603 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching 604 return $wpdb->get_var( 605 $wpdb->prepare( 606 "SELECT COUNT(DISTINCT post_password) FROM {$wpdb->posts} WHERE post_password != '' AND post_type = %s AND post_status IN ('in-progress','pending')", 607 array( self::POST_TYPE ) 608 ) 609 ); 610 } 611 612 /** 613 * Generate claim id. 614 * 615 * @return string 616 */ 500 617 protected function generate_claim_id() { 501 $claim_id = md5(microtime(true) . rand(0,1000)); 502 return substr($claim_id, 0, 20); // to fit in db field with 20 char limit 503 } 504 505 /** 506 * @param string $claim_id 507 * @param int $limit 618 $claim_id = md5( microtime( true ) . wp_rand( 0, 1000 ) ); 619 return substr( $claim_id, 0, 20 ); // to fit in db field with 20 char limit. 620 } 621 622 /** 623 * Claim actions. 624 * 625 * @param string $claim_id Claim ID. 626 * @param int $limit Limit. 508 627 * @param DateTime $before_date Should use UTC timezone. 509 628 * @param array $hooks Claim only actions with a hook or hooks. 510 629 * @param string $group Claim only actions in the given group. 511 630 * 512 * @return int The number of actions that were claimed 513 * @throws RuntimeException When there is a database error. 514 * @throws InvalidArgumentException When the group is invalid. 631 * @return int The number of actions that were claimed. 632 * @throws RuntimeException When there is a database error. 515 633 */ 516 634 protected function claim_actions( $claim_id, $limit, DateTime $before_date = null, $hooks = array(), $group = '' ) { … … 525 643 } 526 644 527 /** @var wpdb $wpdb */ 645 /** 646 * Global wpdb object. 647 * 648 * @var wpdb $wpdb 649 */ 528 650 global $wpdb; 529 651 … … 560 682 $where .= ' AND ID IN (' . join( ',', $ids ) . ')'; 561 683 } else { 562 $where .= ' AND post_date_gmt <= %s';684 $where .= ' AND post_date_gmt <= %s'; 563 685 $params[] = $date->format( 'Y-m-d H:i:s' ); 564 686 } … … 569 691 570 692 // Run the query and gather results. 571 $rows_affected = $wpdb->query( $wpdb->prepare( "{$update} {$where} {$order}", $params ) ); 572 if ( $rows_affected === false ) { 693 $rows_affected = $wpdb->query( $wpdb->prepare( "{$update} {$where} {$order}", $params ) ); // phpcs:ignore // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare 694 695 if ( false === $rows_affected ) { 573 696 throw new RuntimeException( __( 'Unable to claim actions. Database error.', 'action-scheduler' ) ); 574 697 } … … 590 713 protected function get_actions_by_group( $group, $limit, DateTime $date ) { 591 714 // Ensure the group exists before continuing. 592 if ( ! term_exists( $group, self::GROUP_TAXONOMY )) { 715 if ( ! term_exists( $group, self::GROUP_TAXONOMY ) ) { 716 /* translators: %s is the group name */ 593 717 throw new InvalidArgumentException( sprintf( __( 'The group "%s" does not exist.', 'action-scheduler' ), $group ) ); 594 718 } … … 610 734 ), 611 735 'date_query' => array( 612 'column' => 'post_date_gmt',613 'before' => $date->format( 'Y-m-d H:i' ),736 'column' => 'post_date_gmt', 737 'before' => $date->format( 'Y-m-d H:i' ), 614 738 'inclusive' => true, 615 739 ), 616 'tax_query' => array(740 'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery 617 741 array( 618 742 'taxonomy' => self::GROUP_TAXONOMY, … … 628 752 629 753 /** 630 * @param string $claim_id 754 * Find actions by claim ID. 755 * 756 * @param string $claim_id Claim ID. 631 757 * @return array 632 758 */ 633 759 public function find_actions_by_claim_id( $claim_id ) { 634 /** @var wpdb $wpdb */ 760 /** 761 * Global wpdb object. 762 * 763 * @var wpdb $wpdb 764 */ 635 765 global $wpdb; 636 637 $sql = "SELECT ID, post_date_gmt FROM {$wpdb->posts} WHERE post_type = %s AND post_password = %s";638 $sql = $wpdb->prepare( $sql, array( self::POST_TYPE, $claim_id ) );639 766 640 767 $action_ids = array(); … … 642 769 $cut_off = $before_date->format( 'Y-m-d H:i:s' ); 643 770 771 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 772 $results = $wpdb->get_results( 773 $wpdb->prepare( 774 "SELECT ID, post_date_gmt FROM {$wpdb->posts} WHERE post_type = %s AND post_password = %s", 775 array( 776 self::POST_TYPE, 777 $claim_id, 778 ) 779 ) 780 ); 781 644 782 // Verify that the scheduled date for each action is within the expected bounds (in some unusual 645 783 // cases, we cannot depend on MySQL to honor all of the WHERE conditions we specify). 646 foreach ( $ wpdb->get_results( $sql )as $claimed_action ) {784 foreach ( $results as $claimed_action ) { 647 785 if ( $claimed_action->post_date_gmt <= $cut_off ) { 648 786 $action_ids[] = absint( $claimed_action->ID ); … … 653 791 } 654 792 793 /** 794 * Release claim. 795 * 796 * @param ActionScheduler_ActionClaim $claim Claim object to release. 797 * @return void 798 * @throws RuntimeException When the claim is not unlocked. 799 */ 655 800 public function release_claim( ActionScheduler_ActionClaim $claim ) { 656 801 $action_ids = $this->find_actions_by_claim_id( $claim->get_id() ); 657 802 if ( empty( $action_ids ) ) { 658 return; // nothing to do 803 return; // nothing to do. 659 804 } 660 805 $action_id_string = implode( ',', array_map( 'intval', $action_ids ) ); 661 /** @var wpdb $wpdb */ 806 /** 807 * Global wpdb object. 808 * 809 * @var wpdb $wpdb 810 */ 662 811 global $wpdb; 663 $sql = "UPDATE {$wpdb->posts} SET post_password = '' WHERE ID IN ($action_id_string) AND post_password = %s"; 664 $sql = $wpdb->prepare( $sql, array( $claim->get_id() ) ); 665 $result = $wpdb->query( $sql ); 666 if ( $result === false ) { 812 813 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 814 $result = $wpdb->query( 815 $wpdb->prepare( 816 "UPDATE {$wpdb->posts} SET post_password = '' WHERE ID IN ($action_id_string) AND post_password = %s", //phpcs:ignore 817 array( 818 $claim->get_id(), 819 ) 820 ) 821 ); 822 if ( false === $result ) { 667 823 /* translators: %s: claim ID */ 668 824 throw new RuntimeException( sprintf( __( 'Unable to unlock claim %s. Database error.', 'action-scheduler' ), $claim->get_id() ) ); … … 671 827 672 828 /** 673 * @param string $action_id 829 * Unclaim action. 830 * 831 * @param string $action_id Action ID. 832 * @throws RuntimeException When unable to unlock claim on action ID. 674 833 */ 675 834 public function unclaim_action( $action_id ) { 676 /** @var wpdb $wpdb */ 835 /** 836 * Global wpdb object. 837 * 838 * @var wpdb $wpdb 839 */ 677 840 global $wpdb; 678 $sql = "UPDATE {$wpdb->posts} SET post_password = '' WHERE ID = %d AND post_type = %s"; 679 $sql = $wpdb->prepare( $sql, $action_id, self::POST_TYPE ); 680 $result = $wpdb->query( $sql ); 681 if ( $result === false ) { 841 842 //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 843 $result = $wpdb->query( 844 $wpdb->prepare( 845 "UPDATE {$wpdb->posts} SET post_password = '' WHERE ID = %d AND post_type = %s", 846 $action_id, 847 self::POST_TYPE 848 ) 849 ); 850 if ( false === $result ) { 682 851 /* translators: %s: action ID */ 683 852 throw new RuntimeException( sprintf( __( 'Unable to unlock claim on action %s. Database error.', 'action-scheduler' ), $action_id ) ); … … 685 854 } 686 855 856 /** 857 * Mark failure on action. 858 * 859 * @param int $action_id Action ID. 860 * 861 * @return void 862 * @throws RuntimeException When unable to mark failure on action ID. 863 */ 687 864 public function mark_failure( $action_id ) { 688 /** @var wpdb $wpdb */ 865 /** 866 * Global wpdb object. 867 * 868 * @var wpdb $wpdb 869 */ 689 870 global $wpdb; 690 $sql = "UPDATE {$wpdb->posts} SET post_status = %s WHERE ID = %d AND post_type = %s"; 691 $sql = $wpdb->prepare( $sql, self::STATUS_FAILED, $action_id, self::POST_TYPE ); 692 $result = $wpdb->query( $sql ); 693 if ( $result === false ) { 871 872 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 873 $result = $wpdb->query( 874 $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_status = %s WHERE ID = %d AND post_type = %s", self::STATUS_FAILED, $action_id, self::POST_TYPE ) 875 ); 876 if ( false === $result ) { 694 877 /* translators: %s: action ID */ 695 878 throw new RuntimeException( sprintf( __( 'Unable to mark failure on action %s. Database error.', 'action-scheduler' ), $action_id ) ); … … 700 883 * Return an action's claim ID, as stored in the post password column 701 884 * 702 * @param string $action_id885 * @param int $action_id Action ID. 703 886 * @return mixed 704 887 */ … … 710 893 * Return an action's status, as stored in the post status column 711 894 * 712 * @param string $action_id 895 * @param int $action_id Action ID. 896 * 713 897 * @return mixed 898 * @throws InvalidArgumentException When the action ID is invalid. 714 899 */ 715 900 public function get_status( $action_id ) { 716 901 $status = $this->get_post_column( $action_id, 'post_status' ); 717 902 718 if ( $status === null) {903 if ( null === $status ) { 719 904 throw new InvalidArgumentException( __( 'Invalid action ID. No status found.', 'action-scheduler' ) ); 720 905 } … … 723 908 } 724 909 910 /** 911 * Get post column 912 * 913 * @param string $action_id Action ID. 914 * @param string $column_name Column Name. 915 * 916 * @return string|null 917 */ 725 918 private function get_post_column( $action_id, $column_name ) { 726 /** @var \wpdb $wpdb */ 919 /** 920 * Global wpdb object. 921 * 922 * @var wpdb $wpdb 923 */ 727 924 global $wpdb; 728 return $wpdb->get_var( $wpdb->prepare( "SELECT {$column_name} FROM {$wpdb->posts} WHERE ID=%d AND post_type=%s", $action_id, self::POST_TYPE ) ); 729 } 730 731 /** 732 * @param string $action_id 925 926 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 927 return $wpdb->get_var( 928 $wpdb->prepare( 929 "SELECT {$column_name} FROM {$wpdb->posts} WHERE ID=%d AND post_type=%s", // phpcs:ignore 930 $action_id, 931 self::POST_TYPE 932 ) 933 ); 934 } 935 936 /** 937 * Log Execution. 938 * 939 * @param string $action_id Action ID. 733 940 */ 734 941 public function log_execution( $action_id ) { 735 /** @var wpdb $wpdb */ 942 /** 943 * Global wpdb object. 944 * 945 * @var wpdb $wpdb 946 */ 736 947 global $wpdb; 737 948 738 $sql = "UPDATE {$wpdb->posts} SET menu_order = menu_order+1, post_status=%s, post_modified_gmt = %s, post_modified = %s WHERE ID = %d AND post_type = %s"; 739 $sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time('mysql', true), current_time('mysql'), $action_id, self::POST_TYPE ); 740 $wpdb->query($sql); 949 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 950 $wpdb->query( 951 $wpdb->prepare( 952 "UPDATE {$wpdb->posts} SET menu_order = menu_order+1, post_status=%s, post_modified_gmt = %s, post_modified = %s WHERE ID = %d AND post_type = %s", 953 self::STATUS_RUNNING, 954 current_time( 'mysql', true ), 955 current_time( 'mysql' ), 956 $action_id, 957 self::POST_TYPE 958 ) 959 ); 741 960 } 742 961 … … 744 963 * Record that an action was completed. 745 964 * 746 * @param int $action_id ID of the completed action. 747 * @throws InvalidArgumentException|RuntimeException 965 * @param string $action_id ID of the completed action. 966 * 967 * @throws InvalidArgumentException When the action ID is invalid. 968 * @throws RuntimeException When there was an error executing the action. 748 969 */ 749 970 public function mark_complete( $action_id ) { 750 971 $post = get_post( $action_id ); 751 if ( empty( $post ) || ( $post->post_type != self::POST_TYPE ) ) { 972 if ( empty( $post ) || ( self::POST_TYPE !== $post->post_type ) ) { 973 /* translators: %s is the action ID */ 752 974 throw new InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 753 975 } 754 976 add_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10, 1 ); 755 977 add_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10, 5 ); 756 $result = wp_update_post(array( 757 'ID' => $action_id, 758 'post_status' => 'publish', 759 ), TRUE); 978 $result = wp_update_post( 979 array( 980 'ID' => $action_id, 981 'post_status' => 'publish', 982 ), 983 true 984 ); 760 985 remove_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10 ); 761 986 remove_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10 ); … … 774 999 array( 775 1000 'ID' => $action_id, 776 'post_status' => 'migrated' 1001 'post_status' => 'migrated', 777 1002 ) 778 1003 ); … … 782 1007 * Determine whether the post store can be migrated. 783 1008 * 1009 * @param [type] $setting - Setting value. 784 1010 * @return bool 785 1011 */ … … 790 1016 if ( empty( $dependencies_met ) ) { 791 1017 $maximum_args_length = apply_filters( 'action_scheduler_maximum_args_length', 191 ); 792 $found_action = $wpdb->get_var( 1018 $found_action = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 793 1019 $wpdb->prepare( 794 1020 "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND CHAR_LENGTH(post_content) > %d LIMIT 1", … … 797 1023 ) 798 1024 ); 799 $dependencies_met = $found_action ? 'no' : 'yes';1025 $dependencies_met = $found_action ? 'no' : 'yes'; 800 1026 set_transient( self::DEPENDENCIES_MET, $dependencies_met, DAY_IN_SECONDS ); 801 1027 } 802 1028 803 return 'yes' == $dependencies_met ? $setting : false;1029 return 'yes' === $dependencies_met ? $setting : false; 804 1030 } 805 1031 … … 811 1037 * developers of this impending requirement. 812 1038 * 813 * @param ActionScheduler_Action $action 1039 * @param ActionScheduler_Action $action Action object. 814 1040 */ 815 1041 protected function validate_action( ActionScheduler_Action $action ) { … … 817 1043 parent::validate_action( $action ); 818 1044 } catch ( Exception $e ) { 1045 /* translators: %s is the error message */ 819 1046 $message = sprintf( __( '%s Support for strings longer than this will be removed in a future version.', 'action-scheduler' ), $e->getMessage() ); 820 _doing_it_wrong( 'ActionScheduler_Action::$args', $message, '2.1.0' );821 } 822 } 823 824 /** 825 * @codeCoverageIgnore1047 _doing_it_wrong( 'ActionScheduler_Action::$args', esc_html( $message ), '2.1.0' ); 1048 } 1049 } 1050 1051 /** 1052 * (@codeCoverageIgnore) 826 1053 */ 827 1054 public function init() { -
action-scheduler/tags/3.4.0/classes/schema/ActionScheduler_StoreSchema.php
r2599552 r2622865 17 17 * @var int Increment this value to trigger a schema update. 18 18 */ 19 protected $schema_version = 5;19 protected $schema_version = 6; 20 20 21 21 public function __construct() { … … 65 65 KEY group_id (group_id), 66 66 KEY last_attempt_gmt (last_attempt_gmt), 67 KEY claim_id (claim_id),68 67 KEY `claim_id_status_scheduled_date_gmt` (`claim_id`, `status`, `scheduled_date_gmt`) 69 68 ) $charset_collate"; -
action-scheduler/tags/3.4.0/deprecated/ActionScheduler_Schedule_Deprecated.php
r2340383 r2622865 10 10 * after a given date & time. 11 11 * 12 * @param DateTime $after 12 * @param DateTime $after DateTime to calculate against. 13 13 * 14 14 * @return DateTime|null 15 15 */ 16 public function next( DateTime $after = NULL) {16 public function next( DateTime $after = null ) { 17 17 if ( empty( $after ) ) { 18 18 $return_value = $this->get_date(); … … 23 23 } 24 24 25 _deprecated_function( __METHOD__, '3.0.0', __CLASS__ . '::' . $replacement_method ); 25 _deprecated_function( __METHOD__, '3.0.0', __CLASS__ . '::' . $replacement_method ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 26 26 27 27 return $return_value; -
action-scheduler/tags/3.4.0/readme.txt
r2599552 r2622865 4 4 Requires at least: 5.2 5 5 Tested up to: 5.7 6 Stable tag: 3. 3.06 Stable tag: 3.4.0 7 7 License: GPLv3 8 8 Requires PHP: 5.6 … … 48 48 == Changelog == 49 49 50 = 3.4.0 - 2021-10-29 = 51 * Enhancement - Number of items per page can now be set for the Scheduled Actions view (props @ovidiul). #771 52 * Fix - Do not lower the max_execution_time if it is already set to 0 (unlimited) (props @barryhughes). #755 53 * Fix - Avoid triggering autoloaders during the version resolution process (props @olegabr). #731 & #776 54 * Dev - ActionScheduler_wcSystemStatus PHPCS fixes (props @ovidiul). #761 55 * Dev - ActionScheduler_DBLogger.php PHPCS fixes (props @ovidiul). #768 56 * Dev - Fixed phpcs for ActionScheduler_Schedule_Deprecated (props @ovidiul). #762 57 * Dev - Improve actions table indicies (props @glagonikas). #774 & #777 58 * Dev - PHPCS fixes for ActionScheduler_DBStore.php (props @ovidiul). #769 & #778 59 * Dev - PHPCS Fixes for ActionScheduler_Abstract_ListTable (props @ovidiul). #763 & #779 60 * Dev - Adds new filter action_scheduler_claim_actions_order_by to allow tuning of the claim query (props @glagonikas). #773 61 * Dev - PHPCS fixes for ActionScheduler_WpPostStore class (props @ovidiul). #780 62 50 63 = 3.3.0 - 2021-09-15 = 51 64 * Enhancement - Adds as_has_scheduled_action() to provide a performant way to test for existing actions. #645 -
action-scheduler/trunk/action-scheduler.php
r2599552 r2622865 6 6 * Author: Automattic 7 7 * Author URI: https://automattic.com/ 8 * Version: 3. 3.08 * Version: 3.4.0 9 9 * License: GPLv3 10 10 * … … 27 27 */ 28 28 29 if ( ! function_exists( 'action_scheduler_register_3_dot_ 3_dot_0' ) && function_exists( 'add_action' ) ) {29 if ( ! function_exists( 'action_scheduler_register_3_dot_4_dot_0' ) && function_exists( 'add_action' ) ) { 30 30 31 if ( ! class_exists( 'ActionScheduler_Versions' ) ) {31 if ( ! class_exists( 'ActionScheduler_Versions', false ) ) { 32 32 require_once __DIR__ . '/classes/ActionScheduler_Versions.php'; 33 33 add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 ); 34 34 } 35 35 36 add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_ 3_dot_0', 0, 0 );36 add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_4_dot_0', 0, 0 ); 37 37 38 38 /** 39 39 * Registers this version of Action Scheduler. 40 40 */ 41 function action_scheduler_register_3_dot_ 3_dot_0() {41 function action_scheduler_register_3_dot_4_dot_0() { 42 42 $versions = ActionScheduler_Versions::instance(); 43 $versions->register( '3. 3.0', 'action_scheduler_initialize_3_dot_3_dot_0' );43 $versions->register( '3.4.0', 'action_scheduler_initialize_3_dot_4_dot_0' ); 44 44 } 45 45 … … 47 47 * Initializes this version of Action Scheduler. 48 48 */ 49 function action_scheduler_initialize_3_dot_ 3_dot_0() {49 function action_scheduler_initialize_3_dot_4_dot_0() { 50 50 // A final safety check is required even here, because historic versions of Action Scheduler 51 51 // followed a different pattern (in some unusual cases, we could reach this point and the 52 52 // ActionScheduler class is already defined—so we need to guard against that). 53 if ( ! class_exists( 'ActionScheduler' ) ) {53 if ( ! class_exists( 'ActionScheduler', false ) ) { 54 54 require_once __DIR__ . '/classes/abstracts/ActionScheduler.php'; 55 55 ActionScheduler::init( __FILE__ ); … … 58 58 59 59 // Support usage in themes - load this version if no plugin has loaded a version yet. 60 if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler' ) ) {61 action_scheduler_initialize_3_dot_ 3_dot_0();60 if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) { 61 action_scheduler_initialize_3_dot_4_dot_0(); 62 62 do_action( 'action_scheduler_pre_theme_init' ); 63 63 ActionScheduler_Versions::initialize_latest_version(); -
action-scheduler/trunk/changelog.txt
r2599552 r2622865 1 1 *** Changelog *** 2 3 = 3.4.0 - 2021-10-29 = 4 * Enhancement - Number of items per page can now be set for the Scheduled Actions view (props @ovidiul). #771 5 * Fix - Do not lower the max_execution_time if it is already set to 0 (unlimited) (props @barryhughes). #755 6 * Fix - Avoid triggering autoloaders during the version resolution process (props @olegabr). #731 & #776 7 * Dev - ActionScheduler_wcSystemStatus PHPCS fixes (props @ovidiul). #761 8 * Dev - ActionScheduler_DBLogger.php PHPCS fixes (props @ovidiul). #768 9 * Dev - Fixed phpcs for ActionScheduler_Schedule_Deprecated (props @ovidiul). #762 10 * Dev - Improve actions table indicies (props @glagonikas). #774 & #777 11 * Dev - PHPCS fixes for ActionScheduler_DBStore.php (props @ovidiul). #769 & #778 12 * Dev - PHPCS Fixes for ActionScheduler_Abstract_ListTable (props @ovidiul). #763 & #779 13 * Dev - Adds new filter action_scheduler_claim_actions_order_by to allow tuning of the claim query (props @glagonikas). #773 14 * Dev - PHPCS fixes for ActionScheduler_WpPostStore class (props @ovidiul). #780 2 15 3 16 = 3.3.0 - 2021-09-15 = -
action-scheduler/trunk/classes/ActionScheduler_Compatibility.php
r2599552 r2622865 84 84 * Only allows raising the existing limit and prevents lowering it. Wrapper for wc_set_time_limit(), when available. 85 85 * 86 * @param int The time limit in seconds.86 * @param int $limit The time limit in seconds. 87 87 */ 88 88 public static function raise_time_limit( $limit = 0 ) { 89 if ( $limit < ini_get( 'max_execution_time' ) ) { 89 $limit = (int) $limit; 90 $max_execution_time = (int) ini_get( 'max_execution_time' ); 91 92 /* 93 * If the max execution time is already unlimited (zero), or if it exceeds or is equal to the proposed 94 * limit, there is no reason for us to make further changes (we never want to lower it). 95 */ 96 if ( 97 0 === $max_execution_time 98 || ( $max_execution_time >= $limit && $limit !== 0 ) 99 ) { 90 100 return; 91 101 } … … 94 104 wc_set_time_limit( $limit ); 95 105 } elseif ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved 96 @set_time_limit( $limit ); 106 @set_time_limit( $limit ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged 97 107 } 98 108 } -
action-scheduler/trunk/classes/ActionScheduler_ListTable.php
r2340383 r2622865 176 176 ); 177 177 178 parent::__construct( array( 179 'singular' => 'action-scheduler', 180 'plural' => 'action-scheduler', 181 'ajax' => false, 182 ) ); 183 } 184 178 parent::__construct( 179 array( 180 'singular' => 'action-scheduler', 181 'plural' => 'action-scheduler', 182 'ajax' => false, 183 ) 184 ); 185 186 add_screen_option( 187 'per_page', 188 array( 189 'default' => $this->items_per_page, 190 ) 191 ); 192 193 add_filter( 'set_screen_option_' . $this->get_per_page_option_name(), array( $this, 'set_items_per_page_option' ), 10, 3 ); 194 set_screen_options(); 195 } 196 197 /** 198 * Handles setting the items_per_page option for this screen. 199 * 200 * @param mixed $status Default false (to skip saving the current option). 201 * @param string $option Screen option name. 202 * @param int $value Screen option value. 203 * @return int 204 */ 205 public function set_items_per_page_option( $status, $option, $value ) { 206 return $value; 207 } 185 208 /** 186 209 * Convert an interval of seconds into a two part human friendly string. … … 550 573 $this->prepare_column_headers(); 551 574 552 $per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page ); 575 $per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page ); 576 553 577 $query = array( 554 578 'per_page' => $per_page, … … 610 634 return __( 'Search hook, args and claim ID', 'action-scheduler' ); 611 635 } 636 637 /** 638 * {@inheritDoc} 639 */ 640 protected function get_per_page_option_name() { 641 return str_replace( '-', '_', $this->screen->id ) . '_per_page'; 642 } 612 643 } -
action-scheduler/trunk/classes/ActionScheduler_wcSystemStatus.php
r2542151 r2622865 13 13 protected $store; 14 14 15 function __construct( $store ) { 15 /** 16 * Constructor method for ActionScheduler_wcSystemStatus. 17 * 18 * @param ActionScheduler_Store $store Active store object. 19 * 20 * @return void 21 */ 22 public function __construct( $store ) { 16 23 $this->store = $store; 17 24 } … … 68 75 $order = 'oldest' === $date_type ? 'ASC' : 'DESC'; 69 76 70 $action = $this->store->query_actions( array( 71 'claimed' => false, 72 'status' => $status, 73 'per_page' => 1, 74 'order' => $order, 75 ) ); 77 $action = $this->store->query_actions( 78 array( 79 'claimed' => false, 80 'status' => $status, 81 'per_page' => 1, 82 'order' => $order, 83 ) 84 ); 76 85 77 86 if ( ! empty( $action ) ) { … … 93 102 */ 94 103 protected function get_template( $status_labels, $action_counts, $oldest_and_newest ) { 95 $as_version = ActionScheduler_Versions::instance()->latest_version();104 $as_version = ActionScheduler_Versions::instance()->latest_version(); 96 105 $as_datastore = get_class( ActionScheduler_Store::instance() ); 97 106 ?> … … 125 134 '<tr><td>%1$s</td><td> </td><td>%2$s<span style="display: none;">, Oldest: %3$s, Newest: %4$s</span></td><td>%3$s</td><td>%4$s</td></tr>', 126 135 esc_html( $status_labels[ $status ] ), 127 number_format_i18n( $count),128 $oldest_and_newest[ $status ]['oldest'],129 $oldest_and_newest[ $status ]['newest']136 esc_html( number_format_i18n( $count ) ), 137 esc_html( $oldest_and_newest[ $status ]['oldest'] ), 138 esc_html( $oldest_and_newest[ $status ]['newest'] ) 130 139 ); 131 140 } … … 138 147 139 148 /** 140 * is triggered when invoking inaccessible methods in an object context.149 * Is triggered when invoking inaccessible methods in an object context. 141 150 * 142 * @param string $name 143 * @param array $arguments 151 * @param string $name Name of method called. 152 * @param array $arguments Parameters to invoke the method with. 144 153 * 145 154 * @return mixed -
action-scheduler/trunk/classes/abstracts/ActionScheduler_Abstract_ListTable.php
r2551612 r2622865 2 2 3 3 if ( ! class_exists( 'WP_List_Table' ) ) { 4 require_once ( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );4 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; 5 5 } 6 6 … … 14 14 * 15 15 * This class supports: 16 * - Bulk actions17 * - Search16 * - Bulk actions 17 * - Search 18 18 * - Sortable columns 19 19 * - Automatic translations of the columns … … 26 26 /** 27 27 * The table name 28 * 29 * @var string 28 30 */ 29 31 protected $table_name; … … 31 33 /** 32 34 * Package name, used to get options from WP_List_Table::get_items_per_page. 35 * 36 * @var string 33 37 */ 34 38 protected $package; … … 36 40 /** 37 41 * How many items do we render per page? 42 * 43 * @var int 38 44 */ 39 45 protected $items_per_page = 10; … … 42 48 * Enables search in this table listing. If this array 43 49 * is empty it means the listing is not searchable. 50 * 51 * @var array 44 52 */ 45 53 protected $search_by = array(); … … 49 57 * key must much the table column name and the value is the label, which is 50 58 * automatically translated. 59 * 60 * @var array 51 61 */ 52 62 protected $columns = array(); … … 59 69 * (with the prefix row_action_<key>) and the value is the label 60 70 * and title. 71 * 72 * @var array 61 73 */ 62 74 protected $row_actions = array(); … … 64 76 /** 65 77 * The Primary key of our table 78 * 79 * @var string 66 80 */ 67 81 protected $ID = 'ID'; … … 70 84 * Enables sorting, it expects an array 71 85 * of columns (the column names are the values) 86 * 87 * @var array 72 88 */ 73 89 protected $sort_by = array(); 74 90 91 /** 92 * The default sort order 93 * 94 * @var string 95 */ 75 96 protected $filter_by = array(); 76 97 77 98 /** 78 * @var array The status name => count combinations for this table's items. Used to display status filters. 99 * The status name => count combinations for this table's items. Used to display status filters. 100 * 101 * @var array 79 102 */ 80 103 protected $status_counts = array(); 81 104 82 105 /** 83 * @var array Notices to display when loading the table. Array of arrays of form array( 'class' => {updated|error}, 'message' => 'This is the notice text display.' ). 106 * Notices to display when loading the table. Array of arrays of form array( 'class' => {updated|error}, 'message' => 'This is the notice text display.' ). 107 * 108 * @var array 84 109 */ 85 110 protected $admin_notices = array(); 86 111 87 112 /** 88 * @var string Localised string displayed in the <h1> element above the able. 113 * Localised string displayed in the <h1> element above the able. 114 * 115 * @var string 89 116 */ 90 117 protected $table_header; … … 100 127 * is the array with primary keys, the second argument is a string with a list of the primary keys, 101 128 * escaped and ready to use (with `IN`). 129 * 130 * @var array 102 131 */ 103 132 protected $bulk_actions = array(); … … 107 136 * `_x` with some default (the package name). 108 137 * 109 * @deprecated 3.0.0 138 * @param string $text The new text to translate. 139 * @param string $context The context of the text. 140 * @return string|void The translated text. 141 * 142 * @deprecated 3.0.0 Use `_x()` instead. 110 143 */ 111 144 protected function translate( $text, $context = '' ) { … … 117 150 * also validates that the bulk method handler exists. It throws an exception because 118 151 * this is a library meant for developers and missing a bulk method is a development-time error. 152 * 153 * @return array 154 * 155 * @throws RuntimeException Throws RuntimeException when the bulk action does not have a callback method. 119 156 */ 120 157 protected function get_bulk_actions() { … … 147 184 check_admin_referer( 'bulk-' . $this->_args['plural'] ); 148 185 149 $method = 'bulk_' . $action;186 $method = 'bulk_' . $action; 150 187 if ( array_key_exists( $action, $this->bulk_actions ) && is_callable( array( $this, $method ) ) && ! empty( $_GET['ID'] ) && is_array( $_GET['ID'] ) ) { 151 188 $ids_sql = '(' . implode( ',', array_fill( 0, count( $_GET['ID'] ), '%s' ) ) . ')'; 152 $this->$method( $_GET['ID'], $wpdb->prepare( $ids_sql, $_GET['ID'] ) ); 153 } 154 155 wp_redirect( remove_query_arg( 156 array( '_wp_http_referer', '_wpnonce', 'ID', 'action', 'action2' ), 157 wp_unslash( $_SERVER['REQUEST_URI'] ) 158 ) ); 159 exit; 189 $id = array_map( 'absint', $_GET['ID'] ); 190 $this->$method( $id, $wpdb->prepare( $ids_sql, $id ) ); //phpcs:ignore WordPress.DB.PreparedSQL 191 } 192 193 if ( isset( $_SERVER['REQUEST_URI'] ) ) { 194 wp_safe_redirect( 195 remove_query_arg( 196 array( '_wp_http_referer', '_wpnonce', 'ID', 'action', 'action2' ), 197 esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) 198 ) 199 ); 200 exit; 201 } 160 202 } 161 203 … … 163 205 * Default code for deleting entries. 164 206 * validated already by process_bulk_action() 207 * 208 * @param array $ids ids of the items to delete. 209 * @param string $ids_sql the sql for the ids. 210 * @return void 165 211 */ 166 212 protected function bulk_delete( array $ids, $ids_sql ) { … … 218 264 global $wpdb; 219 265 220 $per_page = $this->get_items_per_page( $this-> package . '_items_per_page', $this->items_per_page );266 $per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page ); 221 267 return $wpdb->prepare( 'LIMIT %d', $per_page ); 222 268 } … … 228 274 */ 229 275 protected function get_items_offset() { 230 $per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page );276 $per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page ); 231 277 $current_page = $this->get_pagenum(); 232 278 if ( 1 < $current_page ) { … … 277 323 $valid_sortable_columns = array_values( $this->sort_by ); 278 324 279 if ( ! empty( $_GET['orderby'] ) && in_array( $_GET['orderby'], $valid_sortable_columns ) ) {280 $orderby = sanitize_text_field( $_GET['orderby'] );325 if ( ! empty( $_GET['orderby'] ) && in_array( $_GET['orderby'], $valid_sortable_columns, true ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 326 $orderby = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended 281 327 } else { 282 328 $orderby = $valid_sortable_columns[0]; … … 293 339 protected function get_request_order() { 294 340 295 if ( ! empty( $_GET['order'] ) && 'desc' === strtolower( $_GET['order'] ) ) {341 if ( ! empty( $_GET['order'] ) && 'desc' === strtolower( sanitize_text_field( wp_unslash( $_GET['order'] ) ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 296 342 $order = 'DESC'; 297 343 } else { … … 308 354 */ 309 355 protected function get_request_status() { 310 $status = ( ! empty( $_GET['status'] ) ) ? $_GET['status'] : '';356 $status = ( ! empty( $_GET['status'] ) ) ? sanitize_text_field( wp_unslash( $_GET['status'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended 311 357 return $status; 312 358 } … … 318 364 */ 319 365 protected function get_request_search_query() { 320 $search_query = ( ! empty( $_GET['s'] ) ) ? $_GET['s'] : '';366 $search_query = ( ! empty( $_GET['s'] ) ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended 321 367 return $search_query; 322 368 } … … 330 376 protected function get_table_columns() { 331 377 $columns = array_keys( $this->columns ); 332 if ( ! in_array( $this->ID, $columns ) ) {378 if ( ! in_array( $this->ID, $columns, true ) ) { 333 379 $columns[] = $this->ID; 334 380 } … … 344 390 * that feature it will return an empty string. 345 391 * 346 * TODO:347 * - Improve search doing LIKE by word rather than by phrases.348 *349 392 * @return string 350 393 */ … … 352 395 global $wpdb; 353 396 354 if ( empty( $_GET['s'] ) || empty( $this->search_by ) ) { 397 if ( empty( $_GET['s'] ) || empty( $this->search_by ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 355 398 return ''; 356 399 } 357 400 358 $filter = array(); 401 $search_string = sanitize_text_field( wp_unslash( $_GET['s'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended 402 403 $filter = array(); 359 404 foreach ( $this->search_by as $column ) { 360 $filter[] = $wpdb->prepare('`' . $column . '` like "%%s%"', $wpdb->esc_like( $_GET['s'] )); 405 $wild = '%'; 406 $sql_like = $wild . $wpdb->esc_like( $search_string ) . $wild; 407 $filter[] = $wpdb->prepare( '`' . $column . '` LIKE %s', $sql_like ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.DB.PreparedSQL.NotPrepared 361 408 } 362 409 return implode( ' OR ', $filter ); … … 370 417 global $wpdb; 371 418 372 if ( ! $this->filter_by || empty( $_GET['filter_by'] ) || ! is_array( $_GET['filter_by'] ) ) { 419 if ( ! $this->filter_by || empty( $_GET['filter_by'] ) || ! is_array( $_GET['filter_by'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 373 420 return ''; 374 421 } … … 377 424 378 425 foreach ( $this->filter_by as $column => $options ) { 379 if ( empty( $_GET['filter_by'][ $column ] ) || empty( $options[ $_GET['filter_by'][ $column ] ] ) ) { 426 if ( empty( $_GET['filter_by'][ $column ] ) || empty( $options[ $_GET['filter_by'][ $column ] ] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 380 427 continue; 381 428 } 382 429 383 $filter[] = $wpdb->prepare( "`$column` = %s", $_GET['filter_by'][ $column ] );430 $filter[] = $wpdb->prepare( "`$column` = %s", sanitize_text_field( wp_unslash( $_GET['filter_by'][ $column ] ) ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.DB.PreparedSQL.InterpolatedNotPrepared 384 431 } 385 432 … … 404 451 $this->process_row_actions(); 405 452 406 if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {453 if ( ! empty( $_REQUEST['_wp_http_referer'] && ! empty( $_SERVER['REQUEST_URI'] ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended 407 454 // _wp_http_referer is used only on bulk actions, we remove it to keep the $_GET shorter 408 wp_ redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI']) ) );455 wp_safe_redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ); 409 456 exit; 410 457 } … … 415 462 $offset = $this->get_items_query_offset(); 416 463 $order = $this->get_items_query_order(); 417 $where = array_filter(array( 418 $this->get_items_query_search(), 419 $this->get_items_query_filters(), 420 )); 464 $where = array_filter( 465 array( 466 $this->get_items_query_search(), 467 $this->get_items_query_filters(), 468 ) 469 ); 421 470 $columns = '`' . implode( '`, `', $this->get_table_columns() ) . '`'; 422 471 423 472 if ( ! empty( $where ) ) { 424 $where = 'WHERE (' . implode( ') AND (', $where ) . ')';473 $where = 'WHERE (' . implode( ') AND (', $where ) . ')'; 425 474 } else { 426 475 $where = ''; … … 429 478 $sql = "SELECT $columns FROM {$this->table_name} {$where} {$order} {$limit} {$offset}"; 430 479 431 $this->set_items( $wpdb->get_results( $sql, ARRAY_A ) ); 480 $this->set_items( $wpdb->get_results( $sql, ARRAY_A ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 432 481 433 482 $query_count = "SELECT COUNT({$this->ID}) FROM {$this->table_name} {$where}"; 434 $total_items = $wpdb->get_var( $query_count ); 435 $per_page = $this->get_items_per_page( $this->package . '_items_per_page', $this->items_per_page ); 436 $this->set_pagination_args( array( 437 'total_items' => $total_items, 438 'per_page' => $per_page, 439 'total_pages' => ceil( $total_items / $per_page ), 440 ) ); 441 } 442 483 $total_items = $wpdb->get_var( $query_count ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 484 $per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page ); 485 $this->set_pagination_args( 486 array( 487 'total_items' => $total_items, 488 'per_page' => $per_page, 489 'total_pages' => ceil( $total_items / $per_page ), 490 ) 491 ); 492 } 493 494 /** 495 * Display the table. 496 * 497 * @param string $which The name of the table. 498 */ 443 499 public function extra_tablenav( $which ) { 444 500 if ( ! $this->filter_by || 'top' !== $which ) { … … 449 505 450 506 foreach ( $this->filter_by as $id => $options ) { 451 $default = ! empty( $_GET['filter_by'][ $id ] ) ? $_GET['filter_by'][ $id ] : '';507 $default = ! empty( $_GET['filter_by'][ $id ] ) ? sanitize_text_field( wp_unslash( $_GET['filter_by'][ $id ] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended 452 508 if ( empty( $options[ $default ] ) ) { 453 509 $default = ''; … … 457 513 458 514 foreach ( $options as $value => $label ) { 459 echo '<option value="' . esc_attr( $value ) . '" ' . esc_html( $value == $default ? 'selected' : '' ) .'>'515 echo '<option value="' . esc_attr( $value ) . '" ' . esc_html( $value === $default ? 'selected' : '' ) . '>' 460 516 . esc_html( $label ) 461 517 . '</option>'; … … 472 528 * Set the data for displaying. It will attempt to unserialize (There is a chance that some columns 473 529 * are serialized). This can be override in child classes for futher data transformation. 530 * 531 * @param array $items Items array. 474 532 */ 475 533 protected function set_items( array $items ) { … … 484 542 * of how the primary key is named (to keep the code simpler). The bulk actions will do the proper 485 543 * name transformation though using `$this->ID`. 544 * 545 * @param array $row The row to render. 486 546 */ 487 547 public function column_cb( $row ) { 488 return '<input name="ID[]" type="checkbox" value="' . esc_attr( $row[ $this->ID ] ) . '" />';548 return '<input name="ID[]" type="checkbox" value="' . esc_attr( $row[ $this->ID ] ) . '" />'; 489 549 } 490 550 … … 495 555 * and it checks that the row action method exists before rendering it. 496 556 * 497 * @param array $row Row to render498 * @param $column_name Current row499 * @return 557 * @param array $row Row to be rendered. 558 * @param string $column_name Column name. 559 * @return string 500 560 */ 501 561 protected function maybe_render_actions( $row, $column_name ) { … … 506 566 $row_id = $row[ $this->ID ]; 507 567 508 $actions = '<div class="row-actions">';568 $actions = '<div class="row-actions">'; 509 569 $action_count = 0; 510 570 foreach ( $this->row_actions[ $column_name ] as $action_key => $action ) { … … 516 576 } 517 577 518 $action_link = ! empty( $action['link'] ) ? $action['link'] : add_query_arg( array( 'row_action' => $action_key, 'row_id' => $row_id, 'nonce' => wp_create_nonce( $action_key . '::' . $row_id ) ) ); 578 $action_link = ! empty( $action['link'] ) ? $action['link'] : add_query_arg( 579 array( 580 'row_action' => $action_key, 581 'row_id' => $row_id, 582 'nonce' => wp_create_nonce( $action_key . '::' . $row_id ), 583 ) 584 ); 519 585 $span_class = ! empty( $action['class'] ) ? $action['class'] : $action_key; 520 586 $separator = ( $action_count < count( $this->row_actions[ $column_name ] ) ) ? ' | ' : ''; … … 528 594 } 529 595 596 /** 597 * Process the bulk actions. 598 * 599 * @return void 600 */ 530 601 protected function process_row_actions() { 531 602 $parameters = array( 'row_action', 'row_id', 'nonce' ); 532 603 foreach ( $parameters as $parameter ) { 533 if ( empty( $_REQUEST[ $parameter ] ) ) { 604 if ( empty( $_REQUEST[ $parameter ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 534 605 return; 535 606 } 536 607 } 537 608 538 $method = 'row_action_' . $_REQUEST['row_action']; 539 540 if ( $_REQUEST['nonce'] === wp_create_nonce( $_REQUEST[ 'row_action' ] . '::' . $_REQUEST[ 'row_id' ] ) && method_exists( $this, $method ) ) { 541 $this->$method( $_REQUEST['row_id'] ); 542 } 543 544 wp_redirect( remove_query_arg( 545 array( 'row_id', 'row_action', 'nonce' ), 546 wp_unslash( $_SERVER['REQUEST_URI'] ) 547 ) ); 548 exit; 609 $action = sanitize_text_field( wp_unslash( $_REQUEST['row_action'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated 610 $row_id = sanitize_text_field( wp_unslash( $_REQUEST['row_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated 611 $nonce = sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated 612 $method = 'row_action_' . $action; // phpcs:ignore WordPress.Security.NonceVerification.Recommended 613 614 if ( wp_verify_nonce( $nonce, $action . '::' . $row_id ) && method_exists( $this, $method ) ) { 615 $this->$method( sanitize_text_field( wp_unslash( $row_id ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended 616 } 617 618 if ( isset( $_SERVER['REQUEST_URI'] ) ) { 619 wp_safe_redirect( 620 remove_query_arg( 621 array( 'row_id', 'row_action', 'nonce' ), 622 esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) 623 ) 624 ); 625 exit; 626 } 549 627 } 550 628 551 629 /** 552 630 * Default column formatting, it will escape everythig for security. 631 * 632 * @param array $item The item array. 633 * @param string $column_name Column name to display. 634 * 635 * @return string 553 636 */ 554 637 public function column_default( $item, $column_name ) { 555 $column_html = esc_html( $item[ $column_name ] );638 $column_html = esc_html( $item[ $column_name ] ); 556 639 $column_html .= $this->maybe_render_actions( $item, $column_name ); 557 640 return $column_html; … … 575 658 protected function display_admin_notices() { 576 659 foreach ( $this->admin_notices as $notice ) { 577 echo '<div id="message" class="' . $notice['class']. '">';660 echo '<div id="message" class="' . esc_attr( $notice['class'] ) . '">'; 578 661 echo ' <p>' . wp_kses_post( $notice['message'] ) . '</p>'; 579 662 echo '</div>'; … … 589 672 $request_status = $this->get_request_status(); 590 673 591 // Helper to set 'all' filter when not set on status counts passed in 674 // Helper to set 'all' filter when not set on status counts passed in. 592 675 if ( ! isset( $this->status_counts['all'] ) ) { 593 676 $this->status_counts = array( 'all' => array_sum( $this->status_counts ) ) + $this->status_counts; … … 613 696 if ( $status_list_items ) { 614 697 echo '<ul class="subsubsub">'; 615 echo implode( " | \n", $status_list_items ); 698 echo implode( " | \n", $status_list_items ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 616 699 echo '</ul>'; 617 700 } … … 625 708 protected function display_table() { 626 709 echo '<form id="' . esc_attr( $this->_args['plural'] ) . '-filter" method="get">'; 627 foreach ( $_GET as $key => $value ) { 710 foreach ( $_GET as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 628 711 if ( '_' === $key[0] || 'paged' === $key || 'ID' === $key ) { 629 712 continue; … … 632 715 } 633 716 if ( ! empty( $this->search_by ) ) { 634 echo $this->search_box( $this->get_search_box_button_text(), 'plugin' ); // WPCS: XSS OK717 echo $this->search_box( $this->get_search_box_button_text(), 'plugin' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 635 718 } 636 719 parent::display(); … … 645 728 $this->process_row_actions(); 646 729 647 if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {730 if ( ! empty( $_REQUEST['_wp_http_referer'] ) && ! empty( $_SERVER['REQUEST_URI'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 648 731 // _wp_http_referer is used only on bulk actions, we remove it to keep the $_GET shorter 649 wp_ redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI']) ) );732 wp_safe_redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ); 650 733 exit; 651 734 } … … 672 755 return esc_html__( 'Search', 'action-scheduler' ); 673 756 } 757 758 /** 759 * Gets the screen per_page option name. 760 * 761 * @return string 762 */ 763 protected function get_per_page_option_name() { 764 return $this->package . '_items_per_page'; 765 } 674 766 } -
action-scheduler/trunk/classes/data-stores/ActionScheduler_DBLogger.php
r2599552 r2622865 30 30 $date_local = $date->format( 'Y-m-d H:i:s' ); 31 31 32 /** @var \wpdb $wpdb */ 32 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 33 33 global $wpdb; 34 $wpdb->insert( $wpdb->actionscheduler_logs, [ 35 'action_id' => $action_id, 36 'message' => $message, 37 'log_date_gmt' => $date_gmt, 38 'log_date_local' => $date_local, 39 ], [ '%d', '%s', '%s', '%s' ] ); 34 $wpdb->insert( 35 $wpdb->actionscheduler_logs, 36 array( 37 'action_id' => $action_id, 38 'message' => $message, 39 'log_date_gmt' => $date_gmt, 40 'log_date_local' => $date_local, 41 ), 42 array( '%d', '%s', '%s', '%s' ) 43 ); 40 44 41 45 return $wpdb->insert_id; … … 50 54 */ 51 55 public function get_entry( $entry_id ) { 52 /** @var \wpdb $wpdb */ 56 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 53 57 global $wpdb; 54 58 $entry = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->actionscheduler_logs} WHERE log_id=%d", $entry_id ) ); … … 86 90 */ 87 91 public function get_logs( $action_id ) { 88 /** @var \wpdb $wpdb */ 92 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 89 93 global $wpdb; 90 94 91 95 $records = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->actionscheduler_logs} WHERE action_id=%d", $action_id ) ); 92 96 93 return array_map( [ $this, 'create_entry_from_db_record' ], $records );97 return array_map( array( $this, 'create_entry_from_db_record' ), $records ); 94 98 } 95 99 … … 106 110 parent::init(); 107 111 108 add_action( 'action_scheduler_deleted_action', [ $this, 'clear_deleted_action_logs' ], 10, 1 );112 add_action( 'action_scheduler_deleted_action', array( $this, 'clear_deleted_action_logs' ), 10, 1 ); 109 113 } 110 114 … … 115 119 */ 116 120 public function clear_deleted_action_logs( $action_id ) { 117 /** @var \wpdb $wpdb */ 121 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 118 122 global $wpdb; 119 $wpdb->delete( $wpdb->actionscheduler_logs, [ 'action_id' => $action_id, ], [ '%d' ]);123 $wpdb->delete( $wpdb->actionscheduler_logs, array( 'action_id' => $action_id ), array( '%d' ) ); 120 124 } 121 125 … … 130 134 } 131 135 132 /** @var \wpdb $wpdb */ 136 /** @var \wpdb $wpdb */ //phpcs:ignore Generic.Commenting.DocComment.MissingShort 133 137 global $wpdb; 134 138 $date = as_get_datetime_object(); … … 139 143 $format = '(%d, ' . $wpdb->prepare( '%s, %s, %s', $message, $date_gmt, $date_local ) . ')'; 140 144 $sql_query = "INSERT {$wpdb->actionscheduler_logs} (action_id, message, log_date_gmt, log_date_local) VALUES "; 141 $value_rows = [];145 $value_rows = array(); 142 146 143 147 foreach ( $action_ids as $action_id ) { 144 $value_rows[] = $wpdb->prepare( $format, $action_id ); 148 $value_rows[] = $wpdb->prepare( $format, $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 145 149 } 146 150 $sql_query .= implode( ',', $value_rows ); 147 151 148 $wpdb->query( $sql_query ); 152 $wpdb->query( $sql_query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 149 153 } 150 154 } -
action-scheduler/trunk/classes/data-stores/ActionScheduler_DBStore.php
r2599552 r2622865 41 41 * 42 42 * @param ActionScheduler_Action $action Action object. 43 * @param DateTime $date Optional schedule date. Default null.43 * @param DateTime $date Optional schedule date. Default null. 44 44 * 45 45 * @return int Action ID. 46 * @throws RuntimeException Throws exception when saving the action fails. 46 47 */ 47 48 public function save_action( ActionScheduler_Action $action, \DateTime $date = null ) { … … 52 53 /** @var \wpdb $wpdb */ 53 54 global $wpdb; 54 $data = [55 $data = array( 55 56 'hook' => $action->get_hook(), 56 57 'status' => ( $action->is_finished() ? self::STATUS_COMPLETE : self::STATUS_PENDING ), 57 58 'scheduled_date_gmt' => $this->get_scheduled_date_string( $action, $date ), 58 59 'scheduled_date_local' => $this->get_scheduled_date_string_local( $action, $date ), 59 'schedule' => serialize( $action->get_schedule() ), 60 'schedule' => serialize( $action->get_schedule() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize 60 61 'group_id' => $this->get_group_id( $action->get_group() ), 61 ];62 ); 62 63 $args = wp_json_encode( $action->get_args() ); 63 64 if ( strlen( $args ) <= static::$max_index_length ) { … … 73 74 74 75 if ( is_wp_error( $action_id ) ) { 75 throw new RuntimeException( $action_id->get_error_message() ); 76 } 77 elseif ( empty( $action_id ) ) { 78 throw new RuntimeException( $wpdb->last_error ? $wpdb->last_error : __( 'Database error.', 'action-scheduler' ) ); 76 throw new \RuntimeException( $action_id->get_error_message() ); 77 } elseif ( empty( $action_id ) ) { 78 throw new \RuntimeException( $wpdb->last_error ? $wpdb->last_error : __( 'Database error.', 'action-scheduler' ) ); 79 79 } 80 80 … … 115 115 * 116 116 * @param string $slug The string name of a group. 117 * @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group.117 * @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group. 118 118 * 119 119 * @return int The group's ID, if it exists or is created, or 0 if it does not exist and is not created. … … 143 143 /** @var \wpdb $wpdb */ 144 144 global $wpdb; 145 $wpdb->insert( $wpdb->actionscheduler_groups, [ 'slug' => $slug ]);145 $wpdb->insert( $wpdb->actionscheduler_groups, array( 'slug' => $slug ) ); 146 146 147 147 return (int) $wpdb->insert_id; … … 158 158 /** @var \wpdb $wpdb */ 159 159 global $wpdb; 160 $data = $wpdb->get_row( $wpdb->prepare( 161 "SELECT a.*, g.slug AS `group` FROM {$wpdb->actionscheduler_actions} a LEFT JOIN {$wpdb->actionscheduler_groups} g ON a.group_id=g.group_id WHERE a.action_id=%d", 162 $action_id 163 ) ); 160 $data = $wpdb->get_row( 161 $wpdb->prepare( 162 "SELECT a.*, g.slug AS `group` FROM {$wpdb->actionscheduler_actions} a LEFT JOIN {$wpdb->actionscheduler_groups} g ON a.group_id=g.group_id WHERE a.action_id=%d", 163 $action_id 164 ) 165 ); 164 166 165 167 if ( empty( $data ) ) { … … 173 175 174 176 // Convert NULL dates to zero dates. 175 $date_fields = [177 $date_fields = array( 176 178 'scheduled_date_gmt', 177 179 'scheduled_date_local', 178 180 'last_attempt_gmt', 179 'last_attempt_gmt' 180 ];181 foreach ( $date_fields as $date_field ) {181 'last_attempt_gmt', 182 ); 183 foreach ( $date_fields as $date_field ) { 182 184 if ( is_null( $data->$date_field ) ) { 183 185 $data->$date_field = ActionScheduler_StoreSchema::DEFAULT_DATE; … … 215 217 $hook = $data->hook; 216 218 $args = json_decode( $data->args, true ); 217 $schedule = unserialize( $data->schedule ); 219 $schedule = unserialize( $data->schedule ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize 218 220 219 221 $this->validate_args( $args, $data->action_id ); … … 237 239 * 238 240 * @return string SQL statement already properly escaped. 241 * @throws InvalidArgumentException If the query is invalid. 239 242 */ 240 243 protected function get_query_actions_sql( array $query, $select_or_count = 'select' ) { 241 244 242 if ( ! in_array( $select_or_count, array( 'select', 'count' ) ) ) {245 if ( ! in_array( $select_or_count, array( 'select', 'count' ), true ) ) { 243 246 throw new InvalidArgumentException( __( 'Invalid value for select or count parameter. Cannot query actions.', 'action-scheduler' ) ); 244 247 } 245 248 246 $query = wp_parse_args( $query, [ 247 'hook' => '', 248 'args' => null, 249 'date' => null, 250 'date_compare' => '<=', 251 'modified' => null, 252 'modified_compare' => '<=', 253 'group' => '', 254 'status' => '', 255 'claimed' => null, 256 'per_page' => 5, 257 'offset' => 0, 258 'orderby' => 'date', 259 'order' => 'ASC', 260 ] ); 261 262 /** @var \wpdb $wpdb */ 263 global $wpdb; 264 $sql = ( 'count' === $select_or_count ) ? 'SELECT count(a.action_id)' : 'SELECT a.action_id'; 265 $sql .= " FROM {$wpdb->actionscheduler_actions} a"; 266 $sql_params = []; 267 268 if ( ! empty( $query[ 'group' ] ) || 'group' === $query[ 'orderby' ] ) { 249 $query = wp_parse_args( 250 $query, 251 array( 252 'hook' => '', 253 'args' => null, 254 'date' => null, 255 'date_compare' => '<=', 256 'modified' => null, 257 'modified_compare' => '<=', 258 'group' => '', 259 'status' => '', 260 'claimed' => null, 261 'per_page' => 5, 262 'offset' => 0, 263 'orderby' => 'date', 264 'order' => 'ASC', 265 ) 266 ); 267 268 /** @var \wpdb $wpdb */ 269 global $wpdb; 270 $sql = ( 'count' === $select_or_count ) ? 'SELECT count(a.action_id)' : 'SELECT a.action_id'; 271 $sql .= " FROM {$wpdb->actionscheduler_actions} a"; 272 $sql_params = array(); 273 274 if ( ! empty( $query['group'] ) || 'group' === $query['orderby'] ) { 269 275 $sql .= " LEFT JOIN {$wpdb->actionscheduler_groups} g ON g.group_id=a.group_id"; 270 276 } 271 277 272 $sql .= " WHERE 1=1";273 274 if ( ! empty( $query[ 'group'] ) ) {275 $sql .= " AND g.slug=%s";276 $sql_params[] = $query[ 'group'];277 } 278 279 if ( $query[ 'hook'] ) {280 $sql .= " AND a.hook=%s";281 $sql_params[] = $query[ 'hook'];282 } 283 if ( ! is_null( $query[ 'args'] ) ) {284 $sql .= " AND a.args=%s";285 $sql_params[] = $this->get_args_for_query( $query[ 'args'] );278 $sql .= ' WHERE 1=1'; 279 280 if ( ! empty( $query['group'] ) ) { 281 $sql .= ' AND g.slug=%s'; 282 $sql_params[] = $query['group']; 283 } 284 285 if ( $query['hook'] ) { 286 $sql .= ' AND a.hook=%s'; 287 $sql_params[] = $query['hook']; 288 } 289 if ( ! is_null( $query['args'] ) ) { 290 $sql .= ' AND a.args=%s'; 291 $sql_params[] = $this->get_args_for_query( $query['args'] ); 286 292 } 287 293 … … 293 299 } 294 300 295 if ( $query[ 'date'] instanceof \DateTime ) {296 $date = clone $query[ 'date'];301 if ( $query['date'] instanceof \DateTime ) { 302 $date = clone $query['date']; 297 303 $date->setTimezone( new \DateTimeZone( 'UTC' ) ); 298 304 $date_string = $date->format( 'Y-m-d H:i:s' ); 299 $comparator = $this->validate_sql_comparator( $query[ 'date_compare'] );300 $sql .= " AND a.scheduled_date_gmt $comparator %s";305 $comparator = $this->validate_sql_comparator( $query['date_compare'] ); 306 $sql .= " AND a.scheduled_date_gmt $comparator %s"; 301 307 $sql_params[] = $date_string; 302 308 } 303 309 304 if ( $query[ 'modified'] instanceof \DateTime ) {305 $modified = clone $query[ 'modified'];310 if ( $query['modified'] instanceof \DateTime ) { 311 $modified = clone $query['modified']; 306 312 $modified->setTimezone( new \DateTimeZone( 'UTC' ) ); 307 313 $date_string = $modified->format( 'Y-m-d H:i:s' ); 308 $comparator = $this->validate_sql_comparator( $query[ 'modified_compare'] );309 $sql .= " AND a.last_attempt_gmt $comparator %s";314 $comparator = $this->validate_sql_comparator( $query['modified_compare'] ); 315 $sql .= " AND a.last_attempt_gmt $comparator %s"; 310 316 $sql_params[] = $date_string; 311 317 } 312 318 313 if ( $query[ 'claimed' ] === true) {314 $sql .= " AND a.claim_id != 0";315 } elseif ( $query[ 'claimed' ] === false) {316 $sql .= " AND a.claim_id = 0";317 } elseif ( ! is_null( $query[ 'claimed'] ) ) {318 $sql .= " AND a.claim_id = %d";319 $sql_params[] = $query[ 'claimed'];319 if ( true === $query['claimed'] ) { 320 $sql .= ' AND a.claim_id != 0'; 321 } elseif ( false === $query['claimed'] ) { 322 $sql .= ' AND a.claim_id = 0'; 323 } elseif ( ! is_null( $query['claimed'] ) ) { 324 $sql .= ' AND a.claim_id = %d'; 325 $sql_params[] = $query['claimed']; 320 326 } 321 327 322 328 if ( ! empty( $query['search'] ) ) { 323 $sql .= " AND (a.hook LIKE %s OR (a.extended_args IS NULL AND a.args LIKE %s) OR a.extended_args LIKE %s";324 for ( $i = 0; $i < 3; $i++ ) {329 $sql .= ' AND (a.hook LIKE %s OR (a.extended_args IS NULL AND a.args LIKE %s) OR a.extended_args LIKE %s'; 330 for ( $i = 0; $i < 3; $i++ ) { 325 331 $sql_params[] = sprintf( '%%%s%%', $query['search'] ); 326 332 } … … 328 334 $search_claim_id = (int) $query['search']; 329 335 if ( $search_claim_id ) { 330 $sql .= ' OR a.claim_id = %d';336 $sql .= ' OR a.claim_id = %d'; 331 337 $sql_params[] = $search_claim_id; 332 338 } … … 362 368 } 363 369 364 if ( $query[ 'per_page'] > 0 ) {365 $sql .= " LIMIT %d, %d";366 $sql_params[] = $query[ 'offset'];367 $sql_params[] = $query[ 'per_page'];370 if ( $query['per_page'] > 0 ) { 371 $sql .= ' LIMIT %d, %d'; 372 $sql_params[] = $query['offset']; 373 $sql_params[] = $query['per_page']; 368 374 } 369 375 } 370 376 371 377 if ( ! empty( $sql_params ) ) { 372 $sql = $wpdb->prepare( $sql, $sql_params ); 378 $sql = $wpdb->prepare( $sql, $sql_params ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 373 379 } 374 380 … … 388 394 * @return string|array|null The IDs of actions matching the query. Null on failure. 389 395 */ 390 public function query_actions( $query = [], $query_type = 'select' ) {396 public function query_actions( $query = array(), $query_type = 'select' ) { 391 397 /** @var wpdb $wpdb */ 392 398 global $wpdb; … … 394 400 $sql = $this->get_query_actions_sql( $query, $query_type ); 395 401 396 return ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); 402 return ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoSql, WordPress.DB.DirectDatabaseQuery.NoCaching 397 403 } 398 404 … … 407 413 $sql = "SELECT a.status, count(a.status) as 'count'"; 408 414 $sql .= " FROM {$wpdb->actionscheduler_actions} a"; 409 $sql .= " GROUP BY a.status";415 $sql .= ' GROUP BY a.status'; 410 416 411 417 $actions_count_by_status = array(); 412 418 $action_stati_and_labels = $this->get_status_labels(); 413 419 414 foreach ( $wpdb->get_results( $sql ) as $action_data ) { 415 // Ignore any actions with invalid status 420 foreach ( $wpdb->get_results( $sql ) as $action_data ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 421 // Ignore any actions with invalid status. 416 422 if ( array_key_exists( $action_data->status, $action_stati_and_labels ) ) { 417 423 $actions_count_by_status[ $action_data->status ] = $action_data->count; … … 428 434 * 429 435 * @return void 436 * @throws \InvalidArgumentException If the action update failed. 430 437 */ 431 438 public function cancel_action( $action_id ) { … … 435 442 $updated = $wpdb->update( 436 443 $wpdb->actionscheduler_actions, 437 [ 'status' => self::STATUS_CANCELED ],438 [ 'action_id' => $action_id ],439 [ '%s' ],440 [ '%d' ]444 array( 'status' => self::STATUS_CANCELED ), 445 array( 'action_id' => $action_id ), 446 array( '%s' ), 447 array( '%d' ) 441 448 ); 442 449 if ( empty( $updated ) ) { … … 457 464 */ 458 465 public function cancel_actions_by_hook( $hook ) { 459 $this->bulk_cancel_actions( [ 'hook' => $hook ]);466 $this->bulk_cancel_actions( array( 'hook' => $hook ) ); 460 467 } 461 468 … … 468 475 */ 469 476 public function cancel_actions_by_group( $group ) { 470 $this->bulk_cancel_actions( [ 'group' => $group ]);477 $this->bulk_cancel_actions( array( 'group' => $group ) ); 471 478 } 472 479 … … 487 494 488 495 // Don't cancel actions that are already canceled. 489 if ( isset( $query_args['status'] ) && $query_args['status'] == self::STATUS_CANCELED) {496 if ( isset( $query_args['status'] ) && self::STATUS_CANCELED === $query_args['status'] ) { 490 497 return; 491 498 } … … 494 501 $query_args = wp_parse_args( 495 502 $query_args, 496 [503 array( 497 504 'per_page' => 1000, 498 505 'status' => self::STATUS_PENDING, 499 506 'orderby' => 'action_id', 500 ]507 ) 501 508 ); 502 509 … … 513 520 514 521 $wpdb->query( 515 $wpdb->prepare( // wpcs: PreparedSQLPlaceholders replacement count ok.516 "UPDATE {$wpdb->actionscheduler_actions} SET status = %s WHERE action_id IN {$query_in}", 522 $wpdb->prepare( 523 "UPDATE {$wpdb->actionscheduler_actions} SET status = %s WHERE action_id IN {$query_in}", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 517 524 $parameters 518 525 ) … … 527 534 * 528 535 * @param int $action_id Action ID. 536 * @throws \InvalidArgumentException If the action deletion failed. 529 537 */ 530 538 public function delete_action( $action_id ) { 531 539 /** @var \wpdb $wpdb */ 532 540 global $wpdb; 533 $deleted = $wpdb->delete( $wpdb->actionscheduler_actions, [ 'action_id' => $action_id ], [ '%d' ]);541 $deleted = $wpdb->delete( $wpdb->actionscheduler_actions, array( 'action_id' => $action_id ), array( '%d' ) ); 534 542 if ( empty( $deleted ) ) { 535 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 543 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment 536 544 } 537 545 do_action( 'action_scheduler_deleted_action', $action_id ); … … 543 551 * @param string $action_id Action ID. 544 552 * 545 * @throws \InvalidArgumentException546 553 * @return \DateTime The local date the action is scheduled to run, or the date that it ran. 547 554 */ … … 557 564 * @param int $action_id Action ID. 558 565 * 559 * @throws \InvalidArgumentException 566 * @throws \InvalidArgumentException If action cannot be identified. 560 567 * @return \DateTime The GMT date the action is scheduled to run, or the date that it ran. 561 568 */ … … 565 572 $record = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d", $action_id ) ); 566 573 if ( empty( $record ) ) { 567 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 568 } 569 if ( $record->status == self::STATUS_PENDING) {574 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment 575 } 576 if ( self::STATUS_PENDING === $record->status ) { 570 577 return as_get_datetime_object( $record->scheduled_date_gmt ); 571 578 } else { … … 579 586 * @param int $max_actions Maximum number of action to include in claim. 580 587 * @param \DateTime $before_date Jobs must be schedule before this date. Defaults to now. 588 * @param array $hooks Hooks to filter for. 589 * @param string $group Group to filter for. 581 590 * 582 591 * @return ActionScheduler_ActionClaim … … 587 596 $this->claim_before_date = $before_date; 588 597 $this->claim_actions( $claim_id, $max_actions, $before_date, $hooks, $group ); 589 $action_ids = $this->find_actions_by_claim_id( $claim_id );598 $action_ids = $this->find_actions_by_claim_id( $claim_id ); 590 599 $this->claim_before_date = null; 591 600 … … 602 611 global $wpdb; 603 612 $now = as_get_datetime_object(); 604 $wpdb->insert( $wpdb->actionscheduler_claims, [ 'date_created_gmt' => $now->format( 'Y-m-d H:i:s' ) ]);613 $wpdb->insert( $wpdb->actionscheduler_claims, array( 'date_created_gmt' => $now->format( 'Y-m-d H:i:s' ) ) ); 605 614 606 615 return $wpdb->insert_id; … … 613 622 * @param int $limit Number of action to include in claim. 614 623 * @param \DateTime $before_date Should use UTC timezone. 624 * @param array $hooks Hooks to filter for. 625 * @param string $group Group to filter for. 615 626 * 616 627 * @return int The number of actions that were claimed. 617 * @throws \RuntimeException 628 * @throws \InvalidArgumentException Throws InvalidArgumentException if group doesn't exist. 629 * @throws \RuntimeException Throws RuntimeException if unable to claim action. 618 630 */ 619 631 protected function claim_actions( $claim_id, $limit, \DateTime $before_date = null, $hooks = array(), $group = '' ) { … … 624 636 $date = is_null( $before_date ) ? $now : clone $before_date; 625 637 626 // can't use $wpdb->update() because of the <= condition 638 // can't use $wpdb->update() because of the <= condition. 627 639 $update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s"; 628 640 $params = array( … … 632 644 ); 633 645 634 $where = "WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s";646 $where = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s'; 635 647 $params[] = $date->format( 'Y-m-d H:i:s' ); 636 648 $params[] = self::STATUS_PENDING; … … 646 658 $group_id = $this->get_group_id( $group, false ); 647 659 648 // throw exception if no matching group found, this matches ActionScheduler_wpPostStore's behaviour 660 // throw exception if no matching group found, this matches ActionScheduler_wpPostStore's behaviour. 649 661 if ( empty( $group_id ) ) { 650 662 /* translators: %s: group name */ … … 656 668 } 657 669 658 $order = "ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC LIMIT %d"; 670 /** 671 * Sets the order-by clause used in the action claim query. 672 * 673 * @since x.x.x 674 * 675 * @param string $order_by_sql 676 */ 677 $order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC' ); 659 678 $params[] = $limit; 660 679 661 $sql = $wpdb->prepare( "{$update} {$where} {$order}", $params ); 662 663 $rows_affected = $wpdb->query( $sql ); 664 if ( $rows_affected === false ) { 680 $sql = $wpdb->prepare( "{$update} {$where} {$order} LIMIT %d", $params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders 681 $rows_affected = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 682 if ( false === $rows_affected ) { 665 683 throw new \RuntimeException( __( 'Unable to claim actions. Database error.', 'action-scheduler' ) ); 666 684 } … … 678 696 679 697 $sql = "SELECT COUNT(DISTINCT claim_id) FROM {$wpdb->actionscheduler_actions} WHERE claim_id != 0 AND status IN ( %s, %s)"; 680 $sql = $wpdb->prepare( $sql, [ self::STATUS_PENDING, self::STATUS_RUNNING ] );681 682 return (int) $wpdb->get_var( $sql ); 698 $sql = $wpdb->prepare( $sql, array( self::STATUS_PENDING, self::STATUS_RUNNING ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 699 700 return (int) $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 683 701 } 684 702 … … 694 712 695 713 $sql = "SELECT claim_id FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d"; 696 $sql = $wpdb->prepare( $sql, $action_id ); 697 698 return (int) $wpdb->get_var( $sql ); 714 $sql = $wpdb->prepare( $sql, $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 715 716 return (int) $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 699 717 } 700 718 … … 702 720 * Retrieve the action IDs of action in a claim. 703 721 * 722 * @param int $claim_id Claim ID. 704 723 * @return int[] 705 724 */ … … 719 738 // Verify that the scheduled date for each action is within the expected bounds (in some unusual 720 739 // cases, we cannot depend on MySQL to honor all of the WHERE conditions we specify). 721 foreach ( $wpdb->get_results( $sql ) as $claimed_action ) { 740 foreach ( $wpdb->get_results( $sql ) as $claimed_action ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 722 741 if ( $claimed_action->scheduled_date_gmt <= $cut_off ) { 723 742 $action_ids[] = absint( $claimed_action->action_id ); … … 736 755 /** @var \wpdb $wpdb */ 737 756 global $wpdb; 738 $wpdb->update( $wpdb->actionscheduler_actions, [ 'claim_id' => 0 ], [ 'claim_id' => $claim->get_id() ], [ '%d' ], [ '%d' ]);739 $wpdb->delete( $wpdb->actionscheduler_claims, [ 'claim_id' => $claim->get_id() ], [ '%d' ]);757 $wpdb->update( $wpdb->actionscheduler_actions, array( 'claim_id' => 0 ), array( 'claim_id' => $claim->get_id() ), array( '%d' ), array( '%d' ) ); 758 $wpdb->delete( $wpdb->actionscheduler_claims, array( 'claim_id' => $claim->get_id() ), array( '%d' ) ); 740 759 } 741 760 … … 752 771 $wpdb->update( 753 772 $wpdb->actionscheduler_actions, 754 [ 'claim_id' => 0 ],755 [ 'action_id' => $action_id ],756 [ '%s' ],757 [ '%d' ]773 array( 'claim_id' => 0 ), 774 array( 'action_id' => $action_id ), 775 array( '%s' ), 776 array( '%d' ) 758 777 ); 759 778 } … … 763 782 * 764 783 * @param int $action_id Action ID. 784 * @throws \InvalidArgumentException Throw an exception if action was not updated. 765 785 */ 766 786 public function mark_failure( $action_id ) { … … 769 789 $updated = $wpdb->update( 770 790 $wpdb->actionscheduler_actions, 771 [ 'status' => self::STATUS_FAILED ],772 [ 'action_id' => $action_id ],773 [ '%s' ],774 [ '%d' ]791 array( 'status' => self::STATUS_FAILED ), 792 array( 'action_id' => $action_id ), 793 array( '%s' ), 794 array( '%d' ) 775 795 ); 776 796 if ( empty( $updated ) ) { 777 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 797 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment 778 798 } 779 799 } … … 791 811 792 812 $sql = "UPDATE {$wpdb->actionscheduler_actions} SET attempts = attempts+1, status=%s, last_attempt_gmt = %s, last_attempt_local = %s WHERE action_id = %d"; 793 $sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time( 'mysql', true ), current_time( 'mysql' ), $action_id ); 794 $wpdb->query( $sql ); 813 $sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time( 'mysql', true ), current_time( 'mysql' ), $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 814 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 795 815 } 796 816 … … 801 821 * 802 822 * @return void 823 * @throws \InvalidArgumentException Throw an exception if action was not updated. 803 824 */ 804 825 public function mark_complete( $action_id ) { … … 807 828 $updated = $wpdb->update( 808 829 $wpdb->actionscheduler_actions, 809 [830 array( 810 831 'status' => self::STATUS_COMPLETE, 811 832 'last_attempt_gmt' => current_time( 'mysql', true ), 812 833 'last_attempt_local' => current_time( 'mysql' ), 813 ],814 [ 'action_id' => $action_id ],815 [ '%s' ],816 [ '%d' ]834 ), 835 array( 'action_id' => $action_id ), 836 array( '%s' ), 837 array( '%d' ) 817 838 ); 818 839 if ( empty( $updated ) ) { 819 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 840 throw new \InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); //phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment 820 841 } 821 842 } … … 827 848 * 828 849 * @return string 850 * @throws \InvalidArgumentException Throw an exception if not status was found for action_id. 851 * @throws \RuntimeException Throw an exception if action status could not be retrieved. 829 852 */ 830 853 public function get_status( $action_id ) { … … 832 855 global $wpdb; 833 856 $sql = "SELECT status FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d"; 834 $sql = $wpdb->prepare( $sql, $action_id ); 835 $status = $wpdb->get_var( $sql ); 836 837 if ( $status === null) {857 $sql = $wpdb->prepare( $sql, $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 858 $status = $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 859 860 if ( null === $status ) { 838 861 throw new \InvalidArgumentException( __( 'Invalid action ID. No status found.', 'action-scheduler' ) ); 839 862 } elseif ( empty( $status ) ) { -
action-scheduler/trunk/classes/data-stores/ActionScheduler_wpPostStore.php
r2599552 r2622865 5 5 */ 6 6 class ActionScheduler_wpPostStore extends ActionScheduler_Store { 7 const POST_TYPE = 'scheduled-action';8 const GROUP_TAXONOMY = 'action-group';7 const POST_TYPE = 'scheduled-action'; 8 const GROUP_TAXONOMY = 'action-group'; 9 9 const SCHEDULE_META_KEY = '_action_manager_schedule'; 10 const DEPENDENCIES_MET = 'as-post-store-dependencies-met';10 const DEPENDENCIES_MET = 'as-post-store-dependencies-met'; 11 11 12 12 /** … … 20 20 private $claim_before_date = null; 21 21 22 /** @var DateTimeZone */ 23 protected $local_timezone = NULL; 24 25 public function save_action( ActionScheduler_Action $action, DateTime $scheduled_date = NULL ){ 22 /** 23 * Local Timezone. 24 * 25 * @var DateTimeZone 26 */ 27 protected $local_timezone = null; 28 29 /** 30 * Save action. 31 * 32 * @param ActionScheduler_Action $action Scheduled Action. 33 * @param DateTime $scheduled_date Scheduled Date. 34 * 35 * @throws RuntimeException Throws an exception if the action could not be saved. 36 * @return int 37 */ 38 public function save_action( ActionScheduler_Action $action, DateTime $scheduled_date = null ) { 26 39 try { 27 40 $this->validate_action( $action ); 28 41 $post_array = $this->create_post_array( $action, $scheduled_date ); 29 $post_id = $this->save_post_array( $post_array );42 $post_id = $this->save_post_array( $post_array ); 30 43 $this->save_post_schedule( $post_id, $action->get_schedule() ); 31 44 $this->save_action_group( $post_id, $action->get_group() ); … … 33 46 return $post_id; 34 47 } catch ( Exception $e ) { 48 /* translators: %s: action error message */ 35 49 throw new RuntimeException( sprintf( __( 'Error saving action: %s', 'action-scheduler' ), $e->getMessage() ), 0 ); 36 50 } 37 51 } 38 52 39 protected function create_post_array( ActionScheduler_Action $action, DateTime $scheduled_date = NULL ) { 53 /** 54 * Create post array. 55 * 56 * @param ActionScheduler_Action $action Scheduled Action. 57 * @param DateTime $scheduled_date Scheduled Date. 58 * 59 * @return array Returns an array of post data. 60 */ 61 protected function create_post_array( ActionScheduler_Action $action, DateTime $scheduled_date = null ) { 40 62 $post = array( 41 'post_type' => self::POST_TYPE,42 'post_title' => $action->get_hook(),43 'post_content' => json_encode($action->get_args()),44 'post_status' => ( $action->is_finished() ? 'publish' : 'pending' ),63 'post_type' => self::POST_TYPE, 64 'post_title' => $action->get_hook(), 65 'post_content' => wp_json_encode( $action->get_args() ), 66 'post_status' => ( $action->is_finished() ? 'publish' : 'pending' ), 45 67 'post_date_gmt' => $this->get_scheduled_date_string( $action, $scheduled_date ), 46 68 'post_date' => $this->get_scheduled_date_string_local( $action, $scheduled_date ), … … 49 71 } 50 72 73 /** 74 * Save post array. 75 * 76 * @param array $post_array Post array. 77 * @return int Returns the post ID. 78 * @throws RuntimeException Throws an exception if the action could not be saved. 79 */ 51 80 protected function save_post_array( $post_array ) { 52 81 add_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10, 1 ); … … 60 89 } 61 90 62 $post_id = wp_insert_post( $post_array);91 $post_id = wp_insert_post( $post_array ); 63 92 64 93 if ( $has_kses ) { … … 69 98 remove_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10 ); 70 99 71 if ( is_wp_error( $post_id) || empty($post_id) ) {100 if ( is_wp_error( $post_id ) || empty( $post_id ) ) { 72 101 throw new RuntimeException( __( 'Unable to save action.', 'action-scheduler' ) ); 73 102 } … … 75 104 } 76 105 106 /** 107 * Filter insert post data. 108 * 109 * @param array $postdata Post data to filter. 110 * 111 * @return array 112 */ 77 113 public function filter_insert_post_data( $postdata ) { 78 if ( $postdata['post_type'] == self::POST_TYPE) {114 if ( self::POST_TYPE === $postdata['post_type'] ) { 79 115 $postdata['post_author'] = 0; 80 if ( $postdata['post_status'] == 'future') {116 if ( 'future' === $postdata['post_status'] ) { 81 117 $postdata['post_status'] = 'publish'; 82 118 } … … 114 150 */ 115 151 public function set_unique_post_slug( $override_slug, $slug, $post_ID, $post_status, $post_type ) { 116 if ( self::POST_TYPE == $post_type ) {152 if ( self::POST_TYPE === $post_type ) { 117 153 $override_slug = uniqid( self::POST_TYPE . '-', true ) . '-' . wp_generate_password( 32, false ); 118 154 } … … 120 156 } 121 157 158 /** 159 * Save post schedule. 160 * 161 * @param int $post_id Post ID of the scheduled action. 162 * @param string $schedule Schedule to save. 163 * 164 * @return void 165 */ 122 166 protected function save_post_schedule( $post_id, $schedule ) { 123 167 update_post_meta( $post_id, self::SCHEDULE_META_KEY, $schedule ); 124 168 } 125 169 170 /** 171 * Save action group. 172 * 173 * @param int $post_id Post ID. 174 * @param string $group Group to save. 175 * @return void 176 */ 126 177 protected function save_action_group( $post_id, $group ) { 127 if ( empty( $group) ) {128 wp_set_object_terms( $post_id, array(), self::GROUP_TAXONOMY, FALSE);178 if ( empty( $group ) ) { 179 wp_set_object_terms( $post_id, array(), self::GROUP_TAXONOMY, false ); 129 180 } else { 130 wp_set_object_terms( $post_id, array($group), self::GROUP_TAXONOMY, FALSE ); 131 } 132 } 133 181 wp_set_object_terms( $post_id, array( $group ), self::GROUP_TAXONOMY, false ); 182 } 183 } 184 185 /** 186 * Fetch actions. 187 * 188 * @param int $action_id Action ID. 189 * @return object 190 */ 134 191 public function fetch_action( $action_id ) { 135 192 $post = $this->get_post( $action_id ); 136 if ( empty( $post) || $post->post_type != self::POST_TYPE) {193 if ( empty( $post ) || self::POST_TYPE !== $post->post_type ) { 137 194 return $this->get_null_action(); 138 195 } … … 148 205 } 149 206 207 /** 208 * Get post. 209 * 210 * @param string $action_id - Action ID. 211 * @return WP_Post|null 212 */ 150 213 protected function get_post( $action_id ) { 151 if ( empty($action_id) ) { 152 return NULL; 153 } 154 return get_post($action_id); 155 } 156 214 if ( empty( $action_id ) ) { 215 return null; 216 } 217 return get_post( $action_id ); 218 } 219 220 /** 221 * Get NULL action. 222 * 223 * @return ActionScheduler_NullAction 224 */ 157 225 protected function get_null_action() { 158 226 return new ActionScheduler_NullAction(); 159 227 } 160 228 229 /** 230 * Make action from post. 231 * 232 * @param WP_Post $post Post object. 233 * @return WP_Post 234 */ 161 235 protected function make_action_from_post( $post ) { 162 236 $hook = $post->post_title; … … 168 242 $this->validate_schedule( $schedule, $post->ID ); 169 243 170 $group = wp_get_object_terms( $post->ID, self::GROUP_TAXONOMY, array( 'fields' => 'names') );171 $group = empty( $group ) ? '' : reset( $group);244 $group = wp_get_object_terms( $post->ID, self::GROUP_TAXONOMY, array( 'fields' => 'names' ) ); 245 $group = empty( $group ) ? '' : reset( $group ); 172 246 173 247 return ActionScheduler::factory()->get_stored_action( $this->get_action_status_by_post_status( $post->post_status ), $hook, $args, $schedule, $group ); … … 175 249 176 250 /** 177 * @param string $post_status 178 * 179 * @throws InvalidArgumentException if $post_status not in known status fields returned by $this->get_status_labels() 251 * Get action status by post status. 252 * 253 * @param string $post_status Post status. 254 * 255 * @throws InvalidArgumentException Throw InvalidArgumentException if $post_status not in known status fields returned by $this->get_status_labels(). 180 256 * @return string 181 257 */ … … 183 259 184 260 switch ( $post_status ) { 185 case 'publish' :261 case 'publish': 186 262 $action_status = self::STATUS_COMPLETE; 187 263 break; 188 case 'trash' :264 case 'trash': 189 265 $action_status = self::STATUS_CANCELED; 190 266 break; 191 default :267 default: 192 268 if ( ! array_key_exists( $post_status, $this->get_status_labels() ) ) { 193 269 throw new InvalidArgumentException( sprintf( 'Invalid post status: "%s". No matching action status available.', $post_status ) ); … … 201 277 202 278 /** 203 * @param string $action_status 204 * @throws InvalidArgumentException if $post_status not in known status fields returned by $this->get_status_labels() 279 * Get post status by action status. 280 * 281 * @param string $action_status Action status. 282 * 283 * @throws InvalidArgumentException Throws InvalidArgumentException if $post_status not in known status fields returned by $this->get_status_labels(). 205 284 * @return string 206 285 */ … … 208 287 209 288 switch ( $action_status ) { 210 case self::STATUS_COMPLETE :289 case self::STATUS_COMPLETE: 211 290 $post_status = 'publish'; 212 291 break; 213 case self::STATUS_CANCELED :292 case self::STATUS_CANCELED: 214 293 $post_status = 'trash'; 215 294 break; 216 default :295 default: 217 296 if ( ! array_key_exists( $action_status, $this->get_status_labels() ) ) { 218 297 throw new InvalidArgumentException( sprintf( 'Invalid action status: "%s".', $action_status ) ); … … 228 307 * Returns the SQL statement to query (or count) actions. 229 308 * 230 * @param array $query Filtering options 231 * @param string $select_or_count Whether the SQL should select and return the IDs or just the row count 232 * @throws InvalidArgumentException if $select_or_count not count or select 309 * @param array $query - Filtering options. 310 * @param string $select_or_count - Whether the SQL should select and return the IDs or just the row count. 311 * 312 * @throws InvalidArgumentException - Throw InvalidArgumentException if $select_or_count not count or select. 233 313 * @return string SQL statement. The returned SQL is already properly escaped. 234 314 */ 235 315 protected function get_query_actions_sql( array $query, $select_or_count = 'select' ) { 236 316 237 if ( ! in_array( $select_or_count, array( 'select', 'count' ) ) ) {317 if ( ! in_array( $select_or_count, array( 'select', 'count' ), true ) ) { 238 318 throw new InvalidArgumentException( __( 'Invalid schedule. Cannot save action.', 'action-scheduler' ) ); 239 319 } 240 320 241 $query = wp_parse_args( $query, array( 242 'hook' => '', 243 'args' => NULL, 244 'date' => NULL, 245 'date_compare' => '<=', 246 'modified' => NULL, 247 'modified_compare' => '<=', 248 'group' => '', 249 'status' => '', 250 'claimed' => NULL, 251 'per_page' => 5, 252 'offset' => 0, 253 'orderby' => 'date', 254 'order' => 'ASC', 255 'search' => '', 256 ) ); 257 258 /** @var wpdb $wpdb */ 321 $query = wp_parse_args( 322 $query, 323 array( 324 'hook' => '', 325 'args' => null, 326 'date' => null, 327 'date_compare' => '<=', 328 'modified' => null, 329 'modified_compare' => '<=', 330 'group' => '', 331 'status' => '', 332 'claimed' => null, 333 'per_page' => 5, 334 'offset' => 0, 335 'orderby' => 'date', 336 'order' => 'ASC', 337 'search' => '', 338 ) 339 ); 340 341 /** 342 * Global wpdb object. 343 * 344 * @var wpdb $wpdb 345 */ 259 346 global $wpdb; 260 $sql = ( 'count' === $select_or_count ) ? 'SELECT count(p.ID)' : 'SELECT p.ID ';261 $sql .= "FROM {$wpdb->posts} p";347 $sql = ( 'count' === $select_or_count ) ? 'SELECT count(p.ID)' : 'SELECT p.ID '; 348 $sql .= "FROM {$wpdb->posts} p"; 262 349 $sql_params = array(); 263 350 if ( empty( $query['group'] ) && 'group' === $query['orderby'] ) { … … 266 353 $sql .= " LEFT JOIN {$wpdb->terms} t ON tt.term_id=t.term_id"; 267 354 } elseif ( ! empty( $query['group'] ) ) { 268 $sql .= " INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id=p.ID";269 $sql .= " INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id=tt.term_taxonomy_id";270 $sql .= " INNER JOIN {$wpdb->terms} t ON tt.term_id=t.term_id";271 $sql .= " AND t.slug=%s";355 $sql .= " INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id=p.ID"; 356 $sql .= " INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id=tt.term_taxonomy_id"; 357 $sql .= " INNER JOIN {$wpdb->terms} t ON tt.term_id=t.term_id"; 358 $sql .= ' AND t.slug=%s'; 272 359 $sql_params[] = $query['group']; 273 360 } 274 $sql .= " WHERE post_type=%s";361 $sql .= ' WHERE post_type=%s'; 275 362 $sql_params[] = self::POST_TYPE; 276 363 if ( $query['hook'] ) { 277 $sql .= " AND p.post_title=%s";364 $sql .= ' AND p.post_title=%s'; 278 365 $sql_params[] = $query['hook']; 279 366 } 280 if ( ! is_null($query['args']) ) {281 $sql .= " AND p.post_content=%s";282 $sql_params[] = json_encode($query['args']);367 if ( ! is_null( $query['args'] ) ) { 368 $sql .= ' AND p.post_content=%s'; 369 $sql_params[] = wp_json_encode( $query['args'] ); 283 370 } 284 371 … … 292 379 if ( $query['date'] instanceof DateTime ) { 293 380 $date = clone $query['date']; 294 $date->setTimezone( new DateTimeZone( 'UTC') );295 $date_string = $date->format('Y-m-d H:i:s');296 $comparator = $this->validate_sql_comparator($query['date_compare']);297 $sql .= " AND p.post_date_gmt $comparator %s";381 $date->setTimezone( new DateTimeZone( 'UTC' ) ); 382 $date_string = $date->format( 'Y-m-d H:i:s' ); 383 $comparator = $this->validate_sql_comparator( $query['date_compare'] ); 384 $sql .= " AND p.post_date_gmt $comparator %s"; 298 385 $sql_params[] = $date_string; 299 386 } … … 301 388 if ( $query['modified'] instanceof DateTime ) { 302 389 $modified = clone $query['modified']; 303 $modified->setTimezone( new DateTimeZone( 'UTC') );304 $date_string = $modified->format('Y-m-d H:i:s');305 $comparator = $this->validate_sql_comparator($query['modified_compare']);306 $sql .= " AND p.post_modified_gmt $comparator %s";390 $modified->setTimezone( new DateTimeZone( 'UTC' ) ); 391 $date_string = $modified->format( 'Y-m-d H:i:s' ); 392 $comparator = $this->validate_sql_comparator( $query['modified_compare'] ); 393 $sql .= " AND p.post_modified_gmt $comparator %s"; 307 394 $sql_params[] = $date_string; 308 395 } 309 396 310 if ( $query['claimed'] === TRUE) {397 if ( true === $query['claimed'] ) { 311 398 $sql .= " AND p.post_password != ''"; 312 } elseif ( $query['claimed'] === FALSE) {399 } elseif ( false === $query['claimed'] ) { 313 400 $sql .= " AND p.post_password = ''"; 314 } elseif ( ! is_null($query['claimed']) ) {315 $sql .= " AND p.post_password = %s";401 } elseif ( ! is_null( $query['claimed'] ) ) { 402 $sql .= ' AND p.post_password = %s'; 316 403 $sql_params[] = $query['claimed']; 317 404 } 318 405 319 406 if ( ! empty( $query['search'] ) ) { 320 $sql .= " AND (p.post_title LIKE %s OR p.post_content LIKE %s OR p.post_password LIKE %s)";321 for ( $i = 0; $i < 3; $i++ ) {407 $sql .= ' AND (p.post_title LIKE %s OR p.post_content LIKE %s OR p.post_password LIKE %s)'; 408 for ( $i = 0; $i < 3; $i++ ) { 322 409 $sql_params[] = sprintf( '%%%s%%', $query['search'] ); 323 410 } … … 354 441 $sql .= " ORDER BY $orderby $order"; 355 442 if ( $query['per_page'] > 0 ) { 356 $sql .= " LIMIT %d, %d";443 $sql .= ' LIMIT %d, %d'; 357 444 $sql_params[] = $query['offset']; 358 445 $sql_params[] = $query['per_page']; … … 360 447 } 361 448 362 return $wpdb->prepare( $sql, $sql_params ); 449 return $wpdb->prepare( $sql, $sql_params ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 363 450 } 364 451 … … 376 463 */ 377 464 public function query_actions( $query = array(), $query_type = 'select' ) { 378 /** @var wpdb $wpdb */ 465 /** 466 * Global $wpdb object. 467 * 468 * @var wpdb $wpdb 469 */ 379 470 global $wpdb; 380 471 381 472 $sql = $this->get_query_actions_sql( $query, $query_type ); 382 473 383 return ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); 474 return ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared 384 475 } 385 476 … … 400 491 $action_status_name = $this->get_action_status_by_post_status( $post_status_name ); 401 492 } catch ( Exception $e ) { 402 // Ignore any post statuses that aren't for actions 493 // Ignore any post statuses that aren't for actions. 403 494 continue; 404 495 } … … 412 503 413 504 /** 414 * @param string $action_id 415 * 416 * @throws InvalidArgumentException 505 * Cancel action. 506 * 507 * @param int $action_id Action ID. 508 * 509 * @throws InvalidArgumentException If $action_id is not identified. 417 510 */ 418 511 public function cancel_action( $action_id ) { 419 512 $post = get_post( $action_id ); 420 if ( empty( $post ) || ( $post->post_type != self::POST_TYPE ) ) { 513 if ( empty( $post ) || ( self::POST_TYPE !== $post->post_type ) ) { 514 /* translators: %s is the action ID */ 421 515 throw new InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 422 516 } … … 427 521 } 428 522 523 /** 524 * Delete action. 525 * 526 * @param int $action_id Action ID. 527 * @return void 528 * @throws InvalidArgumentException If action is not identified. 529 */ 429 530 public function delete_action( $action_id ) { 430 531 $post = get_post( $action_id ); 431 if ( empty( $post ) || ( $post->post_type != self::POST_TYPE ) ) { 532 if ( empty( $post ) || ( self::POST_TYPE !== $post->post_type ) ) { 533 /* translators: %s is the action ID */ 432 534 throw new InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 433 535 } 434 536 do_action( 'action_scheduler_deleted_action', $action_id ); 435 537 436 wp_delete_post( $action_id, TRUE);437 } 438 439 /** 440 * @param string $action_id441 * 442 * @ throws InvalidArgumentException538 wp_delete_post( $action_id, true ); 539 } 540 541 /** 542 * Get date for claim id. 543 * 544 * @param int $action_id Action ID. 443 545 * @return ActionScheduler_DateTime The date the action is schedule to run, or the date that it ran. 444 546 */ … … 449 551 450 552 /** 451 * @param string $action_id 452 * 453 * @throws InvalidArgumentException 553 * Get Date GMT. 554 * 555 * @param int $action_id Action ID. 556 * 557 * @throws InvalidArgumentException If $action_id is not identified. 454 558 * @return ActionScheduler_DateTime The date the action is schedule to run, or the date that it ran. 455 559 */ 456 560 public function get_date_gmt( $action_id ) { 457 561 $post = get_post( $action_id ); 458 if ( empty( $post ) || ( $post->post_type != self::POST_TYPE ) ) { 562 if ( empty( $post ) || ( self::POST_TYPE !== $post->post_type ) ) { 563 /* translators: %s is the action ID */ 459 564 throw new InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 460 565 } 461 if ( $post->post_status == 'publish') {566 if ( 'publish' === $post->post_status ) { 462 567 return as_get_datetime_object( $post->post_modified_gmt ); 463 568 } else { … … 467 572 468 573 /** 469 * @param int $max_actions 574 * Stake claim. 575 * 576 * @param int $max_actions Maximum number of actions. 470 577 * @param DateTime $before_date Jobs must be schedule before this date. Defaults to now. 471 578 * @param array $hooks Claim only actions with a hook or hooks. … … 478 585 public function stake_claim( $max_actions = 10, DateTime $before_date = null, $hooks = array(), $group = '' ) { 479 586 $this->claim_before_date = $before_date; 480 $claim_id = $this->generate_claim_id();587 $claim_id = $this->generate_claim_id(); 481 588 $this->claim_actions( $claim_id, $max_actions, $before_date, $hooks, $group ); 482 $action_ids = $this->find_actions_by_claim_id( $claim_id );589 $action_ids = $this->find_actions_by_claim_id( $claim_id ); 483 590 $this->claim_before_date = null; 484 591 … … 487 594 488 595 /** 596 * Get claim count. 597 * 489 598 * @return int 490 599 */ 491 public function get_claim_count() {600 public function get_claim_count() { 492 601 global $wpdb; 493 602 494 $sql = "SELECT COUNT(DISTINCT post_password) FROM {$wpdb->posts} WHERE post_password != '' AND post_type = %s AND post_status IN ('in-progress','pending')"; 495 $sql = $wpdb->prepare( $sql, array( self::POST_TYPE ) ); 496 497 return $wpdb->get_var( $sql ); 498 } 499 603 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching 604 return $wpdb->get_var( 605 $wpdb->prepare( 606 "SELECT COUNT(DISTINCT post_password) FROM {$wpdb->posts} WHERE post_password != '' AND post_type = %s AND post_status IN ('in-progress','pending')", 607 array( self::POST_TYPE ) 608 ) 609 ); 610 } 611 612 /** 613 * Generate claim id. 614 * 615 * @return string 616 */ 500 617 protected function generate_claim_id() { 501 $claim_id = md5(microtime(true) . rand(0,1000)); 502 return substr($claim_id, 0, 20); // to fit in db field with 20 char limit 503 } 504 505 /** 506 * @param string $claim_id 507 * @param int $limit 618 $claim_id = md5( microtime( true ) . wp_rand( 0, 1000 ) ); 619 return substr( $claim_id, 0, 20 ); // to fit in db field with 20 char limit. 620 } 621 622 /** 623 * Claim actions. 624 * 625 * @param string $claim_id Claim ID. 626 * @param int $limit Limit. 508 627 * @param DateTime $before_date Should use UTC timezone. 509 628 * @param array $hooks Claim only actions with a hook or hooks. 510 629 * @param string $group Claim only actions in the given group. 511 630 * 512 * @return int The number of actions that were claimed 513 * @throws RuntimeException When there is a database error. 514 * @throws InvalidArgumentException When the group is invalid. 631 * @return int The number of actions that were claimed. 632 * @throws RuntimeException When there is a database error. 515 633 */ 516 634 protected function claim_actions( $claim_id, $limit, DateTime $before_date = null, $hooks = array(), $group = '' ) { … … 525 643 } 526 644 527 /** @var wpdb $wpdb */ 645 /** 646 * Global wpdb object. 647 * 648 * @var wpdb $wpdb 649 */ 528 650 global $wpdb; 529 651 … … 560 682 $where .= ' AND ID IN (' . join( ',', $ids ) . ')'; 561 683 } else { 562 $where .= ' AND post_date_gmt <= %s';684 $where .= ' AND post_date_gmt <= %s'; 563 685 $params[] = $date->format( 'Y-m-d H:i:s' ); 564 686 } … … 569 691 570 692 // Run the query and gather results. 571 $rows_affected = $wpdb->query( $wpdb->prepare( "{$update} {$where} {$order}", $params ) ); 572 if ( $rows_affected === false ) { 693 $rows_affected = $wpdb->query( $wpdb->prepare( "{$update} {$where} {$order}", $params ) ); // phpcs:ignore // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare 694 695 if ( false === $rows_affected ) { 573 696 throw new RuntimeException( __( 'Unable to claim actions. Database error.', 'action-scheduler' ) ); 574 697 } … … 590 713 protected function get_actions_by_group( $group, $limit, DateTime $date ) { 591 714 // Ensure the group exists before continuing. 592 if ( ! term_exists( $group, self::GROUP_TAXONOMY )) { 715 if ( ! term_exists( $group, self::GROUP_TAXONOMY ) ) { 716 /* translators: %s is the group name */ 593 717 throw new InvalidArgumentException( sprintf( __( 'The group "%s" does not exist.', 'action-scheduler' ), $group ) ); 594 718 } … … 610 734 ), 611 735 'date_query' => array( 612 'column' => 'post_date_gmt',613 'before' => $date->format( 'Y-m-d H:i' ),736 'column' => 'post_date_gmt', 737 'before' => $date->format( 'Y-m-d H:i' ), 614 738 'inclusive' => true, 615 739 ), 616 'tax_query' => array(740 'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery 617 741 array( 618 742 'taxonomy' => self::GROUP_TAXONOMY, … … 628 752 629 753 /** 630 * @param string $claim_id 754 * Find actions by claim ID. 755 * 756 * @param string $claim_id Claim ID. 631 757 * @return array 632 758 */ 633 759 public function find_actions_by_claim_id( $claim_id ) { 634 /** @var wpdb $wpdb */ 760 /** 761 * Global wpdb object. 762 * 763 * @var wpdb $wpdb 764 */ 635 765 global $wpdb; 636 637 $sql = "SELECT ID, post_date_gmt FROM {$wpdb->posts} WHERE post_type = %s AND post_password = %s";638 $sql = $wpdb->prepare( $sql, array( self::POST_TYPE, $claim_id ) );639 766 640 767 $action_ids = array(); … … 642 769 $cut_off = $before_date->format( 'Y-m-d H:i:s' ); 643 770 771 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 772 $results = $wpdb->get_results( 773 $wpdb->prepare( 774 "SELECT ID, post_date_gmt FROM {$wpdb->posts} WHERE post_type = %s AND post_password = %s", 775 array( 776 self::POST_TYPE, 777 $claim_id, 778 ) 779 ) 780 ); 781 644 782 // Verify that the scheduled date for each action is within the expected bounds (in some unusual 645 783 // cases, we cannot depend on MySQL to honor all of the WHERE conditions we specify). 646 foreach ( $ wpdb->get_results( $sql )as $claimed_action ) {784 foreach ( $results as $claimed_action ) { 647 785 if ( $claimed_action->post_date_gmt <= $cut_off ) { 648 786 $action_ids[] = absint( $claimed_action->ID ); … … 653 791 } 654 792 793 /** 794 * Release claim. 795 * 796 * @param ActionScheduler_ActionClaim $claim Claim object to release. 797 * @return void 798 * @throws RuntimeException When the claim is not unlocked. 799 */ 655 800 public function release_claim( ActionScheduler_ActionClaim $claim ) { 656 801 $action_ids = $this->find_actions_by_claim_id( $claim->get_id() ); 657 802 if ( empty( $action_ids ) ) { 658 return; // nothing to do 803 return; // nothing to do. 659 804 } 660 805 $action_id_string = implode( ',', array_map( 'intval', $action_ids ) ); 661 /** @var wpdb $wpdb */ 806 /** 807 * Global wpdb object. 808 * 809 * @var wpdb $wpdb 810 */ 662 811 global $wpdb; 663 $sql = "UPDATE {$wpdb->posts} SET post_password = '' WHERE ID IN ($action_id_string) AND post_password = %s"; 664 $sql = $wpdb->prepare( $sql, array( $claim->get_id() ) ); 665 $result = $wpdb->query( $sql ); 666 if ( $result === false ) { 812 813 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 814 $result = $wpdb->query( 815 $wpdb->prepare( 816 "UPDATE {$wpdb->posts} SET post_password = '' WHERE ID IN ($action_id_string) AND post_password = %s", //phpcs:ignore 817 array( 818 $claim->get_id(), 819 ) 820 ) 821 ); 822 if ( false === $result ) { 667 823 /* translators: %s: claim ID */ 668 824 throw new RuntimeException( sprintf( __( 'Unable to unlock claim %s. Database error.', 'action-scheduler' ), $claim->get_id() ) ); … … 671 827 672 828 /** 673 * @param string $action_id 829 * Unclaim action. 830 * 831 * @param string $action_id Action ID. 832 * @throws RuntimeException When unable to unlock claim on action ID. 674 833 */ 675 834 public function unclaim_action( $action_id ) { 676 /** @var wpdb $wpdb */ 835 /** 836 * Global wpdb object. 837 * 838 * @var wpdb $wpdb 839 */ 677 840 global $wpdb; 678 $sql = "UPDATE {$wpdb->posts} SET post_password = '' WHERE ID = %d AND post_type = %s"; 679 $sql = $wpdb->prepare( $sql, $action_id, self::POST_TYPE ); 680 $result = $wpdb->query( $sql ); 681 if ( $result === false ) { 841 842 //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 843 $result = $wpdb->query( 844 $wpdb->prepare( 845 "UPDATE {$wpdb->posts} SET post_password = '' WHERE ID = %d AND post_type = %s", 846 $action_id, 847 self::POST_TYPE 848 ) 849 ); 850 if ( false === $result ) { 682 851 /* translators: %s: action ID */ 683 852 throw new RuntimeException( sprintf( __( 'Unable to unlock claim on action %s. Database error.', 'action-scheduler' ), $action_id ) ); … … 685 854 } 686 855 856 /** 857 * Mark failure on action. 858 * 859 * @param int $action_id Action ID. 860 * 861 * @return void 862 * @throws RuntimeException When unable to mark failure on action ID. 863 */ 687 864 public function mark_failure( $action_id ) { 688 /** @var wpdb $wpdb */ 865 /** 866 * Global wpdb object. 867 * 868 * @var wpdb $wpdb 869 */ 689 870 global $wpdb; 690 $sql = "UPDATE {$wpdb->posts} SET post_status = %s WHERE ID = %d AND post_type = %s"; 691 $sql = $wpdb->prepare( $sql, self::STATUS_FAILED, $action_id, self::POST_TYPE ); 692 $result = $wpdb->query( $sql ); 693 if ( $result === false ) { 871 872 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 873 $result = $wpdb->query( 874 $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_status = %s WHERE ID = %d AND post_type = %s", self::STATUS_FAILED, $action_id, self::POST_TYPE ) 875 ); 876 if ( false === $result ) { 694 877 /* translators: %s: action ID */ 695 878 throw new RuntimeException( sprintf( __( 'Unable to mark failure on action %s. Database error.', 'action-scheduler' ), $action_id ) ); … … 700 883 * Return an action's claim ID, as stored in the post password column 701 884 * 702 * @param string $action_id885 * @param int $action_id Action ID. 703 886 * @return mixed 704 887 */ … … 710 893 * Return an action's status, as stored in the post status column 711 894 * 712 * @param string $action_id 895 * @param int $action_id Action ID. 896 * 713 897 * @return mixed 898 * @throws InvalidArgumentException When the action ID is invalid. 714 899 */ 715 900 public function get_status( $action_id ) { 716 901 $status = $this->get_post_column( $action_id, 'post_status' ); 717 902 718 if ( $status === null) {903 if ( null === $status ) { 719 904 throw new InvalidArgumentException( __( 'Invalid action ID. No status found.', 'action-scheduler' ) ); 720 905 } … … 723 908 } 724 909 910 /** 911 * Get post column 912 * 913 * @param string $action_id Action ID. 914 * @param string $column_name Column Name. 915 * 916 * @return string|null 917 */ 725 918 private function get_post_column( $action_id, $column_name ) { 726 /** @var \wpdb $wpdb */ 919 /** 920 * Global wpdb object. 921 * 922 * @var wpdb $wpdb 923 */ 727 924 global $wpdb; 728 return $wpdb->get_var( $wpdb->prepare( "SELECT {$column_name} FROM {$wpdb->posts} WHERE ID=%d AND post_type=%s", $action_id, self::POST_TYPE ) ); 729 } 730 731 /** 732 * @param string $action_id 925 926 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 927 return $wpdb->get_var( 928 $wpdb->prepare( 929 "SELECT {$column_name} FROM {$wpdb->posts} WHERE ID=%d AND post_type=%s", // phpcs:ignore 930 $action_id, 931 self::POST_TYPE 932 ) 933 ); 934 } 935 936 /** 937 * Log Execution. 938 * 939 * @param string $action_id Action ID. 733 940 */ 734 941 public function log_execution( $action_id ) { 735 /** @var wpdb $wpdb */ 942 /** 943 * Global wpdb object. 944 * 945 * @var wpdb $wpdb 946 */ 736 947 global $wpdb; 737 948 738 $sql = "UPDATE {$wpdb->posts} SET menu_order = menu_order+1, post_status=%s, post_modified_gmt = %s, post_modified = %s WHERE ID = %d AND post_type = %s"; 739 $sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time('mysql', true), current_time('mysql'), $action_id, self::POST_TYPE ); 740 $wpdb->query($sql); 949 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 950 $wpdb->query( 951 $wpdb->prepare( 952 "UPDATE {$wpdb->posts} SET menu_order = menu_order+1, post_status=%s, post_modified_gmt = %s, post_modified = %s WHERE ID = %d AND post_type = %s", 953 self::STATUS_RUNNING, 954 current_time( 'mysql', true ), 955 current_time( 'mysql' ), 956 $action_id, 957 self::POST_TYPE 958 ) 959 ); 741 960 } 742 961 … … 744 963 * Record that an action was completed. 745 964 * 746 * @param int $action_id ID of the completed action. 747 * @throws InvalidArgumentException|RuntimeException 965 * @param string $action_id ID of the completed action. 966 * 967 * @throws InvalidArgumentException When the action ID is invalid. 968 * @throws RuntimeException When there was an error executing the action. 748 969 */ 749 970 public function mark_complete( $action_id ) { 750 971 $post = get_post( $action_id ); 751 if ( empty( $post ) || ( $post->post_type != self::POST_TYPE ) ) { 972 if ( empty( $post ) || ( self::POST_TYPE !== $post->post_type ) ) { 973 /* translators: %s is the action ID */ 752 974 throw new InvalidArgumentException( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ); 753 975 } 754 976 add_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10, 1 ); 755 977 add_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10, 5 ); 756 $result = wp_update_post(array( 757 'ID' => $action_id, 758 'post_status' => 'publish', 759 ), TRUE); 978 $result = wp_update_post( 979 array( 980 'ID' => $action_id, 981 'post_status' => 'publish', 982 ), 983 true 984 ); 760 985 remove_filter( 'wp_insert_post_data', array( $this, 'filter_insert_post_data' ), 10 ); 761 986 remove_filter( 'pre_wp_unique_post_slug', array( $this, 'set_unique_post_slug' ), 10 ); … … 774 999 array( 775 1000 'ID' => $action_id, 776 'post_status' => 'migrated' 1001 'post_status' => 'migrated', 777 1002 ) 778 1003 ); … … 782 1007 * Determine whether the post store can be migrated. 783 1008 * 1009 * @param [type] $setting - Setting value. 784 1010 * @return bool 785 1011 */ … … 790 1016 if ( empty( $dependencies_met ) ) { 791 1017 $maximum_args_length = apply_filters( 'action_scheduler_maximum_args_length', 191 ); 792 $found_action = $wpdb->get_var( 1018 $found_action = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 793 1019 $wpdb->prepare( 794 1020 "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND CHAR_LENGTH(post_content) > %d LIMIT 1", … … 797 1023 ) 798 1024 ); 799 $dependencies_met = $found_action ? 'no' : 'yes';1025 $dependencies_met = $found_action ? 'no' : 'yes'; 800 1026 set_transient( self::DEPENDENCIES_MET, $dependencies_met, DAY_IN_SECONDS ); 801 1027 } 802 1028 803 return 'yes' == $dependencies_met ? $setting : false;1029 return 'yes' === $dependencies_met ? $setting : false; 804 1030 } 805 1031 … … 811 1037 * developers of this impending requirement. 812 1038 * 813 * @param ActionScheduler_Action $action 1039 * @param ActionScheduler_Action $action Action object. 814 1040 */ 815 1041 protected function validate_action( ActionScheduler_Action $action ) { … … 817 1043 parent::validate_action( $action ); 818 1044 } catch ( Exception $e ) { 1045 /* translators: %s is the error message */ 819 1046 $message = sprintf( __( '%s Support for strings longer than this will be removed in a future version.', 'action-scheduler' ), $e->getMessage() ); 820 _doing_it_wrong( 'ActionScheduler_Action::$args', $message, '2.1.0' );821 } 822 } 823 824 /** 825 * @codeCoverageIgnore1047 _doing_it_wrong( 'ActionScheduler_Action::$args', esc_html( $message ), '2.1.0' ); 1048 } 1049 } 1050 1051 /** 1052 * (@codeCoverageIgnore) 826 1053 */ 827 1054 public function init() { -
action-scheduler/trunk/classes/schema/ActionScheduler_StoreSchema.php
r2599552 r2622865 17 17 * @var int Increment this value to trigger a schema update. 18 18 */ 19 protected $schema_version = 5;19 protected $schema_version = 6; 20 20 21 21 public function __construct() { … … 65 65 KEY group_id (group_id), 66 66 KEY last_attempt_gmt (last_attempt_gmt), 67 KEY claim_id (claim_id),68 67 KEY `claim_id_status_scheduled_date_gmt` (`claim_id`, `status`, `scheduled_date_gmt`) 69 68 ) $charset_collate"; -
action-scheduler/trunk/deprecated/ActionScheduler_Schedule_Deprecated.php
r2340383 r2622865 10 10 * after a given date & time. 11 11 * 12 * @param DateTime $after 12 * @param DateTime $after DateTime to calculate against. 13 13 * 14 14 * @return DateTime|null 15 15 */ 16 public function next( DateTime $after = NULL) {16 public function next( DateTime $after = null ) { 17 17 if ( empty( $after ) ) { 18 18 $return_value = $this->get_date(); … … 23 23 } 24 24 25 _deprecated_function( __METHOD__, '3.0.0', __CLASS__ . '::' . $replacement_method ); 25 _deprecated_function( __METHOD__, '3.0.0', __CLASS__ . '::' . $replacement_method ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 26 26 27 27 return $return_value; -
action-scheduler/trunk/readme.txt
r2599552 r2622865 4 4 Requires at least: 5.2 5 5 Tested up to: 5.7 6 Stable tag: 3. 3.06 Stable tag: 3.4.0 7 7 License: GPLv3 8 8 Requires PHP: 5.6 … … 48 48 == Changelog == 49 49 50 = 3.4.0 - 2021-10-29 = 51 * Enhancement - Number of items per page can now be set for the Scheduled Actions view (props @ovidiul). #771 52 * Fix - Do not lower the max_execution_time if it is already set to 0 (unlimited) (props @barryhughes). #755 53 * Fix - Avoid triggering autoloaders during the version resolution process (props @olegabr). #731 & #776 54 * Dev - ActionScheduler_wcSystemStatus PHPCS fixes (props @ovidiul). #761 55 * Dev - ActionScheduler_DBLogger.php PHPCS fixes (props @ovidiul). #768 56 * Dev - Fixed phpcs for ActionScheduler_Schedule_Deprecated (props @ovidiul). #762 57 * Dev - Improve actions table indicies (props @glagonikas). #774 & #777 58 * Dev - PHPCS fixes for ActionScheduler_DBStore.php (props @ovidiul). #769 & #778 59 * Dev - PHPCS Fixes for ActionScheduler_Abstract_ListTable (props @ovidiul). #763 & #779 60 * Dev - Adds new filter action_scheduler_claim_actions_order_by to allow tuning of the claim query (props @glagonikas). #773 61 * Dev - PHPCS fixes for ActionScheduler_WpPostStore class (props @ovidiul). #780 62 50 63 = 3.3.0 - 2021-09-15 = 51 64 * Enhancement - Adds as_has_scheduled_action() to provide a performant way to test for existing actions. #645
Note: See TracChangeset
for help on using the changeset viewer.