Commit 3be89691 authored by Eric Windham's avatar Eric Windham Committed by GitHub

Merge pull request #3586 from wpninjas/cleanup-process

Cleanup process
parents 0a0d0a85 f336da41
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -234,6 +234,26 @@ jBox
}
}
/*
Progress Bar
---------------------------------------------*/
.nf-progress-bar {
height: 20px;
border-radius: 3px;
margin: 25px 0px;
border: 1px solid #1ea9ea;
position: relative;
overflow: hidden;
.nf-progress-bar-slider {
height: 20px;
width: 0%;
background-color: #1ea9ea;
transition: width 0.3s;
}
}
/*
Loading Spinner
---------------------------------------------*/
......
......@@ -106,17 +106,6 @@ define( [ 'views/sections/widgets.js', 'views/sections/services.js', 'views/sect
var childView = new WidgetView();
}
this.showChildView('content', childView );
// If form telemetry is defined...
// AND if we should run it...
if ( 'undefined' !== typeof nfAdmin.formTelemetry && 1 == nfAdmin.formTelemetry ) {
// Make our AJAX call.
var data = {
action: 'nf_form_telemetry',
security: nfAdmin.ajaxNonce
}
// Make our AJAX call.
jQuery.post( ajaxurl, data );
}
// If the user has not seen the opt-in modal yet...
if ( '1' == nfAdmin.showOptin ) {
// Declare all of our opt-in code here.
......@@ -247,6 +236,81 @@ define( [ 'views/sections/widgets.js', 'views/sections/services.js', 'views/sect
optinModal.close();
} );
} );
} // If we've been told to run cleanup...
else if ( '1' == nfAdmin.doingCleanup ) {
var cleanupModal = new jBox( 'Modal', {
closeOnEsc: false,
closeOnClick: false,
width: 400
} );
var that = this;
// Define the modal content.
var content = document.createElement( 'div' );
content.classList.add( 'message' );
content.style.padding = '0px 20px 20px 20px';
content.innerHTML = nfi18n.cleanupContent;
var bar = document.createElement( 'div' );
bar.id = 'nf-progress-bar';
bar.classList.add( 'nf-progress-bar' );
bar.style.display = 'none';
var progress = document.createElement( 'div' );
progress.classList.add( 'nf-progress-bar-slider' );
bar.appendChild( progress );
content.appendChild( bar );
var loading = document.createElement( 'p' );
loading.id = 'nf-loading-text';
loading.style.color = '#1ea9ea';
loading.style.fontWeight = 'bold';
loading.innerHTML = nfi18n.cleanupLoading;
loading.style.display = 'none';
content.appendChild( loading );
var actions = document.createElement( 'div' );
actions.id = 'nf-action-buttons';
actions.classList.add( 'buttons' );
var cancel = document.createElement( 'div' );
cancel.id = 'nf-cancel';
cancel.classList.add( 'nf-button', 'secondary' );
cancel.innerHTML = nfi18n.cleanupSecondary;
actions.appendChild( cancel );
var confirm = document.createElement( 'button' );
confirm.id = 'nf-confirm';
confirm.classList.add( 'nf-button', 'primary', 'pull-right' );
confirm.innerHTML = nfi18n.cleanupPrimary;
actions.appendChild( confirm );
content.appendChild( actions );
// Set the options for the modal and open it.
cleanupModal.setContent( document.createElement( 'div' ).appendChild( content ).innerHTML );
cleanupModal.open();
// Setup the cancel click event.
jQuery( '#nf-cancel' ).click( function( e ) {
cleanupModal.close();
} );
// Setup the confirm click event.
jQuery( '#nf-confirm' ).click( function( e ) {
// Prevent the user from leaving without firing an alert.
jQuery( window ).bind( 'beforeunload', function() {
return 'Are you sure? Leaving before the process completes could cause damage to your data.';
} );
// Hide the buttons.
jQuery( '#nf-cancel' ).hide();
jQuery( '#nf-confirm' ).hide();
// Show the progress bar.
jQuery( '#nf-progress-bar' ).show();
jQuery( '#nf-loading-text' ).show();
// Begin our cleanup process.
that.cleanupProcess( that, -1, cleanupModal );
} );
}
// If form telemetry is defined...
// AND if we should run it...
if ( 'undefined' !== typeof nfAdmin.formTelemetry && 1 == nfAdmin.formTelemetry ) {
// Make our AJAX call.
var data = {
action: 'nf_form_telemetry',
security: nfAdmin.ajaxNonce
}
// Make our AJAX call.
jQuery.post( ajaxurl, data );
}
},
......@@ -268,6 +332,62 @@ define( [ 'views/sections/widgets.js', 'views/sections/services.js', 'views/sect
return content.innerHTML;
},
}
},
/**
* Function to manage our data cleanup batch process response.
*
* @since 3.3.1
*
* @param context (this) The context at the time of function definition.
* @param steps (int) The total number of steps in this process.
* @param modal (jBox) A reference to the modal where this process is running.
*/
cleanupProcess: function( context, steps, modal ) {
var data = {
action: 'nf_batch_process',
batch_type: 'data_cleanup',
security: nfAdmin.ajaxNonce
};
jQuery.post( ajaxurl, data, function( response ) {
response = JSON.parse( response );
// If we're done...
if ( response.batch_complete ) {
// Push our progress bar to 100%.
jQuery( '.nf-progress-bar-slider' ).css( 'width', '100%' );
// Allow the user to leave the page now.
jQuery( window ).unbind( 'beforeunload' );
modal.close();
// Exit.
return false;
}
// If we do not yet have a determined number of steps...
if ( -1 == steps ) {
// If step_toal is defined...
if ( 'undefined' != typeof response.step_total ) {
// Use the step_total.
steps = response.step_total;
} // Otherwise... (step_total is not defined)
else {
// Use step_remaining.
steps = response.step_remaining;
}
}
// Calculate our current step.
var step = steps - response.step_remaining;
// Calculate our maximum progress for this step.
var maxProgress = Math.round( step / steps * 100 );
// Get our current progress for this step.
var currentProgress = Math.round( jQuery( '.nf-progress-bar-slider' ).width() / jQuery( '.nf-progress-bar-slider' ).parent().width() * 100 );
// If our maximum progress is more than our current progress...
if ( maxProgress > currentProgress ) {
// Increment our progress bar.
currentProgress = Number( currentProgress ) + 1;
jQuery( '.nf-progress-bar-slider' ).css( 'width', currentProgress + '%' );
}
// Recall our function...
context.cleanupProcess( context, steps, modal );
} );
}
} );
return view;
......
......@@ -32,7 +32,9 @@ class NF_AJAX_REST_BatchProcess extends NF_AJAX_REST_Controller
$batch = new NF_Admin_Processes_ChunkPublish(
$request_data );
break;
case 'delete_submissions':
case 'data_cleanup':
$batch = new NF_Admin_Processes_DataCleanup(
$request_data );
break;
default:
$data[ 'error' ] = __( 'Invalid request.', 'ninja-forms' );
......
......@@ -142,7 +142,10 @@ final class NF_Admin_Menus_Forms extends NF_Abstracts_Menu
'formTelemetry' => ( get_option( 'nf_form_tel_sent' ) ) ? 0 : 1,
'showOptin' => ( get_option( 'ninja_forms_do_not_allow_tracking' ) ||
get_option( 'ninja_forms_allow_tracking' ) ) ? 0 : 1,
'currentUserEmail' => $current_user->user_email
'currentUserEmail' => $current_user->user_email,
'doingCleanup' => ( ! get_option( 'ninja_forms_data_is_clean' ) &&
isset( $_REQUEST[ 'action' ] ) &&
'cleanup' == $_REQUEST[ 'action' ] ) ? 1 : 0,
) );
wp_enqueue_style( 'nf-builder', Ninja_Forms::$url . 'assets/css/builder.css' );
......
......@@ -115,6 +115,7 @@ class NF_Admin_Notices
$admin_display_msg = ( isset( $admin_notices[ $slug ][ 'msg' ] ) ? $admin_notices[ $slug ][ 'msg'] : '' );
$admin_display_title = ( isset( $admin_notices[ $slug ][ 'title' ] ) ? $admin_notices[ $slug ][ 'title'] : '' );
$admin_display_link = ( isset( $admin_notices[ $slug ][ 'link' ] ) ? $admin_notices[ $slug ][ 'link' ] : '' );
$admin_can_dismiss = ( isset( $admin_notices[ $slug ][ 'dismiss' ] ) ? $admin_notices[ $slug ][ 'dismiss' ] : 1 );
$output_css = false;
// Ensure the notice hasn't been hidden and that the current date is after the start date
......@@ -135,7 +136,9 @@ class NF_Admin_Notices
echo '<ul class="nf-notice-body nf-red">
' . $admin_display_link . '
</ul>';
echo '<a href="' . wp_nonce_url( esc_attr( $query_str ) ) . '" class="dashicons dashicons-dismiss"></a>';
if ( $admin_can_dismiss ) {
echo '<a href="' . wp_nonce_url( esc_attr( $query_str ) ) . '" class="dashicons dashicons-dismiss"></a>';
}
echo '</div>';
$this->notice_spam += 1;
......
<?php if ( ! defined( 'ABSPATH' ) ) exit;
/**
* Class NF_Abstracts_Batch_Process
*/
class NF_Admin_Processes_DataCleanup extends NF_Abstracts_BatchProcess
{
private $response = array(
'batch_complete' => false
);
protected $delete = array();
/**
* Constructor
*/
public function __construct( $data = array() )
{
//Bail if we aren't in the admin.
if ( ! is_admin() )
return false;
// Run process.
$this->process();
}
/**
* Function to loop over the batch.
*/
public function process()
{
global $wpdb;
// If we've not already started the cleanup process...
if ( ! get_option( 'nf_doing_data_cleanup' ) ) {
// Run the startup process.
$this->startup();
} // Otherwise... (We've already run startup.)
else {
// Get our data.
$data = get_option( 'nf_data_cleanup_ids' );
$this->delete = explode( ',', $data );
}
// If our array isn't emtpy...
if ( ! empty( $this->delete ) ) {
// Fetch the last item on it.
$id = array_pop( $this->delete );
// Get a list of post IDs to delete.
$sql = "SELECT DISTINCT(`id`) FROM `" . $wpdb->prefix . "posts` WHERE `id` IN( SELECT DISTINCT(`post_id`) FROM `" . $wpdb->prefix . "postmeta` WHERE `meta_key` = '_form_id' AND `meta_value` = '" . $id . "' ) AND `post_type` = 'nf_sub' LIMIT 500";
$result = $wpdb->get_results( $sql, 'ARRAY_A' );
// If we got 500 or more results...
if ( 500 == count( $result ) ) {
// Put this id back in our array to run again.
array_push( $this->delete, $id );
}
// Convert our results to something we can use in a query.
array_walk( $result, array( $this, 'smush_results' ) );
$sub_sql = implode( ', ', $result );
// If we have something to query...
if ( '' != $sub_sql ) {
// Delete postmeta data.
$sql = "DELETE FROM `" . $wpdb->prefix . "postmeta` WHERE `post_id` IN(" . $sub_sql . ")";
$wpdb->query( $sql );
// Delete post data.
$sql = "DELETE FROM `" . $wpdb->prefix . "posts` WHERE `id` IN(" . $sub_sql . ")";
$wpdb->query( $sql );
}
}
// If our array isn't empty...
if ( ! empty( $this->delete ) ) {
// Determine how many steps we have left.
$this->response[ 'step_remaining' ] = count( $this->delete );
update_option( 'nf_data_cleanup_ids', implode( ',', $this->delete ) );
echo wp_json_encode( $this->response );
wp_die();
}
// Run our cleanup process.
$this->cleanup();
echo wp_json_encode( $this->response );
wp_die();
}
/**
* Function to run any setup steps necessary to begin processing.
*/
public function startup()
{
global $wpdb;
// Get a list of IDs from the forms table.
$sql = "SELECT DISTINCT(`id`) FROM `" . $wpdb->prefix . "nf3_forms`";
$forms = $wpdb->get_results( $sql, 'ARRAY_A' );
// Get a list of IDs from the Submissions data.
$sql = "SELECT DISTINCT(m.meta_value) AS id FROM `" . $wpdb->prefix . "postmeta` AS m LEFT JOIN `" . $wpdb->prefix . "posts` AS p on p.id = m.post_id WHERE m.meta_key = '_form_id' AND p.post_type = 'nf_sub'";
$sub_forms = $wpdb->get_results( $sql, 'ARRAY_A' );
// For each form ID in the submission records...
foreach( $sub_forms AS $form ) {
// If the form is not currently defined in our forms table...
if ( ! in_array( $form, $forms ) ) {
// Add it to our list of things to delete.
$this->delete[] = $form[ 'id' ];
}
}
// Get our number of steps for the progress bar.
$this->response[ 'step_total' ] = count( $this->delete );
// Flag startup done.
add_option( 'nf_doing_data_cleanup', 'true' );
}
/**
* Function to cleanup any lingering temporary elements of a batch process after completion.
*/
public function cleanup()
{
global $wpdb;
// Delete our options.
delete_option( 'nf_data_cleanup_ids' );
delete_option( 'nf_doing_data_cleanup' );
// Add our "finished" option.
add_option( 'ninja_forms_data_is_clean', 'true' );
// Tell our JS that we're done.
$this->response[ 'step_remaining' ] = 0;
$this->response[ 'batch_complete' ] = true;
}
/**
* Function to compress data array and eliminate unnecessary keys.
*/
public function smush_results( &$value, $key ) {
$value = $value[ 'id' ];
}
}
\ No newline at end of file
......@@ -74,6 +74,9 @@ return apply_filters( 'ninja_forms_i18n_dashboard', array(
'optinSecondary' => __( 'Not Now', 'ninja-forms' ),
'optinPrimary' => __( 'Yes, I agree!', 'ninja-forms' ),
'optinAwesome' => __( 'Keep being awesome!', 'ninja-forms' ),
'optinThanks' => __( 'Thank you for opting in!', 'ninja-forms' )
'optinThanks' => __( 'Thank you for opting in!', 'ninja-forms' ),
'cleanupContent' => sprintf( __( '%sOnce we begin this process, it might take several minutes to complete.%sNavigating away from this page before it is finished could lead to unexpected results.%sPlease confirm when you are ready to begin.%s', 'ninja-forms' ), '<p>', '</p><p>', '</p><p>', '</p>' ),
'cleanupSecondary' => __( 'Cancel', 'ninja-forms' ),
'cleanupPrimary' => sprintf( __( 'Clean up my data', 'ninja-forms' ) ),
'cleanupLoading' => __( 'Processing...', 'ninja-forms' )
));
......@@ -423,6 +423,12 @@ if( get_option( 'ninja_forms_load_deprecated', FALSE ) && ! ( isset( $_POST[ 'nf
}
add_filter( 'ninja_forms_dashboard_menu_items', array( $this, 'maybe_hide_dashboard_items' ) );
// If we don't have clean data...
if ( ! get_option( 'ninja_forms_data_is_clean' ) ) {
// Register a new notice.
add_filter( 'ninja_forms_admin_notices', array( $this, 'data_cleanup_notice' ) );
}
}
/**
......@@ -809,6 +815,8 @@ if( get_option( 'ninja_forms_load_deprecated', FALSE ) && ! ( isset( $_POST[ 'nf
if( Ninja_Forms()->form()->get_forms() ) return;
// Assume we're on a clean installation.
update_option( 'ninja_forms_data_is_clean', 'true' );
$form = Ninja_Forms::template( 'formtemplate-contactform.nff', array(), TRUE );
Ninja_Forms()->form()->import_form( $form );
}
......@@ -842,6 +850,25 @@ if( get_option( 'ninja_forms_load_deprecated', FALSE ) && ! ( isset( $_POST[ 'nf
}
}
}
/**
* Function to register an admin notice if we detect that this installation has "unclean" Ninja Forms data.
*
* @since 3.3.1
*
* @param $notices (Array) Our array of admin notices.
* @return $notices (Array) Our array of admin notices.
*/
public function data_cleanup_notice( $notices ) {
$notices[ 'data_cleanup' ] = array(
'title' => __( 'Data Cleanup', 'ninja-forms' ),
'msg' => sprintf( __( 'Ninja Forms has detected data on your site leftover from old forms or Ninja Forms versions.%sWe would like to run a quick cleanup process to remove this old data. Your forms will not be impacted by this process, but it may take several minutes to complete.%sPlease %sclick here%s to begin.', 'ninja-forms' ), '<br />', '<br /><br />', '<a href="' . admin_url( 'admin.php?page=ninja-forms&action=cleanup' ) . '">', '</a>' ),
'int' => 0,
'ignore_spam' => true,
'dismiss' => 0
);
return $notices;
}
} // End Class Ninja_Forms
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment