AnsPress_Ajax()

Description #

Register all ajax callback

Source #

File: includes/ajax-hooks.php

class AnsPress_Ajax {
	/**
	 * Initialize the plugin by setting localization and loading public scripts
	 * and styles.
	 */
	public static function init() {
		anspress()->add_action( 'ap_ajax_suggest_similar_questions', __CLASS__, 'suggest_similar_questions' );
		anspress()->add_action( 'ap_ajax_load_tinymce', __CLASS__, 'load_tinymce' );
		anspress()->add_action( 'ap_ajax_load_comments', 'AnsPress_Comment_Hooks', 'load_comments' );
		anspress()->add_action( 'ap_ajax_edit_comment_form', 'AnsPress_Comment_Hooks', 'edit_comment_form' );
		anspress()->add_action( 'ap_ajax_edit_comment', 'AnsPress_Comment_Hooks', 'edit_comment' );
		anspress()->add_action( 'ap_ajax_approve_comment', 'AnsPress_Comment_Hooks', 'approve_comment' );
		anspress()->add_action( 'ap_ajax_vote', 'AnsPress_Vote', 'vote' );

		anspress()->add_action( 'ap_ajax_delete_comment', 'AnsPress\Ajax\Comment_Delete', 'init' );
		anspress()->add_action( 'wp_ajax_comment_modal', 'AnsPress\Ajax\Comment_Modal', 'init' );
		anspress()->add_action( 'wp_ajax_nopriv_comment_modal', 'AnsPress\Ajax\Comment_Modal', 'init' );
		anspress()->add_action( 'wp_ajax_ap_toggle_best_answer', 'AnsPress\Ajax\Toggle_Best_Answer', 'init' );

		// Post actions.
		anspress()->add_action( 'ap_ajax_post_actions', 'AnsPress_Theme', 'post_actions' );
		anspress()->add_action( 'ap_ajax_action_toggle_featured', __CLASS__, 'toggle_featured' );
		anspress()->add_action( 'ap_ajax_action_close', __CLASS__, 'close_question' );
		anspress()->add_action( 'ap_ajax_action_toggle_delete_post', __CLASS__, 'toggle_delete_post' );
		anspress()->add_action( 'ap_ajax_action_delete_permanently', __CLASS__, 'permanent_delete_post' );
		anspress()->add_action( 'ap_ajax_action_status', 'AnsPress_Post_Status', 'change_post_status' );
		anspress()->add_action( 'ap_ajax_action_convert_to_post', __CLASS__, 'convert_to_post' );

		// Flag ajax callbacks.
		anspress()->add_action( 'ap_ajax_action_flag', 'AnsPress_Flag', 'action_flag' );

		// Uploader hooks.
		anspress()->add_action( 'ap_ajax_delete_attachment', 'AnsPress_Uploader', 'delete_attachment' );

		// List filtering.
		anspress()->add_action( 'ap_ajax_load_filter_order_by', __CLASS__, 'load_filter_order_by' );

		// Subscribe.
		anspress()->add_action( 'ap_ajax_subscribe', __CLASS__, 'subscribe_to_question' );
		anspress()->add_action( 'wp_ajax_ap_repeatable_field', 'AnsPress\Ajax\Repeatable_Field', 'init' );
		anspress()->add_action( 'wp_ajax_nopriv_ap_repeatable_field', 'AnsPress\Ajax\Repeatable_Field', 'init' );

		anspress()->add_action( 'wp_ajax_ap_form_question', 'AP_Form_Hooks', 'submit_question_form', 11, 0 );
		anspress()->add_action( 'wp_ajax_nopriv_ap_form_question', 'AP_Form_Hooks', 'submit_question_form', 11, 0 );
		anspress()->add_action( 'wp_ajax_ap_form_answer', 'AP_Form_Hooks', 'submit_answer_form', 11, 0 );
		anspress()->add_action( 'wp_ajax_nopriv_ap_form_answer', 'AP_Form_Hooks', 'submit_answer_form', 11, 0 );
		anspress()->add_action( 'wp_ajax_ap_form_comment', 'AP_Form_Hooks', 'submit_comment_form', 11, 0 );
		anspress()->add_action( 'wp_ajax_nopriv_ap_form_comment', 'AP_Form_Hooks', 'submit_comment_form', 11, 0 );
		anspress()->add_action( 'wp_ajax_ap_search_tags', __CLASS__, 'search_tags' );
		anspress()->add_action( 'wp_ajax_nopriv_ap_search_tags', __CLASS__, 'search_tags' );
		anspress()->add_action( 'wp_ajax_ap_image_upload', 'AnsPress_Uploader', 'image_upload' );
		anspress()->add_action( 'wp_ajax_ap_upload_modal', 'AnsPress_Uploader', 'upload_modal' );
		anspress()->add_action( 'wp_ajax_nopriv_ap_upload_modal', 'AnsPress_Uploader', 'upload_modal' );
	}

	/**
	 * Show similar questions while asking a question.
	 *
	 * @since 2.0.1
	 */
	public static function suggest_similar_questions() {
		// Die if question suggestion is disabled.
		if ( ap_disable_question_suggestion() ) {
			wp_die( 'false' );
		}

		$keyword = ap_sanitize_unslash( 'value', 'request' );
		if ( empty( $keyword ) || ( ! ap_verify_default_nonce() && ! current_user_can( 'manage_options' ) ) ) {
				wp_die( 'false' );
		}

		$keyword   = ap_sanitize_unslash( 'value', 'request' );
		$is_admin  = (bool) ap_isset_post_value( 'is_admin', false );
		$questions = get_posts(
			array(
				'post_type' => 'question',
				'showposts' => 10,
				's'         => $keyword,
			)
		);

		if ( $questions ) {
				$items = '<div class="ap-similar-questions-head">';
				// translators: %d is count of questions.
				$items .= '<p><strong>' . sprintf( _n( '%d similar question found', '%d similar questions found', count( $questions ), 'anspress-question-answer' ), count( $questions ) ) . '</strong></p>';
				$items .= '<p>' . __( 'We have found some similar questions that have been asked earlier.', 'anspress-question-answer' ) . '</p>';
				$items .= '</div>';

			$items .= '<div class="ap-similar-questions">';

			foreach ( (array) $questions as $p ) {
				$count         = ap_get_answers_count( $p->ID );
				$p->post_title = ap_highlight_words( $p->post_title, $keyword );

				if ( $is_admin ) {
					$items .= '<div class="ap-q-suggestion-item clearfix"><a class="select-question-button button button-primary button-small" href="' . add_query_arg(
						array(
							'post_type'   => 'answer',
							'post_parent' => $p->ID,
						),
						admin_url( 'post-new.php' )
					) . '">' . __( 'Select', 'anspress-question-answer' ) . '</a><span class="question-title">' .
					// translators: %d is total answer count.
					$p->post_title . '</span><span class="acount">' . sprintf( _n( '%d Answer', '%d Answers', $count, 'anspress-question-answer' ), $count ) . '</span></div>';
				} else {
					// translators: %d is total answer count.
					$items .= '<a class="ap-sqitem clearfix" target="_blank" href="' . get_permalink( $p->ID ) . '"><span class="acount">' . sprintf( _n( '%d Answer', '%d Answers', $count, 'anspress-question-answer' ), $count ) . '</span><span class="ap-title">' . $p->post_title . '</span></a>';
				}
			}

			$items .= '</div>';
			$result = array(
				'status' => true,
				'html'   => $items,
			);
		} else {
			$result = array(
				'status'  => false,
				'message' => __( 'No related questions found.', 'anspress-question-answer' ),
			);
		}

		ap_ajax_json( $result );
	}

	/**
	 * Process ajax trash posts callback.
	 */
	public static function toggle_delete_post() {
		$post_id = (int) ap_sanitize_unslash( 'post_id', 'request' );

		$failed_response = array(
			'success'  => false,
			'snackbar' => array( 'message' => __( 'Unable to trash this post', 'anspress-question-answer' ) ),
		);

		if ( ! anspress_verify_nonce( 'trash_post_' . $post_id ) ) {
			ap_ajax_json( $failed_response );
		}

		$post = ap_get_post( $post_id );

		$post_type = 'question' === $post->post_type ? __( 'Question', 'anspress-question-answer' ) : __( 'Answer', 'anspress-question-answer' );

		if ( 'trash' === $post->post_status ) {
			if ( ! ap_user_can_restore( $post ) ) {
				ap_ajax_json( $failed_response );
			}

			wp_untrash_post( $post->ID );

			ap_ajax_json(
				array(
					'success'     => true,
					'action'      => array(
						'active' => false,
						'label'  => __( 'Delete', 'anspress-question-answer' ),
						'title'  => sprintf(
							/* Translators: %s Question or Answer post type label for deleting a question or answer. */
							__( 'Delete this %s (can be restored again)', 'anspress-question-answer' ),
							( 'question' === $post->post_type ) ? esc_html__( 'question', 'anspress-question-answer' ) : esc_html__( 'answer', 'anspress-question-answer' )
						),
					),
					// translators: %s post type.
					'snackbar'    => array( 'message' => sprintf( __( '%s is restored', 'anspress-question-answer' ), $post_type ) ),
					'newStatus'   => 'publish',
					'postmessage' => ap_get_post_status_message( $post_id ),
				)
			);
		}

		if ( ! ap_user_can_delete_post( $post_id ) ) {
			ap_ajax_json( $failed_response );
		}

		// Delete lock feature.
		// Do not allow post to be trashed if defined time elapsed.
		if ( ( time() > ( get_the_time( 'U', $post->ID ) + (int) ap_opt( 'disable_delete_after' ) ) ) && ! is_super_admin() ) {
			ap_ajax_json(
				array(
					'success'  => false,
					// translators: %s is human time difference.
					'snackbar' => array( 'message' => sprintf( __( 'This post was created %s, hence you cannot trash it', 'anspress-question-answer' ), ap_human_time( get_the_time( 'U', $post->ID ) ) ) ),
				)
			);
		}

		wp_trash_post( $post_id );

		ap_ajax_json(
			array(
				'success'     => true,
				'action'      => array(
					'active' => true,
					'label'  => __( 'Undelete', 'anspress-question-answer' ),
					'title'  => sprintf(
						/* Translators: %s Question or Answer post type label for restoring a question or answer. */
						__( 'Restore this %s', 'anspress-question-answer' ),
						( 'question' === $post->post_type ) ? esc_html__( 'question', 'anspress-question-answer' ) : esc_html__( 'answer', 'anspress-question-answer' )
					),
				),
				// translators: %s is post type.
				'snackbar'    => array( 'message' => sprintf( __( '%s is trashed', 'anspress-question-answer' ), $post_type ) ),
				'newStatus'   => 'trash',
				'postmessage' => ap_get_post_status_message( $post_id ),
			)
		);
	}

	/**
	 * Handle Ajax callback for permanent delete of post.
	 */
	public static function permanent_delete_post() {
		$post_id = (int) ap_sanitize_unslash( 'post_id', 'request' );

		if ( ! anspress_verify_nonce( 'delete_post_' . $post_id ) || ! ap_user_can_permanent_delete( $post_id ) ) {
			ap_ajax_json(
				array(
					'success'  => false,
					'snackbar' => array( 'message' => __( 'Sorry, unable to delete post', 'anspress-question-answer' ) ),
				)
			);
		}

		$post = ap_get_post( $post_id );

		if ( 'question' === $post->post_type ) {
			/**
			 * Triggered right before deleting question.
			 *
			 * @param  integer $post_id question ID.
			 */
			do_action( 'ap_wp_trash_question', $post_id );
		} else {
			/**
			 * Triggered right before deleting answer.
			 *
			 * @param  integer $post_id answer ID.
			 */
			do_action( 'ap_wp_trash_answer', $post_id );
		}

		wp_delete_post( $post_id, true );

		if ( 'question' === $post->post_type ) {
			ap_ajax_json(
				array(
					'success'  => true,
					'redirect' => ap_base_page_link(),
					'snackbar' => array( 'message' => __( 'Question is deleted permanently', 'anspress-question-answer' ) ),
				)
			);
		}

		$current_ans = ap_count_published_answers( $post->post_parent );

		// translators: %d is answers count.
		$count_label = sprintf( _n( '%d Answer', '%d Answers', $current_ans, 'anspress-question-answer' ), $current_ans );

		ap_ajax_json(
			array(
				'success'      => true,
				'snackbar'     => array( 'message' => __( 'Answer is deleted permanently', 'anspress-question-answer' ) ),
				'deletePost'   => $post_id,
				'answersCount' => array(
					'text'   => $count_label,
					'number' => $current_ans,
				),
			)
		);
	}

	/**
	 * Handle toggle featured question ajax callback
	 *
	 * @since unknown
	 * @since 4.1.2 Insert to activity table when question is featured.
	 */
	public static function toggle_featured() {
		$post_id = (int) ap_sanitize_unslash( 'post_id', 'request' );

		if ( ! ap_user_can_toggle_featured() || ! anspress_verify_nonce( 'set_featured_' . $post_id ) ) {
			ap_ajax_json(
				array(
					'success'  => false,
					'snackbar' => array( 'message' => __( 'Sorry, you cannot toggle a featured question', 'anspress-question-answer' ) ),
				)
			);
		}

		$post = ap_get_post( $post_id );

		// Do nothing if post type is not question.
		if ( 'question' !== $post->post_type ) {
			ap_ajax_json(
				array(
					'success'  => false,
					'snackbar' => array( 'message' => __( 'Only question can be set as featured', 'anspress-question-answer' ) ),
				)
			);
		}

		// Check if current question ID is in featured question array.
		if ( ap_is_featured_question( $post ) ) {
			ap_unset_featured_question( $post->ID );

			// Update activity.
			ap_activity_add(
				array(
					'q_id'   => $post->ID,
					'action' => 'unfeatured',
				)
			);

			ap_ajax_json(
				array(
					'success'  => true,
					'action'   => array(
						'active' => false,
						'title'  => __( 'Mark this question as featured', 'anspress-question-answer' ),
						'label'  => __( 'Feature', 'anspress-question-answer' ),
					),
					'snackbar' => array( 'message' => __( 'Question is unmarked as featured.', 'anspress-question-answer' ) ),
				)
			);
		}

		ap_set_featured_question( $post->ID );

		// Update activity.
		ap_activity_add(
			array(
				'q_id'   => $post->ID,
				'action' => 'featured',
			)
		);

		ap_ajax_json(
			array(
				'success'  => true,
				'action'   => array(
					'active' => true,
					'title'  => __( 'Unmark this question as featured', 'anspress-question-answer' ),
					'label'  => __( 'Unfeature', 'anspress-question-answer' ),
				),
				'snackbar' => array( 'message' => __( 'Question is marked as featured.', 'anspress-question-answer' ) ),
			)
		);
	}

	/**
	 * Close question callback.
	 *
	 * @since unknown
	 * @since 4.1.2 Add activity when question is closed.
	 */
	public static function close_question() {
		$post_id = ap_sanitize_unslash( 'post_id', 'p' );

		// Check permission and nonce.
		if ( ! is_user_logged_in() || ! check_ajax_referer( 'close_' . $post_id, 'nonce', false ) || ! ap_user_can_close_question() ) {
			ap_ajax_json(
				array(
					'success'  => false,
					'snackbar' => array( 'message' => __( 'You cannot close a question', 'anspress-question-answer' ) ),
				)
			);
		}

		$_post       = ap_get_post( $post_id );
		$toggle      = ap_toggle_close_question( $post_id );
		$close_label = $_post->closed ? __( 'Close', 'anspress-question-answer' ) : __( 'Open', 'anspress-question-answer' );
		$close_title = $_post->closed ? __( 'Close this question for new answer.', 'anspress-question-answer' ) : __( 'Open this question for new answers', 'anspress-question-answer' );

		$message = 1 === $toggle ? __( 'Question closed', 'anspress-question-answer' ) : __( 'Question is opened', 'anspress-question-answer' );

		// Log in activity table.
		if ( 1 === $toggle ) {
			ap_activity_add(
				array(
					'q_id'   => $_post->ID,
					'action' => 'closed_q',
				)
			);
		} else {
			ap_activity_add(
				array(
					'q_id'   => $_post->ID,
					'action' => 'open_q',
				)
			);
		}

		$results = array(
			'success'     => true,
			'action'      => array(
				'label' => $close_label,
				'title' => $close_title,
			),
			'snackbar'    => array( 'message' => $message ),
			'postmessage' => ap_get_post_status_message( $post_id ),
		);

		ap_ajax_json( $results );
	}

	/**
	 * Send JSON response and terminate.
	 *
	 * @param array|string $result Ajax response.
	 */
	public static function send( $result ) {
		ap_send_json( ap_ajax_responce( $result ) );
	}

	/**
	 * Load tinyMCE assets using ajax.
	 *
	 * @since 3.0.0
	 */
	public static function load_tinymce() {
		ap_answer_form( ap_sanitize_unslash( 'question_id', 'r' ) );
		ap_ajax_tinymce_assets();

		wp_die();
	}

	/**
	 * Ajax callback for converting a question into a post.
	 *
	 * @since 3.0.0
	 */
	public static function convert_to_post() {
		$post_id = ap_sanitize_unslash( 'post_id', 'r' );

		if ( ! anspress_verify_nonce( 'convert-post-' . $post_id ) || ! ( is_super_admin() || current_user_can( 'manage_options' ) ) ) {
			ap_ajax_json(
				array(
					'success'  => false,
					'snackbar' => array( 'message' => __( 'Sorry, you are not allowed to convert this question to post', 'anspress-question-answer' ) ),
				)
			);
		}

		$row = set_post_type( $post_id, 'post' );

		// After success trash all answers.
		if ( $row ) {
			global $wpdb;

			// Get IDs of all answer.
			$answer_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d and post_type = 'answer' ", (int) $post_id ) ); // phpcs:ignore WordPress.DB

			foreach ( (array) $answer_ids as $id ) {
				wp_delete_post( $id );
			}

			ap_ajax_json(
				array(
					'success'  => true,
					// translators: %s is post title.
					'snackbar' => array( 'message' => sprintf( __( ' Question &ldquo;%s&rdquo; is converted to post and its answers are trashed', 'anspress-question-answer' ), esc_html( get_the_title( $post_id ) ) ) ),
					'redirect' => get_the_permalink( $post_id ),
				)
			);
		}
	}

	/**
	 * Ajax callback for loading order by filter.
	 *
	 * @since 4.0.0
	 */
	public static function load_filter_order_by() {
		$filter = ap_sanitize_unslash( 'filter', 'r' );
		check_ajax_referer( 'filter_' . $filter, '__nonce' );

		ap_ajax_json(
			array(
				'success'  => true,
				'multiple' => false,
				'items'    => ap_get_questions_orderby(),
			)
		);
	}

	/**
	 * Subscribe user to a question.
	 *
	 * @return void
	 * @since unknown
	 */
	public static function subscribe_to_question() {
		$post_id = (int) ap_sanitize_unslash( 'id', 'r' );

		if ( ! is_user_logged_in() ) {
			ap_ajax_json(
				array(
					'success'  => false,
					'snackbar' => array( 'message' => __( 'You must be logged in to subscribe to a question', 'anspress-question-answer' ) ),
				)
			);
		}

		$_post = ap_get_post( $post_id );

		if ( 'question' === $_post->post_type && ! anspress_verify_nonce( 'subscribe_' . $post_id ) ) {
			ap_ajax_json(
				array(
					'success'  => false,
					'snackbar' => array( 'message' => __( 'Sorry, unable to subscribe', 'anspress-question-answer' ) ),
				)
			);
		}

		// Check if already subscribed, toggle if subscribed.
		$exists = ap_get_subscriber( false, 'question', $post_id );

		if ( $exists ) {
			ap_delete_subscriber( $post_id, get_current_user_id(), 'question' );
			ap_ajax_json(
				array(
					'success'  => true,
					'snackbar' => array(
						'message' => sprintf(
							/* Translators: %s Unsubscribed to the question title */
							__( 'Successfully unsubscribed from question: %s', 'anspress-question-answer' ),
							$_post->post_title
						),
					),
					'count'    => ap_get_post_field( 'subscribers', $post_id ),
					'label'    => __( 'Subscribe', 'anspress-question-answer' ),
				)
			);
		}

		// Insert subscriber.
		$insert = ap_new_subscriber( false, 'question', $post_id );

		if ( false === $insert ) {
			ap_ajax_json(
				array(
					'success'  => false,
					'snackbar' => array( 'message' => __( 'Sorry, unable to subscribe', 'anspress-question-answer' ) ),
				)
			);
		}

		ap_ajax_json(
			array(
				'success'  => true,
				'snackbar' => array(
					'message' => sprintf(
						/* Translators: %s Subscribed to the question title */
						__( 'Successfully subscribed to question: %s', 'anspress-question-answer' ),
						$_post->post_title
					),
				),
				'count'    => ap_get_post_field( 'subscribers', $post_id ),
				'label'    => __( 'Unsubscribe', 'anspress-question-answer' ),
			)
		);
	}

	/**
	 * Ajax callback for `ap_search_tags`. This was called by tags field
	 * for fetching tags suggestions.
	 *
	 * @return void
	 * @since 4.1.5
	 */
	public static function search_tags() {
		$q          = ap_sanitize_unslash( 'q', 'r' );
		$form       = ap_sanitize_unslash( 'form', 'r' );
		$field_name = ap_sanitize_unslash( 'field', 'r' );

		if ( ! anspress_verify_nonce( 'tags_' . $form . $field_name ) ) {
			wp_send_json( '{}' );
		}

		// Die if not valid form.
		if ( ! anspress()->form_exists( $form ) ) {
			ap_ajax_json( 'something_wrong' );
		}

		$field = anspress()->get_form( $form )->find( $field_name );

		// Check if field exists and type is tags.
		if ( ! is_a( $field, 'AnsPress\Form\Field\Tags' ) ) {
			ap_ajax_json( 'something_wrong' );
		}

		$taxo = $field->get( 'terms_args.taxonomy' );
		$taxo = ! empty( $taxo ) ? $taxo : 'tag';

		$terms = get_terms(
			array(
				'taxonomy'   => $taxo,
				'search'     => $q,
				'count'      => true,
				'number'     => 20,
				'hide_empty' => false,
				'orderby'    => 'count',
			)
		);

		$format = array();

		if ( $terms ) {
			foreach ( $terms as $t ) {
				$format[] = array(
					'term_id'     => $t->term_id,
					'name'        => $t->name,
					'description' => $t->description,
					// translators: %d is question count.
					'count'       => sprintf( _n( '%d Question', '%d Questions', $t->count, 'anspress-question-answer' ), $t->count ),
				);
			}
		}

		wp_send_json( $format );
	}
}

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Add your comment