From 35a59101baac7e12e018973acc41981110ca2e10 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 16:39:33 -0500 Subject: [PATCH 001/485] i18n fix --- classes/class-supportflow-admin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index b1baa66..8540eb3 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -409,7 +409,7 @@ public function meta_box_comments() { echo '
'; echo '
'; - echo '
' . __( 'Drop a file in the message to attach it' ) . '
'; + echo '
' . __( 'Drop a file in the message to attach it', 'supportflow' ) . '
'; echo '
    '; echo '
'; echo ''; From d2691bf88e0dc8b673a8f99ec4a8939177cd7d0e Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 17:14:01 -0500 Subject: [PATCH 002/485] Moar i18n fixes --- supportflow.php | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/supportflow.php b/supportflow.php index acd7486..2143484 100644 --- a/supportflow.php +++ b/supportflow.php @@ -8,7 +8,7 @@ * Author URI: * Version: 0.1 * - * Text Domain: support-flow + * Text Domain: supportflow * Domain Path: /languages/ */ @@ -169,20 +169,20 @@ private function setup_globals() { $this->post_statuses = apply_filters( 'supportflow_thread_post_statuses', array( 'sf_new' => array( - 'label' => __( 'New', 'support-flow' ), - 'label_count' => _n_noop( 'New (%s)', 'New (%s)', 'support-flow' ), + 'label' => __( 'New', 'supportflow' ), + 'label_count' => _n_noop( 'New (%s)', 'New (%s)', 'supportflow' ), ), 'sf_open' => array( - 'label' => __( 'Open', 'support-flow' ), - 'label_count' => _n_noop( 'Open (%s)', 'Open (%s)', 'support-flow' ), + 'label' => __( 'Open', 'supportflow' ), + 'label_count' => _n_noop( 'Open (%s)', 'Open (%s)', 'supportflow' ), ), 'sf_pending' => array( - 'label' => __( 'Pending', 'support-flow' ), - 'label_count' => _n_noop( 'Pending (%s)', 'Pending (%s)', 'support-flow' ), + 'label' => __( 'Pending', 'supportflow' ), + 'label_count' => _n_noop( 'Pending (%s)', 'Pending (%s)', 'supportflow' ), ), 'sf_closed' => array( - 'label' => __( 'Closed', 'support-flow' ), - 'label_count' => _n_noop( 'Closed (%s)', 'Closed (%s)', 'support-flow' ), + 'label' => __( 'Closed', 'supportflow' ), + 'label_count' => _n_noop( 'Closed (%s)', 'Closed (%s)', 'supportflow' ), ), ) ); @@ -254,18 +254,18 @@ private function setup_actions() { public function action_init_register_post_type() { register_post_type( $this->post_type, array( 'labels' => array( - 'menu_name' => __( 'SupportFlow', 'support-flow' ), - 'name' => __( 'Threads', 'support-flow' ), - 'singular_name' => __( 'Thread', 'support-flow' ), - 'all_items' => __( 'All Threads', 'support-flow' ), - 'add_new' => __( 'New Thread', 'support-flow' ), - 'add_new_item' => __( 'Start New Thread', 'support-flow' ), - 'edit_item' => __( 'Discussion', 'support-flow' ), - 'new_item' => __( 'New Thread', 'support-flow' ), - 'view_item' => __( 'View Thread', 'support-flow' ), - 'search_items' => __( 'Search Threads', 'support-flow' ), - 'not_found' => __( 'No threads found', 'support-flow' ), - 'not_found_in_trash' => __( 'No threads found in trash', 'support-flow' ), + 'menu_name' => __( 'SupportFlow', 'supportflow' ), + 'name' => __( 'Threads', 'supportflow' ), + 'singular_name' => __( 'Thread', 'supportflow' ), + 'all_items' => __( 'All Threads', 'supportflow' ), + 'add_new' => __( 'New Thread', 'supportflow' ), + 'add_new_item' => __( 'Start New Thread', 'supportflow' ), + 'edit_item' => __( 'Discussion', 'supportflow' ), + 'new_item' => __( 'New Thread', 'supportflow' ), + 'view_item' => __( 'View Thread', 'supportflow' ), + 'search_items' => __( 'Search Threads', 'supportflow' ), + 'not_found' => __( 'No threads found', 'supportflow' ), + 'not_found_in_trash' => __( 'No threads found in trash', 'supportflow' ), ), 'public' => true, 'menu_position' => 3, @@ -281,12 +281,12 @@ public function action_init_register_post_type() { public function action_init_register_taxonomies() { $args = array( - 'label' => __( 'Respondents', 'support-flow' ), + 'label' => __( 'Respondents', 'supportflow' ), 'labels' => array( - 'search_items' => __( 'Search Respondents', 'support-flow' ), - 'edit_item' => __( 'Edit Respondent', 'support-flow' ), - 'update_item' => __( 'Update Respondent', 'support-flow' ), - 'add_new_item' => __( 'Add New Respondent', 'support-flow' ), + 'search_items' => __( 'Search Respondents', 'supportflow' ), + 'edit_item' => __( 'Edit Respondent', 'supportflow' ), + 'update_item' => __( 'Update Respondent', 'supportflow' ), + 'add_new_item' => __( 'Add New Respondent', 'supportflow' ), ), 'public' => true, 'show_in_nav_menus' => true, From 2b737846ef6c3b7268abe8ccf62ada28e49a2185 Mon Sep 17 00:00:00 2001 From: Greg Johnson Date: Sat, 3 Nov 2012 17:05:59 -0500 Subject: [PATCH 003/485] Added Date Opened, and Last Activity details to the Details sidebar widget on view Discussion page. Added styling to reveal the widget handle as well --- classes/class-supportflow-admin.php | 20 +++++++++++++++++++- css/admin.css | 13 +++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index b1baa66..9256cb1 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -323,7 +323,22 @@ public function action_add_meta_boxes() { public function meta_box_details() { global $pagenow; + // Display Meta Data for Post echo '
'; + + // Date Created and Last Activity for Existing Posts + if( 'post.php' == $pagenow ) { + + $modified_gmt = get_post_modified_time( 'U', true, $thread_id ); + $last_activity = sprintf( __( '%s ago', 'supportflow' ), human_time_diff( $modified_gmt ) ); + + echo '
'; + echo ''; + echo '' . get_the_date() . ' ' .get_the_time() . ''; + echo '
' . __( 'Last Activity', 'SupportFlow' ) . ': ' . $last_activity . '
'; + echo '
'; + } + // Post status dropdown $current_status = get_post_status( get_the_ID() ); echo '
'; @@ -334,6 +349,7 @@ public function meta_box_details() { } echo ''; echo '
'; + // Agent assignment dropdown $post_author = get_post( get_the_ID() )->post_author; echo '
'; @@ -348,8 +364,10 @@ public function meta_box_details() { wp_dropdown_users( $args ); echo '
'; - echo '
'; + echo '
'; // end div#misc-publishing-actions + + // Start/Update Thread (submit) if ( 'post-new.php' == $pagenow ) $submit_text = __( 'Start Thread', 'supportflow' ); else diff --git a/css/admin.css b/css/admin.css index 88327b2..8d308fc 100644 --- a/css/admin.css +++ b/css/admin.css @@ -42,6 +42,11 @@ display:none; } +#supportflow-details .handlediv, +#supportflow-details h3.hndle { + display: block; +} + #supportflow-details #misc-publishing-actions label { width: 50px; display: inline-block; @@ -51,6 +56,14 @@ width: 100px; } +#supportflow-details #misc-publishing-actions span.the-date { + font-weight: bold; +} + +#supportflow-details #misc-publishing-actions div.last-activity { + font-size: 11px; +} + #supportflow-subject h4, #supportflow-respondents h4, #supportflow-comments h4 { From bf8479e9880b8c70abe70de88f8d718bedbf1acf Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 17:20:33 -0500 Subject: [PATCH 004/485] Create Thread: New threads must have subjects See #52 --- supportflow.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/supportflow.php b/supportflow.php index 2143484..b06d8cf 100644 --- a/supportflow.php +++ b/supportflow.php @@ -242,6 +242,8 @@ private function setup_actions() { add_action( 'init', array( $this, 'action_init_register_taxonomies' ) ); add_action( 'init', array( $this, 'action_init_register_post_statuses' ) ); + add_filter( 'wp_insert_post_empty_content', array( $this, 'filter_wp_insert_post_empty_content' ), 10, 2 ); + do_action_ref_array( 'supportflow_after_setup_actions', array( &$this ) ); } @@ -412,6 +414,20 @@ public function create_thread( $args ) { return $thread_id; } + /** + * Threads must have subjects + */ + public function filter_wp_insert_post_empty_content( $maybe_empty, $postarr ) { + + if ( $this->post_type != $postarr['post_type'] ) + return $maybe_empty; + + if ( empty( $postarr['post_title'] ) ) + return true; + + return $maybe_empty; + } + /** * @todo This should produce an object with thread details, respondents, and comments */ From 8429bd1471c41fb433a2d8716501d4b7305e2991 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 17:26:09 -0500 Subject: [PATCH 005/485] Fix error "Undefined variable" See #79 --- classes/class-supportflow-admin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index d834461..990eecd 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -329,7 +329,7 @@ public function meta_box_details() { // Date Created and Last Activity for Existing Posts if( 'post.php' == $pagenow ) { - $modified_gmt = get_post_modified_time( 'U', true, $thread_id ); + $modified_gmt = get_post_modified_time( 'U', true, get_the_ID() ); $last_activity = sprintf( __( '%s ago', 'supportflow' ), human_time_diff( $modified_gmt ) ); echo '
'; From a7fda3d841e5da8762236a3b7b3a3074431f1351 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 17:26:58 -0500 Subject: [PATCH 006/485] Spacing fix --- classes/class-supportflow-admin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index 990eecd..cf81a17 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -334,7 +334,7 @@ public function meta_box_details() { echo '
'; echo ''; - echo '' . get_the_date() . ' ' .get_the_time() . ''; + echo '' . get_the_date() . ' ' . get_the_time() . ''; echo '
' . __( 'Last Activity', 'SupportFlow' ) . ': ' . $last_activity . '
'; echo '
'; } From 7b85d3ca90aa1c5b38fecccf1ccf50e5dbe924ac Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 18:23:41 -0500 Subject: [PATCH 007/485] Manage Threads: Apply the most recent public comment as the excerpt for mode=excerpt Closes #26 --- classes/class-supportflow-admin.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index cf81a17..a190a59 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -39,11 +39,14 @@ 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' ) ); } /** @@ -536,6 +539,17 @@ public function manage_sortable_columns( $columns ) { return $columns; } + /** + * Use the most recent public comment as the post excerpt + * on the Manage Threads view so mode=excerpt works well + */ + public function filter_get_the_excerpt( $orig ) { + if ( $comment = array_pop( SupportFlow()->get_thread_comments( get_the_ID() ) ) ) + return $comment->comment_author .': "' . wp_trim_excerpt( $comment->comment_content ) . '"'; + else + return $orig; + } + /** * Produce the column values for the custom columns we created */ From af2d51ffa0d5574c16c06258a4edbca9a4ba0a54 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 18:42:02 -0500 Subject: [PATCH 008/485] Manage Threads: The count for 'All' shouldn't include the closed status See #61 --- classes/class-supportflow-admin.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index a190a59..9ec6242 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -27,6 +27,7 @@ public function setup_actions() { 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' ) ); @@ -124,6 +125,24 @@ public function filter_post_updated_messages( $messages ) { return $messages; } + /** + * + */ + public function filter_views( $views ) { + + // The 'all' count shouldn't include closed posts + $post_type = SupportFlow()->post_type; + $num_posts = wp_count_posts( $post_type, 'readable' ); + $total_posts = array_sum( (array)$num_posts ); + foreach ( get_post_stati( array('show_in_admin_all_list' => false) ) as $state ) + $total_posts -= $num_posts->$state; + $total_posts -= $num_posts->sf_closed; + $class = empty( $class ) && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['show_sticky'] ) ? ' class="current"' : ''; + $views['all'] = "" . sprintf( _nx( 'All (%s)', 'All (%s)', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . ''; + + return $views; + } + /** * Add custom filters for the Manage Threads view */ From a92bff3e9bf534cb49e952cde910732c64e07c24 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 18:48:18 -0500 Subject: [PATCH 009/485] Permissions: We don't need this named function for now See #78 --- classes/class-supportflow-permissions.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/classes/class-supportflow-permissions.php b/classes/class-supportflow-permissions.php index 1503f88..91bd3a7 100644 --- a/classes/class-supportflow-permissions.php +++ b/classes/class-supportflow-permissions.php @@ -161,17 +161,4 @@ public function get_cap( $cap ) { } } -SupportFlow()->extend->permissions = new SupportFlow_Permissions(); - -/** - * Get the name of an individual capability. - * - * @since 0.1 - * @uses SupportFlow, SupportFlow_Permissions::get_cap - * - * @param string $cap Capability to get. - * @return bool|string Internal name of the capability on success; false on failure; - */ -function sf_get_cap( $cap ) { - return SupportFlow()->extend->permissions->get_cap( $cap ); -} \ No newline at end of file +SupportFlow()->extend->permissions = new SupportFlow_Permissions(); \ No newline at end of file From ba18861f4355fad69e78b547177509270157cf8d Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sat, 3 Nov 2012 18:48:59 -0500 Subject: [PATCH 010/485] Permissions: Filters should just be named explicitly --- classes/class-supportflow-permissions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/class-supportflow-permissions.php b/classes/class-supportflow-permissions.php index 91bd3a7..4a52a14 100644 --- a/classes/class-supportflow-permissions.php +++ b/classes/class-supportflow-permissions.php @@ -60,7 +60,7 @@ public function setup_actions() { */ private function _setup_caps() { // Setup the default caps for SupportFlow - $this->_caps = apply_filters( 'sf_caps', array( + $this->_caps = apply_filters( 'supportflow_caps', array( 'close_others_threads' => 'sf_close_others_threads', 'open_others_threads' => 'sf_open_others_threads', 'reopen_others_threads' => 'sf_reopen_others_threads', @@ -72,7 +72,7 @@ private function _setup_caps() { ) ); // Map the default caps onto WordPress roles - $this->_role_cap_map = apply_filters( 'sf_role_cap_map', array( + $this->_role_cap_map = apply_filters( 'supportflow_role_cap_map', array( 'administrator' => $this->get_caps(), // Apply all caps 'editor' => $this->get_caps(), // Apply all caps 'author' => array( From 8a522347afb9616a0669234e4e00be8f6920864e Mon Sep 17 00:00:00 2001 From: jeremyfelt Date: Sat, 3 Nov 2012 17:34:32 -0700 Subject: [PATCH 011/485] Initial handling of attachments First draft at the handling of attachments. Grabs the message structure, processes the part containing the attachment data, creates a new temporary file, then sideloads it into WordPress. The next step will be to dynamically handle the structure of incoming attachments properly as it won't always be one jpeg. --- classes/class-supportflow-email-replies.php | 31 ++++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index 3d7c227..c493ff1 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -50,10 +50,11 @@ public function download_and_process_email_replies( $connection_details ) { for( $i = 1; $i <= $email_count; $i++ ) { $email = new stdClass; $email->headers = imap_headerinfo( $this->imap_connection, $i ); + $email->structure = imap_fetchstructure( $this->imap_connection, $i ); $email->body = $this->get_body_from_connection( $this->imap_connection, $i ); // @todo Confirm this a message we want to process - $ret = $this->process_email( $email ); + $ret = $this->process_email( $email, $i ); // If it was successful, move the email to the archive if ( $ret ) { imap_mail_move( $this->imap_connection, $i, $archive ); @@ -65,10 +66,28 @@ public function download_and_process_email_replies( $connection_details ) { /** * Given an email object, maybe create a new ticket - * - * @todo upload the attachment if there is one */ - public function process_email( $email ) { + public function process_email( $email, $i ) { + + // Initial handling of one jpg attachment via hard coded part 2 + $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, '2' ); + + // The raw data from imap is base64 encoded, but php-imap has us covered! + $attachment_data = imap_base64( $raw_attachment_data ); + + + $temp_file = get_temp_dir() . time() . '_supportflow_temp.tmp'; + touch( $temp_file ); + $temp_handle = fopen( $temp_file, 'w+' ); + fwrite( $temp_handle, $attachment_data ); + fclose( $temp_handle ); + + $file_array = array( + 'tmp_name' => $temp_file, + 'name' => 'attached-email.jpg', // @todo this should be the real attachment name + ); + + $new_attachment_id = media_handle_sideload( $file_array, NULL ); if ( ! empty( $email->headers->subject ) ) $subject = $email->headers->subject; @@ -118,6 +137,10 @@ public function process_email( $email ) { ); $thread_id = SupportFlow()->create_thread( $new_thread_args ); } + + // Associate the thread ID as the parent to our new attachment + wp_update_post( array( 'ID' => $new_attachment_id, 'post_parent' => $thread_id, 'post_status' => 'inherit' ) ); + $new_comment = array_pop( SupportFlow()->get_thread_comments( $thread_id ) ); // Add anyone else that was in the 'to' or 'cc' fields as respondents From 529784f2ff47b1ad9f2164b0d7d376840eab1ff7 Mon Sep 17 00:00:00 2001 From: jeremyfelt Date: Sat, 3 Nov 2012 18:25:01 -0700 Subject: [PATCH 012/485] Improved handling of incoming attachments Attachment data is now processed dynamically. This allows for multiple file types, proper file names, and multiple attachments in one email. There is no strict checking, so the code will need to be tightened up a bit before we can trust it. Also need to associate the new attachment IDs with comments in addition to the threads for better interface display. --- classes/class-supportflow-email-replies.php | 50 +++++++++++++-------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index c493ff1..0f31b07 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -69,25 +69,37 @@ public function download_and_process_email_replies( $connection_details ) { */ public function process_email( $email, $i ) { - // Initial handling of one jpg attachment via hard coded part 2 - $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, '2' ); - - // The raw data from imap is base64 encoded, but php-imap has us covered! - $attachment_data = imap_base64( $raw_attachment_data ); - + $accepted_attachments = array( 'PDF', 'JPEG', 'PNG', 'GIF' ); + $new_attachment_ids = array(); + + $k = 0; + foreach( $email->structure->parts as $email_part ) { + if ( 'ATTACHMENT' == $email_part->disposition && in_array( $email_part->subtype, $accepted_attachments ) ) { + // We need to add 1 to our array key each time to get the correct email part + $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, $k+1 ); + + // The raw data from imap is base64 encoded, but php-imap has us covered! + $attachment_data = imap_base64( $raw_attachment_data ); + + $temp_file = get_temp_dir() . time() . '_supportflow_temp.tmp'; + touch( $temp_file ); + $temp_handle = fopen( $temp_file, 'w+' ); + fwrite( $temp_handle, $attachment_data ); + fclose( $temp_handle ); + + $file_array = array( + 'tmp_name' => $temp_file, + 'name' => $email_part->dparameters[0]->value, + ); - $temp_file = get_temp_dir() . time() . '_supportflow_temp.tmp'; - touch( $temp_file ); - $temp_handle = fopen( $temp_file, 'w+' ); - fwrite( $temp_handle, $attachment_data ); - fclose( $temp_handle ); + $upload_result = media_handle_sideload( $file_array, NULL ); - $file_array = array( - 'tmp_name' => $temp_file, - 'name' => 'attached-email.jpg', // @todo this should be the real attachment name - ); + if ( ! is_wp_error( $upload_result ) ) + $new_attachment_ids[] = $upload_result; - $new_attachment_id = media_handle_sideload( $file_array, NULL ); + } + $k++; + } if ( ! empty( $email->headers->subject ) ) $subject = $email->headers->subject; @@ -138,8 +150,10 @@ public function process_email( $email, $i ) { $thread_id = SupportFlow()->create_thread( $new_thread_args ); } - // Associate the thread ID as the parent to our new attachment - wp_update_post( array( 'ID' => $new_attachment_id, 'post_parent' => $thread_id, 'post_status' => 'inherit' ) ); + foreach ( $new_attachment_ids as $new_attachment_id ) { + // Associate the thread ID as the parent to our new attachment + wp_update_post( array( 'ID' => $new_attachment_id, 'post_parent' => $thread_id, 'post_status' => 'inherit' ) ); + } $new_comment = array_pop( SupportFlow()->get_thread_comments( $thread_id ) ); From 512e4b5f56dc46205e2e55deb4ef1900c58f5ac6 Mon Sep 17 00:00:00 2001 From: jeremyfelt Date: Sat, 3 Nov 2012 18:32:00 -0700 Subject: [PATCH 013/485] Do some checking to make sure the object we're working with is valid enough --- classes/class-supportflow-email-replies.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index 0f31b07..893abe0 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -69,11 +69,17 @@ public function download_and_process_email_replies( $connection_details ) { */ public function process_email( $email, $i ) { + // @todo can probably make this the same as accepted MIME types in WordPress $accepted_attachments = array( 'PDF', 'JPEG', 'PNG', 'GIF' ); $new_attachment_ids = array(); $k = 0; foreach( $email->structure->parts as $email_part ) { + + // We should at least be dealing with something that resembles an email object at this point + if ( ! isset( $email_part->disposition ) || ! isset( $email_part->subtype ) || ! isset( $email_part->dparameters[0]->value ) ) + continue; + if ( 'ATTACHMENT' == $email_part->disposition && in_array( $email_part->subtype, $accepted_attachments ) ) { // We need to add 1 to our array key each time to get the correct email part $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, $k+1 ); From f39fdfcd0659ecc6c7a6fd63540c7d280ef337f6 Mon Sep 17 00:00:00 2001 From: jeremyfelt Date: Sat, 3 Nov 2012 18:39:44 -0700 Subject: [PATCH 014/485] Add new attachment IDs as meta data to the newly created comment on this thread --- classes/class-supportflow-email-replies.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index 893abe0..171d5d3 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -179,9 +179,10 @@ public function process_email( $email, $i ) { // Store the original email ID so we don't accidentally dupe it $email_id = trim( $email->headers->message_id, '<>' ); - if ( is_object( $new_comment ) ) + if ( is_object( $new_comment ) ) { update_comment_meta( $new_comment->comment_ID, self::email_id_key, $email_id ); - + update_comment_meta( $new_comment->comment_ID, '_sf_attachment_ids', $new_attachment_ids ); + } return true; } From 120fd49e3248659c24409bb877e269ecfdd13f8a Mon Sep 17 00:00:00 2001 From: jeremyfelt Date: Sat, 3 Nov 2012 18:41:53 -0700 Subject: [PATCH 015/485] comment meta key s/_sf_attachment_ids/attachment_ids --- classes/class-supportflow-email-replies.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index 171d5d3..5a173b7 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -181,7 +181,7 @@ public function process_email( $email, $i ) { $email_id = trim( $email->headers->message_id, '<>' ); if ( is_object( $new_comment ) ) { update_comment_meta( $new_comment->comment_ID, self::email_id_key, $email_id ); - update_comment_meta( $new_comment->comment_ID, '_sf_attachment_ids', $new_attachment_ids ); + update_comment_meta( $new_comment->comment_ID, 'attachment_ids', $new_attachment_ids ); } return true; } From 4339bdaa80b5413949c1b478da672969bf903266 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 4 Nov 2012 17:55:36 +0000 Subject: [PATCH 016/485] Move the part number up a notch to account for email structure. 0 is headers, 1 is message, 2 and up are attachments. This needs to be tested more. --- classes/class-supportflow-email-replies.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index 5a173b7..3662e05 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -81,8 +81,9 @@ public function process_email( $email, $i ) { continue; if ( 'ATTACHMENT' == $email_part->disposition && in_array( $email_part->subtype, $accepted_attachments ) ) { - // We need to add 1 to our array key each time to get the correct email part - $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, $k+1 ); + // We need to add 2 to our array key each time to get the correct email part + //@todo this needs more testing with different emails, should be smarter about which parts + $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, $k+2 ); // The raw data from imap is base64 encoded, but php-imap has us covered! $attachment_data = imap_base64( $raw_attachment_data ); @@ -199,4 +200,4 @@ public function get_body_from_connection( $connection, $num, $type = 'text/plain } -SupportFlow()->extend->email_replies = new SupportFlow_Email_Replies(); \ No newline at end of file +SupportFlow()->extend->email_replies = new SupportFlow_Email_Replies(); From d1f254080e0efa2fdebc26be914f2100387bf1a5 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sun, 4 Nov 2012 20:03:39 -0600 Subject: [PATCH 017/485] get_thread_from_secret() should always return an integer --- classes/class-supportflow-email-replies.php | 2 +- supportflow.php | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index 3662e05..c97da8e 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -138,7 +138,7 @@ public function process_email( $email, $i ) { // Check to see if this message was in response to an existing thread $thread_id = false; if ( preg_match( '#\[([a-zA-Z0-9]{8})\]$#', $subject, $matches ) ) - $thread_id = (int)SupportFlow()->get_thread_from_secret( $matches[1] ); + $thread_id = SupportFlow()->get_thread_from_secret( $matches[1] ); if ( $thread_id ) { $message_args = array( diff --git a/supportflow.php b/supportflow.php index b06d8cf..0d2f912 100644 --- a/supportflow.php +++ b/supportflow.php @@ -667,7 +667,14 @@ public function get_secret_for_thread( $thread_id ) { public function get_thread_from_secret( $secret ) { global $wpdb; $thread_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key=%s AND meta_value=%s", $this->thread_secret_key, $secret ) ); - return ( $thread_id ) ? $thread_id : false; + return ( $thread_id ) ? (int)$thread_id : 0; + } + + /** + * Get the special capability given a string + */ + public function get_cap( $cap ) { + return SupportFlow()->extend->permissions->get_cap( $cap ); } } From 04cf686e978790bb7a7a2cfa33466efcf002b93a Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sun, 4 Nov 2012 20:21:22 -0600 Subject: [PATCH 018/485] Reply by Email: Don't check the filetype on importing because it's properly checked by media_handle_sideload(), and wp_handle_sideload() Closes #71 --- classes/class-supportflow-email-replies.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index c97da8e..523c6c3 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -69,8 +69,6 @@ public function download_and_process_email_replies( $connection_details ) { */ public function process_email( $email, $i ) { - // @todo can probably make this the same as accepted MIME types in WordPress - $accepted_attachments = array( 'PDF', 'JPEG', 'PNG', 'GIF' ); $new_attachment_ids = array(); $k = 0; @@ -80,7 +78,7 @@ public function process_email( $email, $i ) { if ( ! isset( $email_part->disposition ) || ! isset( $email_part->subtype ) || ! isset( $email_part->dparameters[0]->value ) ) continue; - if ( 'ATTACHMENT' == $email_part->disposition && in_array( $email_part->subtype, $accepted_attachments ) ) { + if ( 'ATTACHMENT' == $email_part->disposition ) { // We need to add 2 to our array key each time to get the correct email part //@todo this needs more testing with different emails, should be smarter about which parts $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, $k+2 ); @@ -101,7 +99,9 @@ public function process_email( $email, $i ) { $upload_result = media_handle_sideload( $file_array, NULL ); - if ( ! is_wp_error( $upload_result ) ) + if ( is_wp_error( $upload_result ) ) + WP_CLI::warning( $upload_result->get_error_message() ); + else $new_attachment_ids[] = $upload_result; } From 24431b41fdb521f0b1c0b22325c52fa20c108724 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sun, 4 Nov 2012 20:25:51 -0600 Subject: [PATCH 019/485] User Widget: Hide from activation by removing the plugin headers. It's not ready for activation yet Closes #77 --- supportflow-user-widget.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/supportflow-user-widget.php b/supportflow-user-widget.php index ec19e95..3b7bc66 100644 --- a/supportflow-user-widget.php +++ b/supportflow-user-widget.php @@ -1,17 +1,5 @@ Date: Sun, 4 Nov 2012 20:56:49 -0600 Subject: [PATCH 020/485] Manage Theads: Add a "Mine" link to the top set of quick links which allows you to quickly filter to your tickets This should only appear for users who have 'manage_other_sf_threads', but that's dependent on #55 Closes #61 --- classes/class-supportflow-admin.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index 9ec6242..eeb0318 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -129,6 +129,7 @@ public function filter_post_updated_messages( $messages ) { * */ public function filter_views( $views ) { + global $wpdb; // The 'all' count shouldn't include closed posts $post_type = SupportFlow()->post_type; @@ -138,7 +139,25 @@ public function filter_views( $views ) { $total_posts -= $num_posts->$state; $total_posts -= $num_posts->sf_closed; $class = empty( $class ) && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['show_sticky'] ) ? ' class="current"' : ''; - $views['all'] = "" . sprintf( _nx( 'All (%s)', 'All (%s)', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . ''; + $view_all = "" . sprintf( _nx( 'All (%s)', 'All (%s)', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . ''; + + // @todo Only show "Mine" if the user is an agent + $mine_args = array( + 'post_type' => SupportFlow()->post_type, + 'author' => get_current_user_id(), + ); + $post_statuses = SupportFlow()->post_statuses; + array_pop( $post_statuses ); + $post_statuses = "'" . implode( "','", array_map( 'sanitize_key', array_keys( $post_statuses ) ) ) . "'"; + $my_posts = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type=%s AND post_author=%d AND post_status IN ({$post_statuses})", SupportFlow()->post_type, get_current_user_id() ) ); + $view_mine = '' . sprintf( _nx( 'Mine (%s)', 'Mine (%s)', $my_posts, 'posts' ), number_format_i18n( $my_posts ) ) . ''; + + // Put 'All' and 'Mine' at the beginning of the array + array_shift( $views ); + $views = array_reverse( $views ); + $views['mine'] = $view_mine; + $views['all'] = $view_all; + $views = array_reverse( $views ); return $views; } From 18301c3d4352dfdfe10993fd440a90dd63d8f9bc Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sun, 4 Nov 2012 21:42:56 -0600 Subject: [PATCH 021/485] Email notifications: Include the notification context in the subject filter in the situation where something different should be added for agents vs. respondents See #3 --- classes/class-supportflow-emails.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/class-supportflow-emails.php b/classes/class-supportflow-emails.php index 654db04..258173e 100644 --- a/classes/class-supportflow-emails.php +++ b/classes/class-supportflow-emails.php @@ -59,7 +59,7 @@ public function notify_agents_thread_comment( $comment_id ) { } $subject = '[' . get_bloginfo( 'name' ) . '] ' . get_the_title( $thread->ID ); - $subject = apply_filters( 'supportflow_emails_comment_notify_subject', $subject, $comment_id, $thread->ID ); + $subject = apply_filters( 'supportflow_emails_comment_notify_subject', $subject, $comment_id, $thread->ID, 'agent' ); $message = stripslashes( $comment->comment_content ); if ( $attachment_ids = get_comment_meta( $comment->comment_ID, 'attachment_ids', true ) ) { @@ -94,7 +94,7 @@ public function notify_respondents_thread_comment( $comment_id ) { } $subject = '[' . get_bloginfo( 'name' ) . '] ' . get_the_title( $thread->ID ); - $subject = apply_filters( 'supportflow_emails_comment_notify_subject', $subject, $comment_id, $thread->ID ); + $subject = apply_filters( 'supportflow_emails_comment_notify_subject', $subject, $comment_id, $thread->ID, 'respondent' ); $message = stripslashes( $comment->comment_content ); if ( $attachment_ids = get_comment_meta( $comment->comment_ID, 'attachment_ids', true ) ) { From bcd4da4ef72004f75ed7c0720cfe79b18702a8db Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sun, 4 Nov 2012 21:45:35 -0600 Subject: [PATCH 022/485] Email Notifications: In the emails to agents, append the ticket status and assigned agent to the end of the message See #3 --- classes/class-supportflow-emails.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/classes/class-supportflow-emails.php b/classes/class-supportflow-emails.php index 258173e..db833db 100644 --- a/classes/class-supportflow-emails.php +++ b/classes/class-supportflow-emails.php @@ -68,6 +68,15 @@ public function notify_agents_thread_comment( $comment_id ) { $message .= "\n" . wp_get_attachment_url( $attachment_id ); } } + // Ticket details that are relevant to the agent + $message .= "\n\n-------"; + // Thread status + $post_status = SupportFlow()->post_statuses[$thread->post_status]['label']; + $message .= "\n" . sprintf( __( "Status: %s", 'supportflow' ), $post_status ); + // Assigned agent + $assigned_agent = ( $thread->post_author ) ? get_user_by( 'id', $thread->post_author )->display_name : __( 'None assigned', 'supportflow' ); + $message .= "\n" . sprintf( __( "Agent: %s", 'supportflow' ), $assigned_agent ); + foreach( $agent_emails as $agent_email ) { wp_mail( $agent_email, $subject, $message ); } From b3dc58600bc4dcdad111fbdc7b545088d6df07b0 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sun, 4 Nov 2012 21:50:10 -0600 Subject: [PATCH 023/485] Email Notifications: Allow the messages to be filtered as well See #3 --- classes/class-supportflow-emails.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/class-supportflow-emails.php b/classes/class-supportflow-emails.php index db833db..6d89064 100644 --- a/classes/class-supportflow-emails.php +++ b/classes/class-supportflow-emails.php @@ -77,6 +77,8 @@ public function notify_agents_thread_comment( $comment_id ) { $assigned_agent = ( $thread->post_author ) ? get_user_by( 'id', $thread->post_author )->display_name : __( 'None assigned', 'supportflow' ); $message .= "\n" . sprintf( __( "Agent: %s", 'supportflow' ), $assigned_agent ); + $message = apply_filters( 'supportflow_emails_comment_notify_message', $message, $comment_id, $thread->ID, 'agent' ); + foreach( $agent_emails as $agent_email ) { wp_mail( $agent_email, $subject, $message ); } @@ -112,6 +114,9 @@ public function notify_respondents_thread_comment( $comment_id ) { $message .= "\n" . wp_get_attachment_url( $attachment_id ); } } + + $message = apply_filters( 'supportflow_emails_comment_notify_message', $message, $comment_id, $thread->ID, 'respondent' ); + foreach( $respondents as $respondent_email ) { self::mail( $respondent_email, $subject, $message ); } From 4fe96991ecf64f446b09bd7f6533e47c6bc8687d Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sun, 4 Nov 2012 22:02:56 -0600 Subject: [PATCH 024/485] Email Notifications: A more core-friendly way of filtering the "From" values used by wp_mail(). Allows the filters to only be applied when SupportFlow is sending an email. See #3 --- classes/class-supportflow-emails.php | 38 ++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/classes/class-supportflow-emails.php b/classes/class-supportflow-emails.php index 6d89064..630b322 100644 --- a/classes/class-supportflow-emails.php +++ b/classes/class-supportflow-emails.php @@ -5,8 +5,8 @@ class SupportFlow_Emails extends SupportFlow { - var $from_name; - var $from_address; + private $from_name = false; + private $from_email = false; function __construct() { add_action( 'supportflow_after_setup_actions', array( $this, 'setup_actions' ) ); @@ -18,7 +18,7 @@ function __construct() { public function setup_actions() { $this->from_name = apply_filters( 'supportflow_emails_from_name', $this->from_name ); - $this->from_address = apply_filters( 'supportflow_emails_from_address', $this->from_address ); + $this->from_email = apply_filters( 'supportflow_emails_from_address', $this->from_email ); // Don't send out any notifications when importing or using WP-CLI if ( ( defined('WP_IMPORTING') && WP_IMPORTING ) || ( defined('WP_CLI') && WP_CLI ) ) @@ -80,7 +80,7 @@ public function notify_agents_thread_comment( $comment_id ) { $message = apply_filters( 'supportflow_emails_comment_notify_message', $message, $comment_id, $thread->ID, 'agent' ); foreach( $agent_emails as $agent_email ) { - wp_mail( $agent_email, $subject, $message ); + self::mail( $agent_email, $subject, $message ); } } @@ -126,10 +126,32 @@ public function notify_respondents_thread_comment( $comment_id ) { * Send an email from SupportFlow */ public function mail( $respondent_email, $subject, $message ) { - $headers = array(); - if ( ! empty( $this->from_name ) && is_email( $this->from_address ) ) - $headers[] = 'From: ' . $this->from_name . ' <' . $this->from_address . '>'; - wp_mail( $respondent_email, $subject, $message, $headers ); + + add_filter( 'wp_mail_from', array( $this, 'filter_wp_mail_from' ) ); + add_filter( 'wp_mail_from_name', array( $this, 'filter_wp_mail_from_name' ) ); + wp_mail( $respondent_email, $subject, $message ); + remove_filter( 'wp_mail_from', array( $this, 'filter_wp_mail_from' ) ); + remove_filter( 'wp_mail_from_name', array( $this, 'filter_wp_mail_from_name' ) ); + } + + /** + * Filter the 'from address' value used by wp_mail + */ + public function filter_wp_mail_from( $from_email ) { + if ( $this->from_email ) + return $this->from_email; + else + $from_email; + } + + /** + * Filter the 'from name' value used by wp_mail + */ + public function filter_wp_mail_from_name( $from_name ) { + if ( $this->from_name ) + return $this->from_name; + else + $from_name; } } From 7b5e9e95aeec993241d593719a2b91f81184ec54 Mon Sep 17 00:00:00 2001 From: Greg Johnson Date: Sun, 4 Nov 2012 15:30:20 -0600 Subject: [PATCH 025/485] Added autocomplete stub for respondents and registered in SupportFlow_Admin::action_admin_enqueue_scripts --- classes/class-supportflow-admin.php | 1 + js/respondents-autocomplete.js | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 js/respondents-autocomplete.js diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index eeb0318..5507006 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -60,6 +60,7 @@ public function action_admin_enqueue_scripts() { 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(); + wp_enqueue_script( 'supportflow-respondents-autocomplete', SupportFlow()->plugin_url . 'js/respondents-autocomplete', array('jquery', 'jquery-ui-autocomplete' ) ); } } diff --git a/js/respondents-autocomplete.js b/js/respondents-autocomplete.js new file mode 100644 index 0000000..ff6b04b --- /dev/null +++ b/js/respondents-autocomplete.js @@ -0,0 +1,5 @@ +jQuery(document).ready(function($) { + + + +}); \ No newline at end of file From a38bbd56cd1787b9a93aa08117cb41a33a2ffbc9 Mon Sep 17 00:00:00 2001 From: Greg Johnson Date: Sun, 4 Nov 2012 18:24:21 -0600 Subject: [PATCH 026/485] Added SupportFlow::get_respondents() to search for respondents by email address --- supportflow.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/supportflow.php b/supportflow.php index 0d2f912..475b05a 100644 --- a/supportflow.php +++ b/supportflow.php @@ -471,6 +471,29 @@ public function get_threads( $args = array() ) { return $threads->posts; } + /** + * Get respondents matching $query + * + * @param string $query partial email address to search for + */ + public function get_respondents( $query ) { + + $args = array( + 'orderby' => 'name', + 'hide_empty' => 0, + 'fields' => 'all', + 'name__like' => $query, + 'number' => 10 + ); + + $matches = get_terms( $this->respondents_tax, $args ); + + if( !$matches ) + return array(); + + return $matches; + } + /** * Get a thread's respondents * From d94c987843b988e2fcd0a08665e53f2c7a2df352 Mon Sep 17 00:00:00 2001 From: Greg Johnson Date: Sun, 4 Nov 2012 18:25:13 -0600 Subject: [PATCH 027/485] Added SupportFlow::get_respondents() wrapper to JSON api using action 'get-respondents' --- classes/class-supportflow-json-api.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/classes/class-supportflow-json-api.php b/classes/class-supportflow-json-api.php index 863bb52..27670c3 100644 --- a/classes/class-supportflow-json-api.php +++ b/classes/class-supportflow-json-api.php @@ -53,6 +53,19 @@ public function action_wp_ajax_supportflow_json() { break; + case 'get-respondents': + $search_for = sanitize_text_field( $_REQUEST['respondents'] ); + $respondent_matches = SupportFlow()->get_respondents( $search_for ); + if( is_wp_error($respondent_matches) ) { + $response['message'] = $respondent_matches->get_error_message(); + } else { + $response['query'] = $search_for; + $response['status'] = "ok"; + $response['respondents'] = $respondent_matches; + } + + break; + case 'get-thread': $thread_id = (int)$_REQUEST['thread_id']; $response['status'] = 'ok'; From 5e739674ada4bfbe88b57aff971c5bf1c2f2b307 Mon Sep 17 00:00:00 2001 From: Greg Johnson Date: Sun, 4 Nov 2012 18:26:56 -0600 Subject: [PATCH 028/485] enqueued js/respondents-autocomplete javascript --- classes/class-supportflow-admin.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index 5507006..fcbd661 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -60,7 +60,10 @@ public function action_admin_enqueue_scripts() { 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(); - wp_enqueue_script( 'supportflow-respondents-autocomplete', SupportFlow()->plugin_url . 'js/respondents-autocomplete', array('jquery', 'jquery-ui-autocomplete' ) ); + wp_enqueue_script( 'supportflow-respondents-autocomplete', SupportFlow()->plugin_url . 'js/respondents-autocomplete.js', array('jquery', 'jquery-ui-autocomplete' ) ); + + $ajaxurl = add_query_arg( 'action', SupportFlow()->extend->jsonapi->action, admin_url( 'admin-ajax.php' ) ); + wp_localize_script('supportflow-respondents-autocomplete', 'SFRespondentsAc', array('ajax_url' => $ajaxurl ) ); } } From 80b54cad2eca9b79cf4cbd8a4ece5fa4d9cf2b5e Mon Sep 17 00:00:00 2001 From: Greg Johnson Date: Sun, 4 Nov 2012 18:29:10 -0600 Subject: [PATCH 029/485] adding comments --- js/respondents-autocomplete.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/js/respondents-autocomplete.js b/js/respondents-autocomplete.js index ff6b04b..dbf3117 100644 --- a/js/respondents-autocomplete.js +++ b/js/respondents-autocomplete.js @@ -1,5 +1,34 @@ jQuery(document).ready(function($) { - + var getSearchTerm = function() { + + // Input of the Respondents field can contain multiple addresses, + // we only want to search on the last address + var resps = $('#respondents').val().replace(" ", '').split(","); + var search_for = resps.pop(); + + return { "respondents":search_for, "api-action":'get-respondents' }; + + } + + $('#respondents').autocomplete({ + + source: function( req, response ) { + + $.ajax({ + url: SFRespondentsAc.ajax_url, + dataType: 'json', + data: getSearchTerm(), + success: function(data) { + console.log(data); + } + }); + + }, + select: function( event, ui ) { + console.log(ui); + }, + minLength: 2, + }); }); \ No newline at end of file From f8d7fec9f315de5657ff6ee08ff4de055f898bb3 Mon Sep 17 00:00:00 2001 From: Greg Johnson Date: Sun, 4 Nov 2012 22:48:03 -0600 Subject: [PATCH 030/485] implemented respondents input auto-completion --- js/respondents-autocomplete.js | 38 ++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/js/respondents-autocomplete.js b/js/respondents-autocomplete.js index dbf3117..55f015d 100644 --- a/js/respondents-autocomplete.js +++ b/js/respondents-autocomplete.js @@ -5,7 +5,13 @@ jQuery(document).ready(function($) { // Input of the Respondents field can contain multiple addresses, // we only want to search on the last address var resps = $('#respondents').val().replace(" ", '').split(","); - var search_for = resps.pop(); + + if( $.isArray(resps) && resps.length > 1 ) { + var search_for = resps.pop(); + } + else { + search_for = resps[0]; + } return { "respondents":search_for, "api-action":'get-respondents' }; @@ -20,15 +26,35 @@ jQuery(document).ready(function($) { dataType: 'json', data: getSearchTerm(), success: function(data) { - console.log(data); + if(data.query == "") { + response(false); + return false; + } + response( $.map( data.respondents, function (item) { + + // normaliz input + var resps = $('#respondents').val().replace(" ", '').split(","); + + // remove partial respondent + resps.pop(); + + // replace with full email + resps.push(item.name); + + // make it a string + var retval = resps.join(", "); + + // return those respondents! + return { + label: item.name, + value: retval + } + })); } }); }, - select: function( event, ui ) { - console.log(ui); - }, - minLength: 2, + minLength: 2 }); }); \ No newline at end of file From a5f6abb17d55198a051184cc05de4323fd093411 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Mon, 5 Nov 2012 23:23:23 -0600 Subject: [PATCH 031/485] Open the door for potentially other arguments later by passing an array to get_respondents method See #85 --- classes/class-supportflow-json-api.php | 2 +- supportflow.php | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/classes/class-supportflow-json-api.php b/classes/class-supportflow-json-api.php index 27670c3..c9e68a7 100644 --- a/classes/class-supportflow-json-api.php +++ b/classes/class-supportflow-json-api.php @@ -55,7 +55,7 @@ public function action_wp_ajax_supportflow_json() { case 'get-respondents': $search_for = sanitize_text_field( $_REQUEST['respondents'] ); - $respondent_matches = SupportFlow()->get_respondents( $search_for ); + $respondent_matches = SupportFlow()->get_respondents( array( 'search' => $search_for ) ); if( is_wp_error($respondent_matches) ) { $response['message'] = $respondent_matches->get_error_message(); } else { diff --git a/supportflow.php b/supportflow.php index 475b05a..9131043 100644 --- a/supportflow.php +++ b/supportflow.php @@ -476,17 +476,22 @@ public function get_threads( $args = array() ) { * * @param string $query partial email address to search for */ - public function get_respondents( $query ) { + public function get_respondents( $args = array() ) { - $args = array( + $defaults = array( + 'search' => '', + 'number' => 10 + ); + $args = array_merge( $defaults, $args ); + + $term_args = array( 'orderby' => 'name', 'hide_empty' => 0, 'fields' => 'all', - 'name__like' => $query, - 'number' => 10 + 'name__like' => $args['search'], + 'number' => $args['number'], ); - - $matches = get_terms( $this->respondents_tax, $args ); + $matches = get_terms( $this->respondents_tax, $term_args ); if( !$matches ) return array(); From 23dc83f54384715e489dffa6e9df08726dae111c Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Fri, 16 May 2014 23:56:18 +0530 Subject: [PATCH 032/485] Edited code to match with WordPress coding standards --- classes/class-supportflow-admin.php | 328 +++++++------- classes/class-supportflow-attachments.php | 72 ++-- classes/class-supportflow-email-replies.php | 101 +++-- classes/class-supportflow-emails.php | 51 ++- classes/class-supportflow-json-api.php | 58 +-- classes/class-supportflow-permissions.php | 121 +++--- .../class-supportflow-ui-submissionform.php | 67 +-- classes/class-supportflow-ui-widget.php | 56 +-- classes/class-supportflow-wp-cli.php | 102 ++--- css/admin.css | 23 +- css/widget.css | 26 +- js/plupload.js | 30 +- js/respondents-autocomplete.js | 28 +- js/supportflow-user-widget.js | 96 ++--- supportflow-user-widget.php | 23 +- supportflow.php | 400 ++++++++++-------- 16 files changed, 861 insertions(+), 721 deletions(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index fcbd661..5537656 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -15,8 +15,9 @@ public function setup_actions() { add_action( 'add_meta_boxes', array( $this, 'action_add_meta_boxes' ) ); add_action( 'save_post', array( $this, 'action_save_post' ) ); - if ( !$this->is_edit_screen() ) + if ( ! $this->is_edit_screen() ) { return; + } // Everything add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) ); @@ -46,8 +47,9 @@ function action_admin_init() { unset( $wp_post_statuses['trash'] ); $wp_post_statuses['trash'] = $trash_status; - if ( 'edit.php' == $pagenow ) + if ( 'edit.php' == $pagenow ) { add_filter( 'get_the_excerpt', array( $this, 'filter_get_the_excerpt' ) ); + } } /** @@ -58,12 +60,12 @@ public function action_admin_enqueue_scripts() { 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' ) ); + wp_enqueue_script( 'supportflow-plupload', SupportFlow()->plugin_url . 'js/plupload.js', array( 'wp-plupload', 'jquery' ) ); self::add_default_plupload_settings(); - wp_enqueue_script( 'supportflow-respondents-autocomplete', SupportFlow()->plugin_url . 'js/respondents-autocomplete.js', array('jquery', 'jquery-ui-autocomplete' ) ); + wp_enqueue_script( 'supportflow-respondents-autocomplete', SupportFlow()->plugin_url . 'js/respondents-autocomplete.js', array( 'jquery', 'jquery-ui-autocomplete' ) ); $ajaxurl = add_query_arg( 'action', SupportFlow()->extend->jsonapi->action, admin_url( 'admin-ajax.php' ) ); - wp_localize_script('supportflow-respondents-autocomplete', 'SFRespondentsAc', array('ajax_url' => $ajaxurl ) ); + wp_localize_script( 'supportflow-respondents-autocomplete', 'SFRespondentsAc', array( 'ajax_url' => $ajaxurl ) ); } } @@ -80,12 +82,12 @@ private static function add_default_plupload_settings() { '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' => '*') ), + 'filters' => array( array( 'title' => __( 'Allowed Files' ), 'extensions' => '*' ) ), 'multipart' => true, 'urlstream_upload' => true, 'multipart_params' => array( - 'action' => 'upload-attachment', - '_wpnonce' => wp_create_nonce( 'media-form' ) + 'action' => 'upload-attachment', + '_wpnonce' => wp_create_nonce( 'media-form' ) ) ); @@ -100,8 +102,9 @@ private static function add_default_plupload_settings() { $script = 'var _wpPluploadSettings = ' . json_encode( $settings ) . ';'; $data = $wp_scripts->get_data( 'wp-plupload', 'data' ); - if ( ! empty( $data ) ) + if ( ! empty( $data ) ) { $script = "$data\n$script"; + } $wp_scripts->add_data( 'wp-plupload', 'data', $script ); } @@ -113,19 +116,20 @@ 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 => __( 'Thread updated.', 'supportflow' ), + 2 => __( 'Custom field updated.', 'supportflow' ), + 3 => __( 'Custom field deleted.', 'supportflow' ), + 4 => __( 'Thread 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' ), + 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' ), ); + return $messages; } @@ -136,32 +140,33 @@ public function filter_views( $views ) { global $wpdb; // The 'all' count shouldn't include closed posts - $post_type = SupportFlow()->post_type; - $num_posts = wp_count_posts( $post_type, 'readable' ); - $total_posts = array_sum( (array)$num_posts ); - foreach ( get_post_stati( array('show_in_admin_all_list' => false) ) as $state ) + $post_type = SupportFlow()->post_type; + $num_posts = wp_count_posts( $post_type, 'readable' ); + $total_posts = array_sum( (array) $num_posts ); + foreach ( get_post_stati( array( 'show_in_admin_all_list' => false ) ) as $state ) { $total_posts -= $num_posts->$state; + } $total_posts -= $num_posts->sf_closed; - $class = empty( $class ) && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['show_sticky'] ) ? ' class="current"' : ''; + $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 ) ) . ''; // @todo Only show "Mine" if the user is an agent - $mine_args = array( - 'post_type' => SupportFlow()->post_type, - 'author' => get_current_user_id(), - ); + $mine_args = array( + 'post_type' => SupportFlow()->post_type, + 'author' => get_current_user_id(), + ); $post_statuses = SupportFlow()->post_statuses; array_pop( $post_statuses ); $post_statuses = "'" . implode( "','", array_map( 'sanitize_key', array_keys( $post_statuses ) ) ) . "'"; - $my_posts = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type=%s AND post_author=%d AND post_status IN ({$post_statuses})", SupportFlow()->post_type, get_current_user_id() ) ); - $view_mine = '' . sprintf( _nx( 'Mine (%s)', 'Mine (%s)', $my_posts, 'posts' ), number_format_i18n( $my_posts ) ) . ''; + $my_posts = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type=%s AND post_author=%d AND post_status IN ({$post_statuses})", SupportFlow()->post_type, get_current_user_id() ) ); + $view_mine = '' . sprintf( _nx( 'Mine (%s)', 'Mine (%s)', $my_posts, 'posts' ), number_format_i18n( $my_posts ) ) . ''; // Put 'All' and 'Mine' at the beginning of the array array_shift( $views ); - $views = array_reverse( $views ); + $views = array_reverse( $views ); $views['mine'] = $view_mine; - $views['all'] = $view_all; - $views = array_reverse( $views ); + $views['all'] = $view_all; + $views = array_reverse( $views ); return $views; } @@ -173,11 +178,11 @@ 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 ); } @@ -188,8 +193,9 @@ public function action_restrict_manage_posts() { function filter_post_row_actions( $row_actions, $post ) { // Rename these actions - if ( isset( $row_actions['edit'] ) ) + if ( isset( $row_actions['edit'] ) ) { $row_actions['edit'] = str_replace( __( 'Edit' ), __( 'View', 'supportflow' ), str_replace( __( 'Edit this item' ), __( 'View Thread', 'supportflow' ), $row_actions['edit'] ) ); + } // Save the trash action for the end if ( isset( $row_actions['trash'] ) ) { @@ -200,29 +206,30 @@ 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, + 'thread_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 Thread', 'supportflow' ); $action_text = esc_html__( 'Close', 'supportflow' ); } else { - $title_attr = esc_attr__( 'Reopen Thread', 'supportflow' ); + $title_attr = esc_attr__( 'Reopen Thread', 'supportflow' ); $action_text = esc_html__( 'Reopen', 'supportflow' ); } $row_actions['change_status'] = '' . $action_text . ''; @@ -232,8 +239,9 @@ function filter_post_row_actions( $row_actions, $post ) { 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; } @@ -243,6 +251,7 @@ function filter_post_row_actions( $row_actions, $post ) { */ public function filter_bulk_actions( $actions ) { unset( $actions['edit'] ); + return $actions; } @@ -252,20 +261,21 @@ public function filter_bulk_actions( $actions ) { 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; + $statuses = SupportFlow()->post_statuses; $status_slugs = array_keys( $statuses ); - $last_status = array_pop( $status_slugs ); + $last_status = array_pop( $status_slugs ); // Order posts by post_modified if there's no orderby set - if ( !$query->get( 'orderby' ) ) { - $sort_order = array( - 'orderby' => 'modified', - ); + 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 ) { + foreach ( $sort_order as $key => $value ) { $query->set( $key, $value ); $_GET[$key] = $value; } @@ -274,24 +284,25 @@ function action_pre_get_posts( $query ) { // Do our own custom search handling so we can search against comment text if ( $search = $query->get( 's' ) ) { // Get any comments that match our results - $args = array( - 'search' => $search, - 'status' => 'any', - ); + $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', - ); + $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', + ); $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 @@ -300,8 +311,9 @@ function action_pre_get_posts( $query ) { // Only show threads 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 ); + } } @@ -319,19 +331,21 @@ public function filter_posts_search( $posts_search ) { */ 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['thread_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']; + $thread_id = (int) $_GET['thread_id']; $new_thread = array( - 'ID' => $thread_id, - 'post_status' => $post_status, - ); + 'ID' => $thread_id, + 'post_status' => $post_status, + ); wp_update_post( $new_thread ); wp_safe_redirect( wp_get_referer() ); exit; @@ -346,15 +360,16 @@ function handle_action_change_status() { * */ public function action_add_meta_boxes() { - if ( ! $this->is_edit_screen() ) + 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' ); + 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' ); add_meta_box( 'supportflow-details', __( 'Details', 'supportflow' ), array( $this, 'meta_box_details' ), SupportFlow()->post_type, 'side' ); add_meta_box( 'supportflow-subject', __( 'Subject', 'supportflow' ), array( $this, 'meta_box_subject' ), SupportFlow()->post_type, 'normal' ); @@ -372,15 +387,15 @@ public function meta_box_details() { echo '
'; // Date Created and Last Activity for Existing Posts - if( 'post.php' == $pagenow ) { + if ( 'post.php' == $pagenow ) { - $modified_gmt = get_post_modified_time( 'U', true, get_the_ID() ); + $modified_gmt = get_post_modified_time( 'U', true, get_the_ID() ); $last_activity = sprintf( __( '%s ago', 'supportflow' ), human_time_diff( $modified_gmt ) ); echo '
'; echo ''; - echo '' . get_the_date() . ' ' . get_the_time() . ''; - echo '
' . __( 'Last Activity', 'SupportFlow' ) . ': ' . $last_activity . '
'; + echo '' . get_the_date() . ' ' . get_the_time() . ''; + echo '
' . __( 'Last Activity', 'SupportFlow' ) . ': ' . $last_activity . '
'; echo '
'; } @@ -389,7 +404,7 @@ public function meta_box_details() { echo '
'; echo ''; echo ''; @@ -400,12 +415,12 @@ public function meta_box_details() { echo '
'; echo ''; $args = array( - 'show_option_none' => __( '-- Unassigned --', 'supportflow' ), - 'selected' => $post_author, - 'id' => 'post_author', - 'name' => 'post_author', - 'who' => 'author', - ); + 'show_option_none' => __( '-- Unassigned --', 'supportflow' ), + 'selected' => $post_author, + 'id' => 'post_author', + 'name' => 'post_author', + 'who' => 'author', + ); wp_dropdown_users( $args ); echo '
'; @@ -413,10 +428,11 @@ public function meta_box_details() { // Start/Update Thread (submit) - if ( 'post-new.php' == $pagenow ) + if ( 'post-new.php' == $pagenow ) { $submit_text = __( 'Start Thread', 'supportflow' ); - else + } else { $submit_text = __( 'Update Thread', 'supportflow' ); + } echo '
'; echo '
'; submit_button( $submit_text, 'primary', 'save', false ); @@ -443,9 +459,9 @@ public function meta_box_subject() { */ public function meta_box_respondents() { - $respondents = SupportFlow()->get_thread_respondents( get_the_ID(), array( 'fields' => 'emails' ) ); + $respondents = SupportFlow()->get_thread_respondents( get_the_ID(), array( 'fields' => 'emails' ) ); $respondents_string = implode( ', ', $respondents ); - $placeholder = __( 'Who are you starting a conversation with?', 'supportflow' ); + $placeholder = __( 'Who are you starting a conversation with?', 'supportflow' ); echo '

' . __( 'Respondent(s)', 'supportflow' ) . '

'; echo ''; echo '

' . __( 'Enter each respondent email address, separated with a comma', 'supportflow' ) . '

'; @@ -459,9 +475,9 @@ public function meta_box_comments() { global $pagenow; $placeholders = array( - __( "What's burning?", 'supportflow' ), - __( 'What do you need to get off your chest?', 'supportflow' ), - ); + __( "What's burning?", 'supportflow' ), + __( 'What do you need to get off your chest?', 'supportflow' ), + ); $rand = array_rand( $placeholders ); echo '

' . __( 'Conversation', 'supportflow' ) . '

'; @@ -480,10 +496,11 @@ public function meta_box_comments() { echo '
'; echo ''; echo ''; - if ( 'post-new.php' == $pagenow ) + if ( 'post-new.php' == $pagenow ) { $submit_text = __( 'Start Thread', 'supportflow' ); - else + } else { $submit_text = __( 'Send Message', 'supportflow' ); + } submit_button( $submit_text, 'primary', 'save', false ); echo '
'; echo '
'; @@ -498,23 +515,23 @@ public function meta_box_comments() { public function display_thread_comments() { $private_comments = SupportFlow()->get_thread_comments( get_the_ID(), array( 'status' => 'private' ) ); - if ( !empty( $private_comments ) ) { + if ( ! empty( $private_comments ) ) { echo '
    '; - foreach( $private_comments as $comment ) { + foreach ( $private_comments as $comment ) { echo '
  • '; echo '
    '; echo wpautop( stripslashes( $comment->comment_content ) ); if ( $attachment_ids = get_comment_meta( $comment->comment_ID, 'attachment_ids', true ) ) { echo ''; } echo '
    '; - $comment_date = get_comment_date( get_option( 'date_format' ), $comment->comment_ID ); - $comment_time = get_comment_date( get_option( 'time_format' ), $comment->comment_ID ); + $comment_date = get_comment_date( get_option( 'date_format' ), $comment->comment_ID ); + $comment_time = get_comment_date( get_option( 'time_format' ), $comment->comment_ID ); $comment_timestamp = sprintf( __( 'Noted by %1$s on %2$s at %3$s', 'supportflow' ), $comment->comment_author, $comment_date, $comment_time ); echo '
    ' . esc_html( $comment_timestamp ) . '
    '; echo '
  • '; @@ -523,18 +540,18 @@ public function display_thread_comments() { } $comments = SupportFlow()->get_thread_comments( get_the_ID(), array( 'status' => 'public' ) ); - if ( !empty( $comments ) ) { + if ( ! empty( $comments ) ) { echo '
      '; - foreach( $comments as $comment ) { + foreach ( $comments as $comment ) { echo '
    • '; echo '
      ' . get_avatar( $comment->comment_author_email, 72 ); - echo '

      ' . esc_html( $comment->comment_author ) .'

      '; + echo '

      ' . esc_html( $comment->comment_author ) . '

      '; echo '
      '; echo '
      '; echo wpautop( stripslashes( $comment->comment_content ) ); if ( $attachment_ids = get_comment_meta( $comment->comment_ID, 'attachment_ids', true ) ) { echo '
        '; - foreach( $attachment_ids as $attachment_id ) { + foreach ( $attachment_ids as $attachment_id ) { $attachment_link = wp_get_attachment_url( $attachment_id ); echo '
      • ' . esc_html( get_the_title( $attachment_id ) ) . '
      • '; } @@ -560,15 +577,16 @@ public function display_thread_comments() { public function filter_manage_post_columns( $columns ) { $new_columns = array( - 'cb' => $columns['cb'], - 'updated' => __( 'Updated', 'supportflow' ), - 'title' => __( 'Subject', 'supportflow' ), - 'respondents' => __( 'Respondents', 'supportflow' ), - 'status' => __( 'Status', 'supportflow' ), - 'author' => __( 'Agent', 'supportflow' ), - 'sf_comments' => '' . esc_attr__( 'Comments', 'supportflow' ) . '', - 'created' => __( 'Created', 'support' ), - ); + 'cb' => $columns['cb'], + 'updated' => __( 'Updated', 'supportflow' ), + 'title' => __( 'Subject', 'supportflow' ), + 'respondents' => __( 'Respondents', 'supportflow' ), + 'status' => __( 'Status', 'supportflow' ), + 'author' => __( 'Agent', 'supportflow' ), + 'sf_comments' => '' . esc_attr__( 'Comments', 'supportflow' ) . '', + 'created' => __( 'Created', 'support' ), + ); + return $new_columns; } @@ -578,6 +596,7 @@ public function filter_manage_post_columns( $columns ) { public function manage_sortable_columns( $columns ) { $columns['updated'] = 'modified'; $columns['created'] = 'date'; + return $columns; } @@ -586,10 +605,11 @@ public function manage_sortable_columns( $columns ) { * on the Manage Threads view so mode=excerpt works well */ public function filter_get_the_excerpt( $orig ) { - if ( $comment = array_pop( SupportFlow()->get_thread_comments( get_the_ID() ) ) ) - return $comment->comment_author .': "' . wp_trim_excerpt( $comment->comment_content ) . '"'; - else + if ( $comment = array_pop( SupportFlow()->get_thread_comments( get_the_ID() ) ) ) { + return $comment->comment_author . ': "' . wp_trim_excerpt( $comment->comment_content ) . '"'; + } else { return $orig; + } } /** @@ -597,31 +617,32 @@ public function filter_get_the_excerpt( $orig ) { */ function action_manage_posts_custom_column( $column_name, $thread_id ) { - switch( $column_name ) { + switch ( $column_name ) { case 'updated': $modified_gmt = get_post_modified_time( 'U', true, $thread_id ); echo sprintf( __( '%s ago', 'supportflow' ), human_time_diff( $modified_gmt ) ); break; case 'respondents': $respondents = SupportFlow()->get_thread_respondents( $thread_id, array( 'fields' => 'emails' ) ); - if ( empty( $respondents ) ) + if ( empty( $respondents ) ) { break; - foreach( $respondents as $key => $respondent_email ) { - $args = array( - SupportFlow()->respondents_tax => SupportFlow()->get_email_hash( $respondent_email ), - 'post_type' => SupportFlow()->post_type, - ); - $respondent_link = '' . $respondent_email . ''; + } + foreach ( $respondents as $key => $respondent_email ) { + $args = array( + SupportFlow()->respondents_tax => SupportFlow()->get_email_hash( $respondent_email ), + 'post_type' => SupportFlow()->post_type, + ); + $respondent_link = '' . $respondent_email . ''; $respondents[$key] = get_avatar( $respondent_email, 16 ) . '  ' . $respondent_link; } echo implode( '
        ', $respondents ); break; case 'status': $post_status = get_post_status( $thread_id ); - $args = array( - 'post_type' => SupportFlow()->post_type, - 'post_status' => $post_status, - ); + $args = array( + 'post_type' => SupportFlow()->post_type, + 'post_status' => $post_status, + ); $status_name = get_post_status_object( $post_status )->label; $filter_link = add_query_arg( $args, admin_url( 'edit.php' ) ); echo '' . esc_html( $status_name ) . ''; @@ -652,6 +673,7 @@ public function is_edit_screen() { return $pagenow; } elseif ( 'post.php' == $pagenow && ! empty( $_GET['action'] ) && 'edit' == $_GET['action'] && ! empty( $_GET['post'] ) ) { $the_post = get_post( $_GET['post'] ); + return ( $the_post->post_type == SupportFlow()->post_type ) ? $pagenow : false; } else { return false; @@ -667,25 +689,27 @@ public function is_edit_screen() { */ public function action_save_post( $thread_id ) { - if( SupportFlow()->post_type != get_post_type( $thread_id ) ) + if ( SupportFlow()->post_type != get_post_type( $thread_id ) ) { return; + } if ( isset( $_POST['respondents'] ) ) { $respondents = array_map( 'sanitize_email', explode( ',', $_POST['respondents'] ) ); SupportFlow()->update_thread_respondents( $thread_id, $respondents ); } - if ( isset( $_POST['comment'] ) && !empty( $_POST['comment' ] ) ) { - $comment = wp_filter_nohtml_kses( $_POST['comment'] ); - $visibility = ( !empty( $_POST['mark-private'] ) ) ? 'private' : 'public'; - if ( !empty( $_POST['comment-attachments'] ) ) + if ( isset( $_POST['comment'] ) && ! empty( $_POST['comment'] ) ) { + $comment = wp_filter_nohtml_kses( $_POST['comment'] ); + $visibility = ( ! empty( $_POST['mark-private'] ) ) ? 'private' : 'public'; + if ( ! empty( $_POST['comment-attachments'] ) ) { $attachment_ids = array_map( 'intval', explode( ',', trim( $_POST['comment-attachments'], ',' ) ) ); - else + } else { $attachment_ids = ''; + } $comment_args = array( - 'comment_approved' => $visibility, - 'attachment_ids' => $attachment_ids, - ); + 'comment_approved' => $visibility, + 'attachment_ids' => $attachment_ids, + ); SupportFlow()->add_thread_comment( $thread_id, $comment, $comment_args ); } diff --git a/classes/class-supportflow-attachments.php b/classes/class-supportflow-attachments.php index 5719eb4..4c25848 100644 --- a/classes/class-supportflow-attachments.php +++ b/classes/class-supportflow-attachments.php @@ -24,13 +24,13 @@ public function setup_actions() { // Generate a secret key for our hashes the first time this is loaded in the admin $this->secret_key = get_option( self::secret_key_option ); - if ( is_admin() && !$this->secret_key ) { + if ( is_admin() && ! $this->secret_key ) { $this->secret_key = wp_generate_password(); update_option( self::secret_key_option, $this->secret_key ); } // Only apply to files that are uploaded to a thread - if ( isset( $_REQUEST['post_id'] ) && SupportFlow()->is_thread( (int)$_REQUEST['post_id'] ) ) { + if ( isset( $_REQUEST['post_id'] ) && SupportFlow()->is_thread( (int) $_REQUEST['post_id'] ) ) { add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ) ); add_filter( 'wp_handle_upload', array( $this, 'wp_handle_upload' ) ); } @@ -42,63 +42,72 @@ public function setup_actions() { public function filter_wp_get_attachment_url( $url, $attachment_id ) { if ( $attachment = get_post( $attachment_id ) ) { - if ( SupportFlow()->is_thread( $attachment->post_parent ) ) + if ( SupportFlow()->is_thread( $attachment->post_parent ) ) { return $attachment->guid; + } } + return $url; } public function handle_file_delivery( $template ) { // First check to see - if ( false === stripos( $_SERVER['REQUEST_URI' ], "secure-files/" ) ) + if ( false === stripos( $_SERVER['REQUEST_URI'], "secure-files/" ) ) { return; + } // Get the file preg_match( '#\/secure\-files\/(.+)$#', $_SERVER['REQUEST_URI'], $matches ); - if ( empty( $matches ) ) + if ( empty( $matches ) ) { return; + } // User must be logged in to see attachments that require permission if ( ! is_user_logged_in() && $this->attachments_require_permission ) { $args = array( - 'redirect_to' => urlencode( esc_url( home_url( $_SERVER['REQUEST_URI'] ) ) ), - ); + 'redirect_to' => urlencode( esc_url( home_url( $_SERVER['REQUEST_URI'] ) ) ), + ); wp_safe_redirect( add_query_arg( $args, site_url( '/wp-login.php' ) ) ); exit; } global $wpdb; - $file = $matches[1]; + $file = $matches[1]; $query = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE guid LIKE %s", '%' . $file ); - $post = $wpdb->get_row( $query ); - if ( empty( $post ) ) + $post = $wpdb->get_row( $query ); + if ( empty( $post ) ) { return get_404_template(); + } // Check to see whether the user has permission to view the thread - if ( $this->attachments_require_permission && ! $this->can_view_attachment( $post->ID ) ) + if ( $this->attachments_require_permission && ! $this->can_view_attachment( $post->ID ) ) { wp_die( __( 'Sorry, you do not have permission to view this attachment.', 'supportflow' ) ); + } $post_id = $post->ID; $file = get_attached_file( $post_id ); - if ( !is_file( $file ) ) + if ( ! is_file( $file ) ) { return get_404_template(); + } // We may override this later. status_header( 200 ); //rest inspired by wp-includes/ms-files.php. $mime = wp_check_filetype( $file ); - if ( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) ) - $mime[ 'type' ] = mime_content_type( $file ); + if ( false === $mime['type'] && function_exists( 'mime_content_type' ) ) { + $mime['type'] = mime_content_type( $file ); + } - if ( $mime[ 'type' ] ) - $mimetype = $mime[ 'type' ]; - else + if ( $mime['type'] ) { + $mimetype = $mime['type']; + } else { $mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 ); + } //fake the filename $filename = $post->post_name; @@ -110,7 +119,7 @@ public function handle_file_delivery( $template ) { header( 'Content-Type: ' . $mimetype ); // always send this header( 'Content-Length: ' . filesize( $file ) ); $last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file ) ); - $etag = '"' . md5( $last_modified ) . '"'; + $etag = '"' . md5( $last_modified ) . '"'; header( "Last-Modified: $last_modified GMT" ); header( 'ETag: ' . $etag ); header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' ); @@ -118,8 +127,9 @@ public function handle_file_delivery( $template ) { // Support for Conditional GET $client_etag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) : false; - if ( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) + if ( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { $_SERVER['HTTP_IF_MODIFIED_SINCE'] = false; + } $client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); @@ -127,13 +137,14 @@ public function handle_file_delivery( $template ) { $client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0; // Make a timestamp for our most recent modification... - $modified_timestamp = strtotime($last_modified); + $modified_timestamp = strtotime( $last_modified ); if ( ( $client_last_modified && $client_etag ) - ? ( ( $client_modified_timestamp >= $modified_timestamp) && ( $client_etag == $etag ) ) - : ( ( $client_modified_timestamp >= $modified_timestamp) || ( $client_etag == $etag ) ) + ? ( ( $client_modified_timestamp >= $modified_timestamp ) && ( $client_etag == $etag ) ) + : ( ( $client_modified_timestamp >= $modified_timestamp ) || ( $client_etag == $etag ) ) ) { status_header( 304 ); + return; } @@ -151,21 +162,24 @@ public function handle_file_delivery( $template ) { */ public function can_view_attachment( $attachment_id, $email_or_login = false ) { - if ( ! $email_or_login && is_user_logged_in() ) + if ( ! $email_or_login && is_user_logged_in() ) { $email_or_login = wp_get_current_user()->user_email; + } if ( ! is_email( $email_or_login ) ) { $user = get_user_by( 'login' ); - if ( $user ) + if ( $user ) { $email_or_login = $user->user_email; + } } - $thread_id = get_post( $attachment_id )->post_parent; + $thread_id = get_post( $attachment_id )->post_parent; $respondents = SupportFlow()->get_thread_respondents( $thread_id, array( 'fields' => 'emails' ) ); // If the email address is a respondent, they can view - if ( in_array( $email_or_login, $respondents ) ) + if ( in_array( $email_or_login, $respondents ) ) { return true; + } // @todo permissions check on whether the user is logged in as some who can view threads @@ -175,14 +189,16 @@ public function can_view_attachment( $attachment_id, $email_or_login = false ) { public function wp_handle_upload_prefilter( $file ) { $this->private_hash = md5( $this->secret_key . date( 'Y-m-d' ) . $file['name'] ); - $file['name'] = $this->private_hash . '.' . basename( $file['name'] ); + $file['name'] = $this->private_hash . '.' . basename( $file['name'] ); + return $file; } public function wp_handle_upload( $file ) { $secondary_hash = md5( $this->secret_key . date( 'Y-m-d' ) ); - $file['url'] = site_url( '/secure-files/' ) . str_replace( $this->private_hash . '.', $secondary_hash . '/', basename( $file['file'] ) ); + $file['url'] = site_url( '/secure-files/' ) . str_replace( $this->private_hash . '.', $secondary_hash . '/', basename( $file['file'] ) ); + return $file; } diff --git a/classes/class-supportflow-email-replies.php b/classes/class-supportflow-email-replies.php index 523c6c3..3bb8670 100644 --- a/classes/class-supportflow-email-replies.php +++ b/classes/class-supportflow-email-replies.php @@ -20,6 +20,7 @@ public function setup_actions() { public function filter_comment_notify_subject( $subject, $comment_id, $thread_id ) { $subject = rtrim( $subject ) . ' [' . SupportFlow()->get_secret_for_thread( $thread_id ) . ']'; + return $subject; } @@ -28,39 +29,43 @@ public function filter_comment_notify_subject( $subject, $comment_id, $thread_id */ public function download_and_process_email_replies( $connection_details ) { - $inbox = $connection_details['inbox']; + $inbox = $connection_details['inbox']; $archive = $connection_details['archive']; $this->imap_connection = imap_open( $connection_details['host'], $connection_details['username'], $connection_details['password'] ); - if ( ! $this->imap_connection ) + if ( ! $this->imap_connection ) { return new WP_Error( 'connection-error', __( 'Error connecting to mailbox', 'supportflow' ) ); + } // Check to see if the archive mailbox exists, and create it if it doesn't $mailboxes = imap_getmailboxes( $this->imap_connection, $connection_details['host'], '*' ); - if ( ! wp_filter_object_list( $mailboxes, array( 'name' => $connection_details['host'] . $archive ) ) ) + if ( ! wp_filter_object_list( $mailboxes, array( 'name' => $connection_details['host'] . $archive ) ) ) { imap_createmailbox( $this->imap_connection, $connection_details['host'] . $archive ); + } // Make sure here are new emails to process $email_count = imap_num_msg( $this->imap_connection ); - if ( $email_count < 1 ) + if ( $email_count < 1 ) { return false; + } // Process each new email and put it in the archive mailbox when done $success = 0; - for( $i = 1; $i <= $email_count; $i++ ) { - $email = new stdClass; - $email->headers = imap_headerinfo( $this->imap_connection, $i ); + for ( $i = 1; $i <= $email_count; $i ++ ) { + $email = new stdClass; + $email->headers = imap_headerinfo( $this->imap_connection, $i ); $email->structure = imap_fetchstructure( $this->imap_connection, $i ); - $email->body = $this->get_body_from_connection( $this->imap_connection, $i ); + $email->body = $this->get_body_from_connection( $this->imap_connection, $i ); // @todo Confirm this a message we want to process $ret = $this->process_email( $email, $i ); // If it was successful, move the email to the archive if ( $ret ) { imap_mail_move( $this->imap_connection, $i, $archive ); - $success++; + $success ++; } } + return sprintf( __( 'Processed %d emails', 'supportflow' ), $success ); } @@ -72,16 +77,17 @@ public function process_email( $email, $i ) { $new_attachment_ids = array(); $k = 0; - foreach( $email->structure->parts as $email_part ) { + foreach ( $email->structure->parts as $email_part ) { // We should at least be dealing with something that resembles an email object at this point - if ( ! isset( $email_part->disposition ) || ! isset( $email_part->subtype ) || ! isset( $email_part->dparameters[0]->value ) ) + if ( ! isset( $email_part->disposition ) || ! isset( $email_part->subtype ) || ! isset( $email_part->dparameters[0]->value ) ) { continue; + } if ( 'ATTACHMENT' == $email_part->disposition ) { // We need to add 2 to our array key each time to get the correct email part //@todo this needs more testing with different emails, should be smarter about which parts - $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, $k+2 ); + $raw_attachment_data = imap_fetchbody( $this->imap_connection, $i, $k + 2 ); // The raw data from imap is base64 encoded, but php-imap has us covered! $attachment_data = imap_base64( $raw_attachment_data ); @@ -94,67 +100,72 @@ public function process_email( $email, $i ) { $file_array = array( 'tmp_name' => $temp_file, - 'name' => $email_part->dparameters[0]->value, + 'name' => $email_part->dparameters[0]->value, ); - $upload_result = media_handle_sideload( $file_array, NULL ); + $upload_result = media_handle_sideload( $file_array, null ); - if ( is_wp_error( $upload_result ) ) + if ( is_wp_error( $upload_result ) ) { WP_CLI::warning( $upload_result->get_error_message() ); - else + } else { $new_attachment_ids[] = $upload_result; + } } - $k++; + $k ++; } - if ( ! empty( $email->headers->subject ) ) + if ( ! empty( $email->headers->subject ) ) { $subject = $email->headers->subject; - else + } else { $subject = sprintf( __( 'New thread from %s', 'supportflow' ), $email->headers->fromaddress ); + } - $respondent_name = $email->headers->from[0]->personal; + $respondent_name = $email->headers->from[0]->personal; $respondent_email = $email->headers->from[0]->mailbox . '@' . $email->headers->from[0]->host; // Parse out the reply body - if ( function_exists( 'What_The_Email' ) ) + if ( function_exists( 'What_The_Email' ) ) { $message = What_The_Email()->get_message( $email->body ); - else + } else { $message = $email->body; + } // Check if this email should be blocked if ( function_exists( 'What_The_Email' ) ) { $check_strings = array( - 'subject' => $subject, - 'sender' => $respondent_email, - 'message' => $message, - ); - foreach( $check_strings as $key => $value ) { - if ( What_The_Email()->is_robot( $key, $value ) ) + 'subject' => $subject, + 'sender' => $respondent_email, + 'message' => $message, + ); + foreach ( $check_strings as $key => $value ) { + if ( What_The_Email()->is_robot( $key, $value ) ) { return true; + } } } // Check to see if this message was in response to an existing thread $thread_id = false; - if ( preg_match( '#\[([a-zA-Z0-9]{8})\]$#', $subject, $matches ) ) + if ( preg_match( '#\[([a-zA-Z0-9]{8})\]$#', $subject, $matches ) ) { $thread_id = SupportFlow()->get_thread_from_secret( $matches[1] ); + } if ( $thread_id ) { $message_args = array( - 'comment_author' => $respondent_name, - 'comment_author_email' => $respondent_email, - ); + 'comment_author' => $respondent_name, + 'comment_author_email' => $respondent_email, + ); SupportFlow()->add_thread_comment( $thread_id, $message, $message_args ); } else { // If this wasn't in reply to an existing message, create a new thread $new_thread_args = array( - 'subject' => $subject, - 'respondent_name' => $respondent_name, - 'respondent_email' => $respondent_email, - 'message' => $message, - ); - $thread_id = SupportFlow()->create_thread( $new_thread_args ); + 'subject' => $subject, + 'respondent_name' => $respondent_name, + 'respondent_email' => $respondent_email, + 'message' => $message, + ); + $thread_id = SupportFlow()->create_thread( $new_thread_args ); } foreach ( $new_attachment_ids as $new_attachment_id ) { @@ -166,13 +177,14 @@ public function process_email( $email, $i ) { // Add anyone else that was in the 'to' or 'cc' fields as respondents $respondents = array(); - $fields = array( 'to', 'cc' ); - foreach( $fields as $field ) { + $fields = array( 'to', 'cc' ); + foreach ( $fields as $field ) { if ( ! empty( $email->headers->$field ) ) { - foreach( $email->headers->$field as $recipient ) { + foreach ( $email->headers->$field as $recipient ) { $email_address = $recipient->mailbox . '@' . $recipient->host; - if ( is_email( $email_address ) && $email_address != SupportFlow()->extend->emails->from_address ) + if ( is_email( $email_address ) && $email_address != SupportFlow()->extend->emails->from_address ) { $respondents[] = $email_address; + } } } } @@ -184,6 +196,7 @@ public function process_email( $email, $i ) { update_comment_meta( $new_comment->comment_ID, self::email_id_key, $email_id ); update_comment_meta( $new_comment->comment_ID, 'attachment_ids', $new_attachment_ids ); } + return true; } @@ -193,8 +206,10 @@ public function process_email( $email, $i ) { public function get_body_from_connection( $connection, $num, $type = 'text/plain' ) { // Hacky way to get the email body. We should support more MIME types in the future $body = imap_fetchbody( $connection, $num, 1.1 ); - if ( empty( $body ) ) + if ( empty( $body ) ) { $body = imap_fetchbody( $connection, $num, 1 ); + } + return $body; } diff --git a/classes/class-supportflow-emails.php b/classes/class-supportflow-emails.php index 630b322..2e8f6e4 100644 --- a/classes/class-supportflow-emails.php +++ b/classes/class-supportflow-emails.php @@ -17,12 +17,13 @@ function __construct() { */ public function setup_actions() { - $this->from_name = apply_filters( 'supportflow_emails_from_name', $this->from_name ); + $this->from_name = apply_filters( 'supportflow_emails_from_name', $this->from_name ); $this->from_email = apply_filters( 'supportflow_emails_from_address', $this->from_email ); // Don't send out any notifications when importing or using WP-CLI - if ( ( defined('WP_IMPORTING') && WP_IMPORTING ) || ( defined('WP_CLI') && WP_CLI ) ) + if ( ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) || ( defined( 'WP_CLI' ) && WP_CLI ) ) { return; + } // When a new comment is added to a thread, notify the respondents and the agents add_action( 'supportflow_thread_comment_added', array( $this, 'notify_agents_thread_comment' ) ); @@ -35,27 +36,31 @@ public function setup_actions() { public function notify_agents_thread_comment( $comment_id ) { $comment = get_comment( $comment_id ); - if ( ! $comment ) + if ( ! $comment ) { return; + } $thread = SupportFlow()->get_thread( $comment->comment_post_ID ); // One agent by default, but easily allow notifications to a triage team $agent_ids = apply_filters( 'supportflow_emails_notify_agent_ids', array( $thread->post_author ), $thread, 'comment' ); - if ( empty( $agent_ids ) ) + if ( empty( $agent_ids ) ) { return; + } $agent_emails = array(); - foreach( $agent_ids as $user_id ) { - if ( $user = get_user_by( 'id', $user_id ) ) + foreach ( $agent_ids as $user_id ) { + if ( $user = get_user_by( 'id', $user_id ) ) { $agent_emails[] = $user->user_email; + } } // Don't email the person creating the comment, unless that's desired behavior - if ( !apply_filters( 'supportflow_emails_notify_creator', false, 'comment' ) ) { + if ( ! apply_filters( 'supportflow_emails_notify_creator', false, 'comment' ) ) { $key = array_search( $comment->comment_author_email, $agent_emails ); - if ( false !== $key ) + if ( false !== $key ) { unset( $agent_emails[$key] ); + } } $subject = '[' . get_bloginfo( 'name' ) . '] ' . get_the_title( $thread->ID ); @@ -64,7 +69,7 @@ public function notify_agents_thread_comment( $comment_id ) { $message = stripslashes( $comment->comment_content ); if ( $attachment_ids = get_comment_meta( $comment->comment_ID, 'attachment_ids', true ) ) { $message .= "\n"; - foreach( $attachment_ids as $attachment_id ) { + foreach ( $attachment_ids as $attachment_id ) { $message .= "\n" . wp_get_attachment_url( $attachment_id ); } } @@ -79,7 +84,7 @@ public function notify_agents_thread_comment( $comment_id ) { $message = apply_filters( 'supportflow_emails_comment_notify_message', $message, $comment_id, $thread->ID, 'agent' ); - foreach( $agent_emails as $agent_email ) { + foreach ( $agent_emails as $agent_email ) { self::mail( $agent_email, $subject, $message ); } } @@ -91,17 +96,19 @@ public function notify_respondents_thread_comment( $comment_id ) { // Respondents shouldn't receive private comments $comment = get_comment( $comment_id ); - if ( ! $comment || 'private' == $comment->comment_approved ) + if ( ! $comment || 'private' == $comment->comment_approved ) { return; + } - $thread = SupportFlow()->get_thread( $comment->comment_post_ID ); + $thread = SupportFlow()->get_thread( $comment->comment_post_ID ); $respondents = SupportFlow()->get_thread_respondents( $thread->ID, array( 'fields' => 'emails' ) ); // Don't email the person creating the comment, unless that's desired behavior - if ( !apply_filters( 'supportflow_emails_notify_creator', false, 'comment' ) ) { + if ( ! apply_filters( 'supportflow_emails_notify_creator', false, 'comment' ) ) { $key = array_search( $comment->comment_author_email, $respondents ); - if ( false !== $key ) + if ( false !== $key ) { unset( $respondents[$key] ); + } } $subject = '[' . get_bloginfo( 'name' ) . '] ' . get_the_title( $thread->ID ); @@ -110,14 +117,14 @@ public function notify_respondents_thread_comment( $comment_id ) { $message = stripslashes( $comment->comment_content ); if ( $attachment_ids = get_comment_meta( $comment->comment_ID, 'attachment_ids', true ) ) { $message .= "\n"; - foreach( $attachment_ids as $attachment_id ) { + foreach ( $attachment_ids as $attachment_id ) { $message .= "\n" . wp_get_attachment_url( $attachment_id ); } } $message = apply_filters( 'supportflow_emails_comment_notify_message', $message, $comment_id, $thread->ID, 'respondent' ); - foreach( $respondents as $respondent_email ) { + foreach ( $respondents as $respondent_email ) { self::mail( $respondent_email, $subject, $message ); } } @@ -126,7 +133,7 @@ public function notify_respondents_thread_comment( $comment_id ) { * Send an email from SupportFlow */ public function mail( $respondent_email, $subject, $message ) { - + add_filter( 'wp_mail_from', array( $this, 'filter_wp_mail_from' ) ); add_filter( 'wp_mail_from_name', array( $this, 'filter_wp_mail_from_name' ) ); wp_mail( $respondent_email, $subject, $message ); @@ -138,20 +145,22 @@ public function mail( $respondent_email, $subject, $message ) { * Filter the 'from address' value used by wp_mail */ public function filter_wp_mail_from( $from_email ) { - if ( $this->from_email ) + if ( $this->from_email ) { return $this->from_email; - else + } else { $from_email; + } } /** * Filter the 'from name' value used by wp_mail */ public function filter_wp_mail_from_name( $from_name ) { - if ( $this->from_name ) + if ( $this->from_name ) { return $this->from_name; - else + } else { $from_name; + } } } diff --git a/classes/class-supportflow-json-api.php b/classes/class-supportflow-json-api.php index c9e68a7..aaa47a9 100644 --- a/classes/class-supportflow-json-api.php +++ b/classes/class-supportflow-json-api.php @@ -17,34 +17,35 @@ public function action_wp_ajax_supportflow_json() { $current_user = wp_get_current_user(); $response = array( - 'api-action' => ( ! empty( $_REQUEST['api-action'] ) ) ? sanitize_key( $_REQUEST['api-action'] ) : '', - 'status' => 'error', - 'message' => '', - 'html' => '', - ); + 'api-action' => ( ! empty( $_REQUEST['api-action'] ) ) ? sanitize_key( $_REQUEST['api-action'] ) : '', + 'status' => 'error', + 'message' => '', + 'html' => '', + ); switch ( $response['api-action'] ) { case 'create-thread': $thread_args = array( - 'subject' => sanitize_text_field( $_REQUEST['subject'] ), - 'message' => wp_filter_nohtml_kses( $_REQUEST['message'] ), - ); - if ( !empty( $_REQUEST['respondent_email'] ) && is_email( $_REQUEST['respondent_email'] ) ) { + 'subject' => sanitize_text_field( $_REQUEST['subject'] ), + 'message' => wp_filter_nohtml_kses( $_REQUEST['message'] ), + ); + if ( ! empty( $_REQUEST['respondent_email'] ) && is_email( $_REQUEST['respondent_email'] ) ) { $thread_args['respondent_email'] = sanitize_email( $_REQUEST['respondent_email'] ); - if ( !empty( $_REQUEST['respondent_name'] ) ) + if ( ! empty( $_REQUEST['respondent_name'] ) ) { $thread_args['respondent_name'] = sanitize_text_field( $_REQUEST['respondent_name'] ); + } } else { $thread_args['respondent_email'] = $current_user->user_email; - $thread_args['respondent_name'] = $current_user->display_name; - $thread_args['respondent_id'] = $current_user->ID; + $thread_args['respondent_name'] = $current_user->display_name; + $thread_args['respondent_id'] = $current_user->ID; } $thread_id = SupportFlow()->create_thread( $thread_args ); if ( is_wp_error( $thread_id ) ) { $response['message'] = $thread_id->get_error_message(); } else { - $response['status'] = 'ok'; + $response['status'] = 'ok'; $response['thread_id'] = $thread_id; } break; @@ -54,42 +55,43 @@ public function action_wp_ajax_supportflow_json() { break; case 'get-respondents': - $search_for = sanitize_text_field( $_REQUEST['respondents'] ); + $search_for = sanitize_text_field( $_REQUEST['respondents'] ); $respondent_matches = SupportFlow()->get_respondents( array( 'search' => $search_for ) ); - if( is_wp_error($respondent_matches) ) { + if ( is_wp_error( $respondent_matches ) ) { $response['message'] = $respondent_matches->get_error_message(); } else { - $response['query'] = $search_for; - $response['status'] = "ok"; + $response['query'] = $search_for; + $response['status'] = "ok"; $response['respondents'] = $respondent_matches; } break; case 'get-thread': - $thread_id = (int)$_REQUEST['thread_id']; - $response['status'] = 'ok'; + $thread_id = (int) $_REQUEST['thread_id']; + $response['status'] = 'ok'; $response['thread_id'] = $thread_id; break; case 'add-thread-comment': - $thread_id = (int)$_REQUEST['thread_id']; - $message = wp_filter_nohtml_kses( $_REQUEST['message'] ); + $thread_id = (int) $_REQUEST['thread_id']; + $message = wp_filter_nohtml_kses( $_REQUEST['message'] ); $comment_args = array(); - if ( !empty( $_REQUEST['comment_author_email'] ) && is_email( $_REQUEST['comment_author_email'] ) ) { + if ( ! empty( $_REQUEST['comment_author_email'] ) && is_email( $_REQUEST['comment_author_email'] ) ) { $comment_args['comment_author_email'] = sanitize_email( $_REQUEST['comment_author_email'] ); - if ( !empty( $_REQUEST['comment_author'] ) ) + if ( ! empty( $_REQUEST['comment_author'] ) ) { $comment_args['comment_author'] = sanitize_text_field( $_REQUEST['comment_author'] ); + } } else { $comment_args['comment_author_email'] = $current_user->user_email; - $comment_args['comment_author'] = $current_user->display_name; - $comment_args['user_id'] = $current_user->ID; + $comment_args['comment_author'] = $current_user->display_name; + $comment_args['user_id'] = $current_user->ID; } - $comment_id = SupportFlow()->add_thread_comment( $thread_id, $message); + $comment_id = SupportFlow()->add_thread_comment( $thread_id, $message ); if ( is_wp_error( $comment_id ) ) { $response['message'] = $comment_id->get_error_message(); } else { - $response['status'] = 'ok'; - $response['thread_id'] = $thread_id; + $response['status'] = 'ok'; + $response['thread_id'] = $thread_id; $response['comment_id'] = $comment_id; } break; diff --git a/classes/class-supportflow-permissions.php b/classes/class-supportflow-permissions.php index 4a52a14..ed22b25 100644 --- a/classes/class-supportflow-permissions.php +++ b/classes/class-supportflow-permissions.php @@ -2,34 +2,34 @@ /** * Sets up the permissions for SupportFlow * - * @since 0.1 + * @since 0.1 */ class SupportFlow_Permissions extends SupportFlow { /** * Holds all of the capabilities managed by SupportFlow. * - * @access private - * @var array + * @access private + * @var array */ private $_caps = array(); /** * Array of user roles and associated capabilities. * - * @access private - * @var array + * @access private + * @var array */ private $_role_cap_map = array(); /** * Construct the object and initiate actions to setup permissions. * - * @access public - * @since 0.1 - * @uses add_action + * @access public + * @since 0.1 + * @uses add_action * - * @return SupportFlow_Permissions + * @return SupportFlow_Permissions */ public function __construct() { add_action( 'supportflow_after_setup_actions', array( $this, 'setup_actions' ) ); @@ -38,11 +38,11 @@ public function __construct() { /** * Initiates the actions for the permissions class. * - * @access public - * @since 0.1 - * @uses add_action + * @access public + * @since 0.1 + * @uses add_action * - * @return void + * @return void */ public function setup_actions() { $this->_setup_caps(); @@ -52,54 +52,59 @@ public function setup_actions() { /** * Setup the mapping of roles to capabilities. * - * @access public - * @since 0.1 - * @uses apply_filters + * @access public + * @since 0.1 + * @uses apply_filters * - * @return void + * @return void */ private function _setup_caps() { // Setup the default caps for SupportFlow - $this->_caps = apply_filters( 'supportflow_caps', array( - 'close_others_threads' => 'sf_close_others_threads', - 'open_others_threads' => 'sf_open_others_threads', - 'reopen_others_threads' => 'sf_reopen_others_threads', - 'comment_on_others_threads' => 'sf_comment_on_others_threads', - 'close_threads' => 'sf_close_threads', - 'open_threads' => 'sf_open_threads', - 'reopen_threads' => 'sf_reopen_threads', - 'comment_on_threads' => 'sf_comment_on_threads', - ) ); + $this->_caps = apply_filters( + 'supportflow_caps', array( + 'close_others_threads' => 'sf_close_others_threads', + 'open_others_threads' => 'sf_open_others_threads', + 'reopen_others_threads' => 'sf_reopen_others_threads', + 'comment_on_others_threads' => 'sf_comment_on_others_threads', + 'close_threads' => 'sf_close_threads', + 'open_threads' => 'sf_open_threads', + 'reopen_threads' => 'sf_reopen_threads', + 'comment_on_threads' => 'sf_comment_on_threads', + ) + ); // Map the default caps onto WordPress roles - $this->_role_cap_map = apply_filters( 'supportflow_role_cap_map', array( - 'administrator' => $this->get_caps(), // Apply all caps - 'editor' => $this->get_caps(), // Apply all caps - 'author' => array( - 'close_threads' => $this->get_cap( 'close_threads' ), - 'open_threads' => $this->get_cap( 'open_threads' ), - 'comment_on_threads' => $this->get_cap( 'comment_on_threads' ), - ), - 'contributor' => array( - 'comment_on_threads' => $this->get_cap( 'comment_on_threads' ), - ), - ) ); + $this->_role_cap_map = apply_filters( + 'supportflow_role_cap_map', array( + 'administrator' => $this->get_caps(), // Apply all caps + 'editor' => $this->get_caps(), // Apply all caps + 'author' => array( + 'close_threads' => $this->get_cap( 'close_threads' ), + 'open_threads' => $this->get_cap( 'open_threads' ), + 'comment_on_threads' => $this->get_cap( 'comment_on_threads' ), + ), + 'contributor' => array( + 'comment_on_threads' => $this->get_cap( 'comment_on_threads' ), + ), + ) + ); } /** * Adds the standard SupportFlow capabilities to built-in WordPress roles. * - * @access public - * @since 0.1 - * @uses get_role, WP_Roles::add_cap + * @access public + * @since 0.1 + * @uses get_role, WP_Roles::add_cap * - * @return void + * @return void */ public function add_capabilities() { $role_cap_map = $this->get_role_cap_map(); - if ( empty( $role_cap_map ) ) + if ( empty( $role_cap_map ) ) { return; + } // Loop through roles, adding the associated caps to each role foreach ( $role_cap_map as $role => $caps ) { @@ -121,10 +126,10 @@ public function add_capabilities() { /** * Get all SF capabilities. * - * @access public - * @since 0.1 + * @access public + * @since 0.1 * - * @return array Array of SF capabilities. + * @return array Array of SF capabilities. */ public function get_caps() { return $this->_caps; @@ -133,10 +138,10 @@ public function get_caps() { /** * Get the mapping of roles to capabilities. * - * @access public - * @since 0.1 + * @access public + * @since 0.1 * - * @return array Array roles and caps. + * @return array Array roles and caps. */ public function get_role_cap_map() { return $this->_role_cap_map; @@ -145,19 +150,21 @@ public function get_role_cap_map() { /** * Get the name of an individual capability. * - * @access public - * @since 0.1 + * @access public + * @since 0.1 + * + * @param string $cap Capability to get. * - * @param string $cap Capability to get. - * @return string|bool Capability name on success; False on failure. + * @return string|bool Capability name on success; False on failure. */ public function get_cap( $cap ) { $all_caps = $this->get_caps(); - if ( array_key_exists( $cap, $all_caps ) ) - return $all_caps[ $cap ]; - else + if ( array_key_exists( $cap, $all_caps ) ) { + return $all_caps[$cap]; + } else { return false; + } } } diff --git a/classes/class-supportflow-ui-submissionform.php b/classes/class-supportflow-ui-submissionform.php index bca448b..8b53e16 100644 --- a/classes/class-supportflow-ui-submissionform.php +++ b/classes/class-supportflow-ui-submissionform.php @@ -19,59 +19,68 @@ public function shortcode_submissionform() { $html = ''; $html .= '
        '; - $html .= '
        '; - - if ( ! empty( $this->messages ) ) { - foreach ( $this->messages as $message ) { - $html .= '

        ' . $message . '

        '; - } - } - - $html .= "

        This is just an ugly form that's meant for testing. We'll probably want something like this in the final version but we can worry about that later.

        "; - $html .= '

        Your Name:

        '; - $html .= '

        Your Email:

        '; - $html .= '

        Subject:

        '; - $html .= '

        Message:

        '; - $html .= '

        '; - $html .= '

        '; - $html .= '
        '; + $html .= '
        '; + + if ( ! empty( $this->messages ) ) { + foreach ( $this->messages as $message ) { + $html .= '

        ' . $message . '

        '; + } + } + + $html .= "

        This is just an ugly form that's meant for testing. We'll probably want something like this in the final version but we can worry about that later.

        "; + $html .= '

        Your Name:

        '; + $html .= '

        Your Email:

        '; + $html .= '

        Subject:

        '; + $html .= '

        Message:

        '; + $html .= '

        '; + $html .= '

        '; + $html .= '
        '; $html .= '
        '; return $html; } public function action_init_handle_form_submission() { - if ( empty( $_POST['supportflow'] ) || ! is_array( $_POST['supportflow'] ) ) + if ( empty( $_POST['supportflow'] ) || ! is_array( $_POST['supportflow'] ) ) { return; + } $_POST['supportflow'] = array_map( 'stripslashes', $_POST['supportflow'] ); - if ( empty( $_POST['supportflow']['name'] ) ) + if ( empty( $_POST['supportflow']['name'] ) ) { $this->messages[] = 'The name field is required.'; + } - if ( empty( $_POST['supportflow']['email'] ) ) + if ( empty( $_POST['supportflow']['email'] ) ) { $this->messages[] = 'The email field is required.'; - elseif ( ! is_email( $_POST['supportflow']['email'] ) ) + } elseif ( ! is_email( $_POST['supportflow']['email'] ) ) { $this->messages[] = 'Please enter a valid e-mail address.'; + } - if ( empty( $_POST['supportflow']['subject'] ) ) + if ( empty( $_POST['supportflow']['subject'] ) ) { $this->messages[] = 'The subject field is required.'; + } - if ( empty( $_POST['supportflow']['message'] ) ) + if ( empty( $_POST['supportflow']['message'] ) ) { $this->messages[] = 'You must enter a message.'; + } - if ( ! empty( $this->messages ) ) + if ( ! empty( $this->messages ) ) { return; + } - $thread_id = SupportFlow()->create_thread( array( - 'subject' => $_POST['supportflow']['subject'], - 'message' => $_POST['supportflow']['message'], - 'requester_name' => $_POST['supportflow']['name'], - 'requester_email' => $_POST['supportflow']['email'], - ) ); + $thread_id = SupportFlow()->create_thread( + array( + 'subject' => $_POST['supportflow']['subject'], + 'message' => $_POST['supportflow']['message'], + 'requester_name' => $_POST['supportflow']['name'], + 'requester_email' => $_POST['supportflow']['email'], + ) + ); if ( is_wp_error( $thread_id ) ) { $this->messages[] = 'There was an error creating the thread: ' . $thread_id->get_error_message(); + return; } diff --git a/classes/class-supportflow-ui-widget.php b/classes/class-supportflow-ui-widget.php index 6e8b156..01e640f 100644 --- a/classes/class-supportflow-ui-widget.php +++ b/classes/class-supportflow-ui-widget.php @@ -5,8 +5,9 @@ class SupportFlow_UI_Widget extends SupportFlow { public $script_slug = 'supportflow-user-widget'; function __construct() { - if ( ! empty( $_REQUEST['supportflow_widget'] ) ) + if ( ! empty( $_REQUEST['supportflow_widget'] ) ) { add_action( 'supportflow_after_setup_actions', array( $this, 'setup_actions' ) ); + } } public function setup_actions() { @@ -20,20 +21,22 @@ public function setup_actions() { */ public function filter_json_api_response( $response ) { - if ( 'error' == $response['status'] ) + if ( 'error' == $response['status'] ) { return $response; + } - switch( $response['api-action'] ) { + switch ( $response['api-action'] ) { case 'create-thread': case 'get-thread': - $response['widget_title'] = get_the_title( (int)$response['thread_id'] ); - $response['html'] = $this->render_single_thread_comments_html( (int)$response['thread_id'] ); + $response['widget_title'] = get_the_title( (int) $response['thread_id'] ); + $response['html'] = $this->render_single_thread_comments_html( (int) $response['thread_id'] ); break; case 'add-thread-comment': - $comment = get_comment( $response['comment_id'] ); + $comment = get_comment( $response['comment_id'] ); $response['html'] = '
      • ' . $this->render_single_comment_html( $comment ) . '
      • '; break; } + return $response; } @@ -42,10 +45,11 @@ public function render_single_thread_comments_html( $thread_id ) { $comments = SupportFlow()->get_thread_comments( $thread_id, array( 'status' => 'public', 'order' => 'ASC' ) ); $output = '
          '; - foreach( $comments as $comment ) { + foreach ( $comments as $comment ) { $output .= '
        • ' . $this->render_single_comment_html( $comment ) . '
        • '; } $output .= '
        '; + return $output; } @@ -53,12 +57,13 @@ public function render_single_comment_html( $comment ) { $comment_timestamp = get_comment_date( 'M. n', $comment->comment_ID ); $output = '
        ' - . wpautop( stripslashes( $comment->comment_content ) ) - . '
        ' - . '
        ' - . '' . esc_html( $comment->comment_author ) . '' - . '' . esc_html( $comment_timestamp ) . '' - . '
        '; + . wpautop( stripslashes( $comment->comment_content ) ) + . '
      ' + . '
      ' + . '' . esc_html( $comment->comment_author ) . '' + . '' . esc_html( $comment_timestamp ) . '' + . '
      '; + return $output; } @@ -71,11 +76,11 @@ public function render_all_threads_html() { $output = '
      ' . __( 'No open threads.', 'supportflow' ) . '
      '; } else { $output = '
        '; - foreach( $threads as $thread ) { + foreach ( $threads as $thread ) { $output .= '
      • '; $output .= '

        ' . get_the_title( $thread->ID ) . '

        '; $output .= '
        '; - $comments = SupportFlow()->get_thread_comments( $thread->ID, array( 'status' => 'public' ) ); + $comments = SupportFlow()->get_thread_comments( $thread->ID, array( 'status' => 'public' ) ); $last_comment = array_shift( $comments ); $output .= $this->render_single_comment_html( $last_comment ); $output .= '
        '; @@ -83,6 +88,7 @@ public function render_all_threads_html() { } $output .= '
      '; } + return $output; } @@ -102,20 +108,20 @@ public function action_template_redirect() { $widget_title = __( 'Support', 'supportflow' ); - $start_thread_text = __( 'Start thread', 'supportflow' ); + $start_thread_text = __( 'Start thread', 'supportflow' ); $starting_thread_text = __( 'Starting thread...', 'supportflow' ); - $send_reply_text = __( 'Send reply', 'supportflow' ); - $sending_reply_text = __( 'Sending reply...', 'supportflow' ); + $send_reply_text = __( 'Send reply', 'supportflow' ); + $sending_reply_text = __( 'Sending reply...', 'supportflow' ); wp_localize_script( $this->script_slug, 'SupportFlowUserWidgetVars', array( - 'ajaxurl' => $ajaxurl, - 'widget_title' => $widget_title, - 'start_thread_text' => $start_thread_text, - 'starting_thread_text' => $starting_thread_text, - 'send_reply_text' => $send_reply_text, - 'sending_reply_text' => $sending_reply_text, + 'ajaxurl' => $ajaxurl, + 'widget_title' => $widget_title, + 'start_thread_text' => $start_thread_text, + 'starting_thread_text' => $starting_thread_text, + 'send_reply_text' => $send_reply_text, + 'sending_reply_text' => $sending_reply_text, ) ); @@ -147,7 +153,7 @@ public function action_template_redirect() {
-render_all_threads_html(); ?> + render_all_threads_html(); ?>
diff --git a/classes/class-supportflow-wp-cli.php b/classes/class-supportflow-wp-cli.php index 68a617e..a551788 100644 --- a/classes/class-supportflow-wp-cli.php +++ b/classes/class-supportflow-wp-cli.php @@ -12,8 +12,9 @@ class SupportFlow_WPCLI extends WP_CLI_Command { */ public static function help() { - WP_CLI::line( << + WP_CLI::line( + << Possible subcommands: download_and_process_email_replies import_remote Import from a remote SupportFlow @@ -31,22 +32,23 @@ public static function help() { */ public function download_and_process_email_replies( $args, $assoc_args ) { - $defaults = array( - 'host' => '', // '{imap.gmail.com:993/imap/ssl/novalidate-cert}' for Gmail - 'username' => '', // Full email address for Gmail - 'password' => '', // Whatever the password is - 'inbox' => 'INBOX', // Where the new emails will go - 'archive' => 'SF_ARCHIVE', // Where you'd like emails put after they've been processed - ); + $defaults = array( + 'host' => '', // '{imap.gmail.com:993/imap/ssl/novalidate-cert}' for Gmail + 'username' => '', // Full email address for Gmail + 'password' => '', // Whatever the password is + 'inbox' => 'INBOX', // Where the new emails will go + 'archive' => 'SF_ARCHIVE', // Where you'd like emails put after they've been processed + ); $connection_details = wp_parse_args( $assoc_args, $defaults ); // Allow the connection details to be stored in a secret config file or similar $connection_details = apply_filters( 'supportflow_imap_connection_details', $connection_details ); - $retval = SupportFlow()->extend->email_replies->download_and_process_email_replies( $connection_details ); - if ( is_wp_error( $retval ) ) + $retval = SupportFlow()->extend->email_replies->download_and_process_email_replies( $connection_details ); + if ( is_wp_error( $retval ) ) { WP_CLI::error( $retval->get_error_message() ); - else + } else { WP_CLI::success( $retval ); + } } /** @@ -57,12 +59,12 @@ public function download_and_process_email_replies( $args, $assoc_args ) { public function import_remote( $args, $assoc_args ) { $defaults = array( - 'db_host' => '', - 'db_name' => '', - 'db_user' => '', - 'db_pass' => '', - 'table_prefix' => 'support_', - ); + 'db_host' => '', + 'db_name' => '', + 'db_user' => '', + 'db_pass' => '', + 'table_prefix' => 'support_', + ); $this->args = wp_parse_args( $assoc_args, $defaults ); @@ -77,31 +79,31 @@ public function import_remote( $args, $assoc_args ) { // Register our tables $sp_tables = array( - 'messagemeta', - 'messages', - 'predefined_messages', - 'tags', - 'threadmeta', - 'threads', - 'usermeta', - 'users', - ); - foreach( $sp_tables as $sp_table ) { + 'messagemeta', + 'messages', + 'predefined_messages', + 'tags', + 'threadmeta', + 'threads', + 'usermeta', + 'users', + ); + foreach ( $sp_tables as $sp_table ) { $table_name = $this->args['table_prefix'] . $sp_table; - if ( !in_array( $table_name, $spdb->tables ) ) { + if ( ! in_array( $table_name, $spdb->tables ) ) { $spdb->tables[$sp_table] = $table_name; - $spdb->$sp_table = $table_name; + $spdb->$sp_table = $table_name; } } - + /** * Import threads and their messages * * @todo Support for importing priorities. This seems to exist in the schema for old SP, but not in the interface */ - $old_threads = $spdb->get_results( "SELECT * FROM $spdb->threads" ); + $old_threads = $spdb->get_results( "SELECT * FROM $spdb->threads" ); $count_threads_created = 0; - foreach( $old_threads as $old_thread ) { + foreach ( $old_threads as $old_thread ) { // Don't import a thread that's already been imported if ( $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE '_imported_id'=%d", $old_thread->thread_id ) ) ) { @@ -111,39 +113,41 @@ public function import_remote( $args, $assoc_args ) { // Create the new thread $thread_args = array( - 'subject' => $old_thread->subject, - 'date' => $old_thread->dt, - 'status' => 'sf_' . $old_thread->state, - ); - $thread_id = SupportFlow()->create_thread( $thread_args ); - if ( is_wp_error( $thread_id ) ) + 'subject' => $old_thread->subject, + 'date' => $old_thread->dt, + 'status' => 'sf_' . $old_thread->state, + ); + $thread_id = SupportFlow()->create_thread( $thread_args ); + if ( is_wp_error( $thread_id ) ) { continue; + } // Add the respondent to the thread SupportFlow()->update_thread_respondents( $thread_id, $old_thread->email ); // Get the thread's messages and import those too - $old_messages = (array)$spdb->get_results( $spdb->prepare( "SELECT * FROM $spdb->messages WHERE thread_id=%d", $old_thread->thread_id ) ); + $old_messages = (array) $spdb->get_results( $spdb->prepare( "SELECT * FROM $spdb->messages WHERE thread_id=%d", $old_thread->thread_id ) ); $count_comments = 0; - foreach( $old_messages as $old_message ) { + foreach ( $old_messages as $old_message ) { $message_args = array( - 'comment_author' => $old_message->email, - 'comment_author_email' => $old_message->email, - 'time' => $old_message->dt, - 'comment_approved' => ( 'note' == $old_message->message_type ) ? 'private' : 'public', - ); - if ( function_exists( 'What_The_Email' ) ) + 'comment_author' => $old_message->email, + 'comment_author_email' => $old_message->email, + 'time' => $old_message->dt, + 'comment_approved' => ( 'note' == $old_message->message_type ) ? 'private' : 'public', + ); + if ( function_exists( 'What_The_Email' ) ) { $old_message->content = What_The_Email()->get_message( $old_message->content ); + } $comment_id = SupportFlow()->add_thread_comment( $thread_id, $old_message->content, $message_args ); add_comment_meta( $comment_id, '_imported_id', $old_message->message_id ); - $count_comments++; + $count_comments ++; } // One the thread is created, log the old thread ID update_post_meta( $thread_id, '_imported_id', $old_thread->thread_id ); WP_CLI::line( "Created: #{$old_thread->thread_id} '{$old_thread->subject}' with {$count_comments} comments" ); - $count_threads_created++; + $count_threads_created ++; } /** diff --git a/css/admin.css b/css/admin.css index 8d308fc..c37d50b 100644 --- a/css/admin.css +++ b/css/admin.css @@ -22,8 +22,8 @@ #supportflow-subject.postbox, #supportflow-respondents.postbox { - border:none; - background:none; + border: none; + background: none; } #supportflow-subject.postbox .inside, @@ -39,7 +39,7 @@ #supportflow-respondents h3.hndle, #supportflow-comments .handlediv, #supportflow-comments h3.hndle { - display:none; + display: none; } #supportflow-details .handlediv, @@ -67,7 +67,7 @@ #supportflow-subject h4, #supportflow-respondents h4, #supportflow-comments h4 { - font-family: Georgia,"Times New Roman","Bitstream Charter",Times,serif; + font-family: Georgia, "Times New Roman", "Bitstream Charter", Times, serif; font-size: 13px; font-weight: normal; margin-left: 0; @@ -90,6 +90,7 @@ padding: 8px; font-size: 1.2em; } + #supportflow-comments.postbox #comment-reply { width: 100%; } @@ -102,7 +103,7 @@ } #supportflow-comments.postbox .inside #message-tools #submit-action { - padding:0; + padding: 0; margin-top: 5px; float: right; } @@ -151,7 +152,7 @@ } #supportflow-comments.postbox ul.thread-comments li { - clear:left; + clear: left; margin-top: 20px; margin-bottom: 20px; } @@ -197,15 +198,17 @@ } input::-webkit-input-placeholder { - color:#BBB; + color: #BBB; } + input:-moz-placeholder { - color:#BBB; + color: #BBB; } textarea::-webkit-input-placeholder { - color:#BBB; + color: #BBB; } + textarea:-moz-placeholder { - color:#BBB; + color: #BBB; } \ No newline at end of file diff --git a/css/widget.css b/css/widget.css index e90c0fe..29dc45a 100644 --- a/css/widget.css +++ b/css/widget.css @@ -14,7 +14,7 @@ body { } #supportflow-back { - display:none; + display: none; position: relative; background-color: #fff; border: 1px solid #bbb; @@ -37,6 +37,7 @@ body { #supportflow-newthread-box { padding: 10px 0; } + #supportflow-newthread, #new-thread-submit { background-color: #fff; @@ -49,6 +50,7 @@ body { white-space: nowrap; -webkit-border-radius: 15px; } + #new-thread-submit { margin-top: 10px; } @@ -61,7 +63,7 @@ body { #supportflow-existing-thread-form textarea { width: 100%; font-size: 16px; - font-weight:200; + font-weight: 200; border: 1px solid #BBB; -webkit-border-radius: 3px; -moz-border-radius: 3px; @@ -89,7 +91,7 @@ body { #supportflow-newthread-form input[type='submit']:hover, #supportflow-existing-thread-form input[type='submit']:hover { - cursor:pointer; + cursor: pointer; border-color: #888; color: #111; } @@ -102,9 +104,11 @@ body { #supportflow-back:active { border-color: #666; } + #supportflow-all-threads .thread .time { color: gray; } + #supportflow-all-threads .nothreads { border-bottom: 1px solid #989898; border-top: 1px solid #989898; @@ -113,17 +117,21 @@ body { padding: 20px 0; text-align: center; } + #supportflow-newthread-form { display: none; margin: 0; } + #supportflow-newthread-form input[name="subject"] { margin-bottom: 6px; } + #supportflow-newthread-form input[type="text"] { font-size: 14px; line-height: 150%; } + #supportflow-newthread-form input[type="text"] { border: 1px solid #ccc; -webkit-border-radius: 3px; @@ -132,48 +140,58 @@ body { padding: 3px 8px; width: 100%; } + #supportflow-help:hover, #supportflow-help:active { background-color: #13455b; } #supportflow-single-thread { - display:none; + display: none; } + #supportflow-single-thread ul, #supportflow-all-threads ul { list-style-type: none; margin: 0; padding: 0; } + #supportflow-single-thread ul li, #supportflow-all-threads ul li { border-bottom: 1px solid #ECECEC; padding: 5px 5px 10px 5px; } + #supportflow-all-threads ul li:first-child { border-top: 1px solid #ECECEC; padding-top: 0; } + #supportflow-all-threads ul li:hover { background-color: #fff; cursor: pointer; } + #supportflow-all-threads ul li h4 { font: bold 14px/18px "Helvetica Neue", sans-serif; margin: 10px 0 0 0; } + .thread-comments .thread-comment-body p { font: 13px/18px "Helvetica Neue", sans-serif; margin: 5px 0; } + .thread-comments .thread-comment-meta { font-size: 12px; color: #888; } + .thread-comments .thread-comment-meta .thread-comment-author { font-weight: bold; } + .thread-comments .thread-comment-meta .thread-comment-timestamp { float: right; } \ No newline at end of file diff --git a/js/plupload.js b/js/plupload.js index 1590a80..81a7f5b 100644 --- a/js/plupload.js +++ b/js/plupload.js @@ -1,26 +1,26 @@ -jQuery(document).ready(function($) { +jQuery(document).ready(function ($) { new wp.Uploader({ /* Selectors */ - browser: '#upload-indicator', - dropzone:  '#comment-reply', + browser : '#upload-indicator', + dropzone: '#comment-reply', /* Callbacks */ - success : function( attachment ) { - $( '#comment-attachments' ).val( $( '#comment-attachments' ).val() + ',' + attachment.id ); - $( '#upload-messages' ).hide(); - $( '
  • ', { + success : function (attachment) { + $('#comment-attachments').val($('#comment-attachments').val() + ',' + attachment.id); + $('#upload-messages').hide(); + $('
  • ', { html: '' + attachment.title + '', - } ).appendTo( $( 'ul#comment-attachments-list' ) ); - $( '#upload-messages' ).removeClass( 'uploading' ); + }).appendTo($('ul#comment-attachments-list')); + $('#upload-messages').removeClass('uploading'); }, - error : function ( reason ) { - $( '#upload-indicator' ).html( reason ).addClass('error'); + error: function (reason) { + $('#upload-indicator').html(reason).addClass('error'); }, - added : function() { - $( '#upload-indicator' ).addClass( 'uploading' ); + added: function () { + $('#upload-indicator').addClass('uploading'); }, // TypeError: file is undefined @@ -28,8 +28,8 @@ jQuery(document).ready(function($) { // $( '#upload-indicator' ).html( "Uploading: " + file.name + ' ' + file.percent + '%' ); // }, - complete : function() { - $( '#upload-indicator' ).html( 'All done!' ); + complete: function () { + $('#upload-indicator').html('All done!'); } }); }); diff --git a/js/respondents-autocomplete.js b/js/respondents-autocomplete.js index 55f015d..514e9b0 100644 --- a/js/respondents-autocomplete.js +++ b/js/respondents-autocomplete.js @@ -1,36 +1,36 @@ -jQuery(document).ready(function($) { - - var getSearchTerm = function() { +jQuery(document).ready(function ($) { + + var getSearchTerm = function () { // Input of the Respondents field can contain multiple addresses, // we only want to search on the last address var resps = $('#respondents').val().replace(" ", '').split(","); - if( $.isArray(resps) && resps.length > 1 ) { + if ($.isArray(resps) && resps.length > 1) { var search_for = resps.pop(); } else { search_for = resps[0]; } - return { "respondents":search_for, "api-action":'get-respondents' }; - - } + return { "respondents": search_for, "api-action": 'get-respondents' }; + + } $('#respondents').autocomplete({ - source: function( req, response ) { - + source : function (req, response) { + $.ajax({ - url: SFRespondentsAc.ajax_url, + url : SFRespondentsAc.ajax_url, dataType: 'json', - data: getSearchTerm(), - success: function(data) { - if(data.query == "") { + data : getSearchTerm(), + success : function (data) { + if (data.query == "") { response(false); return false; } - response( $.map( data.respondents, function (item) { + response($.map(data.respondents, function (item) { // normaliz input var resps = $('#respondents').val().replace(" ", '').split(","); diff --git a/js/supportflow-user-widget.js b/js/supportflow-user-widget.js index 695d4be..2c2395f 100644 --- a/js/supportflow-user-widget.js +++ b/js/supportflow-user-widget.js @@ -4,54 +4,54 @@ var supportflow = {}; -( function( $ ) { +(function ($) { - supportflow.init = function() { + supportflow.init = function () { supportflow.widget_title = $('h1#widget-title'); supportflow.original_widget_title = supportflow.widget_title.html(); supportflow.back_button = $('#supportflow-back'); - supportflow.back_button.click( supportflow.hide_single_thread_view ); + supportflow.back_button.click(supportflow.hide_single_thread_view); // Creating a new thread supportflow.new_thread_button = $('#supportflow-newthread'); supportflow.new_thread_form = $('#supportflow-newthread-form'); - supportflow.new_thread_subject = supportflow.new_thread_form.find('#new-thread-subject' ); - supportflow.new_thread_message = supportflow.new_thread_form.find('#new-thread-message' ); - supportflow.new_thread_submit = supportflow.new_thread_form.find('#new-thread-submit' ); + supportflow.new_thread_subject = supportflow.new_thread_form.find('#new-thread-subject'); + supportflow.new_thread_message = supportflow.new_thread_form.find('#new-thread-message'); + supportflow.new_thread_submit = supportflow.new_thread_form.find('#new-thread-submit'); - supportflow.new_thread_button.click( supportflow.show_new_thread_form ); - supportflow.new_thread_form.submit( supportflow.submit_new_thread_form ); + supportflow.new_thread_button.click(supportflow.show_new_thread_form); + supportflow.new_thread_form.submit(supportflow.submit_new_thread_form); // Viewing all threads supportflow.all_threads_view = $('#supportflow-all-threads'); - supportflow.all_threads_view.find('li').click( supportflow.show_single_thread_view ); + supportflow.all_threads_view.find('li').click(supportflow.show_single_thread_view); // Viewing an existing thread supportflow.single_thread_view = $('#supportflow-single-thread'); supportflow.single_thread_body = supportflow.single_thread_view.find('#supportflow-thread-body'); supportflow.single_thread_form = supportflow.single_thread_view.find('#supportflow-existing-thread-form'); - supportflow.single_thread_id = supportflow.single_thread_form.find('#existing-thread-id' ); + supportflow.single_thread_id = supportflow.single_thread_form.find('#existing-thread-id'); supportflow.single_thread_message = supportflow.single_thread_view.find('#existing-thread-message'); - supportflow.single_thread_submit = supportflow.single_thread_form.find('#existing-thread-submit' ); + supportflow.single_thread_submit = supportflow.single_thread_form.find('#existing-thread-submit'); - supportflow.single_thread_form.submit( supportflow.submit_new_message_form ); + supportflow.single_thread_form.submit(supportflow.submit_new_message_form); }; - supportflow.get_ajax_url = function( action ) { + supportflow.get_ajax_url = function (action) { return SupportFlowUserWidgetVars.ajaxurl + '&api-action=' + action; }; - supportflow.show_new_thread_form = function() { + supportflow.show_new_thread_form = function () { $(this).hide(); $('#supportflow-newthread-form').slideDown(); }; - supportflow.hide_new_thread_form = function() { + supportflow.hide_new_thread_form = function () { } - supportflow.hide_single_thread_view = function() { + supportflow.hide_single_thread_view = function () { supportflow.single_thread_view.hide(); supportflow.widget_title.html(supportflow.original_widget_title); supportflow.all_threads_view.attr('opacity', '1').show(); @@ -59,7 +59,7 @@ var supportflow = {}; supportflow.back_button.hide(); } - supportflow.show_single_thread_view = function( e ) { + supportflow.show_single_thread_view = function (e) { e.preventDefault(); supportflow.back_button.show(); @@ -67,94 +67,94 @@ var supportflow = {}; supportflow.new_thread_button.hide(); supportflow.all_threads_view.attr('opacity', '0.5'); supportflow.widget_title.html($(this).find('.thread-title').html()); - var thread_id = $(this).attr('id').replace('thread-',''); + var thread_id = $(this).attr('id').replace('thread-', ''); var data = { - thread_id: thread_id, - supportflow_widget: true, + thread_id : thread_id, + supportflow_widget: true, } - $.get( supportflow.get_ajax_url( 'get-thread' ), data, supportflow.handle_new_thread_response ); + $.get(supportflow.get_ajax_url('get-thread'), data, supportflow.handle_new_thread_response); } - supportflow.submit_new_thread_form = function( e ) { + supportflow.submit_new_thread_form = function (e) { e.preventDefault(); - supportflow.disable_thread_form( supportflow.new_thread_form, SupportFlowUserWidgetVars.starting_thread_text ); + supportflow.disable_thread_form(supportflow.new_thread_form, SupportFlowUserWidgetVars.starting_thread_text); var data = { - subject: supportflow.new_thread_subject.val(), - message: supportflow.new_thread_message.val(), - supportflow_widget: true, + subject : supportflow.new_thread_subject.val(), + message : supportflow.new_thread_message.val(), + supportflow_widget: true, } - $.post( supportflow.get_ajax_url( 'create-thread' ), data, supportflow.handle_new_thread_response ); + $.post(supportflow.get_ajax_url('create-thread'), data, supportflow.handle_new_thread_response); } - supportflow.submit_new_message_form = function( e ) { + supportflow.submit_new_message_form = function (e) { e.preventDefault(); - supportflow.disable_thread_form( supportflow.single_thread_form, SupportFlowUserWidgetVars.sending_reply_text ); + supportflow.disable_thread_form(supportflow.single_thread_form, SupportFlowUserWidgetVars.sending_reply_text); var data = { - thread_id: supportflow.single_thread_id.val(), - message: supportflow.single_thread_message.val(), - supportflow_widget: true, + thread_id : supportflow.single_thread_id.val(), + message : supportflow.single_thread_message.val(), + supportflow_widget: true, } - $.post( supportflow.get_ajax_url( 'add-thread-comment' ), data, supportflow.handle_new_message_response ); + $.post(supportflow.get_ajax_url('add-thread-comment'), data, supportflow.handle_new_message_response); } - supportflow.enable_thread_form = function( form, clear_form, submit_text ) { + supportflow.enable_thread_form = function (form, clear_form, submit_text) { form.find('input.thread-subject').removeAttr('disabled'); form.find('textarea.thread-message').removeAttr('disabled'); form.find('input.submit-button').removeAttr('disabled').val(submit_text).removeClass('disabled'); - if ( clear_form ) { + if (clear_form) { form.find('input.thread-subject').val(''); form.find('textarea.thread-message').val(''); } } - supportflow.disable_thread_form = function( form, submit_text ) { + supportflow.disable_thread_form = function (form, submit_text) { form.find('input.thread-subject').attr('disabled', 'disabled'); form.find('textarea.thread-message').attr('disabled', 'disabled'); form.find('input.submit-form').attr('disabled', 'disabled').val(submit_text).addClass('disabled'); } - supportflow.handle_new_thread_response = function( response ) { + supportflow.handle_new_thread_response = function (response) { // If there was an error in the process, re-enable the form and show the error message - if ( 'error' == response.status ) { - supportflow.enable_thread_form( supportflow.new_thread_form, false, SupportFlowUserWidgetVars.start_thread_text ); + if ('error' == response.status) { + supportflow.enable_thread_form(supportflow.new_thread_form, false, SupportFlowUserWidgetVars.start_thread_text); return; } // Restore and hide the form - supportflow.enable_thread_form( supportflow.new_thread_form, true, SupportFlowUserWidgetVars.start_thread_text ); + supportflow.enable_thread_form(supportflow.new_thread_form, true, SupportFlowUserWidgetVars.start_thread_text); supportflow.new_thread_form.hide(); supportflow.all_threads_view.hide(); // We're good to render the single thread supportflow.widget_title.html(response.title); - supportflow.single_thread_body.html( response.html ); + supportflow.single_thread_body.html(response.html); supportflow.single_thread_view.show(); supportflow.single_thread_id.val(response.thread_id); } - supportflow.handle_new_message_response = function( response ) { + supportflow.handle_new_message_response = function (response) { // If there was an error in the process, re-enable the form and show the error message - if ( 'error' == response.status ) { + if ('error' == response.status) { // @todo display the error message - supportflow.enable_thread_form( supportflow.single_thread_form, false, SupportFlowUserWidgetVars.send_reply_text ); + supportflow.enable_thread_form(supportflow.single_thread_form, false, SupportFlowUserWidgetVars.send_reply_text); return; } // Renable the form and append the response - supportflow.enable_thread_form( supportflow.single_thread_form, true, SupportFlowUserWidgetVars.send_reply_text ); - supportflow.single_thread_body.find( 'ul' ).append( response.html ); + supportflow.enable_thread_form(supportflow.single_thread_form, true, SupportFlowUserWidgetVars.send_reply_text); + supportflow.single_thread_body.find('ul').append(response.html); } // Initialize everything! - $( document ).ready( supportflow.init ); + $(document).ready(supportflow.init); -} )( jQuery ); \ No newline at end of file +})(jQuery); \ No newline at end of file diff --git a/supportflow-user-widget.php b/supportflow-user-widget.php index 3b7bc66..96ae3c5 100644 --- a/supportflow-user-widget.php +++ b/supportflow-user-widget.php @@ -1,19 +1,20 @@ + ?> - + - + includes(); self::$instance->setup_actions(); } + return self::$instance; } @@ -76,8 +77,8 @@ public static function instance() { * A dummy constructor to prevent SupportFlow from being loaded more than once. * * @since SupportFlow 0.1 - * @see SupportFlow::instance() - * @see SupportFlow(); + * @see SupportFlow::instance() + * @see SupportFlow(); */ private function __construct() { /* Do nothing here */ @@ -134,72 +135,74 @@ public function __set( $key, $value ) { * Set some smart defaults to class variables. Allow some of them to be * filtered to allow for early overriding. * - * @since SupportFlow 0.1 + * @since SupportFlow 0.1 * @access private - * @uses plugin_dir_path() To generate SupportFlow plugin path - * @uses plugin_dir_url() To generate SupportFlow plugin url - * @uses apply_filters() Calls various filters + * @uses plugin_dir_path() To generate SupportFlow plugin path + * @uses plugin_dir_url() To generate SupportFlow plugin url + * @uses apply_filters() Calls various filters */ private function setup_globals() { /** Version ***********************************************************/ - $this->version = '0.1-alpha'; // SupportFlow version + $this->version = '0.1-alpha'; // SupportFlow version /** Paths *************************************************************/ // Setup some base path and URL information - $this->file = __FILE__; - $this->basename = apply_filters( 'supportflow_plugin_basenname', plugin_basename( $this->file ) ); - $this->plugin_dir = apply_filters( 'supportflow_plugin_dir_path', plugin_dir_path( $this->file ) ); - $this->plugin_url = apply_filters( 'supportflow_plugin_dir_url', plugin_dir_url ( $this->file ) ); + $this->file = __FILE__; + $this->basename = apply_filters( 'supportflow_plugin_basenname', plugin_basename( $this->file ) ); + $this->plugin_dir = apply_filters( 'supportflow_plugin_dir_path', plugin_dir_path( $this->file ) ); + $this->plugin_url = apply_filters( 'supportflow_plugin_dir_url', plugin_dir_url( $this->file ) ); // Languages - $this->lang_dir = apply_filters( 'supportflow_lang_dir', trailingslashit( $this->plugin_dir . 'languages' ) ); + $this->lang_dir = apply_filters( 'supportflow_lang_dir', trailingslashit( $this->plugin_dir . 'languages' ) ); /** Identifiers *******************************************************/ - $this->post_type = apply_filters( 'supportflow_thread_post_type', 'sf_thread' ); - $this->respondents_tax = apply_filters( 'supportflow_respondents_taxonomy', 'sf_respondent' ); - $this->comment_type = apply_filters( 'supportflow_thread_comment_type', 'sf_comment' ); + $this->post_type = apply_filters( 'supportflow_thread_post_type', 'sf_thread' ); + $this->respondents_tax = apply_filters( 'supportflow_respondents_taxonomy', 'sf_respondent' ); + $this->comment_type = apply_filters( 'supportflow_thread_comment_type', 'sf_comment' ); $this->email_term_prefix = 'sf-'; $this->thread_secret_key = 'thread_secret'; - $this->post_statuses = apply_filters( 'supportflow_thread_post_statuses', array( - 'sf_new' => array( - 'label' => __( 'New', 'supportflow' ), - 'label_count' => _n_noop( 'New (%s)', 'New (%s)', 'supportflow' ), - ), - 'sf_open' => array( - 'label' => __( 'Open', 'supportflow' ), - 'label_count' => _n_noop( 'Open (%s)', 'Open (%s)', 'supportflow' ), - ), - 'sf_pending' => array( - 'label' => __( 'Pending', 'supportflow' ), - 'label_count' => _n_noop( 'Pending (%s)', 'Pending (%s)', 'supportflow' ), - ), - 'sf_closed' => array( - 'label' => __( 'Closed', 'supportflow' ), - 'label_count' => _n_noop( 'Closed (%s)', 'Closed (%s)', 'supportflow' ), - ), - ) ); + $this->post_statuses = apply_filters( + 'supportflow_thread_post_statuses', array( + 'sf_new' => array( + 'label' => __( 'New', 'supportflow' ), + 'label_count' => _n_noop( 'New (%s)', 'New (%s)', 'supportflow' ), + ), + 'sf_open' => array( + 'label' => __( 'Open', 'supportflow' ), + 'label_count' => _n_noop( 'Open (%s)', 'Open (%s)', 'supportflow' ), + ), + 'sf_pending' => array( + 'label' => __( 'Pending', 'supportflow' ), + 'label_count' => _n_noop( 'Pending (%s)', 'Pending (%s)', 'supportflow' ), + ), + 'sf_closed' => array( + 'label' => __( 'Closed', 'supportflow' ), + 'label_count' => _n_noop( 'Closed (%s)', 'Closed (%s)', 'supportflow' ), + ), + ) + ); /** Misc **************************************************************/ - $this->extend = new stdClass(); // Plugins add data here - $this->extend->ui = new stdClass(); // For UI-related plugins - $this->errors = new WP_Error(); // Feedback + $this->extend = new stdClass(); // Plugins add data here + $this->extend->ui = new stdClass(); // For UI-related plugins + $this->errors = new WP_Error(); // Feedback } /** * Include required files * - * @since SupportFlow 0.1 + * @since SupportFlow 0.1 * @access private - * @todo Be smarter about conditionally loading code - * @uses is_admin() If in WordPress admin, load additional file + * @todo Be smarter about conditionally loading code + * @uses is_admin() If in WordPress admin, load additional file */ private function includes() { @@ -217,8 +220,9 @@ private function includes() { require_once( $this->plugin_dir . 'classes/class-supportflow-ui-widget.php' ); /** Tools *************************************************************/ - if ( defined('WP_CLI') && WP_CLI ) + if ( defined( 'WP_CLI' ) && WP_CLI ) { require_once( $this->plugin_dir . '/classes/class-supportflow-wp-cli.php' ); + } # TODO: Akismet plugin? @@ -233,9 +237,9 @@ private function includes() { /** * Setup the default hooks and actions * - * @since SupportFlow 0.1 + * @since SupportFlow 0.1 * @access private - * @uses add_action() To add various actions + * @uses add_action() To add various actions */ private function setup_actions() { add_action( 'init', array( $this, 'action_init_register_post_type' ) ); @@ -251,30 +255,32 @@ private function setup_actions() { * Register the custom post type * * @since SupportFlow 0.1 - * @uses register_post_type() To register the post type + * @uses register_post_type() To register the post type */ public function action_init_register_post_type() { - register_post_type( $this->post_type, array( - 'labels' => array( - 'menu_name' => __( 'SupportFlow', 'supportflow' ), - 'name' => __( 'Threads', 'supportflow' ), - 'singular_name' => __( 'Thread', 'supportflow' ), - 'all_items' => __( 'All Threads', 'supportflow' ), - 'add_new' => __( 'New Thread', 'supportflow' ), - 'add_new_item' => __( 'Start New Thread', 'supportflow' ), - 'edit_item' => __( 'Discussion', 'supportflow' ), - 'new_item' => __( 'New Thread', 'supportflow' ), - 'view_item' => __( 'View Thread', 'supportflow' ), - 'search_items' => __( 'Search Threads', 'supportflow' ), - 'not_found' => __( 'No threads found', 'supportflow' ), - 'not_found_in_trash' => __( 'No threads found in trash', 'supportflow' ), + register_post_type( + $this->post_type, array( + 'labels' => array( + 'menu_name' => __( 'SupportFlow', 'supportflow' ), + 'name' => __( 'Threads', 'supportflow' ), + 'singular_name' => __( 'Thread', 'supportflow' ), + 'all_items' => __( 'All Threads', 'supportflow' ), + 'add_new' => __( 'New Thread', 'supportflow' ), + 'add_new_item' => __( 'Start New Thread', 'supportflow' ), + 'edit_item' => __( 'Discussion', 'supportflow' ), + 'new_item' => __( 'New Thread', 'supportflow' ), + 'view_item' => __( 'View Thread', 'supportflow' ), + 'search_items' => __( 'Search Threads', 'supportflow' ), + 'not_found' => __( 'No threads found', 'supportflow' ), + 'not_found_in_trash' => __( 'No threads found in trash', 'supportflow' ), ), - 'public' => true, - 'menu_position' => 3, - 'supports' => array( - 'comments', - ), - ) ); + 'public' => true, + 'menu_position' => 3, + 'supports' => array( + 'comments', + ), + ) + ); } /** @@ -283,17 +289,17 @@ public function action_init_register_post_type() { public function action_init_register_taxonomies() { $args = array( - 'label' => __( 'Respondents', 'supportflow' ), - 'labels' => array( - 'search_items' => __( 'Search Respondents', 'supportflow' ), - 'edit_item' => __( 'Edit Respondent', 'supportflow' ), - 'update_item' => __( 'Update Respondent', 'supportflow' ), - 'add_new_item' => __( 'Add New Respondent', 'supportflow' ), - ), - 'public' => true, - 'show_in_nav_menus' => true, - 'rewrite' => false, - ); + 'label' => __( 'Respondents', 'supportflow' ), + 'labels' => array( + 'search_items' => __( 'Search Respondents', 'supportflow' ), + 'edit_item' => __( 'Edit Respondent', 'supportflow' ), + 'update_item' => __( 'Update Respondent', 'supportflow' ), + 'add_new_item' => __( 'Add New Respondent', 'supportflow' ), + ), + 'public' => true, + 'show_in_nav_menus' => true, + 'rewrite' => false, + ); register_taxonomy( $this->respondents_tax, $this->post_type, $args ); } @@ -301,8 +307,8 @@ public function action_init_register_taxonomies() { * Register the custom post (thread) statuses * * @since SupportFlow 0.1 - * @uses register_post_status() To register the post statuses - * @uses apply_filters() To control what statuses are registered + * @uses register_post_status() To register the post statuses + * @uses apply_filters() To control what statuses are registered */ public function action_init_register_post_statuses() { foreach ( $this->post_statuses as $post_status => $args ) { @@ -320,18 +326,17 @@ public function validate_user( $user ) { // User ID if ( is_numeric( $user ) ) { $user_object = get_user_by( 'ID', $user ); - } - // User e-mail address + } // User e-mail address elseif ( is_email( $user ) ) { $user_object = get_user_by( 'email', $user ); - } - // User login + } // User login else { $user_object = get_user_by( 'login', $user ); } - if ( ! $user_object ) + if ( ! $user_object ) { return false; + } return $user_object->data->ID; } @@ -342,8 +347,9 @@ public function validate_user( $user ) { public function get_email_hash( $email ) { $email = strtolower( trim( $email ) ); - if ( ! is_email( $email ) ) + if ( ! is_email( $email ) ) { return false; + } $email = $this->email_term_prefix . md5( $email ); @@ -367,47 +373,49 @@ public function create_thread( $args ) { $post_statuses = $this->post_statuses; $defaults = array( - 'subject' => '', - 'message' => '', - 'date' => '', - 'respondent_id' => 0, // If the requester has a WordPress account (ID or username) - 'respondent_name' => '', // Otherwise supply a name - 'respondent_email' => '', // And an e-mail address - 'status' => key( $post_statuses ), - 'assignee' => -1, // WordPress user ID or username of ticket assignee/owner + 'subject' => '', + 'message' => '', + 'date' => '', + 'respondent_id' => 0, // If the requester has a WordPress account (ID or username) + 'respondent_name' => '', // Otherwise supply a name + 'respondent_email' => '', // And an e-mail address + 'status' => key( $post_statuses ), + 'assignee' => - 1, // WordPress user ID or username of ticket assignee/owner ); $args = wp_parse_args( $args, $defaults ); $thread = array( - 'post_type' => $this->post_type, - 'post_title' => $args['subject'], - 'post_author' => $args['assignee'], - 'post_date' => $args['date'], + 'post_type' => $this->post_type, + 'post_title' => $args['subject'], + 'post_author' => $args['assignee'], + 'post_date' => $args['date'], ); // Validate the thread status - if ( ! get_post_status_object( $args['status'] ) ) + if ( ! get_post_status_object( $args['status'] ) ) { $args['status'] = $defaults['status']; + } $thread['post_status'] = $args['status']; $thread_id = wp_insert_post( $thread, true ); - if ( is_wp_error( $thread_id ) ) + if ( is_wp_error( $thread_id ) ) { return $thread_id; + } // Assign the respondent(s) - if ( !empty( $args['respondent_email'] ) ) { + if ( ! empty( $args['respondent_email'] ) ) { $this->update_thread_respondents( $thread_id, $args['respondent_email'] ); } // If there was a message, add it to the thread - if ( !empty( $args['message'] ) && !empty( $args['respondent_email'] ) ) { + if ( ! empty( $args['message'] ) && ! empty( $args['respondent_email'] ) ) { $comment_details = array( - 'comment_author' => $args['respondent_name'], - 'comment_author_email' => $args['respondent_email'], - 'user_id' => $args['respondent_id'], - ); + 'comment_author' => $args['respondent_name'], + 'comment_author_email' => $args['respondent_email'], + 'user_id' => $args['respondent_id'], + ); $this->add_thread_comment( $thread_id, $args['message'], $comment_details ); } @@ -419,11 +427,13 @@ public function create_thread( $args ) { */ public function filter_wp_insert_post_empty_content( $maybe_empty, $postarr ) { - if ( $this->post_type != $postarr['post_type'] ) + if ( $this->post_type != $postarr['post_type'] ) { return $maybe_empty; + } - if ( empty( $postarr['post_title'] ) ) + if ( empty( $postarr['post_title'] ) ) { return true; + } return $maybe_empty; } @@ -442,59 +452,63 @@ public function get_thread( $thread_id ) { public function get_threads( $args = array() ) { $defaults = array( - 'respondent_email' => '', - 'post_status' => '', - 'orderby' => 'modified', - ); - $args = array_merge( $defaults, $args ); + 'respondent_email' => '', + 'post_status' => '', + 'orderby' => 'modified', + ); + $args = array_merge( $defaults, $args ); $thread_args = array(); - if ( empty( $args['post_status'] ) ) + if ( empty( $args['post_status'] ) ) { $thread_args['post_status'] = array_keys( $this->post_statuses ); + } - if ( !empty( $args['respondent_email'] ) ) + if ( ! empty( $args['respondent_email'] ) ) { $thread_args['tax_query'] = array( - array( - 'taxonomy' => $this->respondents_tax, - 'field' => 'slug', - 'terms' => $this->get_email_hash( $args['respondent_email'] ), - ), + array( + 'taxonomy' => $this->respondents_tax, + 'field' => 'slug', + 'terms' => $this->get_email_hash( $args['respondent_email'] ), + ), ); + } $thread_args['post_type'] = $this->post_type; - $thread_args['orderby'] = $args['orderby']; + $thread_args['orderby'] = $args['orderby']; $threads = new WP_Query( $thread_args ); - if ( is_wp_error( $threads ) ) + if ( is_wp_error( $threads ) ) { return $threads; + } return $threads->posts; } /** * Get respondents matching $query - * + * * @param string $query partial email address to search for */ public function get_respondents( $args = array() ) { $defaults = array( - 'search' => '', - 'number' => 10 - ); - $args = array_merge( $defaults, $args ); + 'search' => '', + 'number' => 10 + ); + $args = array_merge( $defaults, $args ); $term_args = array( - 'orderby' => 'name', - 'hide_empty' => 0, - 'fields' => 'all', - 'name__like' => $args['search'], - 'number' => $args['number'], - ); - $matches = get_terms( $this->respondents_tax, $term_args ); + 'orderby' => 'name', + 'hide_empty' => 0, + 'fields' => 'all', + 'name__like' => $args['search'], + 'number' => $args['number'], + ); + $matches = get_terms( $this->respondents_tax, $term_args ); - if( !$matches ) + if ( ! $matches ) { return array(); + } return $matches; } @@ -507,29 +521,31 @@ public function get_respondents( $args = array() ) { public function get_thread_respondents( $thread_id, $args = array() ) { $default_args = array( - 'fields' => 'all', // 'all', 'emails' - ); - $args = array_merge( $default_args, $args ); + 'fields' => 'all', // 'all', 'emails' + ); + $args = array_merge( $default_args, $args ); $raw_respondents = wp_get_object_terms( $thread_id, $this->respondents_tax ); - if ( is_wp_error( $raw_respondents ) ) + if ( is_wp_error( $raw_respondents ) ) { return array(); + } $respondents = array(); if ( 'emails' == $args['fields'] ) { - foreach( $raw_respondents as $raw_respondent ) { + foreach ( $raw_respondents as $raw_respondent ) { $respondents[] = $raw_respondent->name; } } + return $respondents; } /** * Update a thread's respondents * - * @param int $thread_id + * @param int $thread_id * @param array $respondents An array of email addresses - * @param bool $append Whether or not to append these email addresses to any existing addresses + * @param bool $append Whether or not to append these email addresses to any existing addresses */ public function update_thread_respondents( $thread_id, $respondents, $append = false ) { @@ -538,15 +554,16 @@ public function update_thread_respondents( $thread_id, $respondents, $append = f } $term_ids = array(); - foreach( $respondents as $dirty_respondent ) { - if ( empty( $dirty_respondent ) ) + foreach ( $respondents as $dirty_respondent ) { + if ( empty( $dirty_respondent ) ) { continue; + } // Create a term if it doesn't yet exist $email = ( is_array( $dirty_respondent ) ) ? $dirty_respondent['user_email'] : $dirty_respondent; if ( $term = get_term_by( 'name', $email, $this->respondents_tax ) ) { - $term_ids[] = (int)$term->term_id; + $term_ids[] = (int) $term->term_id; } else { - $term = wp_insert_term( $email, $this->respondents_tax, array( 'slug' => $this->get_email_hash( $email ) ) ); + $term = wp_insert_term( $email, $this->respondents_tax, array( 'slug' => $this->get_email_hash( $email ) ) ); $term_ids[] = $term['term_id']; } } @@ -560,6 +577,7 @@ public function get_thread_comments( $thread_id, $args = array() ) { $args['post_id'] = $thread_id; $thread_comments = SupportFlow()->get_comments( $args ); + return $thread_comments; } @@ -569,24 +587,25 @@ public function get_thread_comments( $thread_id, $args = array() ) { public function get_comments( $args ) { $default_args = array( - 'status' => 'public', // 'public', 'private', 'all' - 'post_id' => '', - 'search' => '', - 'order' => 'DESC', // 'DESC', 'ASC', - ); + 'status' => 'public', // 'public', 'private', 'all' + 'post_id' => '', + 'search' => '', + 'order' => 'DESC', // 'DESC', 'ASC', + ); - $args = array_merge( $default_args, $args ); + $args = array_merge( $default_args, $args ); $comment_args = array( - 'search' => $args['search'], - 'post_id' => $args['post_id'], - 'status' => $args['status'], - 'comment_type' => $this->comment_type, - 'order' => $args['order'], - ); + 'search' => $args['search'], + 'post_id' => $args['post_id'], + 'status' => $args['status'], + 'comment_type' => $this->comment_type, + 'order' => $args['order'], + ); add_filter( 'comments_clauses', array( $this, 'filter_comment_clauses' ), 10, 2 ); $thread_comments = get_comments( $comment_args ); remove_filter( 'comments_clauses', array( $this, 'filter_comment_clauses' ) ); + return $thread_comments; } @@ -595,11 +614,13 @@ public function get_comments( $args ) { */ public function filter_comment_clauses( $clauses, $query ) { $old_comment_approved = "( comment_approved = '0' OR comment_approved = '1' )"; - if ( in_array( $query->query_vars['status'], array( 'public', 'private' ) ) ) + if ( in_array( $query->query_vars['status'], array( 'public', 'private' ) ) ) { $new_comment_approved = "comment_approved = '{$query->query_vars['status']}' "; - else + } else { $new_comment_approved = "comment_approved IN ( 'private', 'public' )"; + } $clauses['where'] = str_replace( $old_comment_approved, $new_comment_approved, $clauses['where'] ); + return $clauses; } @@ -612,7 +633,8 @@ public function get_thread_comment_count( $thread_id, $args = array() ) { global $wpdb; $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_post_ID =%s AND comment_type = %s", $thread_id, $this->comment_type ) ); - return (int)$count; + + return (int) $count; } /** @@ -622,19 +644,19 @@ public function add_thread_comment( $thread_id, $comment_text, $details = array( global $wpdb; $default_details = array( - 'time' => current_time( 'mysql' ), - 'comment_author' => '', - 'comment_author_email' => '', - 'user_id' => '', - 'comment_approved' => 'public', - ); + 'time' => current_time( 'mysql' ), + 'comment_author' => '', + 'comment_author_email' => '', + 'user_id' => '', + 'comment_approved' => 'public', + ); // @todo This actually probably shouldn't default to current user, so // we don't have to mandate the arguments be assigned each time if ( $user = wp_get_current_user() ) { - $default_details['comment_author'] = $user->display_name; + $default_details['comment_author'] = $user->display_name; $default_details['comment_author_email'] = $user->user_email; - $default_details['user_id'] = $user->ID; + $default_details['user_id'] = $user->ID; } $details = array_merge( $default_details, $details ); @@ -647,25 +669,26 @@ public function add_thread_comment( $thread_id, $comment_text, $details = array( $attachment_ids = false; } - $comment = array( - 'comment_content' => esc_sql( $comment_text ), - 'comment_post_ID' => (int)$thread_id, - 'comment_date' => esc_sql( $details['time'] ), - 'comment_approved' => esc_sql( $details['comment_approved'] ), - 'comment_type' => esc_sql( $this->comment_type ), - 'comment_author' => esc_sql( $details['comment_author'] ), - 'comment_author_email' => esc_sql( $details['comment_author_email'] ), - 'user_id' => (int)$details['user_id'], - ); - $comment = apply_filters( 'supportflow_pre_insert_thread_comment', $comment ); + $comment = array( + 'comment_content' => esc_sql( $comment_text ), + 'comment_post_ID' => (int) $thread_id, + 'comment_date' => esc_sql( $details['time'] ), + 'comment_approved' => esc_sql( $details['comment_approved'] ), + 'comment_type' => esc_sql( $this->comment_type ), + 'comment_author' => esc_sql( $details['comment_author'] ), + 'comment_author_email' => esc_sql( $details['comment_author_email'] ), + 'user_id' => (int) $details['user_id'], + ); + $comment = apply_filters( 'supportflow_pre_insert_thread_comment', $comment ); $comment_id = wp_insert_comment( $comment ); // If there are attachment IDs store them as meta - if ( !empty( $attachment_ids ) ) + if ( ! empty( $attachment_ids ) ) { add_comment_meta( $comment_id, 'attachment_ids', $attachment_ids, true ); + } // Adding a thread comment updates the post modified time for the thread - $query = $wpdb->update( $wpdb->posts, array( 'post_modified' => current_time( 'mysql') ), array( 'ID' => $thread_id ) ); + $query = $wpdb->update( $wpdb->posts, array( 'post_modified' => current_time( 'mysql' ) ), array( 'ID' => $thread_id ) ); clean_post_cache( $thread_id ); do_action( 'supportflow_thread_comment_added', $comment_id ); @@ -681,11 +704,13 @@ public function add_thread_comment( $thread_id, $comment_text, $details = array( */ public function get_secret_for_thread( $thread_id ) { - if ( $secret = get_post_meta( $thread_id, $this->thread_secret_key, true ) ) + if ( $secret = get_post_meta( $thread_id, $this->thread_secret_key, true ) ) { return $secret; + } $secret = wp_generate_password( 8, false ); update_post_meta( $thread_id, $this->thread_secret_key, $secret ); + return $secret; } @@ -695,7 +720,8 @@ public function get_secret_for_thread( $thread_id ) { public function get_thread_from_secret( $secret ) { global $wpdb; $thread_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key=%s AND meta_value=%s", $this->thread_secret_key, $secret ) ); - return ( $thread_id ) ? (int)$thread_id : 0; + + return ( $thread_id ) ? (int) $thread_id : 0; } /** From 264571be40a8abc4854320f2fab0b015e59ca7e5 Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Tue, 20 May 2014 19:10:01 +0530 Subject: [PATCH 033/485] Created a page to add/remove IMAP E-Mail accounts --- classes/class-supportflow-email-accounts.php | 175 +++++++++++++++++++ supportflow.php | 1 + 2 files changed, 176 insertions(+) create mode 100644 classes/class-supportflow-email-accounts.php diff --git a/classes/class-supportflow-email-accounts.php b/classes/class-supportflow-email-accounts.php new file mode 100644 index 0000000..3da20ef --- /dev/null +++ b/classes/class-supportflow-email-accounts.php @@ -0,0 +1,175 @@ +$y) { + $email_account = unserialize( $k ); + $imap_host = $email_account['host']; + $imap_port = $email_account['port']; + $imap_user = $email_account['user']; + $data[] = array( + 'imap_host' => $imap_host, + 'imap_port' => $imap_port, + 'imap_user' => $imap_user, + 'imap_action' => "Delete", + ); + } + $this->_data = $data; + } + + function column_default( $item, $column_name ) { + return $item[ $column_name ]; + } + + function get_columns(){ + return array( + 'imap_host' => __( 'Host Name' , 'supportflow' ), + 'imap_port' => __( 'Port No' , 'supportflow' ), + 'imap_user' => __( 'User Name' , 'supportflow' ), + 'imap_action' => __( 'Action' , 'supportflow' ) + ); + } + + function prepare_items() { + $columns = $this->get_columns(); + $data = $this->_data; + $hidden = array(); + $sortable = array(); + $this->_column_headers = array( $columns, $hidden, $sortable ); + $this->items = $data;; + } +} + +class SupportFlow_Email_Accounts extends SupportFlow { + + function __construct() { + add_action( 'admin_menu', array( $this, 'action_admin_menu' ) ); + } + + function action_admin_menu() { + $post_type = SupportFlow()->post_type; + + // Create a menu (SupportFlow->E-Mail Accounts) + add_submenu_page( + "edit.php?post_type=$post_type", + __( 'SupportFlow E-Mail Accounts', 'supportflow' ), + __( 'E-Mail Accounts', 'supportflow' ), + 'manage_options', + 'sf_accounts', + array( $this, 'settings_page' ) + ); + + } + + function settings_page() { + + $email_accounts = get_option( 'sf_email_accounts' ); + if ( ! is_array( $email_accounts ) ) { + $email_accounts = array(); + } + + echo '
    +

    ' . __( 'E-Mail Accounts', 'supportflow' ) . '

    '; + + if ( isset( $_POST['action'] ) ) { + $action = $_POST['action']; + } + + // Create new account + if ( 'add' == $action && isset( $_POST['imap_host'], $_POST['imap_user'], $_POST['imap_pass'], $_POST['imap_port'] ) ) { + $_POST['imap_port'] = is_numeric( $_POST['imap_port'] ) ? (int) $_POST['imap_port'] : 993; + $account_key = serialize( array( 'host' => $_POST['imap_host'], 'user' => $_POST['imap_user'], 'port' => $_POST['imap_port'] ) ); + + if ( isset( $email_accounts[$account_key] ) ) { + echo '

    ' . __( 'There is an account already exists with same host name and user name. Please create a new account with different settings.', 'supportflow' ) . '

    '; + } elseif ( ! @imap_open( "{{$_POST['imap_host']}:{$_POST['imap_port']}/ssl}", $_POST['imap_user'], $_POST['imap_pass'] ) ) { + echo '

    ' . __( 'Unable to login to the IMAP server. Please check your IMAP details.', 'supportflow' ) . '

    '; + } else { + $email_accounts[$account_key] = $_POST['imap_pass']; + update_option( 'sf_email_accounts', $email_accounts ); + echo '

    ' . __( 'Account added successfully', 'supportflow' ) . '

    '; + } + } + + // Delete existing account + if ( 'delete' == $action && isset( $_POST['key'] ) ) { + $account_key = stripcslashes( $_POST['key'] ); + + if ( ! isset( $email_accounts[$account_key] ) ) { + echo '

    ' . __( 'Failed deleting account. There is no such account exists. Please try again.', 'supportflow' ) . '

    '; + } else { + unset( $email_accounts[$account_key] ); + update_option( 'sf_email_accounts', $email_accounts) ; + echo '

    ' . __( 'Account Deleted successfully', 'supportflow' ) . '

    '; + } + } + + + $email_accounts_table = new SupportFlow_Email_Accounts_Table($email_accounts); + $email_accounts_table->prepare_items(); + $email_accounts_table->display(); + + ?> +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + extend->settings = new SupportFlow_Email_Accounts(); \ No newline at end of file diff --git a/supportflow.php b/supportflow.php index 774ac8c..fc6a075 100644 --- a/supportflow.php +++ b/supportflow.php @@ -213,6 +213,7 @@ private function includes() { require_once( $this->plugin_dir . 'classes/class-supportflow-emails.php' ); require_once( $this->plugin_dir . 'classes/class-supportflow-email-replies.php' ); require_once( $this->plugin_dir . 'classes/class-supportflow-permissions.php' ); + require_once( $this->plugin_dir . 'classes/class-supportflow-email-accounts.php' ); /** Extensions ********************************************************/ From 7dc3976c7567b44e351a49fd652cbcb358047cf3 Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Thu, 22 May 2014 02:10:30 +0530 Subject: [PATCH 034/485] Updated E-Mail account setting page --- classes/class-supportflow-email-accounts.php | 279 ++++++++++++++----- 1 file changed, 204 insertions(+), 75 deletions(-) diff --git a/classes/class-supportflow-email-accounts.php b/classes/class-supportflow-email-accounts.php index 3da20ef..d077d0c 100644 --- a/classes/class-supportflow-email-accounts.php +++ b/classes/class-supportflow-email-accounts.php @@ -3,59 +3,77 @@ * */ -if( ! class_exists( 'WP_List_Table' ) ) { - require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); +if ( ! class_exists( 'WP_List_Table' ) ) { + require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); } class SupportFlow_Email_Accounts_Table extends WP_List_Table { protected $_data; - function __construct( $accounts ){ + function __construct( $accounts ) { parent::__construct(); + + $data = & $this->_data; $data = array(); - foreach( $accounts as $k=>$y) { - $email_account = unserialize( $k ); - $imap_host = $email_account['host']; - $imap_port = $email_account['port']; - $imap_user = $email_account['user']; - $data[] = array( - 'imap_host' => $imap_host, - 'imap_port' => $imap_port, - 'imap_user' => $imap_user, - 'imap_action' => "Delete", + foreach ( $accounts as $account_id => $account ) { + $data[] = array( + 'username' => $account['username'], + 'imap_host' => $account['imap_host'], + 'imap_port' => $account['imap_port'], + 'smtp_host' => $account['smtp_host'], + 'smtp_port' => $account['smtp_port'], + 'action' => "Delete", ); } - $this->_data = $data; } function column_default( $item, $column_name ) { - return $item[ $column_name ]; + return $item[$column_name]; } - function get_columns(){ + function get_columns() { return array( - 'imap_host' => __( 'Host Name' , 'supportflow' ), - 'imap_port' => __( 'Port No' , 'supportflow' ), - 'imap_user' => __( 'User Name' , 'supportflow' ), - 'imap_action' => __( 'Action' , 'supportflow' ) + 'username' => __( 'Username', 'supportflow' ), + 'imap_host' => __( 'IMAP Host', 'supportflow' ), + 'imap_port' => __( 'IMAP Port', 'supportflow' ), + 'smtp_host' => __( 'SMTP Host', 'supportflow' ), + 'smtp_port' => __( 'SMTP Port', 'supportflow' ), + 'action' => __( 'Action', 'supportflow' ), ); } function prepare_items() { - $columns = $this->get_columns(); - $data = $this->_data; - $hidden = array(); - $sortable = array(); + $columns = $this->get_columns(); + $data = $this->_data; + $hidden = array(); + $sortable = array(); $this->_column_headers = array( $columns, $hidden, $sortable ); - $this->items = $data;; + $this->items = $data; } } class SupportFlow_Email_Accounts extends SupportFlow { + protected $_email_accounts; + + const SUCCESS = 0; + const ACCOUNT_EXISTS = 1; + const NO_ACCOUNT_EXISTS = 2; + const IMAP_HOST_NOT_FOUND = 3; + const IMAP_INVALID_CRIDENTIALS = 4; + const IMAP_TIME_OUT = 5; + const IMAP_CONNECTION_FAILED = 6; + function __construct() { add_action( 'admin_menu', array( $this, 'action_admin_menu' ) ); + + // Get existing E-Mail accounts from database + $email_accounts = & $this->_email_accounts; + $email_accounts = get_option( 'sf_email_accounts' ); + if ( ! is_array( $email_accounts ) ) { + $email_accounts = array(); + } } function action_admin_menu() { @@ -73,103 +91,214 @@ function action_admin_menu() { } + /* + * Loads the setting page to add/remove E-Mail accounts + */ function settings_page() { - - $email_accounts = get_option( 'sf_email_accounts' ); - if ( ! is_array( $email_accounts ) ) { - $email_accounts = array(); - } + $action = isset( $_POST['action'] ) ? $_POST['action'] : ''; echo '

    ' . __( 'E-Mail Accounts', 'supportflow' ) . '

    '; - if ( isset( $_POST['action'] ) ) { - $action = $_POST['action']; - } - // Create new account - if ( 'add' == $action && isset( $_POST['imap_host'], $_POST['imap_user'], $_POST['imap_pass'], $_POST['imap_port'] ) ) { - $_POST['imap_port'] = is_numeric( $_POST['imap_port'] ) ? (int) $_POST['imap_port'] : 993; - $account_key = serialize( array( 'host' => $_POST['imap_host'], 'user' => $_POST['imap_user'], 'port' => $_POST['imap_port'] ) ); - - if ( isset( $email_accounts[$account_key] ) ) { - echo '

    ' . __( 'There is an account already exists with same host name and user name. Please create a new account with different settings.', 'supportflow' ) . '

    '; - } elseif ( ! @imap_open( "{{$_POST['imap_host']}:{$_POST['imap_port']}/ssl}", $_POST['imap_user'], $_POST['imap_pass'] ) ) { - echo '

    ' . __( 'Unable to login to the IMAP server. Please check your IMAP details.', 'supportflow' ) . '

    '; - } else { - $email_accounts[$account_key] = $_POST['imap_pass']; - update_option( 'sf_email_accounts', $email_accounts ); - echo '

    ' . __( 'Account added successfully', 'supportflow' ) . '

    '; + if ( 'add' == $action && isset( $_POST['imap_host'], $_POST['imap_port'], $_POST['smtp_host'], $_POST['smtp_port'], $_POST['username'], $_POST['password'] ) ) { + $res = $this->add_email_account( $_POST['imap_host'], $_POST['imap_port'], $_POST['smtp_host'], $_POST['smtp_port'], $_POST['username'], $_POST['password'] ); + switch ( $res ) { + + case self::SUCCESS: + echo '

    ' . __( 'Account added successfully', 'supportflow' ) . '

    '; + unset( $_POST['imap_host'], $_POST['imap_port'], $_POST['smtp_host'], $_POST['smtp_port'], $_POST['username'], $_POST['password'] ); + break; + case self::ACCOUNT_EXISTS: + echo '

    ' . __( 'There is an account already exists with same host name and user name. Please create a new account with different settings.', 'supportflow' ) . '

    '; + break; + case self::IMAP_HOST_NOT_FOUND: + echo '

    ' . __( 'Unable to connect to ' . esc_html( $_POST['imap_host'] ) . '. Please check your IMAP host name.', 'supportflow' ) . '

    '; + break; + case self::IMAP_TIME_OUT: + echo '

    ' . __( 'Time out while connecting to ' . esc_html( $_POST['imap_host'] ) . '. Please check your IMAP port number and host name.', 'supportflow' ) . '

    '; + break; + case self::IMAP_INVALID_CRIDENTIALS: + echo '

    ' . __( 'Unable to connect with given username/password combination. Please re-check your username and password', 'supportflow' ) . '

    '; + break; + case self::IMAP_CONNECTION_FAILED: + echo '

    ' . __( 'Unknown error while connecting to the IMAP server. Please check your IMAP settings and try again.', 'supportflow' ) . '

    '; + break; } } // Delete existing account - if ( 'delete' == $action && isset( $_POST['key'] ) ) { - $account_key = stripcslashes( $_POST['key'] ); - - if ( ! isset( $email_accounts[$account_key] ) ) { - echo '

    ' . __( 'Failed deleting account. There is no such account exists. Please try again.', 'supportflow' ) . '

    '; - } else { - unset( $email_accounts[$account_key] ); - update_option( 'sf_email_accounts', $email_accounts) ; - echo '

    ' . __( 'Account Deleted successfully', 'supportflow' ) . '

    '; + if ( 'delete' == $action && isset( $_POST['account_id'] ) ) { + $res = $this->remove_email_account( $_POST['account_id'] ); + switch ( $res ) { + + case self::SUCCESS: + echo '

    ' . __( 'Account Deleted successfully', 'supportflow' ) . '

    '; + break; + case self::NO_ACCOUNT_EXISTS: + echo '

    ' . __( 'Failed deleting account. There is no such account exists. Please try again.', 'supportflow' ) . '

    '; + break; } } - - $email_accounts_table = new SupportFlow_Email_Accounts_Table($email_accounts); + $email_accounts_table = new SupportFlow_Email_Accounts_Table( $this->get_email_accounts() ); $email_accounts_table->prepare_items(); $email_accounts_table->display(); ?> -

    -
    +

    +
    - - + + + + + + + + + + + + - - + + - - + + - - + +
    + +
    + +
    + +
    + +
    + +
    - +
    - - _email_accounts; + sanitize_text_field( $imap_host, $imap_port, $smtp_port, $username, $password ); + + if ( $this->is_exist_email_account( $imap_host, $smtp_host, $username ) ) { + return self::ACCOUNT_EXISTS; + } + + if ( true == $test_login ) { + imap_timeout( IMAP_OPENTIMEOUT, apply_filters( 'supportflow_imap_open_timeout', 5 ) ); + if ( $imap_stream = imap_open( "{{$imap_host}:{$imap_port}/ssl}", $username, $password, 0, 0 ) ) { + imap_close( $imap_stream ); + } else { + $error = imap_errors()[0]; + if ( (string) strpos( $error, 'Host not found' ) != '' ) { + return self::IMAP_HOST_NOT_FOUND; + } elseif ( (string) strpos( $error, 'Timed out' ) != '' ) { + return self::IMAP_TIME_OUT; + } elseif ( (string) strpos( $error, 'Invalid credentials' ) != '' ) { + return self::IMAP_INVALID_CRIDENTIALS; + } else { + return self::IMAP_CONNECTION_FAILED; + } + } + } + + $email_accounts[] = array( + 'imap_host' => $imap_host, + 'imap_port' => $imap_port, + 'smtp_host' => $smtp_host, + 'smtp_port' => $smtp_port, + 'username' => $username, + 'password' => $password, + ); + update_option( 'sf_email_accounts', $email_accounts ); + + return self::SUCCESS; + + } + + /** + * Remove an E-Mail account from database + */ + function remove_email_account( $account_id ) { + $email_accounts = & $this->_email_accounts; + + if ( ! isset( $email_accounts[$account_id] ) ) { + return self::NO_ACCOUNT_EXISTS; + } else { + unset( $email_accounts[$account_id] ); + update_option( 'sf_email_accounts', $email_accounts ); + + return self::SUCCESS; + } + } + + /** + * Return the list of all existing E-Mail accounts + * @return array + */ + function get_email_accounts() { + return $this->_email_accounts; } + /** + * Check if E-Mail account exists in database + * @return boolean + */ + function is_exist_email_account( $imap_host, $smtp_host, $username ) { + $email_accounts = & $this->_email_accounts; + + foreach ( $email_accounts as $email_account ) { + if ( + $email_account['imap_host'] == $imap_host && + $email_account['smtp_host'] == $smtp_host && + $email_account['username'] == $username + ) { + return true; + } + } + return false; + } } SupportFlow()->extend->settings = new SupportFlow_Email_Accounts(); \ No newline at end of file From b668968b316a0e753be0408cc7678f91a8ec3640 Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Tue, 20 May 2014 19:33:53 +0530 Subject: [PATCH 035/485] Registered tags taxonomy --- supportflow.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/supportflow.php b/supportflow.php index 774ac8c..60c5ae7 100644 --- a/supportflow.php +++ b/supportflow.php @@ -162,6 +162,7 @@ private function setup_globals() { $this->post_type = apply_filters( 'supportflow_thread_post_type', 'sf_thread' ); $this->respondents_tax = apply_filters( 'supportflow_respondents_taxonomy', 'sf_respondent' ); + $this->tags_tax = apply_filters( 'supportflow_tags_taxonomy', 'sf_tags' ); $this->comment_type = apply_filters( 'supportflow_thread_comment_type', 'sf_comment' ); $this->email_term_prefix = 'sf-'; @@ -288,7 +289,7 @@ public function action_init_register_post_type() { */ public function action_init_register_taxonomies() { - $args = array( + $args_respondents_tax = array( 'label' => __( 'Respondents', 'supportflow' ), 'labels' => array( 'search_items' => __( 'Search Respondents', 'supportflow' ), @@ -300,7 +301,22 @@ public function action_init_register_taxonomies() { 'show_in_nav_menus' => true, 'rewrite' => false, ); - register_taxonomy( $this->respondents_tax, $this->post_type, $args ); + + $args_tags_tax = array( + 'label' => __( 'Tags', 'supportflow' ), + 'labels' => array( + 'search_items' => __( 'Search Tags', 'supportflow' ), + 'edit_item' => __( 'Edit Tag', 'supportflow' ), + 'update_item' => __( 'Update Tag', 'supportflow' ), + 'add_new_item' => __( 'Add New Tag', 'supportflow' ), + ), + 'public' => true, + 'show_in_nav_menus' => true, + 'rewrite' => false, + ); + + register_taxonomy( $this->respondents_tax, $this->post_type, $args_respondents_tax ); + register_taxonomy( $this->tags_tax, $this->post_type, $args_tags_tax ); } /** From 2666e6922c3aae3511681fd8369d8554f375e876 Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Sun, 25 May 2014 04:08:54 +0530 Subject: [PATCH 036/485] Converted replies from custom comments to custom posts Also changed the usage of word comment/comments with reply/replies --- classes/class-supportflow-admin.php | 123 +++++++++-------- classes/class-supportflow-email-replies.php | 20 +-- classes/class-supportflow-emails.php | 58 ++++---- classes/class-supportflow-json-api.php | 34 ++--- classes/class-supportflow-permissions.php | 24 ++-- classes/class-supportflow-ui-widget.php | 118 ++++++++-------- classes/class-supportflow-wp-cli.php | 20 +-- css/admin.css | 136 +++++++++--------- css/widget.css | 146 ++++++++++---------- js/plupload.js | 6 +- js/supportflow-user-widget.js | 2 +- supportflow.php | 131 +++++++++--------- 12 files changed, 416 insertions(+), 402 deletions(-) diff --git a/classes/class-supportflow-admin.php b/classes/class-supportflow-admin.php index 5537656..d23a5f0 100644 --- a/classes/class-supportflow-admin.php +++ b/classes/class-supportflow-admin.php @@ -281,15 +281,16 @@ function action_pre_get_posts( $query ) { } } - // Do our own custom search handling so we can search against comment text + // 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( + + // Get all replies 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' ); + $matching_replies = SupportFlow()->get_replies( $args ); + $post_ids = wp_list_pluck( $matching_replies, 'post_parent' ); $args = array( 's' => $search, @@ -367,14 +368,12 @@ public function action_add_meta_boxes() { $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' ); add_meta_box( 'supportflow-details', __( 'Details', 'supportflow' ), array( $this, 'meta_box_details' ), SupportFlow()->post_type, 'side' ); 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-replies', __( 'Replies', 'supportflow' ), array( $this, 'meta_box_replies' ), SupportFlow()->post_type, 'normal' ); } /** @@ -468,10 +467,10 @@ public function meta_box_respondents() { } /** - * Standard listing of comments includes a form at the top - * and any existing comments listed in reverse chronological order + * Standard listing of replies includes a form at the top + * and any existing replies listed in reverse chronological order */ - public function meta_box_comments() { + public function meta_box_replies() { global $pagenow; $placeholders = array( @@ -481,17 +480,17 @@ public function meta_box_comments() { $rand = array_rand( $placeholders ); echo '

    ' . __( 'Conversation', 'supportflow' ) . '

    '; - echo '
    '; + echo '
    '; - echo ""; echo '
    '; - echo '
    '; + echo '
    '; echo '
    ' . __( 'Drop a file in the message to attach it', 'supportflow' ) . '
    '; - echo '
      '; + echo '
        '; echo '
      '; - echo ''; + echo ''; echo '
    '; echo '
    '; echo ''; @@ -509,20 +508,21 @@ public function meta_box_comments() { echo '
    '; - $this->display_thread_comments(); + $this->display_thread_replies(); } - public function display_thread_comments() { + public function display_thread_replies() { + + $private_replies = SupportFlow()->get_thread_replies( get_the_ID(), array( 'status' => 'private' ) ); - $private_comments = SupportFlow()->get_thread_comments( get_the_ID(), array( 'status' => 'private' ) ); - if ( ! empty( $private_comments ) ) { - echo '