Current File : /var/www/e360ban/wp-content/plugins/toolset-blocks/inc/wpv-admin-ajax.php
<?php
/**
 * General file for all AJAX calls
 *
 * All AJAX calls used in the backend must be set here.
 */

use const OTGS\Toolset\Views\UserCapabilities\EDIT_VIEWS;
use \OTGS\Toolset\Views\Services\ContentTemplateService;

/* ************************************************************************* *\
        Views & WPA edit sceen
\* ************************************************************************* */

/*
* Views listing screen
*/

/**
 * Render a popup to confirm bulk Views trashing or deleting.
 *
 * This is called by deleteViewsConfirmation() in views_listing_page.js. The Popup is identified by class
 * "js-bulk-delete-views-dialog". It also contains a table with Views to be deleted and buttons to scan for their usage.
 *
 * Delete button (js-bulk-remove-view-permanent) contains data attributes "view-ids" with comma-separated list of
 * View IDs and "nonce" with a wpv_bulk_remove_view_permanent_nonce.
 *
 * Following POST variables are expected:
 * - wpnonce: A valid wpv_view_listing_actions_nonce.
 * - ids: An array of View IDs to be trashed/deleted
 * - view_action: Action to perform: 'delete' or 'trash'.
 *
 * @since 1.7
 */
add_action( 'wp_ajax_wpv_view_bulk_trashdel_render_popup', 'wpv_view_bulk_trashdel_render_popup_callback' );

function wpv_view_bulk_trashdel_render_popup_callback() {
	if ( ! current_user_can( EDIT_VIEWS ) ) {
		$data = array(
			'type' => 'capability',
			'message' => __( 'You do not have permissions for that.', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}
	if (
		! isset( $_POST["wpnonce"] )
		|| ! wp_verify_nonce( $_POST["wpnonce"], 'wpv_view_listing_actions_nonce' )
	) {
		$data = array(
			'type' => 'nonce',
			'message' => __( 'Your security credentials have expired. Please reload the page to get new ones.', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}

	$post_ids = wpv_getpost( 'ids', array() );
	if ( is_string( $_POST['ids'] ) ) {
		$post_ids = array( $_POST['ids'] );
	}
	// We only get IDs and titles
	$post_ids = array_map( 'esc_attr', $post_ids );
	$post_ids = array_map( 'trim', $post_ids );
	// is_numeric does sanitization
	$post_ids = array_filter( $post_ids, 'is_numeric' );
	$post_ids = array_map( 'intval', $post_ids );
	if( ! empty( $post_ids ) ) {
		global $wpdb;
		$post_id_list = implode( ',', $post_ids );
		if ( $post_id_list != '-1' ) {
			$views = $wpdb->get_results(
				"SELECT ID as id, post_title
			FROM {$wpdb->posts}
			WHERE post_type = 'view'
			AND id IN ( $post_id_list )"
			);
		} else {
			$views_and_wpas = $wpdb->get_results(
				"SELECT posts.ID as id, posts.post_title, postmeta.meta_value as meta
			FROM {$wpdb->posts} as posts
			LEFT JOIN {$wpdb->postmeta} AS postmeta
			ON ( posts.ID = postmeta.post_id
			AND postmeta.meta_key = '_wpv_settings' )
			WHERE post_type = 'view'
			AND post_status = 'trash'"
			);

			$views = array();

			foreach ( $views_and_wpas as $view ) {
			    $view_meta = unserialize( $view->meta );
				if (
					! isset( $view_meta['view-query-mode'] )
					|| $view_meta['view-query-mode'] == 'normal'
				) {
                    $views[] = $view;
				}
			}
        }
	} else {
		$views = array(); // This should never happen.
	}
	$view_count = count( $views );
	// Different values based on the action we're confirming (they're all here).
	$view_action = wpv_getpost( 'view_action', 'delete', array( 'delete', 'trash' ) );
	$action_word = ( 'delete' == $view_action ) ? __( 'permanently delete', 'wpv-views' ) : __( 'trash', 'wpv-views' );
	ob_start();
	?>
		<div class="wpv-dialog wpv-shortcode-gui-content-wrapper">
			<h3>
				<?php
					printf(
							_n(
								'Are you sure you want to %s this View?',
								'Are you sure you want to %s these Views?',
								$view_count,
								'wpv-views' ),
							$action_word );
				?>
			</h3>
			<p>
				<?php
					echo _n(
							'Please use the Scan button first to be sure that it is not used anywhere.',
							'Please use Scan buttons first to be sure that they are not used anywhere.',
							$view_count,
							'wpv-views' );
				?>
			</p>
			<table class="wpv-view-table" style="width: 100%;">
				<?php
					foreach( $views as $view ) {
						?>
						<tr>
							<td><strong><?php echo esc_html( $view->post_title ); ?></strong></td>
							<td class="wpv-admin-listing-col-scan">
								<button class="button js-scan-button" data-view-id="<?php echo esc_attr( $view->id ); ?>">
									<?php _e( 'Scan', 'wpv-views' ); ?>
								</button>
								<span class="js-nothing-message hidden"><?php _e( 'Nothing found', 'wpv-views' ); ?></span>
							</td>
						</tr>
						<?php
					}
				?>
			</table>
		</div>
	<?php
	$result = ob_get_clean();
	$data = array(
		'dialog_content' => $result,
        'ids' => wp_list_pluck( $views, 'id' )
	);
	wp_send_json_success( $data );
}


/* ************************************************************************** *\
		WP Archive listing screen
\* ************************************************************************** */


// Add up, down or first WP Archive - popup structure

add_action('wp_ajax_wpv_create_wp_archive_popup', 'wpv_create_wp_archive_popup_callback');

function wpv_create_wp_archive_popup_callback() {
	wpv_ajax_authenticate( 'work_views_listing', array( 'parameter_source' => 'get', 'type_of_death' => 'data' ) );
	global $WPV_view_archive_loop;
	ob_start();
	$WPV_view_archive_loop->create_view_archive_popup();
	$response = ob_get_clean();
	$data = array(
		'dialog_content' => $response
	);
	wp_send_json_success( $data );
}


// Change usage for Archive in name arrange callback function

add_action('wp_ajax_wpv_wp_archive_change_usage', 'wpv_wp_archive_change_usage_callback');

function wpv_wp_archive_change_usage_callback() {
	wpv_ajax_authenticate( 'work_views_listing', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	if ( ! isset( $_POST["form"] ) ) {
		$data = array(
			'message' => __( 'Untrusted data', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}
    global $WPV_view_archive_loop;
    parse_str( $_POST['form'], $form_data );
	$archive_id = $form_data["wpv-archive-view-id"];
	$WPV_view_archive_loop->update_view_archive_settings( $archive_id, $form_data );
	do_action( 'wpv_action_wpv_save_item', $archive_id );
	wp_send_json_success();
}

// Create WP Archive in usage arrange callback function
// @todo we need to use the API to create this, or at least *create* that API if needed

add_action( 'wp_ajax_wpv_create_wpa_for_archive_loop', 'wp_ajax_wpv_create_wpa_for_archive_loop_callback' );

function wp_ajax_wpv_create_wpa_for_archive_loop_callback() {
	wpv_ajax_authenticate( 'work_views_listing', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	global $wpdb, $WPV_view_archive_loop;
	$existing = $wpdb->get_var(
		$wpdb->prepare(
			"SELECT ID FROM {$wpdb->posts}
			WHERE ( post_title = %s OR post_name = %s )
			AND post_type = 'view'
			LIMIT 1",
			$_POST["title"],
			$_POST["title"]
		)
	);
	if ( $existing ) {
		$data = array(
			'message'	=> __( 'Another item already uses this name. Please use another name.', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}

	$proposed_name = sanitize_text_field( sanitize_title( $_POST["title"] ) );

	if ( empty( $proposed_name ) ) {
		$proposed_name = 'view-rand-' . uniqid();
	}

	$new_archive = array(
		'post_title'    => sanitize_text_field( $_POST["title"] ),
		'post_name'		=> $proposed_name,
		'post_type'     => 'view',
		'post_content'  => "[wpv-filter-meta-html]\n[wpv-layout-meta-html]",
		'post_status'   => 'publish',
		'post_author'   => get_current_user_id(),
		'comment_status' => 'closed'
	);
	$post_id = wp_insert_post( $new_archive );

	$purpose = ( isset( $_POST['purpose'] ) && in_array( $_POST['purpose'], array( 'all', 'parametric' ) ) ) ? $_POST['purpose'] : 'all';

	$archive_defaults = wpv_wordpress_archives_defaults( 'view_settings', $purpose );
	$archive_layout_defaults = wpv_wordpress_archives_defaults( 'view_layout_settings', $purpose );

	update_post_meta( $post_id, '_wpv_settings', $archive_defaults );
	update_post_meta( $post_id, '_wpv_layout_settings', $archive_layout_defaults );

	$form_data = array(
		sanitize_text_field( $_POST['loop'] ) => $post_id
	);
	$WPV_view_archive_loop->update_view_archive_settings( $post_id, $form_data );

	/**
	 * Filter for getting the ID of the created WordPress Archive post.
	 *
	 * @param int                   $wpa_id
	 * @param WPV_WordPress_Archive $wpa
	 *
	 * @since 3.0
	 */
	$post_id = apply_filters( 'wpv_filter_wpa_created', $post_id, \WPV_WordPress_Archive::get_instance( $post_id ) );

	$data = array(
		'id' => $post_id
	);
	wp_send_json_success( $data );
}


/**
 * Render a popup to change WordPress Archive usage.
 *
 * Used in WordPress Archive listing, arranged by usage.
 *
 * Expects following GET arguments:
 * - wpnonce: A valid wpv_wp_archive_arrange_usage nonce.
 * - id: Slug of the loop whose WPA should be changed.
 *
 * @since unknown.
 */
add_action('wp_ajax_wpv_change_wpa_for_archive_loop_popup', 'wpv_change_wpa_for_archive_loop_popup_callback');

function wpv_change_wpa_for_archive_loop_popup_callback() {
	wpv_ajax_authenticate( 'wpv_wp_archive_arrange_usage', array( 'parameter_source' => 'get', 'type_of_death' => 'data' ) );

	if ( ! isset( $_GET["id"] ) ) {
		$data = array(
			'message'	=> __( 'Untrusted data', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}
	global $WPV_view_archive_loop, $WPV_settings;
	// Slug of the loop.
	$loop_key = sanitize_text_field( $_GET["id"] );
    $loops = $WPV_view_archive_loop->_get_post_type_loops();
	/* We will slightly misuse this function, but it gives us exactly what we need:
	 * a list of published WPAs. */
	$views_pre_query_data = wpv_prepare_view_listing_query(
		array( 'archive' ),
		'publish',
		array( 'posts.post_title' => 'post_title' ), // also give us post title
		true, // return rows from the table
		array( "posts.post_status = 'publish'" )
	); // limit mysql query only to published posts
	$views = $views_pre_query_data['rows'];
	// ID of currently assigned view or 0.
	$currently_assigned_view_id = isset( $WPV_settings[ $loop_key ] ) ? $WPV_settings[ $loop_key ] : 0;
	ob_start();
	?>
	<div class="wpv-dialog wpv-shortcode-gui-content-wrapper js-wpv-dialog-change">
		<h3><?php _e( 'Select a WordPress Archive', 'wpv-views' ); ?></h3>
		<?php wp_nonce_field( 'wpv_view_edit_nonce', 'wpv_view_edit_nonce' ); ?>
		<input type="hidden" value="<?php echo esc_attr( $loop_key ); ?>" id="js-wpv-change-wpa-for-archive-loop-key" name="wpv-archive-loop-key" />
		<ul id="js-wpv-change-wpa-for-archive-loop-list" class="wpv-mightlong-list">
			<li>
				<label>
					<input type="radio" name="wpv-view-loop-archive" value="0" <?php checked( 0 == $currently_assigned_view_id ); ?> />
					<?php _e( 'Don\'t use a WordPress Archive for this loop', 'wpv-views' ); ?>
				</label>
			</li>
			<?php
				foreach ( $views as $view ) {
					?>
					<li>
						<label>
							<input type="radio" <?php checked( $view->id == $currently_assigned_view_id ); ?>
									name="wpv-view-loop-archive" value="<?php echo esc_attr( $view->id ); ?>" />
							<?php echo $view->post_title; ?>
						</label>
					</li>
					<?php
				}
			?>
		</ul>
	</div>
	<?php
	$response = ob_get_clean();
	$data = array(
		'dialog_content' => $response
	);
	wp_send_json_success( $data );
}

// Change WP Archive usage in usage arrange callback function

add_action('wp_ajax_wpv_change_wpa_for_archive_loop', 'wpv_change_wpa_for_archive_loop_callback');

function wpv_change_wpa_for_archive_loop_callback() {
	wpv_ajax_authenticate( 'wpv_wp_archive_arrange_usage', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	global $WPV_settings;
	$loop = sanitize_text_field( $_POST["loop"] );
	$selected = sanitize_text_field( $_POST["selected"] );
	$WPV_settings[$loop] = $selected;
	do_action( 'wpv_action_wpv_delete_transient_shortcodes_gui_views' );
	$settings_array = $WPV_settings->get();
	foreach ( $settings_array as $key => $value ) {
        if ( $value == 0 ) {
            unset( $WPV_settings[$key] );
        }
    }
	$WPV_settings->save();
	wp_send_json_success();
}

// Delete one WPA permanently callback function
// @note it also deletes the loop Template if needed

add_action('wp_ajax_wpv_delete_wpa_permanent', 'wpv_delete_wpa_permanent_callback');

function wpv_delete_wpa_permanent_callback() {
	wpv_ajax_authenticate( 'wpv_remove_view_permanent_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	if (
		! isset( $_POST['id'] )
		|| ! is_numeric( $_POST['id'] )
		|| intval( $_POST['id'] ) < 1
	) {
		$data = array(
			'message' => __( 'Wrong data.', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}
	$loop_content_template = get_post_meta( $_POST['id'], '_view_loop_template', true );
	wp_delete_post( $_POST['id'] );
	if ( ! empty( $loop_content_template ) ) {
		wp_delete_post( $loop_content_template, true );
	}
    // Clean options - when deleting WPA
    global $WPV_settings;
	$WPV_settings->refresh_view_settings_data();
    $WPV_settings->save();
	wp_send_json_success();
}

// Change status of View and WPA callback function TODO use a more generic function name

add_action('wp_ajax_wpv_view_change_status', 'wpv_view_change_status_callback');

function wpv_view_change_status_callback(){
	if ( ! current_user_can( EDIT_VIEWS ) ) {
		die( "Untrusted user" );
	}
	if ( ! (
		wp_verify_nonce( $_POST["wpnonce"], 'wpv_view_listing_actions_nonce' )  // from the Views listing screen OR
		|| wp_verify_nonce( $_POST["wpnonce"], 'wpv_view_change_status' ) // from the View edit screen
	) ) {
		die( "Security check" );
	}
	if (
		! isset( $_POST['id'] )
		|| ! is_numeric( $_POST['id'] )
		|| intval( $_POST['id'] ) < 1
	) {
		die( "Untrusted data" );
	}
	if ( ! isset( $_POST['newstatus'] ) ) {
		$_POST['newstatus'] = 'publish';
	}

	$new_status = sanitize_text_field( $_POST['newstatus'] );
	$post_id = intval( $_POST['id'] );
	$previous_status = get_post_meta( $post_id, '_wp_trash_meta_status', true );
	if ( ! empty( $previous_status ) ) {
		$new_status = $previous_status;
		delete_post_meta( $post_id, '_wp_trash_meta_status' );
		delete_post_meta( $post_id, '_wp_trash_meta_time' );
	}

	$my_post = array(
		'ID'			=> $_POST['id'],
		'post_status'	=> $new_status
	);
	$return = wp_update_post( $my_post );
	if ( isset( $_POST['cleararchives'] ) ) {
		global $WPV_settings;
		$settings_array = $WPV_settings->get();
		if ( ! empty( $settings_array ) ) {
			foreach ( $settings_array as $option_name => $option_value ) {
				if ( strpos( $option_name, 'view_' ) === 0  && $option_value == $_POST['id'] ) {
					$WPV_settings[$option_name] = 0;
				}
			}
			$WPV_settings->save();
		}
	}
	do_action( 'wpv_action_wpv_save_item', $_POST['id'] );
	echo $return;
	die();
}

add_action( 'wp_ajax_wpv_archive_bulk_delete_render_popup', 'wpv_archive_bulk_delete_render_popup_callback' );

/**
 * Callback to be executed before opening the WPA bulk delete popup
 *
 * Currently this only summarizes the ids of the WPA that are going to be deleted.
 * todo: This can be extended to facilitate more functionality.
 *
 * Expected POST parameters:
 * - wpnonce: A valid wpv_view_listing_actions_nonce.
 * - ids: An array of WPA IDs
 *
 * @since 2.3
 */
function wpv_archive_bulk_delete_render_popup_callback() {
	wpv_ajax_authenticate( 'wpv_view_listing_actions_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	if( !isset( $_POST['ids'] ) ) {
		$wpa_ids = array();
	} else if( is_string( $_POST['ids'] ) ) {
		$wpa_ids = array( $_POST['ids'] );
	} else {
		$wpa_ids = $_POST['ids'];
	}

	global $wpdb;
	$data = array();

	$wpa_ids = array_map( 'esc_attr', $wpa_ids );
	$wpa_ids = array_map( 'trim', $wpa_ids );
	// is_numeric does sanitization
	$wpa_ids = array_filter( $wpa_ids, 'is_numeric' );
	$wpa_ids = array_map( 'intval', $wpa_ids );

	$data['action'] = 'delete';

	if( sizeof( $wpa_ids ) && $wpa_ids[0] == -1 ) {
		$data['action'] = 'empty-trash';

		$views_and_wpas = $wpdb->get_results(
			"SELECT posts.ID as id, postmeta.meta_value as meta
			FROM {$wpdb->posts} as posts
			LEFT JOIN {$wpdb->postmeta} AS postmeta
			ON ( posts.ID = postmeta.post_id
			AND postmeta.meta_key = '_wpv_settings' )
			WHERE post_type = 'view'
			AND post_status = 'trash'"
		);

		$selected_wpas = array();
		foreach ( $views_and_wpas as $wpa ) {
			$view_meta = unserialize( $wpa->meta );
			if (
				isset( $view_meta['view-query-mode'] )
				&& in_array( $view_meta['view-query-mode'], array( 'archive', 'layouts-loop' ) )
			) {
				$selected_wpas[] = $wpa;
			}
		}


		$wpa_ids = wp_list_pluck( $selected_wpas, 'id' );
	}

	$data['wpa_ids'] = $wpa_ids;

	wp_send_json_success( $data );

}

add_action('wp_ajax_wpv_view_change_post_name', 'wpv_view_change_post_name_callback');

function wpv_view_change_post_name_callback(){
	if ( ! current_user_can( EDIT_VIEWS ) ) {
		wp_send_json_error(
			array( 'message' => 'Untrusted user' )
		);
	}
	if ( ! (
		wp_verify_nonce( $_POST["wpnonce"], 'wpv_view_listing_actions_nonce' )  // from the Views listing screen OR
		|| wp_verify_nonce( $_POST["wpnonce"], 'wpv_view_change_post_name' ) // from the View edit screen
	) ) {
		wp_send_json_error(
			array( 'message' => 'Security check', )
		);
	}

	$view_id  = (int) sanitize_text_field( toolset_getpost( 'id', '' ) );

	if ( $view_id  < 1 ) {
		wp_send_json_error(
			array( 'message' => 'Untrusted data', )
		);
	}



	if (
		! isset( $_POST["post_name"] )
		|| empty( $_POST["post_name"] )
	) {
		$_POST['post_name'] = sanitize_title( get_the_title( $_POST['id'] ) );
	}

	$proposed_name = sanitize_text_field( sanitize_title( $_POST['post_name'] ) );

	if ( empty( $proposed_name ) ) {
		$proposed_name = 'view-rand-' . uniqid();
	}

	$my_post = array(
		'ID'        => $view_id,
		'post_name' => $proposed_name,
	);

	if ( WPV_View_Base::is_name_used( $proposed_name, $view_id ) ) {
		$edited_post = get_post( $view_id );
		$collision_posts = get_posts(
				array(
					'name' => $proposed_name,
					'posts_per_page' => 1,
					'post_type' => $edited_post->post_type,
				)
		);

		if (
				isset( $collision_posts[0] ) &&
				property_exists( $collision_posts[0], 'ID' ) &&
				property_exists( $collision_posts[0], 'post_title' )
		) {
			$view_query_mode = WPV_View_Base::is_archive_view( $collision_posts[0]->ID ) ? __( 'WordPress Archive', 'wpv-views' ) : __( 'View', 'wpv-views' );
			$message = sprintf(
				__( 'Another %1$s (%2$s) already uses this slug. Please use another name.', 'wpv-views' ),
				$view_query_mode,
				sanitize_text_field( $collision_posts[0]->post_title )
			);

			$data = array(
				'message' => $message,
			);
			wp_send_json_error( $data );
		}
	}

	$id = wp_update_post( $my_post );
	$post_data = get_post($id, ARRAY_A);

	wp_send_json_success( array( 'id' => $id, 'slug' => $proposed_name ) );
}

/**
 * Change status of multiple Views, WordPress Archives or Content Templates. Callback function.
 *
 * Following POST parameters are expected:
 *
 * - wpnonce: A valid wpv_view_listing_actions_nonce.
 * - newstatus: New status for posts that should be updated. Default is 'publish'.
 * - ids: An array of post IDs that should be updated. Single (string or numeric) value is also accepted.
 * - cleararchives: If set to 1, assignment of givent posts (WPAs) in archive loops will be cleared.
 *
 * Outputs '0' on failure (when one or more posts couldn't be updated) and '1' on success.
 *
 * @since 1.7
 */
add_action( 'wp_ajax_wpv_view_bulk_change_status', 'wpv_view_bulk_change_status_callback' );

function wpv_view_bulk_change_status_callback() {
	if ( ! current_user_can( EDIT_VIEWS ) ) {
		die( "Untrusted user" );
	}
	if ( ! wp_verify_nonce( $_POST["wpnonce"], 'wpv_view_listing_actions_nonce' ) ) {
		die( "Security check" );
	}
	$new_status = isset( $_POST['newstatus'] ) ? sanitize_text_field( $_POST['newstatus'] ) : 'publish';
	if ( ! isset( $_POST['ids'] ) ) {
		$post_ids = array();
	} else if ( is_string( $_POST['ids'] ) ) {
		$post_ids = array( $_POST['ids'] );
	} else {
		$post_ids = $_POST['ids'];
	}
	$post_ids = array_map( 'esc_attr', $post_ids );
	$post_ids = array_map( 'trim', $post_ids );
	// is_numeric does sanitization
	$post_ids = array_filter( $post_ids, 'is_numeric' );
	$post_ids = array_map( 'intval', $post_ids );
	// Update post statuses
	$is_failure = false;
	foreach ( $post_ids as $post_id ) {
		$my_post = array(
				'ID' => $post_id,
				'post_status' => $new_status );
		$res = wp_update_post( $my_post );
		$is_failure = $is_failure || ( $res == 0 );
		do_action( 'wpv_action_wpv_save_item', $post_id );
	}
	// Clear archive loop assignment, if requested
	if ( isset( $_POST['cleararchives'] ) && ( 1 == $_POST['cleararchives'] ) ) {
		global $WPV_settings;
		$settings_array = $WPV_settings->get();
		if ( ! empty( $settings_array ) ) {
			foreach ( $settings_array as $option_name => $option_value ) {
				if ( ( strpos( $option_name, 'view_' ) === 0 ) && in_array( $option_value, $post_ids ) ) {
					$WPV_settings[ $option_name ] = 0;
				}
			}
			$WPV_settings->save();
		}
	}
	echo $is_failure ? 0 : 1;
	die();
}


/**
 * Permanently delete multiple Views or WordPress Archives. Callback function.
 *
 * Needs a wpv_bulk_remove_view_permanent_nonce to be present.
 * Also deletes associated loop Templates if any
 *
 * Outputs 0 on failure (when one or more posts couldn't be deleted) and 1 on success.
 *
 * $since 1.7
 */
add_action( 'wp_ajax_wpv_bulk_delete_views_permanent', 'wpv_bulk_delete_views_permanent_callback' );

function wpv_bulk_delete_views_permanent_callback() {
	if ( ! current_user_can( EDIT_VIEWS ) ) {
		die( "Untrusted user" );
	}
	if ( ! wp_verify_nonce( $_POST["wpnonce"], 'wpv_bulk_remove_view_permanent_nonce' ) ) {
		die( "Security check" );
	}
	global $wpdb;
	if ( ! isset( $_POST['ids'] ) ) {
		$post_ids = array();
	} else if ( is_string( $_POST['ids'] ) ) {
		$post_ids = array( $_POST['ids'] );
	} else {
		$post_ids = $_POST['ids'];
	}
	$post_ids = array_map( 'esc_attr', $post_ids );
	$post_ids = array_map( 'trim', $post_ids );
	// is_numeric does sanitization
	$post_ids = array_filter( $post_ids, 'is_numeric' );
	$post_ids = array_map( 'intval', $post_ids );
	$is_failure = false;
	// Delete loop Templates if any
	if ( count( $post_ids ) > 0 ) {
		$remove_loop_templates = " AND post_id IN (" . implode( "," , $post_ids ) . ") ";
		$remove_loop_templates_ids = $wpdb->get_col(
			"SELECT DISTINCT meta_value FROM {$wpdb->postmeta}
			WHERE meta_key = '_view_loop_template'
			AND meta_value != '0'
			{$remove_loop_templates}"
		);
		foreach ( $remove_loop_templates_ids as $remove_template ) {
			wp_delete_post( $remove_template, true );
		}
	}
	foreach ( $post_ids as $post_id ) {
		$res = wp_delete_post( $post_id );
		$is_failure = $is_failure || ( $res == false );
	}
	// Clean options - when deleting WPA
	global $WPV_settings;
	$WPV_settings->refresh_view_settings_data();
    $WPV_settings->save();
	echo $is_failure ? 0 : 1;
	die();
}


/**
 * Find out which WordPress Archives are used in some loops.
 *
 * For given WPA IDs output those who are used in archive loops. If there are any, also
 * generate HTML for the colorbox popup - confirmation to trash them.
 *
 * Following GET parameters are expected:
 * - wpnonce: Valid wpv_view_listing_actions_nonce.
 * - ids: An array of WPA IDs that should be checked.
 *
 * Output is a JSON representation of an array with following elements:
 * - used_wpa_ids: An array of IDs of WPAs in use.
 * - colorbox_html: If used_wpa_ids is non-empty, this contains HTML for the colorbox popup.
 *     When user confirms it, *all* of the WPAs will be trashed (not only those from used_wpa_ids).
 *     Otherwise it is an empty string.
 *
 * @since 1.7
 */
add_action( 'wp_ajax_wpv_archive_check_usage', 'wpv_archive_check_usage_callback' );

function wpv_archive_check_usage_callback() {
	wpv_ajax_authenticate( 'wpv_view_listing_actions_nonce', array( 'parameter_source' => 'get', 'type_of_death' => 'data' ) );

	if( ! isset( $_GET['ids'] ) ) {
		$post_ids = array();
	} else if( is_string( $_GET['ids'] ) ) {
		$post_ids = array( $_GET['ids'] );
	} else {
		$post_ids = $_GET['ids'];
	}
	$post_ids = array_map( 'esc_attr', $post_ids );
	$post_ids = array_map( 'trim', $post_ids );
	// is_numeric does sanitization
	$post_ids = array_filter( $post_ids, 'is_numeric' );
	$post_ids = array_map( 'intval', $post_ids );
	$nonce = wp_create_nonce( 'wpv_view_listing_actions_nonce' );
	global $WPV_settings, $WPV_view_archive_loop;
	$loops = $WPV_view_archive_loop->_get_post_type_loops();
	$taxonomies = get_taxonomies( '', 'objects' );
	$exclude_tax_slugs = array();
	$exclude_tax_slugs = apply_filters( 'wpv_admin_exclude_tax_slugs', $exclude_tax_slugs );
	// This will hold IDs of used archives.
	$archive_ids_in_use = array();
	// Check for usage in loops
	foreach ( $loops as $loop => $loop_name ) {
		if ( isset( $WPV_settings[ 'view_' . $loop ] )
			&& in_array( $WPV_settings[ 'view_' . $loop ], $post_ids ) )
		{
			$used_archive_id = $WPV_settings[ 'view_' . $loop ];
			// Use post id for both key and value to ensure it will be present only once as value.
			$archive_ids_in_use[ $used_archive_id ] = $used_archive_id;
		}
	}
	// Check for usage in taxonomies
	foreach ( $taxonomies as $category_slug => $category ) {
		if ( in_array( $category_slug, $exclude_tax_slugs ) ) {
			continue;
		}
		// Only show taxonomies with show_ui set to TRUE
		if ( !$category->show_ui ) {
			continue;
		}
		$name = $category->name;
		if ( isset ( $WPV_settings[ 'view_taxonomy_loop_' . $name ] )
			&& in_array( $WPV_settings[ 'view_taxonomy_loop_' . $name ], $post_ids ) )
		{
			$used_archive_id = $WPV_settings[ 'view_taxonomy_loop_' . $name ];
			$archive_ids_in_use[ $used_archive_id ] = $used_archive_id;
		}
	}
	$archive_ids_in_use = array_map( 'esc_attr', $archive_ids_in_use );
	$archive_ids_in_use = array_map( 'trim', $archive_ids_in_use );
	// is_numeric does sanitization
	$archive_ids_in_use = array_filter( $archive_ids_in_use, 'is_numeric' );
	$archive_ids_in_use = array_map( 'intval', $archive_ids_in_use );
	// If there are some used archives, generate html for the colorbox confirmation popup
	if ( ! empty( $archive_ids_in_use ) ) {
		// We only get IDs and titles
		global $wpdb;
		$used_archive_id_list = implode( ',', $archive_ids_in_use );
		$used_archives = $wpdb->get_results(
			"SELECT ID as id, post_title FROM {$wpdb->posts}
			WHERE post_type = 'view'
			AND id IN ( $used_archive_id_list )"
		);
		$used_archive_count = count( $archive_ids_in_use );
		ob_start();
		?>
		<div class="wpv-dialog wpv-shortcode-gui-content-wrapper js-bulk-trash-archives-dialog">
			<h3>
				<?php
					echo _n(
							'Are you sure you want to trash this WordPress Archive?',
							'Are you sure you want to trash these WordPress Archives?',
							$used_archive_count,
							'wpv-views' );
				?>
			</h3>
			<p>
				<?php
					echo _n(
							'It is assigned to one or more archive or taxonomy loops.',
							'Some of them are assigned to archive or taxonomy loops.',
							$used_archive_count,
							'wpv-views' )
						. WPV_MESSAGE_SPACE_CHAR
						. _n(
							'Trashing it will also unassign it.',
							'Trashing them will also unassign them.',
							$used_archive_count,
							'wpv-views' );
				?>
			</p>
			<ul style="list-style-type: disc; padding-left: 40px;">
				<?php
					foreach( $used_archives as $archive ) {
						?>
							<li><strong><?php echo $archive->post_title; ?></strong></li>
						<?php
					}
				?>
			</ul>
		</div> <!-- .js-bulk-trash-archives-dialog -->
		<?php
		$dialog_content = ob_get_contents();
		ob_end_clean();
	} else {
		$dialog_content = '';
	}
	$data = array(
		'dialog_content'	=> $dialog_content,
		'used_wpa_ids'		=> $archive_ids_in_use
	);
	wp_send_json_success( $data );
}


/* ************************************************************************* *\
        Content Templates
\* ************************************************************************* */


add_action( 'wp_ajax_wpv_apply_ct_to_cpt_posts_popup', 'wpv_apply_ct_to_cpt_posts_popup_callback');


/**
 * Callback function for the AJAX action wp_ajax_wpv_ct_update_posts used to count dissident posts that are not using
 * the Template assigned to its type. This is called on the Content Templates listing screen for single usage
 * and on the Template edit screen.
 *
 * @since 1.3.0
 */
function wpv_apply_ct_to_cpt_posts_popup_callback() {
	wpv_ajax_authenticate( 'work_view_template', array( 'parameter_source' => 'get', 'type_of_death' => 'data' ) );

    if (
		isset ( $_GET['type'] )
		&& isset( $_GET['id'] )
	) {
        $type = sanitize_text_field( $_GET['type'] );
        $id = intval( $_GET['id'] );
    } else {
		$data = array(
			'message' => __( 'Wrong data.', 'wpv-views' )
		);
		wp_send_json_error( $data );
		return;
    }

	try {
        $ct = new WPV_Content_Template_Embedded( $id );
    } catch( Exception $e ) {
        // well, we were not handling non-existent CTs before and I am not sure how to do that now...
        $data = array(
			'message' => __( 'Wrong data.', 'wpv-views' )
		);
		wp_send_json_error( $data );
		return;
    }

    $dissident_post_count = $ct->get_dissident_posts( $type, 'count' );
    if ( $dissident_post_count > 0 ) {
        $ptype = get_post_type_object( $type );

        if ( $dissident_post_count > 1 ){
            $type_label = $ptype->labels->name;
            $message = '<p>' . sprintf( __( '<strong>%d %s</strong> use a different Content Template.', 'wpv-views' ), $dissident_post_count , $type_label ) . '</p>';
        } else {
            $type_label = $ptype->labels->singular_name;
            $message = '<p>' . sprintf( __( '<strong>%d %s</strong> uses a different Content Template.', 'wpv-views' ), $dissident_post_count, $type_label ) . '</p>';
        }
		$message .= '<p>'
			. __( 'Maybe you have manually set a different one on them.', 'wpv-views' )
			. WPV_MESSAGE_SPACE_CHAR
			. sprintf(
					__( 'Click <em>Update</em> to force this Content Template into all single %s', 'wpv-views' ),
					$type_label
				)
			. '</p>';
		$data = array(
			'dialog_content' => '<div class="wpv-dialog wpv-shortcode-gui-content-wrapper">' . $message . '</div>'
		);
		wp_send_json_success( $data );
        ?>
    <?php
    } else {
		$data = array(
			'message' => __( 'Wrong data.', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}
}

// Unlink a Content Template for orphaned single posts types when there is no general Template associated with that type

add_action('wp_ajax_wpv_clear_cpt_from_ct', 'wpv_clear_cpt_from_ct_callback');

function wpv_clear_cpt_from_ct_callback() {
	wpv_ajax_authenticate( 'wpv_clear_cpt_from_ct_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );
	if ( ! isset( $_POST['slug'] ) ) {
		$data = array(
			'message' => __( 'Slug not set in the AJAX call.', 'wpv-views' )
		);
		wp_send_json_error( $data );
	} else {
		global $wpdb;
		$type = $_POST['slug'];
		$posts = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT {$wpdb->posts}.ID FROM {$wpdb->posts}
				WHERE post_type = %s",
				$type
			)
		);
		$count = sizeof( $posts );
		if ( $count > 0 ) {
			foreach( $posts as $post_id ) {
				WPV_Content_Template_Embedded::remove_ct_assignment_from_post_object( $post_id );
			}
		}
		wp_send_json_success();
	}
}

/*
 * Add new Content Template - popup structure
 * Added by Gen TODO check this nonce
 */

add_action('wp_ajax_wpv_ct_create_new_render_popup', 'wpv_ct_create_new_render_popup_callback');

/**
 * Render HTML for the Add new Content Template dialog.
 *
 * @since unknown
 */
function wpv_ct_create_new_render_popup_callback(){
    wpv_ajax_authenticate( 'work_view_template', array( 'parameter_source' => 'get', 'type_of_death' => 'data' ) );

	// @todo None of those parameters are passed on the AJAX Get call at all...
    $ct_title		= wpv_getget( 'ct_title', '' );
    $ct_id_selected	= intval( wpv_getget( 'ct_selected', 0 ) );
    $ct_selected	= WPV_Content_Template::get_instance( $ct_id_selected );

	ob_start();
	?>
    <div class="wpv-dialog wpv-shortcode-gui-content-wrapper js-wpv-dialog-add-new-content-template wpv-dialog-add-new-content-template">
		<label for="wpv-new-content-template-name">
			<strong><?php _e('Name this Content Template','wpv-views') ?></strong>
		</label>
		<input type="text" value="<?php echo esc_attr( $ct_title ); ?>" id="wpv-new-content-template-name" class="js-wpv-new-content-template-name wpv-new-content-template-name" placeholder="<?php echo esc_attr( __('Content Template name','wpv-views') ) ?>" name="wpv-new-content-template-name">
		<h3><?php _e('What content will this template be for?','wpv-views') ?></h3>
		<p>
			<?php
			_e( 'A Content Template can replace the content of the post with whatever you put into it, including Views shortcodes. ', 'wpv-views' );
			?>
		</p>

		<?php
			wpv_render_ct_assignment_sections( $ct_selected );
		?>
		<div class="js-wpv-error-container"></div>
    </div> <!-- wpv-dialog -->
    <?php
	$response = ob_get_clean();
	$data = array(
		'dialog_content' => $response
	);
	wp_send_json_success( $data );
}

/**
 * Delete CT callback function.
 *
 * DEPRECATED since 1.8. Use wpv_ct_bulk_delete instead!
 *
 * Not used in Views, but I'm not sure about other plugins.
 *
 * @see wpv_ct_bulk_delete_callback()
 */
add_action('wp_ajax_wpv_delete_ct', 'wpv_delete_ct_callback');

function wpv_delete_ct_callback(){
	if ( ! current_user_can( EDIT_VIEWS ) ) {
		die( "Untrusted user" );
	}
    if (
		! isset( $_POST["wpnonce"] )
		|| ! wp_verify_nonce($_POST["wpnonce"], 'work_view_template')
	) {
		die( "Undefined Nonce" );
	}

    $tid = intval( $_POST['id'] );
    global $WPV_settings;
	$settings_array = $WPV_settings->get();
    foreach ( $settings_array as $key => $value ) {
        if ( $value == $tid ) {
            $WPV_settings[$key] = 0;
        }
    }

    $WPV_settings->save();

    wp_delete_post( $tid );
    echo $tid;
    die();
}

// Change CT usage - popup structure TODO review this nonces

add_action('wp_ajax_wpv_change_ct_usage_popup', 'wpv_change_ct_usage_popup');

/**
 * Render HTML for the dialog to change CT assignment.
 *
 * Expects following GET parameters:
 * - wpnonce: A valid work_view_template nonce.
 * - id: Valid ID of a Content Template
 *
 * @since unknown
 */
function wpv_change_ct_usage_popup() {
    wpv_ajax_authenticate( 'work_view_template', array( 'parameter_source' => 'get', 'type_of_death' => 'data' ) );

    $id = intval( wpv_getget( 'id', 0 ) );
    $ct = WPV_Content_Template::get_instance( $id );
    if ( null == $ct ) {
        $data = array(
			'type' => 'error',
			'message' => __( 'Invalid CT ID.', 'wpv-views' )
		);
		wp_send_json_error( $data );
    }
	ob_start();
    ?>
    <div class="wpv-dialog wpv-shortcode-gui-content-wrapper js-wpv-dialog-add-new-content-template wpv-dialog-add-new-content-template">
        <h3><?php _e('What content will this template be for?','wpv-views') ?></h3>
		<form method="" id="wpv-add-new-content-template-form">
             <?php wpv_render_ct_assignment_sections( $ct ); ?>
        </form>
	</div>
    <?php
	$response = ob_get_clean();
	$data = array(
		'dialog_content' => $response
	);
	wp_send_json_success( $data );
}


/**
 * Render part of the dialog for assigning CT to different loops/post types.
 *
 * This will render the "toggle" section headers and section content with checkboxes. If a CT is provided,
 * checkbox value and section toggle states will be adjusted to it's current settings.
 *
 * This code is being used in "Add new Content Template" and "Change template usage" dialogs.
 *
 * @param WPV_Content_Template|null $ct Existing CT for default values or null.
 *
 * @since 1.10
 */
function wpv_render_ct_assignment_sections( $ct = null ) {

    global $WPV_view_archive_loop;
    $single_post_type_loops	= $WPV_view_archive_loop->get_archive_loops( 'post_type', false, true, true );
    $post_type_loops		= $WPV_view_archive_loop->get_archive_loops( 'post_type', false, true );
    $taxonomy_archive_loops	= $WPV_view_archive_loop->get_archive_loops( 'taxonomy', false, true );

    $asterisk				= '<span style="color:red;">*</span>';
    $asterisk_explanation	= __( '<span style="color:red">*</span> A different Content Template is already assigned to this item', 'wpv-views' );

    $has_ct = ( null != $ct );

    //
    // Section for single post type assignment
    //

    $show_asterisk_explanation	= false;
	$apply_to_existing_label	= __( 'Apply to existing %s items', 'wpv-views' );
	?>
	<div style="display:table;width:100%;">
	<div style="display:table-row">

	<div class="js-wpv-content-template-dropdown-list wpv-content-template-dropdown-list" style="display:table-cell;width:33%">
		<h4><?php echo __( 'Single pages', 'wpv-views' ); ?></h4>
		<?php
		if ( count( $single_post_type_loops ) > 0 ) {
		?>
        <ul>
			<?php
			if ( ! $has_ct ) {
				?>
			<li class="js-wpv-ct-assign-to-object-container">
				<input id="wpv-content-template-no-use" type="checkbox" class="js-wpv-ct-assign-to-object js-wpv-ct-assign-to-post-type-single js-wpv-ct-dont-assign" <?php checked( true ); ?> name="wpv-new-content-template-post-type[]" value="0" />
				<label for="wpv-content-template-no-use"><?php _e("Don't assign to any post type",'wpv-views') ?></label>
			</li>
				<?php
			}
        foreach ( $single_post_type_loops as $post_type_loop ) {
            $post_type		= $post_type_loop['post_type_name'];
			$singular_name	= $post_type_loop['singular_name'];
            $setting_name	= 'views_template_for_' . esc_attr( $post_type );
            $assigned_ct_id	= $post_type_loop['single_ct'];
            $type_current	= $type_used = false;

			$input_ids		= $setting_name;
			if ( $has_ct ) {
				$input_ids .= '-' . $ct->id;
			} else {
				$input_ids .= '-0';
			}

			$apply_to_current = sprintf(
				$apply_to_existing_label,
				$singular_name
			);

            if ( $assigned_ct_id != 0 ) {
                $type_used = true;
                $show_asterisk_explanation = true;
            }
            if (
				$has_ct
				&& $ct->id == $assigned_ct_id
			) {
                $type_current = true;
                $type_used = false;
                $open_section = true;
            }

			/** @noinspection HtmlUnknownAttribute */
			printf(
                '<li class="js-wpv-ct-assign-to-object-container">
                    <input id="%s" type="checkbox" class="js-wpv-ct-assign-to-object js-wpv-ct-assign-to-post-type-single" name="wpv-new-content-template-post-type[]" %s data-title="%s" value="%s" />
                    <label for="%s">%s%s</label>
					<div class="js-wpv-ct-assign-to-all-existing" style="margin-left:20px;margin-top:5px;font-size:11px;%s">
					<input id="%s-apply" type="checkbox" class="js-wpv-ct-assign-to-object-apply js-wpv-ct-assign-to-post-type-single-apply" name="wpv-new-content-template-post-type-apply[]" %s value="%s" />
                    <label for="%s-apply">%s</label>
					</div>
                </li>',
                $input_ids, checked( $type_current, true, false ), esc_attr( $post_type_loop['display_name'] ), $setting_name,
                $input_ids, $post_type_loop['display_name'], ( $type_used ? $asterisk : '' ),
				( $type_current ? '' : 'display:none;' ),
				$input_ids, checked( true, true, false ), $post_type,
                $input_ids, $apply_to_current
            );

        }

        echo '</ul>';

    } else {
        _e( 'There are no single post types to assign Content Templates to', 'wpv-views' );
    }
    ?>
    </div>

    <div class="js-wpv-content-template-dropdown-list wpv-content-template-dropdown-list" style="display:table-cell;width:33%">
		<h4><?php echo __( 'Post type archives', 'wpv-views' ); ?></h4>
		<?php
		if ( count( $post_type_loops ) > 0 ) {

        echo '<ul>';
        foreach ( $post_type_loops as $post_type_loop ) {
            $post_type = $post_type_loop['post_type_name'];
            $setting_name = 'views_template_archive_for_' . esc_attr( $post_type );
            $assigned_ct_id = $post_type_loop['ct'];
            $type_current = $type_used = false;
            if ( $assigned_ct_id != 0 ) {
                $type_used = true;
                $show_asterisk_explanation = true;
            }
            if (
				$has_ct
				&& $ct->id == $assigned_ct_id
			) {
                $type_current = true;
                $type_used = false;
                $open_section = true;
            }

			$input_ids = $setting_name;
			if ( $has_ct ) {
				$input_ids .= '-' . $ct->id;
			} else {
				$input_ids .= '-0';
			}

			/** @noinspection HtmlUnknownAttribute */
			printf(
                '<li class="js-wpv-ct-assign-to-object-container">
                    <input id="%s" type="checkbox" class="js-wpv-ct-assign-to-object js-wpv-ct-assign-to-post-type-archive" name="wpv-new-content-template-post-type[]" %s data-title="%s" value="%s" />
                    <label for="%s">%s%s</label>
                </li>',
                $input_ids, checked( $type_current, true, false ), esc_attr( $post_type_loop['display_name'] ), $setting_name,
                $input_ids, $post_type_loop['display_name'], ( $type_used ? $asterisk : '' )
            );

        }

        echo '</ul>';

    } else {
        _e( 'There are no post type archives to assign Content Templates to', 'wpv-views' );
    }


    ?>
    </div>

    <div class="js-wpv-content-template-dropdown-list wpv-content-template-dropdown-list" style="display:table-cell;width:33%">
		<h4><?php echo __( 'Taxonomy archives', 'wpv-views' ); ?></h4>
		<?php
		if ( count( $taxonomy_archive_loops ) > 0 ) {
        echo '<ul>';

        foreach ( $taxonomy_archive_loops as $taxonomy_archive_loop ) {

            $type = $taxonomy_archive_loop['slug'];
            $label = $taxonomy_archive_loop['display_name'];
            $setting_name = 'views_template_loop_' . $type;
            $assigned_ct_id = $taxonomy_archive_loop['ct'];
            $type_current = $type_used = false;

            if ( 0 != $assigned_ct_id ) {
                $type_used = true;
                $show_asterisk_explanation = true;
            }
            if (
				$has_ct
				&& $ct->id == $assigned_ct_id
			) {
                $type_current = true;
                $type_used = false;
                $open_section = true;
            }

			$input_ids = $setting_name;
			if ( $has_ct ) {
				$input_ids .= '-' . $ct->id;
			} else {
				$input_ids .= '-0';
			}

			/** @noinspection HtmlUnknownAttribute */
			printf(
                '<li class="js-wpv-ct-assign-to-object-container">
                    <input id="%s" type="checkbox" class="js-wpv-ct-assign-to-object js-wpv-ct-assign-to-taxonomy-archive" name="wpv-new-content-template-post-type[]" %s data-title="%s" value="%s" />
                    <label for="%s">%s%s</label>
                </li>',
                $input_ids, checked( $type_current, true, false ), esc_attr( $label ), $setting_name,
                $input_ids, $label, ( $type_used ? $asterisk : '' )
            );

        }

        echo '</ul>';

    } else {
        _e( 'There are no taxonomy archives to assign Content Templates to', 'wpv-views' );
    }
    ?>
    </div>

	</div><!-- table-row -->
	</div><!-- table -->
    <?php
	if ( $show_asterisk_explanation ) {
		?>
		<p>
			<span class="wpv-asterisk-explanation"><?php echo $asterisk_explanation; ?></span>
		</p>
		<?php
	}
	?>

	<?php
}

add_action('wp_ajax_wpv_change_ct_usage', 'wpv_change_ct_usage_callback');

function wpv_change_ct_usage_callback() {
	wpv_ajax_authenticate( 'work_view_template', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

    global $WPV_settings;
    $id = intval( $_POST['view_template_id'] );

    if (
		isset( $_POST['type'] )
		&& is_array( $_POST['type'] )
	) {
        $type = array_map( 'sanitize_text_field', $_POST['type'] );
    } else {
        $type = array();
    }

	if (
		isset( $_POST['apply'] )
		&& is_array( $_POST['apply'] )
	) {
        $apply = array_map( 'sanitize_text_field', $_POST['apply'] );
    } else {
        $apply = array();
    }

	$settings_array = $WPV_settings->get();
    foreach ( $settings_array as $key => $value ) {
        if ( $value == $id ) {
            $WPV_settings[$key] = 0;
        }
    }

    for ( $i = 0; $i < count( $type ); $i++ ) {
        $WPV_settings[$type[$i]] = $id;
    }

    $WPV_settings->save();

	$content_template = WPV_Content_Template::get_instance( $id );
	$content_template->kill_dissident_posts( $apply );

	do_action( 'wpv_action_wpv_save_item', $id );
    wp_send_json_success();
}

// Change CT action - popup structure

add_action('wp_ajax_wpv_change_ct_assigned_to_something_dialog', 'wpv_change_ct_assigned_to_something_dialog_callback');

function wpv_change_ct_assigned_to_something_dialog_callback(){
    wpv_ajax_authenticate( 'work_view_template', array( 'parameter_source' => 'get', 'type_of_death' => 'data' ) );

    global $wpdb, $WPV_settings;
	$values_to_prepare = array();
	$exclude_loop_templates = '';
	$exclude_loop_templates_ids = wpv_get_loop_content_template_ids();
	if ( count( $exclude_loop_templates_ids ) > 0 ) {
		$exclude_loop_templates_ids_sanitized = array_map( 'esc_attr', $exclude_loop_templates_ids );
		$exclude_loop_templates_ids_sanitized = array_map( 'trim', $exclude_loop_templates_ids_sanitized );
		// is_numeric + intval does sanitization
		$exclude_loop_templates_ids_sanitized = array_filter( $exclude_loop_templates_ids_sanitized, 'is_numeric' );
		$exclude_loop_templates_ids_sanitized = array_map( 'intval', $exclude_loop_templates_ids_sanitized );
		if ( count( $exclude_loop_templates_ids_sanitized ) > 0 ) {
			$exclude_loop_templates = " AND p.ID NOT IN ('" . implode( "','" , $exclude_loop_templates_ids_sanitized ) . "') ";
		}
	}
	$values_to_prepare[] = 'view-template';
	$view_tempates_available = $wpdb->get_results(
		$wpdb->prepare(
			"SELECT p.ID, p.post_title
			FROM {$wpdb->posts} p
			WHERE p.post_status = 'publish'
			AND p.post_type = %s
			{$exclude_loop_templates}
			ORDER BY p.post_title",
			$values_to_prepare
		)
	);

    $post_type = sanitize_text_field( $_GET['pt'] );
	$selected = ( isset( $WPV_settings[$post_type] ) ) ? $WPV_settings[$post_type] : 0;
	ob_start();
    ?>
    <div class="wpv-dialog wpv-shortcode-gui-content-wrapper js-wpv-dialog-add-new-content-template wpv-dialog-add-new-content-template">
		<h3><?php _e( 'Select one of the following Content Templates', 'wpv-views' ); ?></h3>
		<ul class="wpv-mightlong-list">
			<li>
				<input type="radio" id="wpv-content-template-name-0" class="js-wpv-content-template-name" name="wpv-content-template-name" <?php checked( 0, $selected ); ?> value="0" />
				<label for="wpv-content-template-name-0"><?php _e( 'Don’t use any Content Template', 'wpv-views' ); ?></label>
			</li>
			<?php
			if ( count( $view_tempates_available ) > 0 ) {
				foreach ( $view_tempates_available as $row ) {
					?>
						<li>
							<input type="radio" id="wpv-content-template-name-<?php echo esc_attr( $row->ID );?>" class="js-wpv-content-template-name" name="wpv-content-template-name" <?php checked( $row->ID, $selected ); ?> value="<?php echo esc_attr( $row->ID );?>" />
							<label for="wpv-content-template-name-<?php echo esc_attr( $row->ID );?>"><?php echo $row->post_title; ?></label>
						</li>
					<?php
				}
			}
			?>
		</ul>
    </div>
    <?php
	$response = ob_get_clean();
	$data = array(
		'dialog_content' => $response
	);
	wp_send_json_success( $data );
}

// Change CT action callback function TODO check nonces

add_action('wp_ajax_wpv_change_ct_assigned_to_something', 'wpv_change_ct_assigned_to_something_callback');

function wpv_change_ct_assigned_to_something_callback() {
	wpv_ajax_authenticate( 'work_view_template', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

    global $WPV_settings;
    $pt = sanitize_text_field( $_POST["pt"] );
    if ( isset( $_POST['value'] ) ) {
        $value = intval( $_POST['value'] );
        if( $value > 0 ) {
            do_action( 'wpv_action_wpv_save_item', $value );
        }
    } else {
        $value = 0;
    }
	$old_value = $WPV_settings[ $pt ];
	if ( intval( $old_value ) > 0 && isset( $_POST['pt_slug'] ) ) {
		$pt_slug = $_POST['pt_slug'];
		$toolset_dic = apply_filters( 'toolset_dic', false );
		/** @var ContentTemplateService $service */
		$service = $toolset_dic->make( ContentTemplateService::class );
		$service->clean_post_type_assigned_posts( wp_unslash( $pt_slug ), $old_value );
	}

    $WPV_settings[$pt] = $value;
    $WPV_settings->save();

    wp_send_json_success();
}

// Response when updating all posts to use a given CT - popup structure TODO localize!!!! and check nonce
// TODO seems that this is called in a colorbox callback, but BUT is executes the delete... TODO review this all

add_action('wp_ajax_wpv_apply_ct_to_cpt_posts', 'wpv_apply_ct_to_cpt_posts_callback');

function wpv_apply_ct_to_cpt_posts_callback() {
	wpv_ajax_authenticate( 'work_view_template', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

    $id = intval( $_POST['id'] );
    $type = sanitize_text_field( $_POST['type'] );
    wpv_update_dissident_posts_from_template( $id, $type, false );

    wp_send_json_success();
}

add_action('wp_ajax_wpv_content_template_move_to_trash', 'wpv_content_template_move_to_trash_callback');

/** Move CT to trash or show message.
 *
 * Prints a JSON array. If CT is not in use, it is trashed and first element of the array is "move" and second one
 * contains ID of the CT. Otherwise first element is "show" and second contains HTML for the colorbox dialog that should
 * be shown.
 *
 * @deprecated Not used in Views since 1.10. Use wpv_bulk_content_templates_move_to_trash instead.
 */
function wpv_content_template_move_to_trash_callback() {
	wpv_ajax_authenticate( 'wpv_view_listing_actions_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	if ( isset( $_POST['id'] ) ) {
		$ct_id = intval( $_POST['id'] );
	} else {
		$data = array(
			'type' => 'error',
			'message' => __( 'Invalid CT ID.', 'wpv-views' )
		);
		wp_send_json_error( $data );
		return;
	}
	global $wpdb;
	$posts_count = $wpdb->get_var(
		$wpdb->prepare(
			"SELECT count(posts.ID) FROM {$wpdb->posts} as posts,{$wpdb->postmeta} as postmeta
			WHERE postmeta.meta_key = '_views_template'
			AND postmeta.meta_value = %s
			AND postmeta.post_id = posts.ID",
			$ct_id
		)
	);
	if ( $posts_count == 0 ) {

		$my_post = array(
			'ID'          => $ct_id,
			'post_status' => 'trash'
		);
		wp_update_post( $my_post );
		do_action( 'wpv_action_wpv_save_item', $ct_id );
		wpv_replace_views_template_options( $ct_id, 0 );

		$data = array(
			'action' => 'reload'
		);
		wp_send_json_success( $data );
	} else {
		$template_list = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT ID, post_title FROM {$wpdb->posts}
				WHERE post_status = 'publish'
				AND post_type = 'view-template'
				AND ID != %s",
				$ct_id
			)
		);
		ob_start();
		?>
		<div class="wpv-dialog wpv-shortcode-gui-content-wrapper">
			<p>
				<?php echo sprintf( _n('1 item', '%s items', $posts_count, 'wpv-views'), $posts_count ) . __( ' use this content template. What do you want to do?', 'wpv-views' ); ?>
			</p>
			<ul>
                <?php if ( count($template_list) > 0 ){?>
                <li><label>
                    <input type="radio" name="wpv-content-template-replace-to" class="js-wpv-existing-posts-ct-replace-to js-wpv-existing-posts-ct-replace-to-selected-ct" value="0" id="wpv-content-template-replace-to" />
                    <?php _e( 'Choose a different content template for them: ', 'wpv-views' )?>
                    </label>
                    <select name="wpv-ct-list-for-replace" class="js-wpv-ct-list-for-replace" id="wpv-ct-list-for-replace">
                    	<option value=''><?php _e( 'Select Content Template', 'wpv-views' )?> </option>
                    <?php
						foreach( $template_list as $temp_post ) :
							echo '<option value="'. esc_attr( $temp_post->ID ).'">'. $temp_post->post_title .'</option>';
                        endforeach;
					?></select>

                </li>
                <?php }?>
                <li><label>
                    <input type="radio" name="wpv-content-template-replace-to" class="js-wpv-existing-posts-ct-replace-to" value="1" />
                    <?php _e( 'Don\'t use any content template for these items', 'wpv-views' )?>
                    </label>
                </li>
             </ul>
        </div>
        <?php
        $response = ob_get_clean();
		$data = array(
			'action'			=> 'dialog',
			'dialog_content'	=> $response
		);
		wp_send_json_success( $data );
	}
}



add_action( 'wp_ajax_wpv_bulk_content_templates_move_to_trash', 'wpv_bulk_content_templates_move_to_trash_callback' );


/**
 * Initiate a bulk trash action.
 *
 * For given content templates, find out how many posts use them. If no template is used in any post,
 * trash them right away. If one or more templates is being used, render HTML for a colorbox dialog
 * (wpv-dialog-bulk-replace-content-template) that will ask user to decide how to replace those templates.
 * See js events on '.js-ct-bulk-replace-usage' for further information.
 *
 * Expected POST parameters:
 * - ids: A non-empty array of content template IDs.
 * - wpnonce: A valid wpv_view_listing_actions_nonce.
 * @todo use dedicated nonce containing user and CT id; note that this is used also on CT edit page
 *
 * Response is an JSON object containing following properties:
 * - all_ids: Array of all content templates that were/should be trashed.
 * - action: 'trashed' if CTs have been trashed, 'confirm' if a popup should be shown before that.
 * - popup_content: If action is 'confirm', this will contain the HTML of the popup.
 *
 * @since 1.7
 */
function wpv_bulk_content_templates_move_to_trash_callback() {
	wpv_ajax_authenticate( 'wpv_view_listing_actions_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

    // Parse and validate input
    $ct_ids = wpv_getpost( 'ids', array() );
    if( !is_array( $ct_ids ) ) {
        $ct_ids = array( $ct_ids );
    }

    $ct_ids = array_map( 'esc_attr', $ct_ids );
    $ct_ids = array_map( 'trim', $ct_ids );
    // is_numeric does sanitization
    $ct_ids = array_filter( $ct_ids, 'is_numeric' );
    $ct_ids = array_map( 'intval', $ct_ids );

	if ( empty( $ct_ids ) ) {
		// We don't allow empty input
		$data = array(
			'type' => 'error',
			'message' => __( 'Invalid CT ID.', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}


	$data = array(
        'all_ids' => $ct_ids,
        'success' => true
    );

	// Determine which templates are currently in use.
	// This can get quite expensive... can we do it in more effective way?

	// This will hold information about used templates indexed by their IDs
	$used_templates = array();
	foreach( $ct_ids as $ct_id ) {

		$ct = WPV_Content_Template::get_instance( $ct_id );

		if( null == $ct ) {
			$data = array(
				'type' => 'error',
				'message' => __( 'Invalid CT ID.', 'wpv-views' )
			);
			wp_send_json_error( $data );
		}

		$posts_using_ct = $ct->get_posts_using_this( '*', 'count' );

		if( $posts_using_ct > 0 ) {
			$used_templates[ $ct_id ] = array(
				'title' => $ct->title,
				'usage_count' => $posts_using_ct
			);
		}
	}

	if ( empty( $used_templates ) ) {
		// No template is used, we can trash them all.

		$settings = WPV_Settings::get_instance();

		foreach( $ct_ids as $ct_id ) {

			// Trash the template
			wp_trash_post( $ct_id );
			do_action( 'wpv_action_wpv_save_item', $ct_id );

			// Remove references to trashed template from Views options
			$settings->replace_abstract_ct_associations( $ct_id, 0, false );

		}

        $settings->save();

		$data['action'] = 'reload';
		wp_send_json_success( $data );

	} else {
        // One or more templates are in use, we need to show a confirmation.

		$exclude_loop_templates_ids = wpv_get_loop_content_template_ids();
		$excluded_ct_ids = $ct_ids;
		if ( count( $exclude_loop_templates_ids ) > 0 ) {
			$exclude_loop_templates_ids = array_map( 'esc_attr', $exclude_loop_templates_ids );
			$exclude_loop_templates_ids = array_map( 'trim', $exclude_loop_templates_ids );
			// is_numeric + intval does sanitization
			$exclude_loop_templates_ids = array_filter( $exclude_loop_templates_ids, 'is_numeric' );
			$exclude_loop_templates_ids = array_map( 'intval', $exclude_loop_templates_ids );
			if ( count( $exclude_loop_templates_ids ) > 0 ) {
				$excluded_ct_ids = array_merge( $excluded_ct_ids, $exclude_loop_templates_ids );
			}
		}

		// Get list of templates that can be used as a replacement for the trashed ones.
        $template_list = WPV_Content_Template_Embedded::query( array( 'post__not_in' => $excluded_ct_ids ) );

		// Render popup content.
		ob_start();

		?>
		<div class="wpv-dialog wpv-shortcode-gui-content-wrapper js-wpv-dialog-bulk-replace-content-template wpv-dialog-bulk-replace-content-template">
			<p><?php _e( 'These content templates are in use. What do you want to do?', 'wpv-views' ); ?></p>
			<?php
			// Show a div with options for each used template.
			foreach( $used_templates as $ct_id => $template_info ) {
				$template_title = $template_info['title'];
				$template_usage_count = $template_info['usage_count'];

				?>
				<div>
					<?php
						printf(
								'<p><strong>%s</strong> (%s %s)</p>',
								$template_title,
								__( 'used by', 'wpv-views' ),
								sprintf( _n( '1 item', '%s items', $template_usage_count, 'wpv-views' ), $template_usage_count ) );
					?>
					<ul>
						<?php
							/* Show an option to replace this template with another one, if there are some left.
							 * Radio buttons are grouped by name: "wpv-content-template-replace-{$template_id}-to".
							 * Select field for replacement template is identified as "wpv-ct-list-for-replace-{$template_id}"
							 *
							 * Submit button contains attributes 'data-ct-ids' with all Content Template IDs that should be trashed
							 * and 'data-replace-ids' with those that should be replaced. */
							if( !empty( $template_list ) ) {
								?>
								<li>
									<label>
										<?php
											printf(
													'<input type="radio" name="wpv-content-template-replace-%d-to"
														class="js-wpv-bulk-existing-posts-ct-replace-to js-wpv-bulk-existing-posts-ct-replace-to-selected-ct"
														value="different_template" id="wpv-content-template-replace-to" />',
													$ct_id );
											_e( 'Choose a different content template for them: ', 'wpv-views' );
										?>
									</label>
									<?php
										printf( '<select class="js-wpv-bulk-ct-list-for-replace" id="wpv-ct-list-for-replace-%d" data-template-id="%d">', esc_attr( $ct_id ), esc_attr( $ct_id ) );
										printf( '<option value="">%s</option>', __( 'Select Content Template', 'wpv-views' ) );
										foreach( $template_list as $temp_post ) {
											printf( '<option value="%s">%s</option>', $temp_post->ID, $temp_post->post_title );
										}
										printf( '</select>' );
									?>
								</li>
								<?php
							}
						?>
						<li>
							<label>
								<?php
									printf( '<input type="radio" name="wpv-content-template-replace-%d-to"
												class="js-wpv-bulk-existing-posts-ct-replace-to" value="no_template" />',
											$ct_id );
									_e( 'Don\'t use any content template for these items', 'wpv-views' );
								?>
							</label>
						</li>
					</ul>
				</div>
				<?php
				printf(
					'<input type="hidden" class="js-ct-bulk-replace-usage-data" data-ct-ids="%s" data-replace-ids="%s" />',
					urlencode( implode( ',', $ct_ids ) ),
					urlencode( implode( ',', array_keys( $used_templates ) ) )
				);
			}
			?>
		</div>
		<?php

		$dialog_content = ob_get_clean();;

		$data['action'] = 'dialog';
		$data['dialog_content'] = $dialog_content;
		wp_send_json_success( $data );
	}

}




// Change CT usage before move to trash
add_action('wp_ajax_wpv_ct_trash_with_replace', 'wpv_ct_trash_with_replace_callback');

/**
 * @deprecated Since 1.12 or earlier. Not used in Views.
 */
function wpv_ct_trash_with_replace_callback() {
	wpv_ajax_authenticate( 'wpv_view_listing_actions_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	if ( isset( $_POST['id'] ) ) {
		$ct_id = intval( $_POST['id'] );
	} else {
		$data = array(
			'type' => 'error',
			'message' => __( 'Invalid CT ID.', 'wpv-views' )
		);
		wp_send_json_error( $data );
		return; // PhpStorm, calm down!
	}

	global $wpdb;
	$replace = ( $_POST['replace_to'] == 0 ) ? intval( $_POST['replace_ct'] ) : 0;
	$wpdb->query(
		$wpdb->prepare(
			"UPDATE {$wpdb->postmeta}
			SET meta_value = %s
			WHERE meta_key = '_views_template'
			AND meta_value = %s",
			$replace,
			$ct_id
		)
	);

	$settings = WPV_Settings::get_instance();
	$settings->replace_abstract_ct_associations( $ct_id, $replace );

	$my_post = array(
		'ID'           => $ct_id,
		'post_status' => 'trash'
	);
	wp_update_post( $my_post );
	do_action( 'wpv_action_wpv_save_item', $ct_id );

	wp_send_json_success();
}



/**
 * Replace content templates that are being used by some posts and trash all given content templates (which may be a
 * superset of those being used).
 *
 * Expected POST parameters:
 * - ids: an array of IDs of all templates that should be trashed
 * - toreplace: dtto, templates that should be replaced
 * - replacements: dtto, replacement templates (same lenght and order as toreplace)
 * - wpnonce: A valid wpv_view_listing_actions_nonce.
 *
 * Content templates from 'toreplace' used in posts (and post types) will be replaced by 'replacements'. Zero values
 * in 'replacements' are interpreted as "no template". Then, all templates from 'ids' will be trashed.
 *
 * Outputs '1' on success.
 *
 * @since 1.7
 */
add_action( 'wp_ajax_wpv_ct_bulk_trash_with_replace', 'wpv_ct_bulk_trash_with_replace_callback' );

function wpv_ct_bulk_trash_with_replace_callback() {
	wpv_ajax_authenticate( 'wpv_view_listing_actions_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	if ( ! isset( $_POST['ids'] ) ) {
		// Don't allow empty input
		$data = array(
			'type' => 'error',
			'message' => __( 'Invalid CT ID.', 'wpv-views' )
		);
		wp_send_json_error( $data );
		return; // PhpStorm, calm down!
	} else if ( is_string( $_POST['ids'] ) ) {
		$ct_ids = array( $_POST['ids'] );
	} else {
		$ct_ids = $_POST['ids'];
	}

	$ct_ids = array_map( 'esc_attr', $ct_ids );
	$ct_ids = array_map( 'trim', $ct_ids );
	// is_numeric does sanitization
	$ct_ids = array_filter( $ct_ids, 'is_numeric' );
	$ct_ids = array_map( 'intval', $ct_ids );

	// This will hold template IDs as keys and IDs of their replacements as values. Value 0 indicates
	// "don't use any content template".
	$replacements = array();

	if (
		isset( $_POST['replacements'] )
		&& isset( $_POST['toreplace'] )
		&& is_array( $_POST['replacements'] )
		&& is_array( $_POST['toreplace'] )
	) {
		$sanitized_replacements = array_map( 'esc_attr', $_POST['replacements'] );
		$sanitized_replacements = array_map( 'trim', $sanitized_replacements );
		// is_numeric does sanitization
		$sanitized_replacements = array_filter( $sanitized_replacements, 'is_numeric' );
		$sanitized_replacements = array_map( 'intval', $sanitized_replacements );

		$sanitized_toreplace = array_map( 'esc_attr', $_POST['toreplace'] );
		$sanitized_toreplace = array_map( 'trim', $sanitized_toreplace );
		// is_numeric does sanitization
		$sanitized_toreplace = array_filter( $sanitized_toreplace, 'is_numeric' );
		$sanitized_toreplace = array_map( 'intval', $sanitized_toreplace );

		$replacement_count = count( $sanitized_replacements );
		$toreplace_count = count( $sanitized_toreplace );

		if ( $replacement_count == $toreplace_count ) {
			for ( $i = 0; $i < $replacement_count; ++$i ) {
				$replacements[ $sanitized_toreplace[ $i ] ] = $sanitized_replacements[ $i ];
			}
		} else {
			$data = array(
				'type' => 'error',
				'message' => __( 'Invalid data', 'wpv-views' )
			);
			wp_send_json_error( $data );
		}
	} else {
		$data = array(
			'type' => 'error',
			'message' => __( 'Invalid data', 'wpv-views' )
		);
		wp_send_json_error( $data );
	}

	global $wpdb, $WPV_settings;

	// Replace content templates as requested
	foreach( $replacements as $original_template_id => $replacement_template_id ) {
		$wpdb->query(
			$wpdb->prepare(
				"UPDATE {$wpdb->postmeta}
				SET meta_value = %s
				WHERE meta_key = '_views_template'
				AND meta_value = %s",
				$replacement_template_id,
				$original_template_id
			)
		);

		$WPV_settings->replace_abstract_ct_associations( $original_template_id, $replacement_template_id, false );

	}

	// Now trash all requested templates
	foreach( $ct_ids as $template_id ) {
		$my_post = array(
				'ID' => $template_id,
				'post_status' => 'trash' );
		wp_update_post( $my_post );
		do_action( 'wpv_action_wpv_save_item', $template_id );

		// Remove references to trashed template from Views options
		$WPV_settings->replace_abstract_ct_associations( $template_id, 0, false );
	}

	$WPV_settings->save();

	wp_send_json_success();
}



add_action( 'wp_ajax_wpv_ct_bulk_count_usage', 'wpv_ct_bulk_count_usage_callback' );

/**
 * Count posts where given Content Templates are used.
 *
 * For given array of Content Template IDs it calculates in how many posts is each template used.
 * Outputs a JSON representation of an array where keys are CT IDs and values are post counts.
 * This array also allways contains an element "0" with the sum of all post counts.
 *
 * Expected POST parameters:
 * - wpnonce: A valid wpv_view_listing_actions_nonce.
 * - ids: An array of CT IDs
 *
 * @since 1.7
 */
function wpv_ct_bulk_count_usage_callback() {
	wpv_ajax_authenticate( 'wpv_view_listing_actions_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	if( !isset( $_POST['ids'] ) ) {
		$ct_ids = array();
	} else if( is_string( $_POST['ids'] ) ) {
		$ct_ids = array( $_POST['ids'] );
	} else {
		$ct_ids = $_POST['ids'];
	}

	global $wpdb;
	$data = array();

	$ct_ids = array_map( 'esc_attr', $ct_ids );
	$ct_ids = array_map( 'trim', $ct_ids );
	// is_numeric does sanitization
	$ct_ids = array_filter( $ct_ids, 'is_numeric' );
	$ct_ids = array_map( 'intval', $ct_ids );

	$data['action'] = 'delete';

	if( sizeof( $ct_ids ) && $ct_ids[0] == -1 ) {
		$data['action'] = 'empty-trash';

        $ct_ids = $wpdb->get_results(
	        "SELECT ID as id
			FROM {$wpdb->posts}
			WHERE post_type = 'view-template'
			AND post_status = 'trash'"
        );
		$ct_ids = wp_list_pluck( $ct_ids, 'id' );
    }

	$data['ct_ids'] = $ct_ids;

	wp_send_json_success( $data );

}


/**
 * Bulk delete Content Templates.
 *
 * Expects following POST parameters:
 * - wpnonce: A valid wpv_view_listing_actions_nonce.
 * - ids: An array of CT IDs to be deleted.
 *
 * Deletes templates and removes all occurences of their IDs from Views options.
 *
 * Prints standard JSON success response.
 *
 * @since 1.7
 */
add_action( 'wp_ajax_wpv_ct_bulk_delete', 'wpv_ct_bulk_delete_callback' );

function wpv_ct_bulk_delete_callback() {
	wpv_ajax_authenticate( 'wpv_view_listing_actions_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) );

	if( !isset( $_POST['ids'] ) ) {
		$ct_ids = array();
	} else if( is_string( $_POST['ids'] ) ) {
		$ct_ids = array( $_POST['ids'] );
	} else {
		$ct_ids = $_POST['ids'];
	}

	$ct_ids = array_map( 'esc_attr', $ct_ids );
	$ct_ids = array_map( 'trim', $ct_ids );
	// is_numeric does sanitization
	$ct_ids = array_filter( $ct_ids, 'is_numeric' );
	$ct_ids = array_map( 'intval', $ct_ids );

    global $WPV_settings;

    foreach( $ct_ids as $ct_id ) {
		$WPV_settings->replace_abstract_ct_associations( $ct_id, 0, false );

		wp_delete_post( $ct_id );
	}

	$WPV_settings->save();

	wp_send_json_success();
}



add_action( 'wp_ajax_wpv_ct_bind_posts', 'wpv_ct_bind_posts_callback' );

/**
 * Bind specific posts to a Content Template.
 *
 * Following POST parameters are expected:
 * - id: Content Template ID
 * - wpnonce: A valid wpv_ct_{$id}_bind_posts_by_{$user_id} nonce.
 * - posts_to_bind: An array of post IDs that should be bound.
 *
 * Returns a default WP json response (error/success), possibly with a debug message
 * on error.
 *
 * @since 1.9
 */
function wpv_ct_bind_posts_callback() {
    // Authentication and validation
    if ( ! current_user_can( EDIT_VIEWS ) ) {
        wp_send_json_error( 'Untrusted user' );
    }
    $ct_id = (int) wpv_getpost( 'id' );
    $uid = get_current_user_id();

    $nonce_name = "wpv_ct_{$ct_id}_bind_posts_by_{$uid}";
    if ( ! wp_verify_nonce( wpv_getpost( 'wpnonce' ), $nonce_name ) ) {
        wp_send_json_error( "Security check ($nonce_name)" );
    }

    $ct = WPV_Content_Template::get_instance( $ct_id );
    if( null == $ct ) {
        wp_send_json_error( 'Invalid Content Template' );
    }

    $posts_to_bind = wpv_getpost( 'posts_to_bind' );
    if( !is_array( $posts_to_bind ) ) {
        wp_send_json_error( 'Invalid arguments (' . print_r( $posts_to_bind, true ) . ')' );
    }

    // Post binding
    $updated = $ct->bind_posts( $posts_to_bind );

    if( false === $updated ) {
        wp_send_json_error( 'bind_posts failed' );
    }

    wp_send_json_success( array( 'updated' => $updated ) );
}



/* ************************************************************************* *\
        Miscellaneous
\* ************************************************************************* */


/**
 * wpv_dismiss_pointer_callback
 *
 * Dismiss pointers created with Views, when needed
 *
 * @since 1.7
 *
 * @todo this needs a nonce, the earlier the better
 */
add_action( 'wp_ajax_wpv_dismiss_pointer', 'wpv_dismiss_pointer_callback' );

function wpv_dismiss_pointer_callback() {
	if ( ! isset( $_POST['name'] ) ) {
		echo 'wpv_failure';
		die();
	}
	$pointer = sanitize_key( $_POST['name'] );
	if ( empty( $pointer ) ) {
		echo 'wpv_failure';
		die();
	}
	wpv_dismiss_pointer( $pointer );
	echo 'wpv_success';
	die();
}



/* ************************************************************************* *\
        Helper functions
\* ************************************************************************* */


/**
 * Perform basic authentication check.
 *
 * Check user capability and nonce. Dies with an error message (wp_json_error() by default) if the authentization
 * is not successful.
 *
 * @param string $nonce_name Name of the nonce that should be verified.
 * @param array $args Arguments (
 *     @type string $nonce_parameter Name of the parameter containing nonce value.
 *         Optional, defaults to "wpnonce".
 *     @type string $parameter_source Determines where the function should look for the nonce parameter.
 *         Allowed values are 'get' and 'post'. Optional, defaults to 'post'.
 *     @type string $capability_needed Capability that user has to have in order to pass the check.
 *         Optional, default is EDIT_VIEWS.
 *     @type string $type_of_death How to indicate failure:
 *         - 'die': The error message will be echoed as a simple string. Default behaviour.
 *         - 'false': Do not die, just return false.
 *         - 'message': Call wp_json_error() and pass message as data.
 *         - 'data': Call wp_json_error with array( 'type' => 'capability'|'nonce', 'message' => $error_message )
 *         Optional, default is 'die'.
 *     )
 *
 * @return bool|void
 *
 * @since 1.9
 */
function wpv_ajax_authenticate( $nonce_name, $args = array() )
{
    // Read arguments
    $type_of_death = wpv_getarr( $args, 'type_of_death', 'die', array( 'die', 'false', 'message', 'data' ) );
    $nonce_parameter = wpv_getarr( $args, 'nonce_parameter', 'wpnonce' );
    $capability_needed = wpv_getarr( $args, 'capability_needed', EDIT_VIEWS );
    $parameter_source_name = wpv_getarr( $args, 'parameter_source', 'post', array( 'get', 'post' ) );
    $parameter_source = ( $parameter_source_name == 'get' ) ? $_GET : $_POST;

    $is_error = false;
    $error_message = null;
    $error_type = null;

    // Check permissions
    if ( ! current_user_can( $capability_needed ) ) {
        $error_message = __( 'You do not have permissions for that.', 'wpv-views' );
        $error_type = 'capability';
        $is_error = true;
    }

    // Check nonce
    if ( !$is_error && !wp_verify_nonce( wpv_getarr( $parameter_source, $nonce_parameter, '' ), $nonce_name ) ) {
        $error_message = __( 'Your security credentials have expired. Please reload the page to get new ones.', 'wpv-views' );
        $error_type = 'nonce';
        $is_error = true;
    }

    if( $is_error ) {
        switch( $type_of_death ) {

            case 'message':
                wp_send_json_error( $error_message );
                break;

            case 'data':
                wp_send_json_error( array( 'type' => $error_type, 'message' => $error_message ) );
                break;

            case 'false':
                return false;

            case 'die':
            default:
                die( $error_message );
                break;
        }
    }

    return true;
}
Page Not Found
Parece que el enlace que apuntaba aquí no sirve. ¿Quieres probar con una búsqueda?
¡Hola!