diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..64c9e45 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,59 @@ +module.exports = function (grunt) { + // supportflow-user-widget.js and widget.css are left for now as feature is not completed + var js_files = [ + 'js/toggle-links.js', + 'js/email_accounts.js', + 'js/email_conversation.js', + 'js/permissions.js', + 'js/predefined-replies.js', + 'js/preferences.js', + 'js/customers-autocomplete.js', + 'js/ticket_attachments.js', + 'js/tickets.js', + 'js/auto_save.js', + ]; + var css_files = [ + 'css/admin.css', + 'css/dashboard.css', + ]; + + grunt.initConfig({ + uglify: { + options: { + banner: '/*! SupportFlow minified version */\n' + }, + build : { + src : js_files, + dest: 'js/supportflow.min.js' + } + }, + cssmin: { + options: { + banner: '/*! SupportFlow minified version */\n' + }, + build : { + src : css_files, + dest: 'css/supportflow.min.css' + } + }, + watch : { + scripts: { + files: js_files, + tasks: ['uglify'], + }, + styles : { + files: css_files, + tasks: ['cssmin'], + }, + }, + }); + + // Load the required plugins. + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-cssmin'); + + // Running these tasks by default + grunt.registerTask('default', ['uglify', 'cssmin']); + +}; \ No newline at end of file diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index b1baa66..ac35570 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -3,30 +3,38 @@ * */ -class SupportFlow_Admin extends SupportFlow { +defined( 'ABSPATH' ) or die( "Cheatin' uh?" ); + +class SupportFlow_Admin { function __construct() { + add_action( 'wp_ajax_sf_forward_conversation', array( $this, 'action_wp_ajax_sf_email_conversation' ) ); + add_filter( 'heartbeat_received', array( $this, 'filter_heartbeat_received' ), 10, 2 ); add_action( 'supportflow_after_setup_actions', array( $this, 'setup_actions' ) ); + add_action( 'add_attachment', array( $this, 'action_add_attachment' ) ); } public function setup_actions() { - // Creating or updating a thread + // Creating or updating a ticket add_action( 'add_meta_boxes', array( $this, 'action_add_meta_boxes' ) ); add_action( 'save_post', array( $this, 'action_save_post' ) ); + add_action( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 10, 4 ); - if ( !$this->is_edit_screen() ) + if ( ! $this->is_edit_screen() ) { return; + } // Everything add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) ); add_filter( 'post_updated_messages', array( $this, 'filter_post_updated_messages' ) ); add_action( 'admin_init', array( $this, 'action_admin_init' ) ); - // Manage threads view + // Manage tickets view add_filter( 'manage_' . SupportFlow()->post_type . '_posts_columns', array( $this, 'filter_manage_post_columns' ) ); add_filter( 'manage_edit-' . SupportFlow()->post_type . '_sortable_columns', array( $this, 'manage_sortable_columns' ) ); add_action( 'manage_posts_custom_column', array( $this, 'action_manage_posts_custom_column' ), 10, 2 ); + add_filter( 'views_edit-' . SupportFlow()->post_type, array( $this, 'filter_views' ) ); add_filter( 'post_row_actions', array( $this, 'filter_post_row_actions' ), 10, 2 ); add_filter( 'bulk_actions-edit-' . SupportFlow()->post_type, array( $this, 'filter_bulk_actions' ) ); add_action( 'pre_get_posts', array( $this, 'action_pre_get_posts' ) ); @@ -39,11 +47,29 @@ public function setup_actions() { * Re-sort the custom statuses so trash appears last */ function action_admin_init() { - global $wp_post_statuses; + global $wp_post_statuses, $pagenow; $trash_status = $wp_post_statuses['trash']; unset( $wp_post_statuses['trash'] ); $wp_post_statuses['trash'] = $trash_status; + + if ( 'edit.php' == $pagenow ) { + add_filter( 'get_the_excerpt', array( $this, 'filter_get_the_excerpt' ) ); + } + } + + /** + * Do not allow users to view/edit replies outside of a ticket context. + */ + public function filter_map_meta_cap( $caps, $cap, $user_id, $args ) { + if ( $cap == 'edit_post' && ! empty( $args[0] ) ) { + $post = get_post( absint( $args[0] ) ); + if ( $post->post_type == SupportFlow()->reply_type && $post->post_parent > 0 ) { + $caps[] = 'do_not_allow'; + } + } + + return $caps; } /** @@ -52,89 +78,288 @@ function action_admin_init() { public function action_admin_enqueue_scripts() { global $pagenow; - wp_enqueue_style( 'supportflow-admin', SupportFlow()->plugin_url . 'css/admin.css', array(), SupportFlow()->version ); - if ( 'post.php' == $pagenow || 'post-new.php' == $pagenow ) { - wp_enqueue_script( 'supportflow-plupload', SupportFlow()->plugin_url . 'js/plupload.js' , array( 'wp-plupload', 'jquery' ) ); - self::add_default_plupload_settings(); + $handle = SupportFlow()->enqueue_style( 'supportflow-admin', 'admin.css' ); + + if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) ) { + wp_enqueue_media(); + + $customers_autocomplete_handle = SupportFlow()->enqueue_script( 'supportflow-customers-autocomplete', 'customers-autocomplete.js', array( 'jquery', 'jquery-ui-autocomplete' ) ); + $ticket_attachment_handle = SupportFlow()->enqueue_script( 'supportflow-ticket-attachments', 'ticket_attachments.js' ); + $supportflow_tickets_handle = SupportFlow()->enqueue_script( 'supportflow-tickets', 'tickets.js' ); + $auto_save_handle = SupportFlow()->enqueue_script( 'supportflow-auto-save', 'auto_save.js', array( 'jquery', 'heartbeat' ) ); + + wp_localize_script( $customers_autocomplete_handle, 'SFCustomersAc', array( + 'ajax_url' => add_query_arg( 'action', SupportFlow()->extend->jsonapi->action, admin_url( 'admin-ajax.php' ) ), + 'get_customers_nonce' => wp_create_nonce( 'get_customers' ), + ) ); + + wp_localize_script( $ticket_attachment_handle, 'SFTicketAttachments', array( + 'frame_title' => __( 'Attach files', 'supportflow' ), + 'button_title' => __( 'Insert as attachment', 'supportflow' ), + 'remove_attachment' => __( 'Remove', 'supportflow' ), + 'sure_remove' => __( 'Are you sure want to remove this attachment?', 'supportflow' ), + ) ); + + wp_localize_script( $supportflow_tickets_handle, 'SFTickets', array( + 'no_title_msg' => __( 'You must need to specify the subject of the ticket', 'supportpress' ), + 'no_customer_msg' => __( 'You must need to add atleast one customer', 'supportpress' ), + 'pagenow' => $pagenow, + 'send_msg' => __( 'Send Message', 'supportflow' ), + 'add_private_note' => __( 'Add Private Note', 'supportflow' ), + ) ); + + wp_localize_script( $auto_save_handle, 'SFAutoSave', array( + 'ticket_id' => get_the_ID(), + ) ); + + } + + if ( 'post.php' == $pagenow ) { + $email_conversation_handle = SupportFlow()->enqueue_script( 'supportflow-email-conversation', 'email_conversation.js' ); + + wp_localize_script( $email_conversation_handle, 'SFEmailConversation', array( + 'post_id' => get_the_ID(), + 'sending_emails' => __( 'Please wait while sending E-Mail(s)', 'supportflow' ), + 'failed_sending' => __( 'Failed sending E-Mails', 'supportflow' ), + '_email_conversation_nonce' => wp_create_nonce( 'sf_email_conversation' ), + ) ); } } /** - * Sets up some default Plupload settings so we can upload media + * */ - private static function add_default_plupload_settings() { - global $wp_scripts; - - $defaults = array( - 'runtimes' => 'html5,silverlight,flash,html4', - 'file_data_name' => 'async-upload', - 'multiple_queues' => true, - 'url' => add_query_arg( 'post_id', get_the_id(), admin_url( 'admin-ajax.php', 'relative' ) ), - 'flash_swf_url' => includes_url( 'js/plupload/plupload.flash.swf' ), - 'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ), - 'filters' => array( array( 'title' => __( 'Allowed Files' ), 'extensions' => '*') ), - 'multipart' => true, - 'urlstream_upload' => true, - 'multipart_params' => array( - 'action' => 'upload-attachment', - '_wpnonce' => wp_create_nonce( 'media-form' ) - ) - ); + public function action_wp_ajax_sf_email_conversation() { + if ( false === check_ajax_referer( 'sf_email_conversation', '_email_conversation_nonce', false ) ) { + _e( 'Invalid request. Please try refreshing the page.', 'supportflow' ); + die; + } - $settings = array( - 'defaults' => $defaults, - 'browser' => array( - 'mobile' => wp_is_mobile(), - 'supported' => _device_can_upload(), - ) - ); + if ( ! isset( $_REQUEST['email_ids'] ) || ! isset( $_REQUEST['post_id'] ) ) { + _e( 'Invalid request. Please try refreshing the page.', 'supportflow' ); + die; + } + + $email_ids = SupportFlow()->extract_email_ids( $_REQUEST['email_ids'] ); + $ticket_id = (int) $_REQUEST['post_id']; - $script = 'var _wpPluploadSettings = ' . json_encode( $settings ) . ';'; - $data = $wp_scripts->get_data( 'wp-plupload', 'data' ); + if ( ! current_user_can( 'edit_post', $ticket_id ) ) { + _e( 'You are not allowed to edit this item.' ); + die; + } + + if ( empty( $email_ids ) ) { + _e( 'No valid E-Mail ID found', 'supportflow' ); + die; + } - if ( ! empty( $data ) ) - $script = "$data\n$script"; + SupportFlow()->extend->emails->email_conversation( $ticket_id, $email_ids ); + + _e( 'Successfully sented E-Mails', 'supportflow' ); + exit; - $wp_scripts->add_data( 'wp-plupload', 'data', $script ); } /** - * Filter the messages that appear to the user after they perform an action on a thread + * Add random characters to attachment uploaded through SupportFlow web UI + * + * @todo Conversion to a better way to determine if attachment if uploaded through SF web UI rather than HTTP referer + */ + function action_add_attachment( $attachment_id ) { + if ( empty( $_SERVER['HTTP_REFERER'] ) ) { + return; + } + + $post_type = SupportFlow()->post_type; + $referer = $_SERVER['HTTP_REFERER']; + + $url = parse_url( $referer ); + $path = $url['scheme'] . '://' . $url['host'] . $url['path']; + + if ( isset( $url['query'] ) ) { + parse_str( $url['query'], $query ); + } + + // Check if referred by SupportFlow ticket page + if ( admin_url( 'post-new.php' ) == $path ) { + if ( empty( $query['post_type'] ) || $query['post_type'] != $post_type ) { + return; + } + } elseif ( admin_url( 'post.php' ) == $path ) { + if ( empty( $query['post'] ) || get_post_type( (int) $query['post'] ) != $post_type ) { + return; + } + } else { + return; + } + + SupportFlow()->extend->attachments->secure_attachment_file( $attachment_id ); + } + + /** + * Filter the messages that appear to the user after they perform an action on a ticket */ public function filter_post_updated_messages( $messages ) { global $post; $messages[SupportFlow()->post_type] = array( - 0 => '', // Unused. Messages start at index 1. - 1 => __( 'Thread updated.', 'supportflow' ), - 2 => __( 'Custom field updated.', 'supportflow' ), - 3 => __( 'Custom field deleted.', 'supportflow' ), - 4 => __( 'Thread updated.', 'supportflow' ), + 0 => '', // Unused. Messages start at index 1. + 1 => __( 'Ticket updated.', 'supportflow' ), + 2 => __( 'Custom field updated.', 'supportflow' ), + 3 => __( 'Custom field deleted.', 'supportflow' ), + 4 => __( 'Ticket updated.', 'supportflow' ), /* translators: %s: date and time of the revision */ - 5 => isset($_GET['revision']) ? sprintf( __( 'Thread restored to revision from %s', 'supportflow' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, - 6 => __( 'Thread updated.', 'supportflow' ), - 7 => __( 'Thread updated.', 'supportflow' ), - 8 => __( 'Thread updated.', 'supportflow' ), - 9 => __( 'Thread updated.', 'supportflow' ), - 10 => __( 'Thread updated.', 'supportflow' ), + 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Ticket restored to revision from %s', 'supportflow' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, + 6 => __( 'Ticket updated.', 'supportflow' ), + 7 => __( 'Ticket updated.', 'supportflow' ), + 8 => __( 'Ticket updated.', 'supportflow' ), + 9 => __( 'Ticket updated.', 'supportflow' ), + 10 => __( 'Ticket updated.', 'supportflow' ), ); + return $messages; } + public function filter_heartbeat_received( $response, $data ) { + if ( + isset( $data['supportflow-autosave'] ) && + is_array( $data['supportflow-autosave'] ) && + isset( $data['supportflow-autosave']['ticket_id'] ) && + current_user_can( 'edit_post', (int) $data['supportflow-autosave']['ticket_id'] ) + ) { + // Save data received from client to the database as post meta + + $ticket_id = (int) $data['supportflow-autosave']['ticket_id']; + unset( $data['supportflow-autosave']['ticket_id'] ); + + if ( 'auto-draft' == get_post_status( $ticket_id ) ) { + wp_update_post( array( 'ID' => $ticket_id, 'post_status' => 'draft' ) ); + } + + foreach ( $data['supportflow-autosave'] as $element_id => $element_value ) { + update_post_meta( $ticket_id, "_sf_autosave_$element_id", $element_value ); + } + + echo esc_html( $data['supportflow-autosave']['post_title'] ); + if ( ! empty( $data['supportflow-autosave']['post_title'] ) ) { + wp_update_post( array( 'ID' => $ticket_id, 'post_title' => $data['supportflow-autosave']['post_title'] ) ); + } + } + + return $response; + } + + /** - * Add custom filters for the Manage Threads view + * + */ + public function filter_views( $views ) { + $post_type = SupportFlow()->post_type; + $statuses = SupportFlow()->post_statuses; + $status_slugs = array(); + + foreach ( $statuses as $status => $status_data ) { + if ( true == $status_data['show_tickets'] ) { + $status_slugs[] = $status; + } + } + + $wp_query = new WP_Query( array( + 'post_type' => $post_type, + 'post_parent' => 0, + 'posts_per_page' => 1, + 'post_status' => $status_slugs, + ) ); + $total_posts = $wp_query->found_posts; + + $class = empty( $class ) && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['show_sticky'] ) ? ' class="current"' : ''; + $view_all = "" . sprintf( _nx( 'All (%s)', 'All (%s)', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . ''; + + $post_statuses = SupportFlow()->post_statuses; + array_pop( $post_statuses ); + $post_statuses = "'" . implode( "','", array_map( 'sanitize_key', array_keys( $post_statuses ) ) ) . "'"; + + // @todo Only show "Mine" if the user is an agent + $mine_args = array( + 'post_type' => SupportFlow()->post_type, + 'author' => get_current_user_id(), + ); + $wp_query = new WP_Query( array( + 'post_type' => SupportFlow()->post_type, + 'author' => get_current_user_id(), + 'post_status' => $post_statuses, + 'posts_per_page' => 1, + ) ); + + $my_posts = $wp_query->found_posts; + $view_mine = '' . sprintf( _nx( 'Mine (%s)', 'Mine (%s)', $my_posts, 'posts' ), number_format_i18n( $my_posts ) ) . ''; + + $unassigned_args = array( + 'post_type' => SupportFlow()->post_type, + 'author' => 0, + ); + $wp_query = new WP_Query( array( + 'post_type' => SupportFlow()->post_type, + 'author' => 0, + 'post_status' => $post_statuses, + 'posts_per_page' => 1, + ) ); + + $unassigned_posts = $wp_query->found_posts; + $view_unassigned = '' . sprintf( _nx( 'Unassigned (%s)', 'Unassigned (%s)', $unassigned_posts, 'posts' ), number_format_i18n( $unassigned_posts ) ) . ''; + + // Put 'All' and 'Mine' at the beginning of the array + array_shift( $views ); + $views = array_reverse( $views ); + $views['unassigned'] = $view_unassigned; + $views['mine'] = $view_mine; + $views['all'] = $view_all; + $views = array_reverse( $views ); + + // Remove private option from filter links as they are just private replies to ticket + unset( $views['private'] ); + + return $views; + } + + /** + * Add custom filters for the Manage Tickets view */ public function action_restrict_manage_posts() { // Filter to specific agents $agent_dropdown_args = array( - 'show_option_all' => __( 'Show all agents', 'supportflow' ), - 'name' => 'author', - 'selected' => ( ! empty( $_REQUEST['author'] ) ) ? (int)$_REQUEST['author'] : false, - 'who' => 'authors', - ); + 'show_option_all' => __( 'Show all agents', 'supportflow' ), + 'name' => 'author', + 'selected' => ( ! empty( $_REQUEST['author'] ) ) ? (int) $_REQUEST['author'] : false, + 'who' => 'authors', + ); $agent_dropdown_args = apply_filters( 'supportflow_admin_agent_dropdown_args', $agent_dropdown_args ); wp_dropdown_users( $agent_dropdown_args ); + + // Filter to specify tag + $tax_slug = SupportFlow()->tags_tax; + $terms = get_terms( SupportFlow()->tags_tax, array( 'hide_empty' => false ) ); + + echo ""; + + + // Filter to specify E-Mail account + $email_accounts = SupportFlow()->extend->email_accounts->get_email_accounts( true ); + echo ""; + } /** @@ -143,8 +368,9 @@ public function action_restrict_manage_posts() { function filter_post_row_actions( $row_actions, $post ) { // Rename these actions - if ( isset( $row_actions['edit'] ) ) - $row_actions['edit'] = str_replace( __( 'Edit' ), __( 'View', 'supportflow' ), str_replace( __( 'Edit this item' ), __( 'View Thread', 'supportflow' ), $row_actions['edit'] ) ); + if ( isset( $row_actions['edit'] ) ) { + $row_actions['edit'] = str_replace( __( 'Edit' ), __( 'View', 'supportflow' ), str_replace( __( 'Edit this item' ), __( 'View Ticket', 'supportflow' ), $row_actions['edit'] ) ); + } // Save the trash action for the end if ( isset( $row_actions['trash'] ) ) { @@ -155,40 +381,45 @@ function filter_post_row_actions( $row_actions, $post ) { } // Allow an agent to easily close a ticket - $statuses = SupportFlow()->post_statuses; + $statuses = SupportFlow()->post_statuses; $status_slugs = array_keys( $statuses ); - $last_status = array_pop( $status_slugs ); - if ( !in_array( get_query_var( 'post_status' ), array( 'trash' ) ) ) { + $last_status = array_pop( $status_slugs ); + if ( ! in_array( get_query_var( 'post_status' ), array( 'trash' ) ) ) { - if ( $last_status == get_post_status( $post->ID ) ) + if ( $last_status == get_post_status( $post->ID ) ) { $change_to = $status_slugs[2]; - else + } else { $change_to = $last_status; + } - $args = array( - 'action' => 'change_status', - 'sf_nonce' => wp_create_nonce( 'sf-change-status' ), - 'post_status' => $change_to, - 'thread_id' => $post->ID, - 'post_type' => SupportFlow()->post_type, - ); + $args = array( + 'action' => 'change_status', + 'sf_nonce' => wp_create_nonce( 'sf-change-status' ), + 'post_status' => $change_to, + 'ticket_id' => $post->ID, + 'post_type' => SupportFlow()->post_type, + ); $action_link = add_query_arg( $args, admin_url( 'edit.php' ) ); if ( $last_status == $change_to ) { - $title_attr = esc_attr__( 'Close Thread', 'supportflow' ); + $title_attr = esc_attr__( 'Close Ticket', 'supportflow' ); $action_text = esc_html__( 'Close', 'supportflow' ); } else { - $title_attr = esc_attr__( 'Reopen Thread', 'supportflow' ); + $title_attr = esc_attr__( 'Reopen Ticket', 'supportflow' ); $action_text = esc_html__( 'Reopen', 'supportflow' ); } - $row_actions['change_status'] = '' . $action_text . ''; + + if ( current_user_can( 'edit_post', $post->ID ) ) { + $row_actions['change_status'] = '' . $action_text . ''; + } } // Actions we don't want unset( $row_actions['inline hide-if-no-js'] ); unset( $row_actions['view'] ); - if ( $trash_action ) + if ( $trash_action ) { $row_actions['trash'] = $trash_action; + } return $row_actions; } @@ -198,66 +429,88 @@ function filter_post_row_actions( $row_actions, $post ) { */ public function filter_bulk_actions( $actions ) { unset( $actions['edit'] ); + return $actions; } /** - * Handle which threads are show on the Manage Threads view when + * Handle which tickets are show on the Manage Tickets view when */ function action_pre_get_posts( $query ) { global $pagenow; - if ( 'edit.php' != $pagenow || !$query->is_main_query() ) + if ( 'edit.php' != $pagenow || ! $query->is_main_query() ) { return; + } - $statuses = SupportFlow()->post_statuses; - $status_slugs = array_keys( $statuses ); - $last_status = array_pop( $status_slugs ); + $statuses = SupportFlow()->post_statuses; + $status_slugs = array(); - // Order posts by post_modified if there's no orderby set - if ( !$query->get( 'orderby' ) ) { - $sort_order = array( - 'orderby' => 'modified', - ); - $sort_order['order'] = ( in_array( $query->get( 'post_status' ), array( 'trash', $last_status ) ) ) ? 'desc' : 'asc'; - foreach( $sort_order as $key => $value ) { - $query->set( $key, $value ); - $_GET[$key] = $value; + foreach ( $statuses as $status => $status_data ) { + if ( true == $status_data['show_tickets'] ) { + $status_slugs[] = $status; } } - // Do our own custom search handling so we can search against comment text + // Order posts by post_modified if there's no orderby set + if ( ! $query->get( 'orderby' ) ) { + $query->set( 'orderby', 'modified' ); + $query->set( 'order', 'DESC' ); + } + + // Do our own custom search handling so we can search against reply text if ( $search = $query->get( 's' ) ) { - // Get any comments that match our results - $args = array( - 'search' => $search, - 'status' => 'any', - ); - $matching_comments = SupportFlow()->get_comments( $args ); - $post_ids = wp_list_pluck( $matching_comments, 'comment_post_ID' ); - $args = array( - 's' => $search, - 'post_type' => SupportFlow()->post_type, - 'no_found_rows' => true, - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, - 'fields' => 'ids', - ); + // Get all replies that match our results + $args = array( + 'search' => $search, + 'status' => 'any', + ); + $matching_replies = SupportFlow()->get_replies( $args ); + $post_ids = wp_list_pluck( $matching_replies, 'post_parent' ); + + $args = array( + 's' => $search, + 'post_type' => SupportFlow()->post_type, + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'fields' => 'ids', + ); $post_query = new WP_Query( $args ); - if ( !is_wp_error( $post_query ) ) + if ( ! is_wp_error( $post_query ) ) { $post_ids = array_merge( $post_ids, $post_query->posts ); + } $query->set( 'post__in', $post_ids ); // Ignore the original search query add_filter( 'posts_search', array( $this, 'filter_posts_search' ) ); } - // Only show threads with the last status if the last status is set + // Only show tickets with the last status if the last status is set $post_status = $query->get( 'post_status' ); - if ( !$query->get( 's' ) && empty( $post_status ) ) + if ( ! $query->get( 's' ) && empty( $post_status ) ) { $query->set( 'post_status', $status_slugs ); + } + add_action( 'posts_clauses', array( $this, 'filter_author_clause' ), 10, 2 ); + + if ( isset( $_GET['email_account'] ) && ! empty( $_GET['email_account'] ) ) { + $query->set( 'meta_key', 'email_account' ); + $query->set( 'meta_value', (int) $_GET['email_account'] ); + } + } + + /* + * Show unassigned tickets when query author is 0 + */ + public function filter_author_clause( $clauses, $query ) { + + if ( isset( $query->query['author'] ) && 0 == $query->query['author'] ) { + $clauses['where'] .= ' AND post_author = 0 '; + } + + return $clauses; } /** @@ -269,25 +522,29 @@ public function filter_posts_search( $posts_search ) { /** * Handle $_GET actions in the admin - * - * @todo need a caps check too to make sure this user can edit the thread */ function handle_action_change_status() { - if ( ! isset( $_GET['action'], $_GET['sf_nonce'], $_GET['post_status'], $_GET['thread_id'] ) ) + if ( ! isset( $_GET['action'], $_GET['sf_nonce'], $_GET['post_status'], $_GET['ticket_id'] ) ) { return; + } - if ( ! wp_verify_nonce( $_GET['sf_nonce'], 'sf-change-status' ) ) + if ( ! wp_verify_nonce( $_GET['sf_nonce'], 'sf-change-status' ) ) { wp_die( __( "Doin' something phishy, huh?", 'supportflow' ) ); + } - $post_status = sanitize_key( $_GET['post_status'] ); - $thread_id = (int)$_GET['thread_id']; + $ticket_id = (int) $_GET['ticket_id']; - $new_thread = array( - 'ID' => $thread_id, - 'post_status' => $post_status, - ); - wp_update_post( $new_thread ); + if ( ! current_user_can( 'edit_post', $ticket_id ) ) { + wp_die( __( 'You are not allowed to edit this item.' ) ); + } + + $post_status = sanitize_key( $_GET['post_status'] ); + $new_ticket = array( + 'ID' => $ticket_id, + 'post_status' => $post_status, + ); + wp_update_post( $new_ticket ); wp_safe_redirect( wp_get_referer() ); exit; } @@ -295,133 +552,501 @@ function handle_action_change_status() { /** * Manipulate the meta boxes appearing on the edit post view * - * When creating a new thread, you should be able to: + * When creating a new ticket, you should be able to: * - * When updating an existing thread, you should be able to: + * When updating an existing ticket, you should be able to: * */ public function action_add_meta_boxes() { - if ( ! $this->is_edit_screen() ) + global $pagenow; + + if ( ! $this->is_edit_screen() ) { return; + } - $respondents_box = 'tagsdiv-' . SupportFlow()->respondents_tax; - remove_meta_box( 'submitdiv', SupportFlow()->post_type, 'side' ); - remove_meta_box( $respondents_box, SupportFlow()->post_type, 'side' ); - remove_meta_box( 'commentstatusdiv', SupportFlow()->post_type, 'normal' ); - remove_meta_box( 'slugdiv', SupportFlow()->post_type, 'normal' ); - remove_meta_box( 'commentsdiv', SupportFlow()->post_type, 'normal' ); + $customers_box = 'tagsdiv-' . SupportFlow()->customers_tax; + remove_meta_box( 'submitdiv', SupportFlow()->post_type, 'side' ); + remove_meta_box( $customers_box, SupportFlow()->post_type, 'side' ); + remove_meta_box( 'slugdiv', SupportFlow()->post_type, 'normal' ); - add_meta_box( 'supportflow-details', __( 'Details', 'supportflow' ), array( $this, 'meta_box_details' ), SupportFlow()->post_type, 'side' ); + add_meta_box( 'supportflow-details', __( 'Details', 'supportflow' ), array( $this, 'meta_box_details' ), SupportFlow()->post_type, 'side', 'high' ); add_meta_box( 'supportflow-subject', __( 'Subject', 'supportflow' ), array( $this, 'meta_box_subject' ), SupportFlow()->post_type, 'normal' ); - add_meta_box( 'supportflow-respondents', __( 'Respondents', 'supportflow' ), array( $this, 'meta_box_respondents' ), SupportFlow()->post_type, 'normal' ); - add_meta_box( 'supportflow-comments', __( 'Commentss', 'supportflow' ), array( $this, 'meta_box_comments' ), SupportFlow()->post_type, 'normal' ); + add_meta_box( 'supportflow-customers', __( 'Customers', 'supportflow' ), array( $this, 'meta_box_customers' ), SupportFlow()->post_type, 'normal' ); + add_meta_box( 'supportflow-cc-bcc', __( 'CC and BCC', 'supportflow' ), array( $this, 'meta_box_cc_bcc' ), SupportFlow()->post_type, 'normal' ); + add_meta_box( 'supportflow-replies', __( 'Replies', 'supportflow' ), array( $this, 'meta_box_replies' ), SupportFlow()->post_type, 'normal' ); + + if ( 'post.php' == $pagenow ) { + add_meta_box( 'supportflow-other-customers-tickets', __( 'Customer(s) recent Tickets', 'supportflow' ), array( $this, 'meta_box_other_customers_tickets' ), SupportFlow()->post_type, 'side' ); + add_meta_box( 'supportflow-forward_conversation', __( 'Forward this conversation', 'supportflow' ), array( $this, 'meta_box_email_conversation' ), SupportFlow()->post_type, 'side' ); + } + } + + public function meta_box_other_customers_tickets() { + $ticket_customers = SupportFlow()->get_ticket_customers( get_the_ID(), array( 'fields' => 'slugs' ) ); + $statuses = SupportFlow()->post_statuses; + $status_slugs = array_keys($statuses); + + $table = new SupportFlow_Table( '', false, false ); + + if ( empty( $ticket_customers ) ) { + $tickets = array(); + + } else { + $args = array( + 'post_type' => SupportFlow()->post_type, + 'post_parent' => 0, + 'post_status' => $status_slugs, + 'posts_per_page' => 10, + 'post__not_in' => array( get_the_id() ), + 'tax_query' => array( + array( + 'taxonomy' => SupportFlow()->customers_tax, + 'field' => 'slug', + 'terms' => $ticket_customers, + ), + ), + ); + + $wp_query = new WP_Query( $args ); + $tickets = $wp_query->posts; + } + + $no_items = __( 'No recent tickets found.', 'supportflow' ); + $table->set_no_items( $no_items ); + + $table->set_columns( array( + 'title' => __( 'Subject', 'supportflow' ), + 'status' => __( 'Status', 'supportflow' ), + ) ); + + $data = array(); + foreach ( $tickets as $ticket ) { + $post_date = strtotime( $ticket->post_date ); + $post_modified = strtotime( $ticket->post_modified ); + $title = '' . esc_html( $ticket->post_title ) . ''; + $title = "" . $title . ""; + $data[] = array( + 'title' => $title, + 'status' => $statuses[$ticket->post_status]['label'], + ); + } + $table->set_data( $data ); + $table->display(); + } + + public function meta_box_email_conversation() { + ?> +
+' . __( 'Please describe what this thread is about in several words', 'supportflow' ) . '
'; + + + ++ +
+ + get_ticket_customers( get_the_ID(), array( 'fields' => 'emails' ) ); + $customers_string = implode( ', ', $customers ); + $customers_string .= empty( $customers_string ) ? '' : ', '; + } + echo '' . __( 'Enter each customer email address, separated with a comma', 'supportflow' ) . '
'; + } - $respondents = SupportFlow()->get_thread_respondents( get_the_ID(), array( 'fields' => 'emails' ) ); - $respondents_string = implode( ', ', $respondents ); - $placeholder = __( 'Who are you starting a conversation with?', 'supportflow' ); - echo '' . __( 'Enter each respondent email address, separated with a comma', 'supportflow' ) . '
'; + /** + * Add a form element where you can choose cc and bcc receiver of reply + */ + public function meta_box_cc_bcc() { + $cc_value = get_post_meta( get_the_ID(), '_sf_autosave_cc', true ); + $bcc = get_post_meta( get_the_ID(), '_sf_autosave_bcc', true ); + ?> ++ + +
' . __( 'Conversation', 'supportflow' ) . '