Skip to content
Snippets Groups Projects
Commit 3b5b7a0e authored by Erick Hitter's avatar Erick Hitter
Browse files

Merge branch 'develop' into add/move-to-trash

parents 4107bc1a d4d060ca
No related branches found
No related tags found
No related merge requests found
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Description: Process Bulk Edit requests using Cron * Description: Process Bulk Edit requests using Cron
* Author: Erick Hitter, Automattic * Author: Erick Hitter, Automattic
* Author URI: https://automattic.com/ * Author URI: https://automattic.com/
* Text Domain: automattic-bulk-edit-cron-offload * Text Domain: bulk-edit-cron-offload
* Domain Path: /languages * Domain Path: /languages
* Version: 1.0 * Version: 1.0
* *
......
<?php <?php
/**
* Offload "Empty Trash"
*
* @package Bulk_Edit_Cron_Offload
*/
namespace Automattic\WP\Bulk_Edit_Cron_Offload; namespace Automattic\WP\Bulk_Edit_Cron_Offload;
/**
* Class Delete_All
*/
class Delete_All { class Delete_All {
/** /**
* Class constants * Class constants
...@@ -20,7 +28,7 @@ class Delete_All { ...@@ -20,7 +28,7 @@ class Delete_All {
add_action( 'admin_notices', array( __CLASS__, 'admin_notices' ) ); add_action( 'admin_notices', array( __CLASS__, 'admin_notices' ) );
add_filter( 'posts_where', array( __CLASS__, 'hide_posts_pending_delete' ), 999, 2 ); add_filter( 'posts_where', array( __CLASS__, 'hide_posts_pending_delete' ), 999, 2 );
// Limit when caps are intercepted, given frequent execution of the `map_meta_cap` filter // Limit when caps are intercepted, given frequent execution of the `map_meta_cap` filter.
add_action( 'load-edit.php', function() { add_action( 'load-edit.php', function() {
add_filter( 'map_meta_cap', array( __CLASS__, 'hide_empty_trash_pending_delete' ), 10, 2 ); add_filter( 'map_meta_cap', array( __CLASS__, 'hide_empty_trash_pending_delete' ), 10, 2 );
} ); } );
...@@ -28,10 +36,12 @@ class Delete_All { ...@@ -28,10 +36,12 @@ class Delete_All {
/** /**
* Handle a request to delete all trashed items for a given post type * Handle a request to delete all trashed items for a given post type
*
* @param object $vars Bulk-request variables.
*/ */
public static function process( $vars ) { public static function process( $vars ) {
// Special keys are used to trigger this request, and we need to remove them on redirect // Special keys are used to trigger this request, and we need to remove them on redirect.
$extra_keys = array( 'delete_all', 'delete_all2', ); $extra_keys = array( 'delete_all', 'delete_all2' );
$action_scheduled = self::action_next_scheduled( self::CRON_EVENT, $vars->post_type ); $action_scheduled = self::action_next_scheduled( self::CRON_EVENT, $vars->post_type );
...@@ -46,6 +56,8 @@ class Delete_All { ...@@ -46,6 +56,8 @@ class Delete_All {
/** /**
* Cron callback to delete trashed items in a given post type * Cron callback to delete trashed items in a given post type
*
* @param object $vars Bulk-request variables.
*/ */
public static function process_via_cron( $vars ) { public static function process_via_cron( $vars ) {
global $wpdb; global $wpdb;
...@@ -57,22 +69,25 @@ class Delete_All { ...@@ -57,22 +69,25 @@ class Delete_All {
if ( is_array( $post_ids ) && ! empty( $post_ids ) ) { if ( is_array( $post_ids ) && ! empty( $post_ids ) ) {
require_once ABSPATH . '/wp-admin/includes/post.php'; require_once ABSPATH . '/wp-admin/includes/post.php';
$deleted = $locked = $auth_error = $error = array(); $deleted = array();
$locked = array();
$auth_error = array();
$error = array();
foreach ( $post_ids as $post_id ) { foreach ( $post_ids as $post_id ) {
// Can the user delete this post // Can the user delete this post?
if ( ! user_can( $vars->user_id, 'delete_post', $post_id ) ) { if ( ! user_can( $vars->user_id, 'delete_post', $post_id ) ) {
$auth_error[] = $post_id; $auth_error[] = $post_id;
continue; continue;
} }
// Post is locked by someone, so leave it alone // Post is locked by someone, so leave it alone.
if ( false !== wp_check_post_lock( $post_id ) ) { if ( false !== wp_check_post_lock( $post_id ) ) {
$locked[] = $post_id; $locked[] = $post_id;
continue; continue;
} }
// Try deleting // Try deleting.
$post_deleted = wp_delete_post( $post_id ); $post_deleted = wp_delete_post( $post_id );
if ( $post_deleted ) { if ( $post_deleted ) {
$deleted[] = $post_id; $deleted[] = $post_id;
...@@ -80,18 +95,17 @@ class Delete_All { ...@@ -80,18 +95,17 @@ class Delete_All {
$error[] = $post_id; $error[] = $post_id;
} }
// Take a break periodically // Take a break periodically.
if ( 0 === $count++ % 50 ) { if ( 0 === $count++ % 50 ) {
stop_the_insanity(); stop_the_insanity();
sleep( 3 );
} }
} }
// TODO: something meaningful with this data
$results = compact( 'deleted', 'locked', 'auth_error', 'error' ); $results = compact( 'deleted', 'locked', 'auth_error', 'error' );
return $results; do_action( 'bulk_edit_cron_offload_delete_all_request_completed', $results, $vars );
} else { } else {
// TODO: What to do here? do_action( 'bulk_edit_cron_offload_delete_all_request_no_posts', $post_ids, $vars );
return false;
} }
} }
...@@ -104,19 +118,19 @@ class Delete_All { ...@@ -104,19 +118,19 @@ class Delete_All {
if ( isset( $_REQUEST[ self::ADMIN_NOTICE_KEY ] ) ) { if ( isset( $_REQUEST[ self::ADMIN_NOTICE_KEY ] ) ) {
if ( 1 === (int) $_REQUEST[ self::ADMIN_NOTICE_KEY ] ) { if ( 1 === (int) $_REQUEST[ self::ADMIN_NOTICE_KEY ] ) {
$class = 'notice-success'; $class = 'notice-success';
$message = __( 'Success! The trash will be emptied soon.', 'automattic-bulk-edit-cron-offload' ); $message = __( 'Success! The trash will be emptied soon.', 'bulk-edit-cron-offload' );
} else { } else {
$class = 'notice-error'; $class = 'notice-error';
$message = __( 'A request to empty the trash is already pending for this post type.', 'automattic-bulk-edit-cron-offload' ); $message = __( 'A request to empty the trash is already pending for this post type.', 'bulk-edit-cron-offload' );
} }
} elseif ( 'edit' === $screen->base && isset( $_REQUEST['post_status'] ) && 'trash' === $_REQUEST['post_status'] ) { } elseif ( 'edit' === $screen->base && isset( $_REQUEST['post_status'] ) && 'trash' === $_REQUEST['post_status'] ) {
if ( self::action_next_scheduled( self::CRON_EVENT, $screen->post_type ) ) { if ( self::action_next_scheduled( self::CRON_EVENT, $screen->post_type ) ) {
$class = 'notice-warning'; $class = 'notice-warning';
$message = __( 'A pending request to empty the trash will be processed soon.', 'automattic-bulk-edit-cron-offload' ); $message = __( 'A pending request to empty the trash will be processed soon.', 'bulk-edit-cron-offload' );
} }
} }
// Nothing to display // Nothing to display.
if ( ! isset( $class ) || ! isset( $message ) ) { if ( ! isset( $class ) || ! isset( $message ) ) {
return; return;
} }
...@@ -130,6 +144,10 @@ class Delete_All { ...@@ -130,6 +144,10 @@ class Delete_All {
/** /**
* When a delete is pending for a given post type, hide those posts in the admin * When a delete is pending for a given post type, hide those posts in the admin
*
* @param string $where Posts' WHERE clause.
* @param object $q WP_Query object.
* @return string
*/ */
public static function hide_posts_pending_delete( $where, $q ) { public static function hide_posts_pending_delete( $where, $q ) {
if ( ! is_admin() || ! $q->is_main_query() ) { if ( ! is_admin() || ! $q->is_main_query() ) {
...@@ -156,33 +174,33 @@ class Delete_All { ...@@ -156,33 +174,33 @@ class Delete_All {
* *
* Core doesn't provide a filter specifically for this, but permissions are checked before showing the button * Core doesn't provide a filter specifically for this, but permissions are checked before showing the button
* *
* @param array $caps User's capabilities * @param array $caps User's capabilities.
* @param string $cap Cap currently being checked * @param string $cap Cap currently being checked.
* @return array * @return array
*/ */
public static function hide_empty_trash_pending_delete( $caps, $cap ) { public static function hide_empty_trash_pending_delete( $caps, $cap ) {
// Button we're blocking only shows for the "trash" status, understandably // Button we're blocking only shows for the "trash" status, understandably.
if ( ! isset( $_REQUEST['post_status'] ) || 'trash' !== $_REQUEST['post_status'] ) { if ( ! isset( $_REQUEST['post_status'] ) || 'trash' !== $_REQUEST['post_status'] ) {
return $caps; return $caps;
} }
// Get post type as Core envisions // Get post type as Core envisions.
$screen = get_current_screen(); $screen = get_current_screen();
// Cap used to display button, per WP_Posts_List_Table::extra_tablenav() // Cap used to display button, per WP_Posts_List_Table::extra_tablenav().
$cap_to_block = get_post_type_object( $screen->post_type )->cap->edit_others_posts; $cap_to_block = get_post_type_object( $screen->post_type )->cap->edit_others_posts;
// The current cap isn't the one we're looking for // The current cap isn't the one we're looking for.
if ( $cap !== $cap_to_block ) { if ( $cap !== $cap_to_block ) {
return $caps; return $caps;
} }
// There isn't a pending purge, so one should be permitted // There isn't a pending purge, so one should be permitted.
if ( ! self::action_next_scheduled( self::CRON_EVENT, $screen->post_type ) ) { if ( ! self::action_next_scheduled( self::CRON_EVENT, $screen->post_type ) ) {
return $caps; return $caps;
} }
// Block the edit button by disallowing its cap // Block the edit button by disallowing its cap.
$caps[] = 'do_not_allow'; $caps[] = 'do_not_allow';
return $caps; return $caps;
...@@ -191,8 +209,8 @@ class Delete_All { ...@@ -191,8 +209,8 @@ class Delete_All {
/** /**
* Find the next scheduled instance of a given action, regardless of arguments * Find the next scheduled instance of a given action, regardless of arguments
* *
* @param string $action_to_check Hook to search for * @param string $action_to_check Hook to search for.
* @param string $post_type Post type hook is scheduled for * @param string $post_type Post type hook is scheduled for.
* @return array * @return array
*/ */
private static function action_next_scheduled( $action_to_check, $post_type ) { private static function action_next_scheduled( $action_to_check, $post_type ) {
...@@ -203,7 +221,7 @@ class Delete_All { ...@@ -203,7 +221,7 @@ class Delete_All {
} }
foreach ( $events as $timestamp => $timestamp_events ) { foreach ( $events as $timestamp => $timestamp_events ) {
// Skip non-event data that Core includes in the option // Skip non-event data that Core includes in the option.
if ( ! is_numeric( $timestamp ) ) { if ( ! is_numeric( $timestamp ) ) {
continue; continue;
} }
...@@ -217,13 +235,16 @@ class Delete_All { ...@@ -217,13 +235,16 @@ class Delete_All {
$vars = array_shift( $instance_args['args'] ); $vars = array_shift( $instance_args['args'] );
if ( $post_type === $vars->post_type ) { if ( $post_type === $vars->post_type ) {
return array( 'timestamp' => $timestamp, 'args' => $vars, ); return array(
'timestamp' => $timestamp,
'args' => $vars,
);
} }
} }
} }
} }
// No matching event found // No matching event found.
return array(); return array();
} }
} }
......
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
namespace Automattic\WP\Bulk_Edit_Cron_Offload; namespace Automattic\WP\Bulk_Edit_Cron_Offload;
/**
* Class Main
*/
class Main { class Main {
/** /**
* Prefix for bulk-process hook invoked by request-specific classes * Prefix for bulk-process hook invoked by request-specific classes
...@@ -24,15 +27,15 @@ class Main { ...@@ -24,15 +27,15 @@ class Main {
* Call appropriate handler * Call appropriate handler
*/ */
public static function intercept() { public static function intercept() {
// Nothing to do // Nothing to do.
if ( ! self::should_intercept_request() ) { if ( ! self::should_intercept_request() ) {
return; return;
} }
// Validate request // Validate request.
check_admin_referer( 'bulk-posts' ); check_admin_referer( 'bulk-posts' );
// Parse request to determine what to do // Parse request to determine what to do.
$vars = self::capture_vars(); $vars = self::capture_vars();
$action = self::build_hook( $vars->action ); $action = self::build_hook( $vars->action );
...@@ -40,10 +43,10 @@ class Main { ...@@ -40,10 +43,10 @@ class Main {
return; return;
} }
// Pass request to a class to handle offloading to cron, UX, etc // Pass request to a class to handle offloading to cron, UX, etc.
do_action( $action, $vars ); do_action( $action, $vars );
// Only skip Core's default handling when action is offloaded // Only skip Core's default handling when action is offloaded.
if ( has_action( $action ) ) { if ( has_action( $action ) ) {
self::skip_core_processing(); self::skip_core_processing();
} }
...@@ -68,7 +71,7 @@ class Main { ...@@ -68,7 +71,7 @@ class Main {
* Capture relevant variables * Capture relevant variables
*/ */
private static function capture_vars() { private static function capture_vars() {
$vars = (object) array_fill_keys( array( 'user_id', 'action', 'post_type', 'posts', 'tax_input', 'post_author', 'comment_status', 'ping_status', 'post_status', 'post_sticky', 'post_format', ), null ); $vars = (object) array_fill_keys( array( 'user_id', 'action', 'post_type', 'posts', 'tax_input', 'post_author', 'comment_status', 'ping_status', 'post_status', 'post_sticky', 'post_format' ), null );
$vars->user_id = get_current_user_id(); $vars->user_id = get_current_user_id();
...@@ -118,14 +121,13 @@ class Main { ...@@ -118,14 +121,13 @@ class Main {
$vars->post_format = $_REQUEST['post_format']; $vars->post_format = $_REQUEST['post_format'];
} }
// Return captured variables
return $vars; return $vars;
} }
/** /**
* Validate action * Validate action
* *
* @param string $action Action parsed from request vars * @param string $action Action parsed from request vars.
* @return bool * @return bool
*/ */
public static function bulk_action_allowed( $action ) { public static function bulk_action_allowed( $action ) {
...@@ -143,7 +145,7 @@ class Main { ...@@ -143,7 +145,7 @@ class Main {
/** /**
* Build a WP hook specific to a bulk request * Build a WP hook specific to a bulk request
* *
* @param string $action Bulk action to offload * @param string $action Bulk action to offload.
* @return string * @return string
*/ */
public static function build_hook( $action ) { public static function build_hook( $action ) {
...@@ -163,19 +165,19 @@ class Main { ...@@ -163,19 +165,19 @@ class Main {
/** /**
* Redirect, including a flag to indicate if the bulk process was scheduled successfully * Redirect, including a flag to indicate if the bulk process was scheduled successfully
* *
* @param string $return_key Key to include in redirect URL to flag request's origin, use for admin feedback, etc. * @param string $return_key Key to include in redirect URL to flag request's origin, use for admin feedback, etc.
* @param bool $succeeded Whether or not the bulk-delete was scheduled * @param bool $succeeded Whether or not the bulk-delete was scheduled.
* @param array $extra_keys Array of additional action keys to remove from redirect URL. Optional. * @param array $extra_keys Optional. Array of additional action keys to remove from redirect URL.
*/ */
public static function do_admin_redirect( $return_key, $succeeded = false, $extra_keys = array() ) { public static function do_admin_redirect( $return_key, $succeeded = false, $extra_keys = array() ) {
$redirect = wp_unslash( $_SERVER['REQUEST_URI'] ); $redirect = wp_unslash( $_SERVER['REQUEST_URI'] );
// Remove arguments that could re-trigger this bulk-edit // Remove arguments that could re-trigger this bulk-edit.
$action_keys = array( '_wp_http_referer', '_wpnonce', 'action', 'action2', ); $action_keys = array( '_wp_http_referer', '_wpnonce', 'action', 'action2' );
$action_keys = array_merge( $action_keys, $extra_keys ); $action_keys = array_merge( $action_keys, $extra_keys );
$redirect = remove_query_arg( $action_keys, $redirect ); $redirect = remove_query_arg( $action_keys, $redirect );
// Add a flag for the admin notice // Add a flag for the admin notice.
$redirect = add_query_arg( $return_key, $succeeded ? 1 : -1, $redirect ); $redirect = add_query_arg( $return_key, $succeeded ? 1 : -1, $redirect );
$redirect = esc_url_raw( $redirect ); $redirect = esc_url_raw( $redirect );
......
<?php <?php
/**
* Plugin utilities
*
* @package Bulk_Edit_Cron_Offload
*/
namespace Automattic\WP\Bulk_Edit_Cron_Offload; namespace Automattic\WP\Bulk_Edit_Cron_Offload;
...@@ -8,7 +13,7 @@ namespace Automattic\WP\Bulk_Edit_Cron_Offload; ...@@ -8,7 +13,7 @@ namespace Automattic\WP\Bulk_Edit_Cron_Offload;
function stop_the_insanity() { function stop_the_insanity() {
global $wpdb, $wp_object_cache; global $wpdb, $wp_object_cache;
$wpdb->queries = array(); // or define( 'WP_IMPORTING', true ); $wpdb->queries = array();
if ( ! is_object( $wp_object_cache ) ) { if ( ! is_object( $wp_object_cache ) ) {
return; return;
...@@ -20,6 +25,6 @@ function stop_the_insanity() { ...@@ -20,6 +25,6 @@ function stop_the_insanity() {
$wp_object_cache->cache = array(); $wp_object_cache->cache = array();
if ( is_callable( $wp_object_cache, '__remoteset' ) ) { if ( is_callable( $wp_object_cache, '__remoteset' ) ) {
$wp_object_cache->__remoteset(); // important $wp_object_cache->__remoteset(); // important!
} }
} }
...@@ -5,7 +5,7 @@ msgstr "" ...@@ -5,7 +5,7 @@ msgstr ""
"Project-Id-Version: Bulk Edit Cron Offload 1.0\n" "Project-Id-Version: Bulk Edit Cron Offload 1.0\n"
"Report-Msgid-Bugs-To: " "Report-Msgid-Bugs-To: "
"https://wordpress.org/support/plugin/bulk-edit-cron-offload\n" "https://wordpress.org/support/plugin/bulk-edit-cron-offload\n"
"POT-Creation-Date: 2017-09-13 00:10:57+00:00\n" "POT-Creation-Date: 2017-09-13 01:24:54+00:00\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
...@@ -25,6 +25,18 @@ msgstr "" ...@@ -25,6 +25,18 @@ msgstr ""
"X-Poedit-Bookmarks: \n" "X-Poedit-Bookmarks: \n"
"X-Textdomain-Support: yes\n" "X-Textdomain-Support: yes\n"
#: includes/class-delete-all.php:124
msgid "Success! The trash will be emptied soon."
msgstr ""
#: includes/class-delete-all.php:127
msgid "A request to empty the trash is already pending for this post type."
msgstr ""
#: includes/class-delete-all.php:132
msgid "A pending request to empty the trash will be processed soon."
msgstr ""
#. Plugin Name of the plugin/theme #. Plugin Name of the plugin/theme
msgid "Bulk Edit Cron Offload" msgid "Bulk Edit Cron Offload"
msgstr "" msgstr ""
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment