HEX
Server: Apache
System: Linux web5.visualcom.es 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: ensaco.es (10019)
PHP: 8.3.32
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/ensaco.es/httpdocs/wp-content/plugins/awesome-support/includes/functions-post.php
<?php

/**
 * Delete ticket count cache on ticket update
 *
 * @since  3.0.0
 * @param  null 
 */
function wpas_clean_ticketcount_cache() {
	
	set_site_transient( 'wpas_tickets_counts', null, 24 * HOUR_IN_SECONDS );
}
/**
 * Open a new ticket.
 *
 * @since  3.0.0
 * @param  array $data Ticket data
 * @return boolean
 */
function wpas_open_ticket( $data ) {
		
	$title   			= isset( $data['title'] ) ? wp_strip_all_tags( $data['title'] ) : false;
	$content 			= isset( $data['message'] ) ? wp_kses( $data['message'], wp_kses_allowed_html( 'post' ) ) : false;
	$bypass_pre_checks  = isset( $data['bypass_pre_checks'] ) ? boolval(wp_kses( $data['bypass_pre_checks'], false ) ) : false;  // Bypass pre-submission filter checks?

	/**
	 * Prepare vars
	 */
	$submit = isset( $_POST['_wp_http_referer'] ) ? wpas_get_submission_page_url( url_to_postid( sanitize_text_field( wp_unslash( $_POST['_wp_http_referer'] ) ) ) ) : wpas_get_submission_page_url();
	// Fallback in case the referrer failed
	if ( empty( $submit ) ) {
		$submission_pages = wpas_get_option( 'ticket_submit' );
		
		if ( ! is_array( $submission_pages ) ) {
			$submission_pages = (array) $submission_pages;
		}
		
		if( $submission_pages && count( $submission_pages ) > 0 ) {
			$submit           = $submission_pages[0];
			$submit           = wp_sanitize_redirect( get_permalink( $submit ) );
		}
	}

	// Verify user capability
	if ( ! current_user_can( 'create_ticket' ) ) {

		// Save the input
		wpas_save_values();

		// Redirect to submit page
		wpas_add_error( 'cannot_open_ticket', __( 'You do not have the capacity to open a new ticket.', 'awesome-support' ) );
		wp_redirect( $submit );

		// Break
		exit;
	}

	// Make sure we have at least a title and a message
	if ( false === $title || empty( $title ) ) {

		// Save the input
		wpas_save_values();

		// Redirect to submit page
		wpas_add_error( 'missing_title', __( 'It is mandatory to provide a title for your issue.', 'awesome-support' ) );
		wp_redirect( $submit );

		// Break
		exit;
	}

	if ( true === ( $description_mandatory = apply_filters( 'wpas_ticket_submission_description_mandatory', true ) ) && ( false === $content || empty( $content ) ) ) {

		// Save the input
		wpas_save_values();

		// Redirect to submit page
		wpas_add_error( 'missing_description', __( 'It is mandatory to provide a description for your issue.', 'awesome-support' ) );
		wp_redirect( $submit );

		// Break
		exit;

	}

	/**
	 * Allow the submission.
	 *
	 * This variable is used to add additional checks in the submission process.
	 * If the $go var is set to true, it gives a green light to this method
	 * and the ticket will be submitted. If the var is set to false, the process
	 * will be aborted.
	 *
	 * @since  3.0.0
	 */
	$go = true ;
	if ( ! $bypass_pre_checks ) {
		$go = apply_filters( 'wpas_before_submit_new_ticket_checks', true );
	}

	/* Check for the green light */
	if ( is_wp_error( $go ) ) {

		/* Retrieve error message. */
		$message = $go->get_error_message();

		/* Save the input */
		wpas_save_values();

		/* Redirect to submit page */
		wpas_add_error( 'validation_issue', $message );
		wp_redirect( $submit );

		exit;

	}

	/**
	 * Gather current user info
	 */
	if ( is_user_logged_in() ) {

		global $current_user;

		$user_id = $current_user->ID;

	} else {

		// Save the input
		wpas_save_values();

		// Redirect to submit page
		wpas_add_error( 'unknown_user', __( 'Only registered accounts can submit a ticket. Please register first.', 'awesome-support' ) );
		wp_redirect( $submit );

		exit;

	}

	/**
	 * Submit the ticket.
	 *
	 * Now that all the verifications are passed
	 * we can proceed to the actual ticket submission.
	 */
	$post = apply_filters(
		'wpas_open_ticket_data', array(
			'post_content'   => $content,
			'post_name'      => $title,
			'post_title'     => $title,
			'post_status'    => 'queued',
			'post_type'      => 'ticket',
			'post_author'    => $user_id,
			'ping_status'    => 'closed',
			'comment_status' => 'closed',
		)
	);

	return wpas_insert_ticket( $post, false, false, 'standard-ticket-form' );

}

add_action( 'wpas_do_submit_new_ticket', 'wpas_new_ticket_submission' );
/**
 * Instantiate a new ticket submission
 *
 * This helper function is used to trigger the creation of a new ticket
 * after the ticket submission form is posted on the front-end.
 *
 * @since 3.3
 *
 * @param array $data Ticket data required to open a new ticket
 *
 * @return void
 */
function wpas_new_ticket_submission( $data ) {

	if ( ! is_admin() && isset( $data['wpas_title'] ) ) {

		// Verify the nonce first
		if ( ! isset( $data['wpas_nonce'] ) || ! wp_verify_nonce( $data['wpas_nonce'], 'new_ticket' ) ) {

			/* Save the input */
			wpas_save_values();

			// Redirect to submit page
			wpas_add_error( 'nonce_verification_failed', __( 'The authenticity of your submission could not be validated. If this ticket is legitimate please try submitting again.', 'awesome-support' ) );
			wp_redirect( wp_sanitize_redirect( home_url( isset( $_POST['_wp_http_referer']) ? sanitize_text_field( wp_unslash( $_POST['_wp_http_referer'] ) ) : "" ) ) );
			exit;
		}

		$ticket_id = wpas_open_ticket(
			array(
				'title'   => $data['wpas_title'],
				'message' => $data['wpas_message'],
			)
		);

		/* Submission failure */
		if ( false === $ticket_id ) {

			/* Save the input */
			wpas_save_values();

			/**
			 * Redirect to the referrer since ticket creation failed....
			 */
			wpas_add_error( 'submission_error', __( 'The ticket couldn\'t be submitted for an unknown reason.', 'awesome-support' ) );
			wp_redirect( wp_sanitize_redirect( home_url( $data['_wp_http_referer'] ) ) );
			exit;

		} /* Submission succeeded */
		else {

			/**
			 * Empty the temporary sessions
			 */
			WPAS()->session->clean( 'submission_form' );

			/**
			 * Redirect to the newly created ticket
			 */
			if ( ! empty( wpas_get_option( 'new_ticket_redirect_fe', '' ) ) ) {
				wpas_redirect( 'ticket_added', wpas_get_option( 'new_ticket_redirect_fe', '' ), $ticket_id );
			} else {
				wpas_redirect( 'ticket_added', get_permalink( $ticket_id ), $ticket_id );
			}

			exit;

		}
	}

}

/**
 * Insert a new ticket in the database
 *
 * This function is a wrapper function for wp_insert_post
 * with additional checks specific to the ticketing system
 *
 * @param array    $data            Ticket (post) data
 * @param bool|int $post_id         Post ID for an update
 * @param bool|int $agent_id        ID of the agent to assign ticket to
 * @param string   $channel_term    Source of the ticket
 *
 * @return bool|int|WP_Error
 */
