/** * 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($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); return $update; }
/** * Save ticket custom fields. * * This function will save all custom fields associated * to the ticket post type. Be it core custom fields * or user added custom fields. * * @param (int) $post_id Current post ID * @since 3.0.0 */ public function save_ticket($post_id) { /* We should already being avoiding Ajax, but let's make sure */ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) { return; } if (defined('DOING_AJAX') && DOING_AJAX) { return; } /* Now we check the nonce */ if (!isset($_POST[Awesome_Support_Admin::$nonce_name]) || !wp_verify_nonce($_POST[Awesome_Support_Admin::$nonce_name], Awesome_Support_Admin::$nonce_action)) { return; } /* Does the current user has permission? */ if (!current_user_can('edit_ticket', $post_id)) { return; } global $current_user, $wpas_cf; /** * Store possible logs */ $log = array(); /** * If no ticket status is found we are in the situation where * the agent is creating a ticket on behalf of the user. There are * a couple of things that we need to do then. */ if ('' === ($original_status = get_post_meta($post_id, '_wpas_status', true))) { /** * First of all, set the ticket as open. This is very important. */ add_post_meta($post_id, '_wpas_status', 'open', true); /** * Send the confirmation e-mail to the user. * * @since 3.1.5 */ wpas_email_notify($post_id, 'submission_confirmation'); } /* Save the possible ticket reply */ if (isset($_POST['wpas_reply']) && isset($_POST['wpas_reply_ticket']) && '' != $_POST['wpas_reply']) { /* Check for the nonce */ if (wp_verify_nonce($_POST['wpas_reply_ticket'], 'reply_ticket')) { $user_id = $current_user->ID; $content = wp_kses_post($_POST['wpas_reply']); $data = apply_filters('wpas_post_reply_admin_args', array('post_content' => $content, 'post_status' => 'read', 'post_type' => 'ticket_reply', 'post_author' => $user_id, 'post_parent' => $post_id, 'ping_status' => 'closed', 'comment_status' => 'closed')); /** * Remove the save_post hook now as we're going to trigger * a new one by inserting the reply (and logging the history later). */ remove_action('save_post_ticket', array($this, 'save_ticket')); /** * Fires right before a ticket reply is submitted * * @since 3.2.6 * * @param int $post_id Ticket ID * @param array $data Data to be inserted as the reply */ do_action('wpas_post_reply_admin_before', $post_id, $data); /* Insert the reply in DB */ $reply = wpas_add_reply($data, $post_id); /** * Fires right after a ticket reply is submitted * * @since 3.2.6 * * @param int $post_id Ticket ID * @param array $data Data to be inserted as the reply * @param bool|int Reply ID on success, false on failure */ do_action('wpas_post_reply_admin_after', $post_id, $data, $reply); /* In case the insertion failed... */ if (is_wp_error($reply)) { /* Set the redirection */ $_SESSION['wpas_redirect'] = add_query_arg(array('wpas-message' => 'wpas_reply_error'), get_permalink($post_id)); } else { /* E-Mail the client */ $new_reply = new WPAS_Email_Notification($post_id, array('reply_id' => $reply, 'action' => 'reply_agent')); /* The agent wants to close the ticket */ if (isset($_POST['wpas_do']) && 'reply_close' == $_POST['wpas_do']) { /* Confirm the post type and close */ if ('ticket' == get_post_type($post_id)) { /** * wpas_ticket_before_close_by_agent hook */ do_action('wpas_ticket_before_close_by_agent', $post_id); /* Close */ $closed = wpas_close_ticket($post_id); /* E-Mail the client */ new WPAS_Email_Notification($post_id, array('action' => 'closed')); /** * wpas_ticket_closed_by_agent hook */ do_action('wpas_ticket_closed_by_agent', $post_id); } } } } } /* Now we can save the custom fields */ $wpas_cf->save_custom_fields($post_id, $_POST); /* Log the action */ if (!empty($log)) { wpas_log($post_id, $log); } /* If this was a ticket update, we need to know where to go now... */ if ('' !== $original_status) { /* Go back to the tickets list */ if (isset($_POST['wpas_back_to_list']) && true === boolval($_POST['wpas_back_to_list']) || isset($_POST['where_after']) && 'back_to_list' === $_POST['where_after']) { $_SESSION['wpas_redirect'] = add_query_arg(array('post_type' => 'ticket'), admin_url('edit.php')); } } }
/** * Save the custom fields. * * The following method will get all options, * check if they have a submissed value, prefix and save it to the DB. * * @param (integer) $post_id Post ID * @since 3.0.0 */ public function save($post_id = '') { /* Need the parent object */ global $wpas_cf; /** * Clear! We can go ahead now... */ $options = $wpas_cf->get_custom_fields(); /** * Save the possible messages in this array */ $messages = array(); /** * Save all notifications to send in this array for a later expedition */ $notify = array(); /** * If some of the fields are to be logged in the ticket history, we save it here */ $log = array(); /* Go through all our options */ foreach ($options as $option) { $option_name = 'wpas_' . sanitize_text_field($option['name']); $option_args = $option['args']; /* Prepare current value */ if (isset($_POST[$option_name])) { $value = function_exists($option_args['sanitize']) ? $option_args['sanitize']($_POST[$option_name]) : sanitize_text_field($_POST[$option_name]); } /* Use a custom saving function if the save_callback is defined */ if (false !== $option_args['save_callback']) { if (is_null($option_args['save_callback'])) { continue; } if (function_exists($option_args['save_callback'])) { call_user_func($option_args['save_callback'], $value, $post_id); continue; } } /* Process the agent (re)attribution differently */ if ('assignee' === $option['name']) { /* Don't od anything if the agent didn't change */ if ($_POST[$option_name] == get_post_meta($post_id, '_wpas_assignee', true)) { continue; } wpas_assign_ticket($post_id, $_POST[$option_name], $option_args['log']); continue; } /* We handle different option types differently */ if ('taxonomy' != $option_args['callback']) { /* Form the meta key */ $key = "_{$option_name}"; /* Get current option */ $current = get_post_meta($post_id, $key, true); /** * First case scenario * * The option exists in DB but there is no value * for it in the POST. This is often the case * for checkboxes. * * Action: Delete option */ if ('' != $current && !isset($_POST[$option_name])) { /* Delete the post meta */ delete_post_meta($post_id, $key, $current); /* Log the action */ if (true === $option_args['log']) { $log[] = array('action' => 'deleted', 'label' => wpas_get_field_title($option), 'value' => $current, 'field_id' => $option['name']); } } elseif ('' != $current && isset($_POST[$option_name])) { /* If an actual value is set, we udpate the post meta */ if ('' != $value && $current != $value) { update_post_meta($post_id, $key, $value, $current); /* Log the action */ if (true === $option_args['log']) { $log[] = array('action' => 'updated', 'label' => wpas_get_field_title($option), 'value' => $value, 'field_id' => $option['name']); } } elseif ('' == $value) { delete_post_meta($post_id, $key, $current); /* Log the action */ if (true === $option_args['log']) { $log[] = array('action' => 'deleted', 'label' => wpas_get_field_title($option), 'value' => $current, 'field_id' => $option['name']); } } } elseif ('' == $current && isset($_POST[$option_name])) { /* Let's not add an empty value */ if ('' != $value) { add_post_meta($post_id, $key, $value, true); /* Log the action */ if (true === $option_args['log']) { $log[] = array('action' => 'added', 'label' => wpas_get_field_title($option), 'value' => $value, 'field_id' => $option['name']); } } } else { // Do nothing } } elseif ('taxonomy' == $option_args['callback']) { /* Check if this taxonomy has to be handled as a select */ if (true === $option_args['taxo_std']) { continue; } /* Clean the taxonomy name */ $taxonomy = substr($option_name, 5); /* If no value is submitted we delete the term relationship */ if (!isset($_POST[$option_name]) || empty($_POST[$option_name])) { $terms = wp_get_post_terms($post_id, $taxonomy); if (!empty($terms)) { wp_delete_object_term_relationships($post_id, $option_name); /* Log the action */ if (true === $option_args['log']) { $log[] = array('action' => 'deleted', 'label' => wpas_get_field_title($option), 'value' => $current, 'field_id' => $option['name']); } } continue; } /* Get all the terms for this ticket / taxo (we should have only one term) */ $terms = get_the_terms($post_id, $taxonomy); /** * As the taxonomy is handled like a select, we should have only one value. At least * that's what we want. Hence, we loop through the possible multiple terms (which * shouldn't happen) and only keep the last one. */ if (is_array($terms)) { foreach ($terms as $term) { $the_term = $term->term_id; } } else { $the_term = ''; } /* Finally we save the new terms if changed */ if ($the_term !== (int) $value) { $term = get_term_by('id', (int) $value, $taxonomy); if (false === $term) { continue; } /** * Apply the get_term filters. * * @var object */ $term = get_term($term, $taxonomy); wp_set_object_terms($post_id, (int) $value, $taxonomy, false); /* Log the action */ if (true === $option_args['log']) { $log[] = array('action' => 'updated', 'label' => wpas_get_field_title($option), 'value' => $term->name, 'field_id' => $option['name']); } } } /** * Fired right after the option is updated */ do_action("wpas_cf_updated_{$option_name}"); /** * If a message is associated to this option, we add it now */ if (isset($option_args['message'])) { $messages[] = $option_args['message']; } /** * If an e-mail notification has to be sent we store it temporarily */ if (isset($option_args['notification'])) { $notify[] = $option_args['notification']; } } /** * Log the changes */ if (!empty($log)) { wpas_log($post_id, $log); } }
/** * Save all custom fields given in $data to the database. * * @since 3.2.0 * * @param int $post_id ID of the post being saved * @param array $data Array of data that might contain custom fields values. * @param bool $allow_log Whether or not to allow logging actions. If this is set to false and the custom field is * set to true, $log has higher priority. I tis used to prevent logging on ticket creation. * * @return array Array of custom field / value saved to the database */ public function save_custom_fields($post_id, $data = array(), $allow_log = true) { /* We store all the data to log in here */ $log = array(); /* Store all fields saved to DB and the value saved */ $saved = array(); $fields = $this->get_custom_fields(); /** * wpas_save_custom_fields_before hook * * @since 3.0.0 */ do_action('wpas_save_custom_fields_before', $post_id); foreach ($fields as $field_id => $field) { /** * All name attributes are prefixed with wpas_ * so we need to add it to get the real field ID. */ $field_form_id = "wpas_{$field_id}"; /* Process core fields differently. */ if (true === $field['args']['core']) { if (isset($data[$field_form_id])) { $this->save_core_field($post_id, $field, $data[$field_form_id]); } continue; } /** * Ignore fields in "no edit" mode. * * If we're on the admin and the custom field is set as * "no edit" (by restricting the capability), then the field * won't be passed in the $_POST, which as a result would have * the field deleted. * * If the no edit mode is enabled for the current field, we simply ignore it. */ if (is_admin() && !current_user_can($field['args']['capability'])) { continue; } /** * Get the custom field object. */ $custom_field = new WPAS_Custom_Field($field_id, $field); if (isset($data[$field_form_id])) { $value = $custom_field->get_sanitized_value($data[$field_form_id]); $result = $custom_field->update_value($value, $post_id); } else { /** * This is actually important as passing an empty value * for custom fields that aren't set in the form allows * for deleting values that aren't used from the database. * An unchecked checkbox for instance will not be set * in the form even though the value has to be deleted. */ $value = ''; $result = $custom_field->update_value($value, $post_id); } if (1 === $result || 2 === $result) { $saved[$field['name']] = $value; } if (true === $field['args']['log'] && true === $allow_log) { /** * If the custom field is a taxonomy we need to convert the term ID into its name. * * By checking if $result is different from 0 we make sure that the term actually exists. * If the term didn't exist the save function would have seen it and returned 0. */ if ('taxonomy' === $field['args']['field_type'] && 0 !== $result) { $term = get_term((int) $value, $field['name']); $value = $term->name; } /** * If the "options" parameter is set for this field, we assume it is because * the field type has multiple options. In order to make is more readable, * we try to replace the field value by the value label. * * This process is based on the fact that field types options always follow * the schema option_id => option_label. */ if (isset($field['args']['options']) && is_array($field['args']['options'])) { /* Make sure arrays are still readable */ if (is_array($value)) { $new_values = array(); foreach ($value as $val) { if (array_key_exists($val, $field['args']['options'])) { array_push($new_values, $field['args']['options'][$val]); } } /* Only if all original values were replaced we update the $value var. */ if (count($new_values) === count($value)) { $value = $new_values; } $value = implode(', ', $value); } else { if (array_key_exists($value, $field['args']['options'])) { $value = $field['args']['options'][$value]; } } } $tmp = array('action' => '', 'label' => wpas_get_field_title($field), 'value' => $value, 'field_id' => $field['name']); switch ((int) $result) { case 1: $tmp['action'] = 'added'; break; case 2: $tmp['action'] = 'updated'; break; case 3: $tmp['action'] = 'deleted'; break; } /* Only add this to the log if something was done to the field value */ if (!empty($tmp['action'])) { $log[] = $tmp; } } } /** * Log the changes if any. */ if (!empty($log)) { wpas_log($post_id, $log); } /** * wpas_save_custom_fields_before hook * * @since 3.0.0 */ do_action('wpas_save_custom_fields_after', $post_id); return $saved; }
/** * 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)) { $update = update_post_meta(intval($ticket_id), '_wpas_status', 'open'); /* Log the action */ wpas_log($ticket_id, __('The ticket was re-opened.', 'wpas')); /** * wpas_after_reopen_ticket hook * * @since 3.0.2 */ do_action('wpas_after_reopen_ticket', intval($ticket_id), $update); return $update; } else { return false; } }