No Description

Ajax.php 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <?php
  2. /*************************************************
  3. * Copyright (c) 2020, Code Atlantic LLC
  4. *************************************************/
  5. if ( ! defined( 'ABSPATH' ) ) {
  6. exit;
  7. }
  8. /**
  9. * Handles some of our AJAX requests including post/taxonomy search from conditions
  10. */
  11. class PUM_Admin_Ajax {
  12. /**
  13. * Hooks our methods into AJAX actions.
  14. * Hooks our methods into AJAX actions.
  15. */
  16. public static function init() {
  17. add_action( 'wp_ajax_pum_object_search', array( __CLASS__, 'object_search' ) );
  18. add_action( 'wp_ajax_pum_process_batch_request', array( __CLASS__, 'process_batch_request' ) );
  19. add_action( 'wp_ajax_pum_save_enabled_state', array( __CLASS__, 'save_popup_enabled_state' ) );
  20. }
  21. /**
  22. * Sets the enabled meta field to on or off
  23. *
  24. * @since 1.12.0
  25. */
  26. public static function save_popup_enabled_state() {
  27. $args = wp_parse_args(
  28. $_REQUEST,
  29. array(
  30. 'popupID' => 0,
  31. 'active' => 1,
  32. )
  33. );
  34. // Ensures Popup ID is an int and not 0.
  35. $popup_id = intval( $args['popupID'] );
  36. if ( 0 === $popup_id ) {
  37. wp_send_json_error( 'Invalid popup ID provided.' );
  38. }
  39. // Ensures active state is 0 or 1.
  40. $enabled = intval( $args['enabled'] );
  41. if ( ! in_array( $enabled, array( 0, 1 ), true ) ) {
  42. wp_send_json_error( 'Invalid enabled state provided.' );
  43. }
  44. // Verify the nonce.
  45. if ( ! wp_verify_nonce( $_REQUEST['nonce'], "pum_save_enabled_state_$popup_id" ) ) {
  46. wp_send_json_error();
  47. }
  48. // Get our popup and previous value.
  49. $popup = pum_get_popup( $popup_id );
  50. $previous = $popup->get_meta( 'enabled' );
  51. // If value is the same, bail now.
  52. if ( $previous === $enabled ) {
  53. wp_send_json_success();
  54. }
  55. // Update our value.
  56. $results = $popup->update_meta( 'enabled', $enabled );
  57. if ( false === $results ) {
  58. wp_send_json_error( 'Error updating enabled state.' );
  59. PUM_Utils_Logging::instance()->log( "Error updating enabled state on $popup_id. Previous value: $previous. New value: $enabled" );
  60. } else {
  61. wp_send_json_success();
  62. }
  63. }
  64. /**
  65. * Searches posts, taxonomies, and users
  66. *
  67. * Uses passed array with keys of object_type, object_key, include, exclude. Echos our results as JSON.
  68. */
  69. public static function object_search() {
  70. $results = array(
  71. 'items' => array(),
  72. 'total_count' => 0,
  73. );
  74. $object_type = sanitize_text_field( $_REQUEST['object_type'] );
  75. $include = ! empty( $_REQUEST['include'] ) ? wp_parse_id_list( $_REQUEST['include'] ) : [];
  76. $exclude = ! empty( $_REQUEST['exclude'] ) ? wp_parse_id_list( $_REQUEST['exclude'] ) : [];
  77. if ( ! empty( $include ) ) {
  78. $exclude = array_merge( $include, $exclude );
  79. }
  80. switch ( $object_type ) {
  81. case 'post_type':
  82. $post_type = ! empty( $_REQUEST['object_key'] ) ? sanitize_text_field( $_REQUEST['object_key'] ) : 'post';
  83. if ( ! empty( $include ) ) {
  84. $include_query = PUM_Helpers::post_type_selectlist_query(
  85. $post_type,
  86. array(
  87. 'post__in' => $include,
  88. 'posts_per_page' => - 1,
  89. ),
  90. true
  91. );
  92. foreach ( $include_query['items'] as $id => $name ) {
  93. $results['items'][] = array(
  94. 'id' => $id,
  95. 'text' => "$name (ID: $id)",
  96. );
  97. }
  98. $results['total_count'] += (int) $include_query['total_count'];
  99. }
  100. $query = PUM_Helpers::post_type_selectlist_query(
  101. $post_type,
  102. array(
  103. 's' => ! empty( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : null,
  104. 'paged' => ! empty( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : null,
  105. 'post__not_in' => $exclude,
  106. 'posts_per_page' => 10,
  107. ),
  108. true
  109. );
  110. foreach ( $query['items'] as $id => $name ) {
  111. $results['items'][] = array(
  112. 'id' => $id,
  113. 'text' => "$name (ID: $id)",
  114. );
  115. }
  116. $results['total_count'] += (int) $query['total_count'];
  117. break;
  118. case 'taxonomy':
  119. $taxonomy = ! empty( $_REQUEST['object_key'] ) ? sanitize_text_field( $_REQUEST['object_key'] ) : 'category';
  120. if ( ! empty( $include ) ) {
  121. $include_query = PUM_Helpers::taxonomy_selectlist_query(
  122. $taxonomy,
  123. array(
  124. 'include' => $include,
  125. 'number' => 0,
  126. ),
  127. true
  128. );
  129. foreach ( $include_query['items'] as $id => $name ) {
  130. $results['items'][] = array(
  131. 'id' => $id,
  132. 'text' => "$name (ID: $id)",
  133. );
  134. }
  135. $results['total_count'] += (int) $include_query['total_count'];
  136. }
  137. $query = PUM_Helpers::taxonomy_selectlist_query(
  138. $taxonomy,
  139. array(
  140. 'search' => ! empty( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : null,
  141. 'paged' => ! empty( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : null,
  142. 'exclude' => $exclude,
  143. 'number' => 10,
  144. ),
  145. true
  146. );
  147. foreach ( $query['items'] as $id => $name ) {
  148. $results['items'][] = array(
  149. 'id' => $id,
  150. 'text' => "$name (ID: $id)",
  151. );
  152. }
  153. $results['total_count'] += (int) $query['total_count'];
  154. break;
  155. case 'user':
  156. $user_role = ! empty( $_REQUEST['object_key'] ) ? $_REQUEST['object_key'] : null;
  157. if ( ! empty( $include ) ) {
  158. $include_query = PUM_Helpers::user_selectlist_query(
  159. array(
  160. 'role' => $user_role,
  161. 'include' => $include,
  162. 'number' => - 1,
  163. ),
  164. true
  165. );
  166. foreach ( $include_query['items'] as $id => $name ) {
  167. $results['items'][] = array(
  168. 'id' => $id,
  169. 'text' => "$name (ID: $id)",
  170. );
  171. }
  172. $results['total_count'] += (int) $include_query['total_count'];
  173. }
  174. $query = PUM_Helpers::user_selectlist_query(
  175. array(
  176. 'role' => $user_role,
  177. 'search' => ! empty( $_REQUEST['s'] ) ? '*' . $_REQUEST['s'] . '*' : null,
  178. 'paged' => ! empty( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : null,
  179. 'exclude' => $exclude,
  180. 'number' => 10,
  181. ),
  182. true
  183. );
  184. foreach ( $query['items'] as $id => $name ) {
  185. $results['items'][] = array(
  186. 'id' => $id,
  187. 'text' => "$name (ID: $id)",
  188. );
  189. }
  190. $results['total_count'] += (int) $query['total_count'];
  191. break;
  192. }
  193. // Take out keys which were only used to deduplicate.
  194. $results['items'] = array_values( $results['items'] );
  195. echo PUM_Utils_Array::safe_json_encode( $results );
  196. die();
  197. }
  198. /**
  199. * Handles Ajax for processing a single batch request.
  200. */
  201. public static function process_batch_request() {
  202. // Batch ID.
  203. $batch_id = isset( $_REQUEST['batch_id'] ) ? sanitize_key( $_REQUEST['batch_id'] ) : false;
  204. if ( ! $batch_id ) {
  205. wp_send_json_error(
  206. array(
  207. 'error' => __( 'A batch process ID must be present to continue.', 'popup-maker' ),
  208. )
  209. );
  210. }
  211. // Nonce.
  212. if ( ! isset( $_REQUEST['nonce'] ) || ( isset( $_REQUEST['nonce'] ) && false === wp_verify_nonce( $_REQUEST['nonce'], "{$batch_id}_step_nonce" ) ) ) {
  213. wp_send_json_error(
  214. array(
  215. 'error' => __( 'You do not have permission to initiate this request. Contact an administrator for more information.', 'popup-maker' ),
  216. )
  217. );
  218. }
  219. // Attempt to retrieve the batch attributes from memory.
  220. $batch = PUM_Batch_Process_Registry::instance()->get( $batch_id );
  221. if ( false === $batch ) {
  222. wp_send_json_error(
  223. array(
  224. 'error' => sprintf( __( '%s is an invalid batch process ID.', 'popup-maker' ), esc_html( $_REQUEST['batch_id'] ) ),
  225. )
  226. );
  227. }
  228. $class = isset( $batch['class'] ) ? sanitize_text_field( $batch['class'] ) : '';
  229. $class_file = isset( $batch['file'] ) ? $batch['file'] : '';
  230. if ( empty( $class_file ) || ! file_exists( $class_file ) ) {
  231. wp_send_json_error(
  232. array(
  233. 'error' => sprintf( __( 'An invalid file path is registered for the %1$s batch process handler.', 'popup-maker' ), "<code>{$batch_id}</code>" ),
  234. )
  235. );
  236. } else {
  237. require_once $class_file;
  238. }
  239. if ( empty( $class ) || ! class_exists( $class ) ) {
  240. wp_send_json_error(
  241. array(
  242. 'error' => sprintf( __( '%1$s is an invalid handler for the %2$s batch process. Please try again.', 'popup-maker' ), "<code>{$class}</code>", "<code>{$batch_id}</code>" ),
  243. )
  244. );
  245. }
  246. $step = sanitize_text_field( $_REQUEST['step'] );
  247. /**
  248. * Instantiate the batch class.
  249. *
  250. * @var PUM_Interface_Batch_Exporter|PUM_Interface_Batch_Process|PUM_Interface_Batch_PrefetchProcess $process
  251. */
  252. if ( isset( $_REQUEST['data']['upload']['file'] ) ) {
  253. // If this is an import, instantiate with the file and step.
  254. $file = sanitize_text_field( $_REQUEST['data']['upload']['file'] );
  255. $process = new $class( $file, $step );
  256. } else {
  257. // Otherwise just the step.
  258. $process = new $class( $step );
  259. }
  260. // Garbage collect any old temporary data.
  261. // TODO Should this be here? Likely here to prevent case ajax passes step 1 without resetting process counts?
  262. if ( $step < 2 ) {
  263. $process->finish();
  264. }
  265. $using_prefetch = ( $process instanceof PUM_Interface_Batch_PrefetchProcess );
  266. // Handle pre-fetching data.
  267. if ( $using_prefetch ) {
  268. // Initialize any data needed to process a step.
  269. $data = isset( $_REQUEST['form'] ) ? $_REQUEST['form'] : array();
  270. $process->init( $data );
  271. $process->pre_fetch();
  272. }
  273. /** @var int|string|WP_Error $step */
  274. $step = $process->process_step();
  275. if ( is_wp_error( $step ) ) {
  276. wp_send_json_error( $step );
  277. } else {
  278. $response_data = array( 'step' => $step );
  279. // Map fields if this is an import.
  280. if ( isset( $process->field_mapping ) && ( $process instanceof PUM_Interface_CSV_Importer ) ) {
  281. $response_data['columns'] = $process->get_columns();
  282. $response_data['mapping'] = $process->field_mapping;
  283. }
  284. // Finish and set the status flag if done.
  285. if ( 'done' === $step ) {
  286. $response_data['done'] = true;
  287. $response_data['message'] = $process->get_message( 'done' );
  288. // If this is an export class and not an empty export, send the download URL.
  289. if ( method_exists( $process, 'can_export' ) ) {
  290. if ( ! $process->is_empty ) {
  291. $response_data['url'] = pum_admin_url(
  292. 'tools',
  293. array(
  294. 'step' => $step,
  295. 'nonce' => wp_create_nonce( 'pum-batch-export' ),
  296. 'batch_id' => $batch_id,
  297. 'pum_action' => 'download_batch_export',
  298. )
  299. );
  300. }
  301. }
  302. // Once all calculations have finished, run cleanup.
  303. $process->finish();
  304. } else {
  305. $response_data['done'] = false;
  306. $response_data['percentage'] = $process->get_percentage_complete();
  307. }
  308. wp_send_json_success( $response_data );
  309. }
  310. }
  311. }