function wpas_insert_ticket( $data = array(), $post_id = false, $agent_id = false, $channel_term = 'other' ) {
	
	// Save the original data array
	$incoming_data = $data;

	// First of all we want to set the ticket author so that we can check if (s)he is allowed to open a ticket or not.
	if ( empty( $data['post_author'] ) ) {
		global $current_user;
		$data['post_author'] = $current_user->ID;
	}

	if ( ! user_can( $data['post_author'], 'create_ticket' ) ) {
		return false;
	}

	$update = false;

	/* If a post ID is passed we make sure the post actually exists before trying to update it. */
	if ( false !== $post_id ) {
		$post = get_post( intval( $post_id ) );

		if ( is_null( $post ) ) {
			return false;
		}

		$update = true;
	}

	$defaults = array(
		'post_content'   => '',
		'post_name'      => '',
		'post_title'     => '',
		'post_status'    => 'queued',
		'post_type'      => 'ticket',
		'post_author'    => '',
		'ping_status'    => 'closed',
		'comment_status' => 'closed',
	);

	/* Add the post ID if this is an update. */
	if ( $update ) {
		$defaults['ID'] = $post_id;
	}

	/* Parse the input data. */
	$data = wp_parse_args( $data, $defaults );

	/* Sanitize the data */
	if ( isset( $data['post_title'] ) && ! empty( $data['post_title'] ) ) {
		$data['post_title'] = wp_strip_all_tags( $data['post_title'] );
	}

	if ( ! empty( $data['post_content'] ) ) {
		$data['post_content'] = strip_shortcodes( $data['post_content'] );
	}

	/**
	* Sanitize the slug
	*/
	if ( isset( $data['post_name'] ) && ! empty( $data['post_name'] ) ) {
		$data['post_name'] = sanitize_text_field( $data['post_name'] );
	}

	/**
	 * Filter the data right before inserting it in the post.
	 *
	 * @var array
	 */
	$data = apply_filters( 'wpas_open_ticket_data', $data, $incoming_data );

	/**
	 * Fire wpas_before_open_ticket just before the post is actually
	 * inserted in the database.
	 */
	do_action( 'wpas_open_ticket_before', $data, $post_id, $incoming_data );

	/**
	 * Insert the post in database using the regular WordPress wp_insert_post
	 * function with default values corresponding to our post type structure.
	 *
	 * @var boolean
	 */
	$ticket_id = wp_insert_post( $data, false );

	if ( false === $ticket_id ) {

		/**
		 * Fire wpas_open_ticket_failed if the ticket couldn't be inserted.
		 */
		do_action( 'wpas_open_ticket_failed', $data, $post_id, $incoming_data );

		return false;

	}

	/**
	* Change the slug to the postid if that's the option the admin set in the TICKETS->SETTINGS->Advanced tab.
	* Note that we only do this if $update is false signifying a new ticket!
	*/
	if ( ! $update ) {
		wpas_set_ticket_slug( $ticket_id );
	}

	/* Update the channel on the ticket so that hooks can access it - but only if the $update is false which means we've got a new ticket */
	/* It will need to be re-added to the ticket at the bottom of this routine because some hooks overwrite it with a blank. */
	if ( ! empty( $channel_term ) && ( ! $update ) ) {
		wpas_set_ticket_channel( $ticket_id, $channel_term, false );
	}

	/* Set the ticket as open. */
	add_post_meta( $ticket_id, '_wpas_status', 'open', true );

	/* Next - update other some meta values. If you add or delete from this list you also */
	/* need to do the same thing in the /includes/admin/functions-post.php file */
	add_post_meta( $ticket_id, '_wpas_last_reply_date', null, true );
	add_post_meta( $ticket_id, '_wpas_last_reply_date_gmt', null, true );
	add_post_meta( $ticket_id, '_wpas_is_waiting_client_reply', ! user_can( $data['post_author'], 'edit_ticket' ), true );

	if ( false === $agent_id ) {
		$agent_id = wpas_find_agent( $ticket_id );
	}

	/**
	 * Fire wpas_open_ticket_before_assigned after the post is successfully submitted but before it has been assigned to an agent.
	 *
	 * @since 3.2.6
	 */
	do_action( 'wpas_open_ticket_before_assigned', $ticket_id, $data, $incoming_data );

	/**
	 * We might want to assign agent manually
	 */
	if( apply_filters( 'wpas_open_ticket_should_agent_assign', true, $ticket_id ) ) {

		/* Assign an agent to the ticket */
		wpas_assign_ticket( $ticket_id, apply_filters( 'wpas_new_ticket_agent_id', $agent_id, $ticket_id, $agent_id ), false );

	}

	/* Update the channel on the ticket - but only if the $update is false which means we've got a new ticket */
	/* Need to update it here again because some of the action hooks fired above will overwrite the term.			  */
	if ( ! empty( $channel_term ) && ( ! $update ) ) {
		wpas_set_ticket_channel( $ticket_id, $channel_term, false );
	}

	/**
	 * Fire wpas_after_open_ticket just after the post is successfully submitted and assigned.
	 */
	do_action( 'wpas_open_ticket_after', $ticket_id, $data );

	do_action( 'wpas_ticket_after_saved', $ticket_id );
	
	//Delete ticket count cache on ticket update
	wpas_clean_ticketcount_cache();

	return $ticket_id;

}

/**
 * Set the channel (ticket source) term/field
 *
 * @since 3.4.0
 *
 * @param numeric       $ticket_id
 * @param string        $channel_term
 * @param string        $overwrite  whether or not to overwrite existing channel on the ticket - set to false by default
 *
 * @return void
 */
function wpas_set_ticket_channel( $ticket_id = -1, $channel_term = 'other', $overwrite = false ) {

	/* Does a term already exist on the ticket?  If so, do not overwrite it if $overwrite is false */
	if ( false === $overwrite ) {
		$existing_channel = wp_get_post_terms( $ticket_id, 'ticket_channel' );
		if ( ! empty( $existing_channel ) ) {
			return;
		}
	}

	/* Get the term id because wp_set_object_terms require an id instead of just a string */
	$arr_the_term_id = term_exists( $channel_term, 'ticket_channel' );

	if ( $arr_the_term_id ) {

		// Need to get array keys first so we can index and extract the first element in the wp_set_object_terms below.
		$arr_the_term_id_keys = array_keys( $arr_the_term_id );
		$int_the_term_id      = (int) $arr_the_term_id[ $arr_the_term_id_keys[0] ];

		// Now add the terms (this function call doesn't work consistently for some reason!)
		$term_taxonomy_ids = wp_set_object_terms( $ticket_id, (int) $int_the_term_id, 'ticket_channel' );

	}

	return;
}

/**
 * Set ticket slug on new tickets if the admin chooses anything other than the default slug.
 *
 * @since 3.4.0
 *
 * @param numeric       $ticket_id
 *
 * @return void
 */
function wpas_set_ticket_slug( $ticket_id = -1 ) {
	$use_ticket_id_for_slug = wpas_get_option( 'ticket_topic_slug' );

	/* Set ticket slug to the post id / ticket id */
	if ( isset( $use_ticket_id_for_slug ) && ( 'ticketid' == $use_ticket_id_for_slug ) ) {

		/* Set the data to be updated - in this case just post_name (slug) with the key being the ID passed into this function */
		$newdata = array(
			'ID'        => $ticket_id,
			'post_name' => (string) $ticket_id,
		);

		/* Update the post with the new slug */
		wp_update_post( $newdata );
	}

	/* Set ticket slug to a random number  */
	if ( isset( $use_ticket_id_for_slug ) && ( 'randomnumber' == $use_ticket_id_for_slug ) ) {

		/*Calculate a random number */
		$randomslug = wp_rand();

		/* Set the data to be updated - in this case just post_name (slug) with the key being the ID passed into this function */
		$newdata = array(
			'ID'        => $ticket_id,
			'post_name' => (string) $randomslug,
		);

		/* Update the post with the new slug */
		wp_update_post( $newdata );
	}

	/* Set ticket slug to a GUID  */
	if ( isset( $use_ticket_id_for_slug ) && ( 'guid' == $use_ticket_id_for_slug ) ) {

		/*Calculate a guid */
		$randomguid = wpas_create_pseudo_guid();

		/* Set the data to be updated - in this case just post_name (slug) with the key being the ID passed into this function */
		$newdata = array(
			'ID'        => $ticket_id,
			'post_name' => $randomguid,
		);

		/* Update the post with the new slug */
		wp_update_post( $newdata );
	}

	return;
}

/**
 * Get tickets.
 *
 * Get a list of tickets matching the arguments passed.
 * This function is basically a wrapper for WP_Query with
 * the addition of the ticket status.
 *
 * @since  3.0.0
 *
 * @param string       $ticket_status Ticket status (open or closed)
 * @param array        $args          Additional arguments (see WP_Query)
 * @param string|array $post_status   Ticket state
 * @param bool         $cache         Whether or not to cache the results
 *
 * @return array               Array of tickets, empty array if no tickets found
 */
function wpas_get_tickets( $ticket_status = 'open', $args = array(), $post_status = 'any', $cache = false, $return_count = false ) {

	$custom_post_status = wpas_get_post_status();
	$post_status_clean  = array();

	if ( empty( $post_status ) ) {
		$post_status = 'any';
	}

	if ( ! is_array( $post_status ) ) {
		if ( 'any' === $post_status ) {

			foreach ( $custom_post_status as $status_id => $status_label ) {
				$post_status_clean[] = $status_id;
			}

			$post_status = $post_status_clean;

		} else {
			if ( ! array_key_exists( $post_status, $custom_post_status ) ) {
				$post_status = ''; // This basically will return no result if the post status specified doesn't exist
			}
		}
	} else {
		foreach ( $post_status as $key => $status ) {
			if ( ! array_key_exists( $status, $custom_post_status ) ) {
				unset( $post_status[ $key ] );
			}
		}
	}

	$defaults = array(
		'post_type'              => 'ticket',
		'post_status'            => $post_status,
		'posts_per_page'         => - 1,
		'no_found_rows'          => ! (bool) $cache,
		'cache_results'          => (bool) $cache,
		'update_post_term_cache' => (bool) $cache,
		'update_post_meta_cache' => (bool) $cache,
		'wpas_query'             => true, // We use this parameter to identify our own queries so that we can remove the author parameter
	);

	$args = wp_parse_args( $args, $defaults );

	if ( 'any' !== $ticket_status ) {
		if ( in_array( $ticket_status, array( 'open', 'closed' ) ) ) {
			$args['meta_query'][] = array(
				'key'     => '_wpas_status',
				'value'   => $ticket_status,
				'compare' => '=',
				'type'    => 'CHAR',
			);
		}
	}
	if( $return_count == true ){
		$args['no_found_rows'] = false;
		$args['posts_per_page'] = 1;
		$args['fields'] = 'ids';
	}
	$query = new WP_Query( $args );
	if( $return_count == true ){
		return $query->found_posts;
	}
	if ( empty( $query->posts ) ) {
		return array();
	} else {
		return $query->posts;
	}

}

/**
 * Get ticket by ticket id and user id.
 *
 * @since 5.1.1
 *
 * @param int       $id    Ticket ID
 * @param array     $args  Additional arguments (see WP_Query)
 * @param bool      $cache Whether or not to cache the results
 *
 * @return array
 */
function wpas_get_ticket_by_id( $id, $args = array(), $cache = false ) {

	$defaults = [
		'p'                      => intval( $id ),
		'post_type'              => 'ticket',
		'no_found_rows'          => ! (bool) $cache,
		'cache_results'          => (bool) $cache,
		'update_post_term_cache' => (bool) $cache,
		'update_post_meta_cache' => (bool) $cache,
		'wpas_query'             => true, // We use this parameter to identify our own queries so that we can remove the author parameter

	];

	$args = wp_parse_args( $args, $defaults );

	$query = new WP_Query( $args );

	if ( empty( $query->posts ) ) {
		return array();
	} else {
		return $query->posts[0];
	}

}

/**
 * Add a new reply to a ticket.
 *
 * @param array           $data      The reply data to insert
 * @param boolean|integer $parent_id ID of the parent ticket (post)
 * @param boolean|integer $author_id The ID of the reply author (false if none)
 *
 * @return boolean|integer False on failure or reply ID on success
 */
function wpas_add_reply( $data, $parent_id = false, $author_id = false ) {

	if ( false === $parent_id ) {

		if ( isset( $data['parent_id'] ) ) {

			/* Get the parent ID from $data if not provided in the arguments. */
			$parent_id = intval( $data['parent_id'] );
			$parent    = get_post( $parent_id );

			/* Mare sure the parent exists. */
			if ( is_null( $parent ) ) {
				return false;
			}
		} else {
			return false;
		}
	}

	// translators: %s is the ticket reply.
	$x_content = __( 'Reply to ticket %s', 'awesome-support' );

	/**
	 * Submit the reply.
	 *
	 * Now that all the verifications are passed
	 * we can proceed to the actual ticket submission.
	 */
	$defaults = array(
		'post_content'   => '',
		'post_name'      => sprintf( $x_content, "#$parent_id" ),
		'post_title'     => sprintf( $x_content, "#$parent_id" ),
		'post_status'    => 'unread',
		'post_type'      => 'ticket_reply',
		'ping_status'    => 'closed',
		'comment_status' => 'closed',
		'post_parent'    => $parent_id,
	);

	$data = wp_parse_args( $data, $defaults );

	if ( false !== $author_id ) {
		$data['post_author'] = $author_id;
	} else {
		global $current_user;
		$data['post_author'] = $current_user->ID;
	}

	$insert = wpas_insert_reply( $data, $parent_id );

	return $insert;

}

add_action( 'wpas_do_submit_new_reply', 'wpas_new_reply_submission' );
/**
 * Instantiate a new reply submission
 *
 * This helper function is used to trigger the creation of a new reply
 * after the reply submission form is posted on the front-end.
 *
 * @since 3.3
 *
 * @param array $data Reply data required to open a new ticket
 *
 * @return void
 */
function wpas_new_reply_submission( $data ) {

	// Get parent ticket ID
	$parent_id = (int) $data['ticket_id'];

	if( !wpas_can_reply_ticket( false, $parent_id ) ) return false; // Cheating? Hehe..

	if ( 'ticket' !== get_post_type( $parent_id ) ) {
		wpas_add_error( 'reply_added_failed', __( 'Something went wrong. We couldn&#039;t identify your ticket. Please try again.', 'awesome-support' ) );
		wpas_redirect( 'reply_added_failed', get_permalink( $parent_id ) );
		exit;
	}

	// Define if the ticket must be closed
	$close = isset( $data['wpas_close_ticket'] ) ? true : false;

	if ( ! empty( $data['wpas_user_reply'] ) && apply_filters( 'wpas_user_can_reply_ticket', true, $parent_id ) ) {

		/* Sanitize the data */
		$data = array( 'post_content' => wp_kses( $data['wpas_user_reply'], wp_kses_allowed_html( 'post' ) ) );

		/* Add the reply */
		$reply_id = wpas_add_reply( $data, $parent_id );

	}

	$closed = false;

	/* Possibly close the ticket */
	if ( $close && apply_filters( 'wpas_user_can_close_ticket', true, $parent_id ) ) {

		$closed = wpas_close_ticket( $parent_id );

		// Redirect now if no reply was posted
		if ( ! isset( $reply_id ) && $closed ) {
			wpas_add_notification( 'ticket_closed', __( 'The ticket was successfully closed', 'awesome-support' ) );
			wpas_redirect( 'ticket_closed', get_permalink( $parent_id ) );
			exit;
		}
	}

	if ( isset( $reply_id ) ) {

		if ( false === $reply_id ) {
			wpas_add_error( 'reply_added_failed', __( 'Your reply could not be submitted for an unknown reason.', 'awesome-support' ) );
			wpas_redirect( 'reply_added_failed', get_permalink( $parent_id ) );
			exit;
		} else {

			if ( $closed ) {
				wpas_add_notification( 'reply_added_closed', __( 'Thanks for your reply. The ticket is now closed.', 'awesome-support' ) );
			} else {
				wpas_add_notification( 'reply_added', __( 'Your reply has been submitted. Your agent will reply ASAP.', 'awesome-support' ) );
			}

			if ( false !== $link = wpas_get_reply_link( $reply_id ) ) {
				wpas_redirect( 'reply_added', $link );
				exit;
			}
		}
	}

}

/**
 * Update a reply with its edited version
 *
 * @since 3.3.0
 *
 * @param $int  $reply_id       - the id of the reply being edited.
 * @param array $content        - the new content.  If blank, the function will attempt to pull the new content from $_POST.
 *
 * @return void
 */
function wpas_edit_reply( $reply_id = null, $content = '' ) {

	if ( is_null( $reply_id ) ) {
		if ( isset( $_POST['reply_id'] ) ) {
			$reply_id = intval( $_POST['reply_id'] );
		} else {
			return false;
		}
	}

	if ( empty( $content ) ) {
		if ( isset( $_POST['reply_content'] ) ) {
			$reply = isset($_POST['reply_content'] ) ? wp_kses_post( wp_unslash( $_POST['reply_content'] ) ) : "";
			$content = wp_kses( $reply, wp_kses_allowed_html( 'post' ) );
		} else {
			return false;
		}
	}
	//ensuring that the user is allowed to edit the post given by the reply_id POST parameter 	
	if( !current_user_can( 'edit_post', $reply_id ) )
	{		
		return false;
	}
	$original_reply = get_post( $reply_id );

	if ( is_null( $original_reply ) ) {
		return false;
	}	
	//the post type should be checked so arbitrary posts cannot be updated
	if( $original_reply->post_type != 'ticket_reply' )
	{		
		return false;
	}
	$data = apply_filters(
		'wpas_edit_reply_data', array(
			'ID'             => $reply_id,
			'post_content'   => $content,
			'post_status'    => 'read',
			'comment_status' => 'closed',
			'ping_status'    => 'closed',
			'post_date'      => $original_reply->post_date,
			'post_date_gmt'  => $original_reply->post_date_gmt,
			'post_name'      => $original_reply->post_name,
			'post_parent'    => $original_reply->post_parent,
			'post_type'      => $original_reply->post_type,
			'post_author'    => $original_reply->post_author,
		), $reply_id
	);

	$edited = wp_insert_post( $data, true );

	if ( is_wp_error( $edited ) ) {
		do_action( 'wpas_edit_reply_failed', $reply_id, $content, $edited );
		return $edited;
	}

	/* Add a flag to the reply that shows it was edited */
	update_post_meta( $edited, 'wpas_reply_was_edited', '1' );

	/* Fire the after-edit action hook */
	do_action( 'wpas_reply_edited', $reply_id, $original_reply );

	return $reply_id;

}

add_action( 'wpas_reply_edited', 'wpas_log_reply_edits', 10, 2 );
/**
 * Log the original contents of a reply after it is edited.
 *
 * Action hook: wpas_reply_edited
 *
 * @since 5.2.0
 *
 * @param $int  $reply_id       - the id of the reply being edited.
 * @param array $original_reply - the original post before the edit reply was added to the database
 *
 * @TODO: Somehow this hook is getting called three times for every edit when the logging level is LOW.  3 entries end up in the log for every single edit.
 *
 * @return void
 */
function wpas_log_reply_edits( $reply_id, $original_reply ) {

	/* Do we log a summary or detail that includes the original content? */
	if ( 'low' === wpas_get_option( 'log_content_edit_level', 'low' ) ) {
		$reply_contents_to_log = __( 'Original data not available because detailed logging is not turned on or allowed', 'awesome-support' );
	} else {
		$reply_contents_to_log = $original_reply->post_content;
	}

	// translators: %1$s is the reply number, %2$s is the ticket number.
	$x_content = __( 'Reply #%1$s located on ticket #%2$s was edited.', 'awesome-support' );

	wpas_log_edits( $reply_id, sprintf( $x_content, (string) $reply_id, (string) $original_reply->post_parent ), $reply_contents_to_log );

}

/**
 * Mark a reply as read
 *
 * @since 3.3.0
 *
 * @param $int  $reply_id       - the id of the reply being marked as read.
 *
 * @return void
 */
function wpas_mark_reply_read( $reply_id = null ) {

	if ( is_null( $reply_id ) ) {
		if ( isset( $_POST['reply_id'] ) ) {
			$reply_id = intval( $_POST['reply_id'] );
		} else {
			return false;
		}
	}

	$reply = get_post( $reply_id );

	if ( is_null( $reply ) ) {
		return false;
	}

	if ( 'read' === $reply->post_status ) {
		return $reply_id;
	}

	$data = apply_filters(
		'wpas_mark_reply_read_data', array(
			'ID'             => $reply_id,
			'post_status'    => 'read',
			'comment_status' => 'closed',
			'ping_status'    => 'closed',
			'post_content'   => $reply->post_content,
			'post_date'      => $reply->post_date,
			'post_date_gmt'  => $reply->post_date_gmt,
			'post_name'      => $reply->post_name,
			'post_parent'    => $reply->post_parent,
			'post_type'      => $reply->post_type,
			'post_author'    => $reply->post_author,
		), $reply_id
	);

	$edited = wp_insert_post( $data, true );

	if ( is_wp_error( $edited ) ) {
		do_action( 'wpas_mark_reply_read_failed', $reply_id, $edited );
		return $edited;
	}

	do_action( 'wpas_marked_reply_read', $reply_id );

	return $edited;

}

add_action( 'wp_ajax_wpas_mark_reply_read', 'wpas_mark_reply_read_ajax' );
/**
 * Mark a ticket reply as read with Ajax
 *
 * @return void
 */
function wpas_mark_reply_read_ajax() {

	$ID = wpas_mark_reply_read();

	//Check permission for capability of current user
	if ( ! current_user_can( 'edit_ticket') ) {
		wp_send_json_error( array('message' => __('Unauthorized action. You do not have permission to mark a ticket reply as read with Ajax.', 'awesome-support') ), 403);		
	}	

	if ( false === $ID || is_wp_error( $ID ) ) {
		$ID = $ID->get_error_message();
	}

	echo esc_attr( $ID );
	die();
}

add_action( 'wp_ajax_wpas_edit_reply', 'wpas_edit_reply_ajax' );
/**
 * Edit a reply with Ajax
 *
 * @return void
 */
function wpas_edit_reply_ajax() {
	
	if( !check_ajax_referer( 'wpas_edit_reply', 'nonce', false ) ) {
		wp_send_json_error( array( 'message' => "You don't have access to perform this action." ) );
		die();
	}
	$ID = wpas_edit_reply();

	//Check permission for capability of current user
	if ( ! current_user_can( 'edit_ticket') ) {
		wp_send_json_error( array('message' => __('Unauthorized action. You do not have permission to edit a reply with Ajax.', 'awesome-support') ), 403);		
	}
	
	if ( false === $ID ) {
		echo "Invalid data!";
		die();
	}
	
	if ( is_wp_error( $ID ) ) {
		$ID = $ID->get_error_message();
	}

	echo esc_attr( $ID );
	die();
}

/**
 * Insert a new reply.
 *
 * The function is basically a wrapper for wp_insert_post
 * with some additional checks and new default arguments
 * adapted to the needs of the ticket_reply post type.
 * If also gives some useful hooks at different steps of
 * the process.
 *
 * @since  3.0.0
 * @param  array            $data     Array of arguments for this reply
 * @param  boolean          $post_id  ID of the parent post
 * @return integer|WP_Error           The reply ID on success or WP_Error on failure
 */
function wpas_insert_reply( $data, $post_id = false ) {

	if ( false === $post_id ) {
		return false;
	}

    if ( ! current_user_can( 'reply_ticket' ) && ( !defined( 'DOING_CRON' ) || !DOING_CRON ) ) {		
		return false;
	}

	// translators: %s is the ticket reply.
	$x_content = __( 'Reply to ticket %s', 'awesome-support' );
	$defaults = array(
		'post_name'      => sprintf( $x_content, "#$post_id" ),
		'post_title'     => sprintf( $x_content, "#$post_id" ),
		'post_content'   => '',
		'post_status'    => 'unread',
		'post_type'      => 'ticket_reply',
		'post_author'    => '',
		'post_parent'    => $post_id,
		'ping_status'    => 'closed',
		'comment_status' => 'closed',
	);

	$data = wp_parse_args( $data, $defaults );

	/* Set the current user as author if the field is empty. */
	if ( empty( $data['post_author'] ) ) {
		global $current_user;
		$data['post_author'] = $current_user->ID;
	}

	$data = apply_filters( 'wpas_add_reply_data', $data, $post_id );

	/* Sanitize the data */
	if ( isset( $data['post_title'] ) && ! empty( $data['post_title'] ) ) {
		$data['post_title'] = wp_strip_all_tags( $data['post_title'] );
	}

	if ( ! empty( $data['post_content'] ) ) {
		$data['post_content'] = strip_shortcodes( $data['post_content'] );
	}

	if ( isset( $data['post_name'] ) && ! empty( $data['post_name'] ) ) {
		$data['post_name'] = sanitize_title( $data['post_name'] );
	}

	/**
	 * Fire wpas_add_reply_before before the reply is added to the database.
	 * This hook is fired both on the back-end and the front-end.
	 *
	 * @param  array   $data    The data to be inserted to the database
	 * @param  integer $post_id ID of the parent post
	 */
	do_action( 'wpas_add_reply_before', $data, $post_id );

	if ( is_admin() ) {

		/**
		 * Fired right before the data is added to the database on the back-end only.
		 *
		 * @since  3.1.2
		 * @param  array   $data    The data to be inserted to the database
		 * @param  integer $post_id ID of the parent post
		 */
		do_action( 'wpas_add_reply_admin_before', $data, $post_id );

		/**
		 * wpas_save_reply_before
		 *
		 * This hook is now deprecated but stays in the code for backward compatibility.
		 * Instead of wpas_save_reply_before you should now use wpas_add_reply_admin_before
		 *
		 * @deprecated 3.1.2
		 */
		do_action( 'wpas_save_reply_before' );

	} else {

		/**
		 * Fired right before the data is added to the database on the front-end only.
		 *
		 * @since  3.1.2
		 * @param  array   $data    The data to be inserted to the database
		 * @param  integer $post_id ID of the parent post
		 */
		do_action( 'wpas_add_reply_public_before', $data, $post_id );

	}

	/* This is where we actually insert the post */
	$reply_id = wp_insert_post( $data, true );

	if ( is_wp_error( $reply_id ) ) {

		/**
		 * Fire wpas_add_reply_failed if the reply couldn't be inserted.
		 * This hook will be fired both in the admin and in the front-end.
		 *
		 * @param  array   $data     The data we tried to add to the database
		 * @param  integer $post_id  ID of the parent post
		 * @param  object  $reply_id WP_Error object
		 */
		do_action( 'wpas_add_reply_failed', $data, $post_id, $reply_id );

		if ( is_admin() ) {

			/**
			 * Fired if the reply instertion failed.
			 * This hook will only be fired in the admin.
			 *
			 * @since  3.1.2
			 * @param  array   $data     The data we tried to add to the database
			 * @param  integer $post_id  ID of the parent post
			 * @param  object  $reply_id WP_Error object
			 */
			do_action( 'wpas_add_reply_admin_failed', $data, $post_id, $reply_id );

			/**
			 * wpas_save_reply_after_error hook
			 *
			 * This hook is deprecated but stays in the code for backward compatibility.
			 * You should now use wpas_add_reply_admin_failed instead.
			 *
			 * @deprecated  3.1.2
			 * @param      $reply WP_Error object
			 */
			do_action( 'wpas_save_reply_after_error', $reply_id );

		} else {

			/**
			 * Fired if the reply instertion failed.
			 * This hook will only be fired in the frontèend.
			 *
			 * @since  3.1.2
			 * @param  array   $data     The data we tried to add to the database
			 * @param  integer $post_id  ID of the parent post
			 * @param  object  $reply_id WP_Error object
			 */
			do_action( 'wpas_add_reply_public_failed', $data, $post_id, $reply_id );

		}

		return $reply_id;

	}

	/**
	 * Delete the activity transient.
	 */
	delete_transient( "wpas_activity_meta_post_$post_id" );

	/**
	 * Fire wpas_add_reply_after after the reply was successfully added.
	 */
	do_action( 'wpas_add_reply_after', $reply_id, $data );

	if ( is_admin() ) {

		/**
		 * Fired right after the data is added to the database on the back-end only.
		 *
		 * @since  3.1.2
		 * @param  integer $reply_id ID of the reply added to the database
		 * @param  array   $data     Data inserted to the database
		 */
		do_action( 'wpas_add_reply_admin_after', $reply_id, $data );

		/**
		 * wpas_save_reply_after hook
		 *
		 * This hook is deprecated but stays in the code for backward compatibility.
		 * You should now use wpas_add_reply_admin_after instead.
		 *
		 * @deprecated  3.1.2
		 * @param  integer $reply Reply ID
		 * @param  array   $data  Data used to add the reply
		 */
		do_action( 'wpas_save_reply_after', $reply_id, $data );

	} else {

		/**
		 * Fired right after the data is added to the database on the front-end only.
		 *
		 * @since  3.1.2
		 * @param  integer $reply_id ID of the reply added to the database
		 * @param  array   $data     Data inserted to the database
		 */
		do_action( 'wpas_add_reply_public_after', $reply_id, $data );

	}

	/**
	 * Fire wpas_add_reply_complete after the reply and attachments was successfully added.
	 */
	do_action( 'wpas_add_reply_complete', $reply_id, $data );

	/* . */
	update_post_meta( $data['post_parent'], '_wpas_last_reply_date', current_time( 'mysql' ) );
	update_post_meta( $data['post_parent'], '_wpas_last_reply_date_gmt', current_time( 'mysql', 1 ) );

	update_post_meta( $data['post_parent'], '_wpas_is_waiting_client_reply', ! current_user_can( 'edit_ticket' ) );

	return $reply_id;

}

/**
 * Get replies for a specific ticket
 *
 * @param integer      $post_id ID of the post (ticket) to get the replies from
 * @param string|array $status  Status of the replies to get
 * @param array        $args    Additional arguments (see WP_Query)
 * @param string       $output  Type of data to return. wp_query for the WP_Query object, replies for the WP_Query posts
 *
 * @return array|WP_Query
 */
function wpas_get_replies( $post_id, $status = 'any', $args = array(), $output = 'replies' ) {

	$allowed_status = array(
		'any',
		'read',
		'unread',
	);

	if ( ! is_array( $status ) ) {
		$status = (array) $status;
	}

	foreach ( $status as $key => $reply_status ) {
		if ( ! in_array( $reply_status, $allowed_status ) ) {
			unset( $status[ $key ] );
		}
	}

	if ( empty( $status ) ) {
		$status = 'any';
	}

	$defaults = array(
		'post_parent'            => $post_id,
		'post_type'              => 'ticket_reply',
		'post_status'            => $status,
		'order'                  => wpas_get_option( 'replies_order', 'ASC' ),
		'orderby'                => 'date',
		'posts_per_page'         => - 1,
		'no_found_rows'          => true,
		'cache_results'          => false,
		'update_post_term_cache' => false,
		'update_post_meta_cache' => false,
	);

	$args = wp_parse_args( $args, $defaults );

	$replies = new WP_Query( $args );

	if ( is_wp_error( $replies ) ) {
		return $replies;
	}

	return 'wp_query' === $output ? $replies : $replies->posts;

}

/**
 * Find an available agent to assign a ticket to.
 *
 * This is a super basic attribution system. It just finds the agent
 * with the less tickets currently open.
 *
 * @since  3.0.0
 *
 * @param  boolean|integer $ticket_id The ticket that needs an agent
 *
 * @return integer         ID of the best agent for the job
 */
function wpas_find_agent( $ticket_id = false ) {

	if ( defined( 'WPAS_DISABLE_AUTO_ASSIGN' ) && true === WPAS_DISABLE_AUTO_ASSIGN ) {
		return apply_filters( 'wpas_find_available_agent', wpas_get_option( 'assignee_default' ), $ticket_id );
	}

	$users = shuffle_assoc( wpas_get_users( apply_filters( 'wpas_find_agent_get_users_args', array( 'cap' => 'edit_ticket' ) ) ) );
	$agent = array();

	foreach ( $users->members as $user ) {

		$wpas_agent = new WPAS_Member_Agent( $user );

		/**
		 * Make sure the user really is an agent and that he can currently be assigned
		 */
		if ( true !== $wpas_agent->is_agent() || false === $wpas_agent->can_be_assigned() ) {
			continue;
		}

		$count = $wpas_agent->open_tickets(); // Total number of open tickets for this agent

		if ( empty( $agent ) ) {
			$agent = array(
				'tickets' => $count,
				'user_id' => $user->ID,
			);
		} else {

			if ( $count < $agent['tickets'] ) {
				$agent = array(
					'tickets' => $count,
					'user_id' => $user->ID,
				);
			}
		}
	}

	if ( is_array( $agent ) && isset( $agent['user_id'] ) ) {
		$agent_id = $agent['user_id'];
	} else {

		$default_id = wpas_get_option( 'assignee_default', 1 );

		if ( empty( $default_id ) ) {
			$default_id = 1;
		}

		$agent_id = $default_id;

	}

	return apply_filters( 'wpas_find_available_agent', (int) $agent_id, $ticket_id );

}

/**
 * Assign an agent to a ticket.
 *
 * Assign the given agent to a ticket or find an available
 * agent if no agent ID is given.
 *
 * @since  3.0.2
 *
 * @param  integer $ticket_id ID of the post in need of a new agent
 * @param  integer $agent_id  ID of the agent to assign the ticket to
 * @param  boolean $log       Shall the assignment be logged or not
 *
 * @return object|boolean|integer WP_Error in case of problem, true if no change is required or the post meta ID if the
 *                                agent was changed
 */
function wpas_assign_ticket( $ticket_id, $agent_id = null, $log = true ) {

	if ( 'ticket' !== get_post_type( $ticket_id ) ) {
		return new WP_Error( 'incorrect_post_type', __( 'The given post ID is not a ticket', 'awesome-support' ) );
	}

	if ( is_null( $agent_id ) ) {
		$agent_id = wpas_find_agent( $ticket_id );
	}

	if ( ! user_can( $agent_id, 'edit_ticket' ) ) {
		return new WP_Error( 'incorrect_agent', __( 'The chosen agent does not have the sufficient capabilities to be assigned a ticket', 'awesome-support' ) );
	}

	/* Get the current agent if any */
	$current = get_post_meta( $ticket_id, '_wpas_assignee', true );

	if ( $current === $agent_id ) {
		return true;
	}

	$update = update_post_meta( $ticket_id, '_wpas_assignee', $agent_id, $current );

	/* Increment the number of tickets open for this agent */
	$agent = new WPAS_Member_Agent( $agent_id );
	$agent->ticket_plus();

	/* Log the action */
	if ( true === $log ) {
		$log   = array();
		$log[] = array(
			'action'   => 'updated',
			'label'    => __( 'Support Staff', 'awesome-support' ),
			'value'    => $agent_id,
			'field_id' => 'assignee',
		);
	}

	wpas_log_history( $ticket_id, $log );

	/**
	 * wpas_ticket_assigned hook
	 *
	 * since 3.0.2
	 */
	do_action( 'wpas_ticket_assigned', $ticket_id, $agent_id );

	// In case this is a ticket transfer from one agent to another, we fire a dedicated action
	if ( ! empty( $current ) && user_can( (int) $current, 'edit_ticket' ) ) {

		/**
		 * Fired only if the current assignment is a ticket transfer
		 *
		 * @since 3.2.8
		 *
		 * @param int $agent_id ID of the new assignee
		 * @param int $current  ID of the previous assignee
		 */
		do_action( 'wpas_ticket_assignee_changed', $agent_id, (int) $current );

	}

	return $update;

}

/**
 * Save form values.
 *
 * If the submission fails we save the form values in order to
 * pre-populate the form on page reload. This will avoid asking the user
 * to fill all the fields again.
 *
 * @since  3.0.0
 * @return void
 */
function wpas_save_values() {

	$fields = array();

	foreach ( $_POST as $key => $value ) {

		if ( ! empty( $value ) ) {
			$fields[ $key ] = $value;
		}
	}

	WPAS()->session->add( 'submission_form', $fields );

}

add_action( 'pre_user_query', 'wpas_randomize_uers_query', 10, 1 );
/**
 * Randomize user query.
 *
 * In order to correctly balance the tickets attribution
 * we need ot randomize the order in which they are returned.
 * Otherwise, when multiple agents have the same amount of open tickets
 * it is always the first one in the results who will be assigned
 * to new tickets.
 *
 * @since  3.0.0
 *
 * @param  object $query User query
 *
 * @return void
 */
function wpas_randomize_uers_query( $query ) {

	/* Make sure we only alter our own user query */
	if ( 'wpas_random' == $query->query_vars['orderby'] ) {
		$query->query_orderby = 'ORDER BY RAND()';
	}

}

/**
 * Update ticket status.
 *
 * Update the post_status of a ticket
 * using one of the custom status registered by the plugin
 * or its addons.
 *
 * @since  3.0.0
 * @param  integer $post_id ID of the ticket being updated
 * @param  string  $status  New status to attribute
 * @return boolean          True if the query was successfully executed
 */
function wpas_update_ticket_status( $post_id, $status ) {

	$custom_status = wpas_get_post_status();

	if ( ! array_key_exists( $status, $custom_status ) ) {
		return false;
	}

	$post = get_post( $post_id );

	if ( ! $post || $post->post_status === $status ) {
		return false;
	}

	$my_post = array(
		'ID'          => $post_id,
		'post_status' => $status,
	);

	$updated = wp_update_post( $my_post );

	if ( 0 !== intval( $updated ) ) {

		// translators: %s is the ticket state.
		$x_content = __( 'Ticket state changed to %s', 'awesome-support' );
		wpas_log_history( $post_id, sprintf( $x_content, $custom_status[ $status ] ) );
	}

	/**
	 * wpas_ticket_status_updated hook
	 *
	 * @since  3.0.2
	 */
	do_action( 'wpas_ticket_status_updated', $post_id, $status, $updated );

	return $updated;

}

/**
 * Change a ticket status to closed.
 *
 * @since  3.0.2
 *
 * @param  integer $ticket_id ID of the ticket to close
 * @param int      $user_id   ID of the user who closed the ticket
 *
 * @return integer|boolean            ID of the post meta if exists, true on success or false on failure
 */
function wpas_close_ticket( $ticket_id, $user_id = 0, $skip_user_validation = false ) {

	global $current_user;

	// Set the user who closed the ticket to the current user if nothing is specified
	if ( 0 === $user_id ) {
		$user_id = $current_user->ID;
	}

	if ( ! $skip_user_validation ) {
		if ( ! current_user_can( 'close_ticket' ) ) {
			wp_die( esc_html__( 'You do not have the capacity to close this ticket', 'awesome-support' ), esc_html__( 'Can’t close ticket', 'awesome-support' ), array( 'back_link' => true ) );
		}
	}

	$ticket_id = intval( $ticket_id );

	if ( 'ticket' == get_post_type( $ticket_id ) ) {

		$close_ticket = true;

		if ( is_admin() ) {
			$close_ticket = apply_filters( 'wpas_before_close_ticket_admin', $close_ticket, $ticket_id );
		} else {
			$close_ticket = apply_filters( 'wpas_before_close_ticket_public', $close_ticket, $ticket_id );
		}

		if( !$close_ticket ) {
			return false;
		}


		$update = update_post_meta( intval( $ticket_id ), '_wpas_status', 'closed' );

		// Save the date at which the ticket was last closed. The date is updated if the ticket is re-opened and then re-closed.
		update_post_meta( $ticket_id, '_ticket_closed_on', current_time( 'mysql' ) );
		update_post_meta( $ticket_id, '_ticket_closed_on_gmt', current_time( 'mysql', 1 ) );

		/* Decrement the number of tickets open for this agent */
		$agent_id = get_post_meta( $ticket_id, '_wpas_assignee', true );
		$agent    = new WPAS_Member_Agent( $agent_id );
		$agent->ticket_minus();

		/* Log the action */
		wpas_log_history( $ticket_id, __( 'The ticket was closed.', 'awesome-support' ) );

		/**
		 * wpas_after_close_ticket hook
		 *
		 * @since  3.0.0
		 */
		do_action( 'wpas_after_close_ticket', $ticket_id, $update, $user_id );
		
		if ( is_admin() ) {

			/**
			 * Fires after the ticket was closed in the admin only.
			 *
			 * @since  3.1.2
			 *
			 * @param integer $ticket_id ID of the ticket we just closed
			 * @param integer $user_id   ID of the user who did the action
			 * @param boolean $update    True on success, false on fialure
			 */
			do_action( 'wpas_after_close_ticket_admin', $ticket_id, $user_id, $update );

		} else {

			/**
			 * Fires after the ticket was closed in the front-end only.
			 *
			 * @since  3.1.2
			 *
			 * @param integer $ticket_id ID of the ticket we just closed
			 * @param integer $user_id   ID of the user who did the action
			 * @param boolean $update    True on success, false on failure
			 */
			do_action( 'wpas_after_close_ticket_public', $ticket_id, $user_id, $update );

		}

		//Delete ticket count cache on ticket update
		wpas_clean_ticketcount_cache();

		return $update;

	} else {
		return false;
	}

}

/**
 * Change a ticket status to open.
 *
 * @since  3.0.2
 *
 * @param  integer $ticket_id ID of the ticket to re-open
 *
 * @return integer|boolean            ID of the post meta if exists, true on success or false on failure
 */
function wpas_reopen_ticket( $ticket_id ) {

	if ( 'ticket' !== get_post_type( $ticket_id ) ) {
		return false;
	}

	if ( ! current_user_can( 'edit_ticket' ) && ! wpas_can_submit_ticket( $ticket_id ) ) {
		return false;
	}

	$update = update_post_meta( intval( $ticket_id ), '_wpas_status', 'open' );

	/* Log the action */
	wpas_log_history( $ticket_id, __( 'The ticket was re-opened.', 'awesome-support' ) );

	/**
	 * wpas_after_reopen_ticket hook
	 *
	 * @since  3.0.2
	 */
	do_action( 'wpas_after_reopen_ticket', intval( $ticket_id ), $update );

	//Delete ticket count cache on ticket update
	wpas_clean_ticketcount_cache();

	return $update;

}

add_action( 'wpas_do_reopen_ticket', 'wpas_reopen_ticket_trigger' );
/**
 * Trigger the re-open ticket function
 *
 * This is triggered by the wpas_do custom actions.
 *
 * @since 3.3
 *
 * @param array $data Superglobal data
 *
 * @return void
 */
function wpas_reopen_ticket_trigger( $data ) {

	if ( isset( $data['ticket_id'] ) ) {

		$ticket_id = (int) $data['ticket_id'];

		if ( ! wpas_can_submit_ticket( $ticket_id ) && ! current_user_can( 'edit_ticket' ) ) {
			wpas_add_error( 'cannot_reopen_ticket', __( 'You are not allowed to re-open this ticket', 'awesome-support' ) );
			wpas_redirect( 'ticket_reopen', wpas_get_tickets_list_page_url() );
			exit;
		}

		do_action( 'wpas_before_customer_reopen_ticket', $ticket_id );

		wpas_reopen_ticket( $ticket_id );

		wpas_add_notification( 'ticket_reopen', __( 'The ticket has been successfully re-opened.', 'awesome-support' ) );
		wpas_redirect( 'ticket_reopen', wp_sanitize_redirect( get_permalink( $ticket_id ) ) );
		exit;

	}

}

add_action( 'wp_ajax_wpas_edit_reply_editor', 'wpas_edit_reply_editor_ajax' );
/**
 * Load TinyMCE via Ajax request to edit a reply.
 *
 * @since  3.1.5
 * @return string Editor markup
 */
function wpas_edit_reply_editor_ajax() {

	$reply_id = filter_input( INPUT_POST, 'reply_id', FILTER_SANITIZE_NUMBER_INT );

	if ( empty( $reply_id ) ) {
		echo '';
		die();
	}

	//Check permission for capability of current user
	if ( ! current_user_can( 'edit_ticket' ) ) {
		wp_send_json_error( array('message' => __('Unauthorized action. You do not have permission to load TinyMCE via Ajax request to edit a reply.', 'awesome-support') ), 403);		
    }

	$post = get_post( $reply_id );

	if ( 'ticket_reply' !== $post->post_type ) {
		echo '';
		die();
	}

	$editor_id      = "wpas-editreply-$reply_id";
	$editor_content = apply_filters( 'the_content', $post->post_content );

	$settings = array(
		'media_buttons' => false,
		'teeny'         => true,
		'quicktags'     => false,
		'editor_class'  => 'wpas-edittextarea',
		'textarea_name' => 'wpas_edit_reply[' . $reply_id . ']',
		'textarea_rows' => 20,
	);

	wp_editor( $editor_content, $editor_id, $settings );

	die();

}

/**
 * Get the tickets count by ticket status
 *
 * @since 3.2
 *
 * @param string $state
 * @param string $status
 *
 * @return int Tickets count
 */
function wpas_get_ticket_count_by_status( $state = '', $status = 'open', $query = array() ) {

	$args        = array();
	$post_status = wpas_get_post_status();

	// Make the state an array
	if ( ! is_array( $state ) ) {
		$state = array_filter( (array) $state );
	}

	// Sanitize the status
	if ( ! in_array( $status, array( 'open', 'closed', 'any' ) ) ) {
		$status = 'open';
	}

	// Restrict tickets to the specified status
	if ( ! empty( $state ) ) {

		// Force open status if a state is defined. Who cares about counting closed "In Progress" tickets.
		$status = 'open';

		// Make sure the requested ticket state is declared
		foreach ( $state as $key => $s ) {
			if ( ! array_key_exists( $s, $post_status ) ) {
				unset( $state[ $key ] );
			}
		}

		$args['post_status'] = $state;

	}

	// Maybe restrict the count to the current user only
	if (
		( wpas_is_asadmin() && false === (bool) wpas_get_option( 'admin_see_all' ) )
		|| ( ! wpas_is_asadmin() && wpas_is_agent() && false === (bool) wpas_get_option( 'agent_see_all' ) )
	) {

		global $current_user;

		$args['meta_query'][] = array(
			'key'     => '_wpas_assignee',
			'value'   => $current_user->ID,
			'compare' => '=',
		);

	}
	// if query arguments are set then combine query argument with $args varible
	if( is_array( $query ) &&  count( $query ) > 0 ) {
		$args = array_merge( $args, $query );
	}
	//return count( wpas_get_tickets( $status, apply_filters( 'wpas_get_ticket_count_by_status_args',$args ) ) );
	return wpas_get_tickets( $status, apply_filters( 'wpas_get_ticket_count_by_status_args',$args ),'any', true, true ) ;

}

add_action( 'wp_ajax_wpas_load_replies', 'wpas_get_ticket_replies_ajax' );
add_action( 'wp_ajax_nopriv_wpas_load_replies', 'wpas_get_ticket_replies_ajax' );
/**
 * Ajax function that returns a number of ticket replies
 *
 * @since 3.3
 * @return void
 */
function wpas_get_ticket_replies_ajax() {

	// Make sure we have a ticket ID to work with
	if ( ! isset( $_POST['ticket_id'] ) || ( isset( $_POST['ticket_id'] ) && empty( $_POST[ 'ticket_id' ] ) )) {
		echo json_encode( array( 'error' => esc_html__( 'No ticket ID given', 'awesome-support' ) ) );
		die();
	}
	
	$ticket_id = absint( $_POST['ticket_id'] );	

	/* Check if the ticket is public (wpas_pbtk_flag) - only allow public access if the Public Tickets add-on is active */
	$pbtk_flag = get_post_meta( $ticket_id , '_wpas_pbtk_flag', true );		
	if ( class_exists( 'AS_Publictickets_Loader' ) && 'public' === $pbtk_flag ) {
		$can_view = true;
	}
	else
	{
		//Check permission for VIEW capability of current user
		if ( ! wpas_can_view_ticket( $ticket_id ) ) {
			wp_send_json_error( array('message' => esc_html__('You are not allowed to view this ticket.', 'awesome-support') ), 403);
			die();
		}
		//Check permission for REPLY capability of current user
		if ( ! current_user_can( 'reply_ticket' ) ) {
			wp_send_json_error( array('message' => esc_html__('Unauthorized action. You do not have permission to load TinyMCE via Ajax request to edit a reply.', 'awesome-support') ), 403);		
	    }	
		//Check permission for Valid nonce of current user
		if( !check_ajax_referer( 'wpas_loads_replies', 'ticket_replies_nonce', false ) ) {		
			wp_send_json_error( array( 'message' =>  esc_html__("You don't have access to perform this action", 'awesome-support') ), 403 );
			die();
		}
	}
	
	$offset    = isset( $_POST['ticket_replies_total'] ) ? (int) $_POST['ticket_replies_total'] : 0;
	$ticket    = get_post( $ticket_id );

	// Make sure the ID exists
	if ( ! is_object( $ticket ) || ! is_a( $ticket, 'WP_Post' ) ) {
		echo json_encode( array( 'error' => esc_html__( 'Invalid ticket ID', 'awesome-support' ) ) );
		die();
	}

	// Make sure the post is actually a ticket
	if ( 'ticket' !== $ticket->post_type ) {
		echo json_encode( array( 'error' => esc_html__( 'Given ID is not a ticket', 'awesome-support' ) ) );
		die();
	}

	$number_replies = apply_filters( 'wpas_get_ticket_replies_ajax_replies', wpas_get_option( 'replies_per_page', 10 ) );
	$replies        = wpas_get_replies(
		$ticket_id, 'any', array(
			'posts_per_page' => $number_replies,
			'no_found_rows'  => false,
			'offset'         => $offset,
		), 'wp_query'
	);

	if ( empty( $replies->posts ) ) {
		echo json_encode( array( __( 'No Ticket Replies found.', 'awesome-support' ) ) );
		die();
	}

	$output = array(
		'total'   => (int) $replies->found_posts,
		'current' => $offset + (int) $replies->post_count,
		'html'    => '',
	);

	$html = array();

	while ( $replies->have_posts() ) {

		$replies->the_post();
		$user     = get_userdata( $replies->post->post_author );
		if( $user && !empty( $user ) )
		{
			$time_ago = human_time_diff( get_the_time( 'U', $replies->post->ID ), current_time( 'timestamp' ) );

			ob_start();

			wpas_get_template(
				'partials/ticket-reply', array(
					'time_ago' => $time_ago,
					'user'     => $user,
					'post'     => $replies->post,
				)
			);

			$reply = ob_get_contents();

			ob_end_clean();

			$html[] = $reply;
		}
	}

	$output['html'] = implode( '', $html );

	echo json_encode( $output );
	die();

}


add_action( 'wpas_backend_reply_content_after', 'wpas_show_reply_edited_msg', 10, 1 );
/**
 * Show whether a ticket reply has been edited or not.
 *
 * Action hook: wpas_backend_reply_content_after
 *              Hook located in metaboxes/replies-published.php.
 *
 * @since 5.2.0
 *
 * @param string $reply_id - postid of reply being processed.
 *
 * @return void
 */
function wpas_show_reply_edited_msg( $reply_id ) {

	$edited = get_post_meta( $reply_id, 'wpas_reply_was_edited' );

	if ( (int) $edited > 0 ) {
		echo '<br />' . '<div class="wpas_footer_note">' . esc_html__( '* This reply has been edited.  See the logs for a full history of edits.', 'awesome-support' ) . '</div>';
	}

}

add_action( 'wpas_backend_ticket_content_after', 'wpas_show_reply_deleted_msg', 10, 2 );
/**
 * Show whether a ticket reply has been deleted.
 *
 * Because the reply is deleted, we have to show the message on the opening ticket post.
 *
 * Action hook: wpas_backend_ticket_content_after
 *              Hook located in metaboxes/message.php.
 *
 * @since 5.2.0
 *
 * @param string $ticket_id - id of ticket being processed.
 * @param array  $ticket    - post object of ticket being processed.
 *
 * @return void
 */
function wpas_show_reply_deleted_msg( $ticket_id, $ticket ) {

	$post = get_post_meta( $ticket_id, 'wpas_reply_was_deleted' );

	if ( (int) $post > 0 ) {
		echo '<br />' . '<div class="wpas_footer_note">' . esc_html__( '* This ticket has had replies deleted from it.  Depending on your settings at the time of deletion, the logs might have a full history of these edits.', 'awesome-support' ) . '</div>';
	}

}

add_action( 'wp_ajax_wpas_edit_ticket_content', 'wpas_edit_ticket_content' );
add_action( 'wp_ajax_nopriv_wpas_edit_ticket_content', 'wpas_edit_ticket_content' );
/**
 * Save the ticket content from editing
 *
 * @return void
 */
function wpas_edit_ticket_content() {

	/**
	 * Security checking. Verify ajax via nonce.
	 */
	if( !check_ajax_referer( 'wpas-editor-content-nonce', 'nonce', false ) ) {
		$response['message'] = __( "You don't have access to perform this action." , 'awesome-support' );
		wp_send_json( $response );
		die();
	}
		
	/**
	 * The default response
	 */
	$response = array(
		'code'    => 404,
		'message' => __( 'Nothing found!', 'awesome-support' ),
	);

	/**
	 * Variables!
	 */
	$ticket_id = isset( $_POST['post_id'] ) ? sanitize_text_field( wp_unslash( $_POST['post_id'] ) ) : '';
	$content = isset($_POST['content'] ) ? wp_kses_post(  wp_unslash( $_POST['content'] ) ) : '';

	/**
	 * Make sure we have ticket ID
	 */
	if ( ! $ticket_id ) {
		$response['message'] = __( 'Ticket ID missing. Invalid request!', 'awesome-support' );
		wp_send_json( $response );
		wp_die();
	}

	/**
	 * The updated ticket content is missing, exit
	 */
	if ( ! $content ) {
		$response['message'] = __( 'No ticket message found. Invalid request!', 'awesome-support' );
		wp_send_json( $response );
		wp_die();
	}
	
	//Check permission for capability of current user	
	if ( ! current_user_can( 'edit_ticket', $ticket_id ) || !wpas_can_view_ticket( $ticket_id ) ) {		
		$response['message'] = __( "You don't have access to perform this action." , 'awesome-support' );
		wp_send_json( $response );
		die();
	}
		
	/**
	 * Make sure we are on the correct post type
	 */
	$is_ticket = get_post_type( $ticket_id );
	if ( $is_ticket !== 'ticket' ) {
		$response['message'] = __( 'Id provided is not a valid ticket. Invalid request!', 'awesome-support' );
		wp_send_json( $response );
		wp_die();
	}

	/**
	 * Make sure that this is valid ticket and it exists
	 */
	$original_content = get_post( $ticket_id );
	if ( is_null( $original_content ) ) {
		$response['message'] = __( 'No ticket found. Invalid request!', 'awesome-support' );
		wp_send_json( $response );
		wp_die();
	}

	/**
	 * Compare the original content vs the updated content
	 * If they are not same and have differences, log it
	 * then we update the post content
	 */
	if ( $original_content->post_content !== $content ) {

		/**
		 * Update the content
		 */
		$updated_post_id = wp_update_post(
			array(
				'ID'           => $ticket_id,
				'post_content' => $content,
			)
		);

		if ( is_wp_error( $updated_post_id ) ) {
			$response['message'] = $updated_post_id->get_error_messages();
			$response['content'] = $original_content->post_content;
		} else {
			$response['code']    = 200;
			$response['message'] = __( 'You have successfully edited content!', 'awesome-support' );
			$response['content'] = $content;
			/**
			 * Log the edits to ticket
			 */
			wpas_log_ticket_edits( $ticket_id, $original_content );
		}
	} else {
		$response['code']    = 404;
		$response['message'] = __( 'Nothing has been updated. You have same content as before..', 'awesome-support' );
		$response['content'] = $original_content->post_content;
	}

	wp_send_json( $response );
	wp_die();

}

/**
 * Log the original contents of a ticket after it is edited.
 *
 * @since 5.7.1
 *
 * @param $int  $ticket_id       - the id of the ticket being edited.
 * @param array $original_ticket - the original post before the edited ticket was added to the database
 *
 * @return void
 */
function wpas_log_ticket_edits( $ticket_id, $original_ticket ) {

	if ( 'low' === wpas_get_option( 'log_content_edit_level', 'low' ) ) {
		$contents_to_log = __( 'Original data not available because detailed logging is not turned on or allowed', 'awesome-support' );
	} else {
		$contents_to_log = $original_ticket->post_content;
	}

	// translators: %1$s is the ticket number.
	$x_content = __( 'Ticket content located on ticket #%1$s was edited.', 'awesome-support' );
	wpas_log_edits( $ticket_id, sprintf( $x_content, (string) $ticket_id ), $contents_to_log );

}

add_action( 'wp_ajax_wpas_load_reply_history', 'wpas_load_reply_history' );
add_action( 'wp_ajax_nopriv_wpas_load_reply_history', 'wpas_load_reply_history' );
/**
 * Ajax function that returns a the history of replies
 *
 * @since 3.3
 * @return void
 */
function wpas_load_reply_history() {
	/**
	 * Default response messages
	 */
	$response = array(
		'code'    => 404,
		'message' => __( 'Invalid request!', 'awesome-support' ),
		'data'    => array(),
	);

	/**
	 * Reply ID is required
	 */
	if ( ! isset( $_POST['reply_id'] ) || ( isset( $_POST['reply_id'] ) && empty( $_POST[ 'reply_id' ] ) ) ) {
		wp_send_json( $response );
	}	
	
	$reply_id = ! empty( $_POST[ 'reply_id' ] ) ? absint( $_POST[ 'reply_id' ] ) : 0;
	
	if( !current_user_can( 'edit_post', $reply_id ) )
	{		
		wp_send_json_error( array( 'message' => "You don't have access to perform this action." ) );
		die();
	}
	
	if( !check_ajax_referer( 'wpas_history_reply_nonce', 'history_nonce', false ) ) {
		
		wp_send_json_error( array( 'message' => "You don't have access to perform this action." ) );
		die();
	}
	
	/**
	 * Get all reply history
	 */
	$reply_history = get_posts(
		array(
			'post_parent' 		=> $reply_id,
			'post_type'   		=> 'ticket_log',
			'posts_per_page'	=> 10,  //Maybe this should an option?!
			'orderby'			=> 'ID',
			'order'				=> 'DESC'
		)
	);

	if ( ! empty( $reply_history ) ) {
		/**
		 * Update response
		 */
		$response = array(
			'code'    => 200,
			'message' => __( 'Edit history', 'awesome-support' ),
			'data'    => $reply_history,
		);
		wp_send_json( $response );
	} else {
		$response['code']    = 404;
		$response['message'] = __( 'No edit history found!', 'awesome-support' );
		$response['data']    = '';
		wp_send_json( $response );
	}
	wp_die();
}

/**
 * There an issue with the WPAS Options data
 * in which we cannot determine the GDPRs hierarchy IDs
 * This function will attempt to have workaround.
 *
 * Returns GDPR Id's
 * NOTE: If the short description is identical, this
 * function will return the first ID
 */
function wpas_get_gdpr_data( $short_description ) {
	$return_id = false;
	if( $short_description === wpas_get_option( 'gdpr_notice_short_desc_01', false ) ) {
		$return_id = 1;
	}elseif( $short_description === wpas_get_option( 'gdpr_notice_short_desc_02', false ) ) {
		$return_id = 2;
	}elseif( $short_description === wpas_get_option( 'gdpr_notice_short_desc_03', false ) ) {
		$return_id = 3;
	}

	$return_id = apply_filters('gdpr_consent_data_id', $return_id, $short_description );

	return $return_id;
}

/**
 * Delete post attachments
 *
 * @param int $post_id
 */
function wpas_delete_post_attachments( $post_id ) {

	$attachments = get_attached_media( '', $post_id );

	foreach ( $attachments as $attachment ) {
	  wp_delete_attachment( $attachment->ID, true );
	}
}

/**
 * Add custom fields data in cloned ticket
 *
 * @param int $ticket_id
 * @param array $data
 * @param array $incoming_data
 */
function wpas_clone_ticket_before_assigned( $new_ticket_id, $data, $incoming_data ) {

	// Clone custom fields
	$clone_custom_fields_list = is_array( $incoming_data['custom_fields'] ) ? $incoming_data['custom_fields'] : array();

	$ticket_id = isset( $incoming_data['clone_ticket_id'] ) ? $incoming_data['clone_ticket_id'] : '';


	if( !$ticket_id || empty( $clone_custom_fields_list ) ) {
		return;
	}


	$custom_fields =  WPAS()->custom_fields->get_custom_fields();

	foreach( $clone_custom_fields_list as $cf_name )  {

		if( !array_key_exists( $cf_name, $custom_fields ) ) {
			continue;
		}

		$cf_field = new WPAS_Custom_Field( $cf_name, $custom_fields[ $cf_name ] );
		$cf_value = $cf_field->get_field_value( false, $ticket_id );


		if( 'taxonomy' ===  $cf_field->field_type ) {

			$tax_terms = get_the_terms( $ticket_id, $cf_field->field_id );

			if ( is_array( $tax_terms ) ) {
				foreach ( $tax_terms as $term ) {
					$cf_value = $term->term_id;
				}
			}

		}

		$cf_field->update_value( $cf_value, $new_ticket_id );
	}

}



/**
 * Clone a ticket
 *
 * @param int $ticket_id			  Ticket ID to clone
 * @param array $args				  Setting to clone ticket
 * @return integer|WP_Error           New ticket ID on success or WP_Error on failure
 */
function wpas_clone_ticket( $ticket_id, $args = array() ) {
	
	$defaults = array(
		'clone_replies'				=> true,
		'clone_custom_fields_list'	=> array( "product", "department", "ticket_priority" ),
		'clone_agent'				=> true,
		'cloned_ticket_status'		=> 'queued',
		'suppress_notifications'	=> false,
	);



	$args = wp_parse_args( $args, $defaults );

	$args = apply_filters( 'wpas_clone_ticket_args', $args, $ticket_id );



	$ticket = get_post( $ticket_id );

	// Check if source ticket id is valid
	if( !$ticket || 'ticket' !== get_post_type( $ticket ) ) {
		return new WP_Error( 'invalid_source_ticket_id', __( 'Source ticket id is not valid.', 'awesome-support' ) );
	}


	$title = $ticket->post_title;

	// Process tags in ticket content
	$emails = new WPAS_Email_Notification( $ticket_id );
	$content = wpautop( str_replace( '\'', '&apos;', $emails->fetch( $ticket->post_content ) ) );

	$customer = $ticket->post_author;

	$ticket_status = $args['cloned_ticket_status'];

	$ticket_data = apply_filters( 'wpas_clone_ticket_data', array(
		'post_content'   => $content,
		'post_name'      => $title,
		'post_title'     => $title,
		'post_status'    => $ticket_status,
		'post_type'      => 'ticket',
		'post_author'    => $customer,
		'ping_status'    => 'closed',
		'comment_status' => 'closed',
	), $ticket_id, $args );


	$clone_agent   = $args['clone_agent'];
	$agent_id = $clone_agent ?  get_post_meta( $ticket_id, '_wpas_assignee', true ) : false;


	$agent_id = apply_filters( 'wpas_clone_ticket_agent_id', $agent_id, $ticket, $args );


	$ticket_data['custom_fields'] = is_array( $args['clone_custom_fields_list'] ) ? $args['clone_custom_fields_list'] : array();
	$ticket_data['clone_ticket_id'] = $ticket_id;

	// Prevent notification while cloning ticket
	if( $args['suppress_notifications'] ) {
		remove_action( 'wpas_open_ticket_after', 'wpas_notify_confirmation', 11 );
		remove_action( 'wpas_open_ticket_after', 'wpas_notify_assignment', 12 );
	}


	add_action( 'wpas_open_ticket_before_assigned', 'wpas_clone_ticket_before_assigned', 11, 3 );

	$new_ticket_id = wpas_insert_ticket( $ticket_data, false, $agent_id );

	remove_action( 'wpas_open_ticket_before_assigned', 'wpas_clone_ticket_before_assigned', 11 );

	// Add removed notification hooks back
	if( $args['suppress_notifications'] ) {
		add_action( 'wpas_open_ticket_after', 'wpas_notify_confirmation', 11, 2 );
		add_action( 'wpas_open_ticket_after', 'wpas_notify_assignment', 12, 2 );
	}

	if( !$new_ticket_id ) {
		return new WP_Error( 'ticket_clone_failed', __( 'Ticket cloning failed', 'awesome-support' ) );
	}

	do_action( 'wpas_clone_ticket_added_after', $new_ticket_id, $ticket, $args );


	// Clone replies
	$clone_replies = $args['clone_replies'];
	if( $clone_replies ) {

		$replies = wpas_get_replies( $ticket_id );

		if( $args['suppress_notifications'] ) {
			remove_action( 'wpas_add_reply_complete', 'wpas_notify_reply', 10 );
		}

		// translators: %s is the ticket number.
		$x_content = __( 'Reply to ticket %s', 'awesome-support' );

		foreach( $replies as $reply ) {

			$reply_data = array(
				'post_content'   => $reply->post_content,
				'post_name'      => sprintf( $x_content, "#$new_ticket_id" ),
				'post_title'     => sprintf( $x_content, "#$new_ticket_id" ),
				'post_status'    => 'unread',
				'post_type'      => 'ticket_reply',
				'ping_status'    => 'closed',
				'comment_status' => 'closed',
				'post_parent'    => $new_ticket_id,
				'post_author'	 => $reply->post_author
			);

			$reply_id = wpas_insert_reply( $reply_data, $new_ticket_id );

			do_action( 'wpas_clone_ticket_reply_added_after', $reply_id, $new_ticket_id, $ticket, $args );
		}

		if( $args['suppress_notifications'] ) {
			add_action( 'wpas_add_reply_complete', 'wpas_notify_reply', 10, 2 );
		}
	}

	do_action( 'wpas_clone_ticket_completed_after', $new_ticket_id, $ticket, $args );

	return $new_ticket_id;

}