PageRenderTime 9ms CodeModel.GetById 55ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 1ms

/classes/class-supportflow-admin.php

https://github.com/SupportFlow/supportflow
PHP | 1295 lines | 1092 code | 115 blank | 88 comment | 110 complexity | 7cc90a1e7d68dedc798ba47b34237dfb MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/**
   3 *
   4 */
   5
   6defined( 'ABSPATH' ) or die( "Cheatin' uh?" );
   7
   8class SupportFlow_Admin extends SupportFlow {
   9
  10	function __construct() {
  11		add_action( 'wp_ajax_sf_forward_conversation', array( $this, 'action_wp_ajax_sf_email_conversation' ) );
  12		add_filter( 'heartbeat_received', array( $this, 'filter_heartbeat_received' ), 10, 2 );
  13		add_action( 'wp_ajax_ticket_attachment_upload', array( $this, 'action_wp_ajax_ticket_attachment_upload' ) );
  14		add_action( 'supportflow_after_setup_actions', array( $this, 'setup_actions' ) );
  15		add_action( 'add_attachment', array( $this, 'action_add_attachment' ) );
  16	}
  17
  18	public function setup_actions() {
  19
  20		// Creating or updating a ticket
  21		add_action( 'add_meta_boxes', array( $this, 'action_add_meta_boxes' ) );
  22		add_action( 'save_post', array( $this, 'action_save_post' ) );
  23
  24		if ( ! $this->is_edit_screen() ) {
  25			return;
  26		}
  27
  28		// Everything
  29		add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) );
  30		add_filter( 'post_updated_messages', array( $this, 'filter_post_updated_messages' ) );
  31		add_action( 'admin_init', array( $this, 'action_admin_init' ) );
  32
  33		// Manage tickets view
  34		add_filter( 'manage_' . SupportFlow()->post_type . '_posts_columns', array( $this, 'filter_manage_post_columns' ) );
  35		add_filter( 'manage_edit-' . SupportFlow()->post_type . '_sortable_columns', array( $this, 'manage_sortable_columns' ) );
  36		add_action( 'manage_posts_custom_column', array( $this, 'action_manage_posts_custom_column' ), 10, 2 );
  37		add_filter( 'views_edit-' . SupportFlow()->post_type, array( $this, 'filter_views' ) );
  38		add_filter( 'post_row_actions', array( $this, 'filter_post_row_actions' ), 10, 2 );
  39		add_filter( 'bulk_actions-edit-' . SupportFlow()->post_type, array( $this, 'filter_bulk_actions' ) );
  40		add_action( 'pre_get_posts', array( $this, 'action_pre_get_posts' ) );
  41		add_action( 'admin_action_change_status', array( $this, 'handle_action_change_status' ) );
  42		add_action( 'restrict_manage_posts', array( $this, 'action_restrict_manage_posts' ) );
  43
  44	}
  45
  46	/**
  47	 * Re-sort the custom statuses so trash appears last
  48	 */
  49	function action_admin_init() {
  50		global $wp_post_statuses, $pagenow;
  51
  52		$trash_status = $wp_post_statuses['trash'];
  53		unset( $wp_post_statuses['trash'] );
  54		$wp_post_statuses['trash'] = $trash_status;
  55
  56		if ( 'edit.php' == $pagenow ) {
  57			add_filter( 'get_the_excerpt', array( $this, 'filter_get_the_excerpt' ) );
  58		}
  59	}
  60
  61	/**
  62	 * Add any CSS or JS we need for the admin
  63	 */
  64	public function action_admin_enqueue_scripts() {
  65		global $pagenow;
  66
  67		$handle = SupportFlow()->enqueue_style( 'supportflow-admin', 'admin.css' );
  68
  69		if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) ) {
  70			wp_enqueue_media();
  71
  72			$customers_autocomplete_handle   = SupportFlow()->enqueue_script( 'supportflow-customers-autocomplete', 'customers-autocomplete.js', array( 'jquery', 'jquery-ui-autocomplete' ) );
  73			$ticket_attachment_handle        = SupportFlow()->enqueue_script( 'supportflow-ticket-attachments', 'ticket_attachments.js' );
  74			$supportflow_tickets_handle      = SupportFlow()->enqueue_script( 'supportflow-tickets', 'tickets.js' );
  75			$auto_save_handle                = SupportFlow()->enqueue_script( 'supportflow-auto-save', 'auto_save.js', array( 'jquery', 'heartbeat' ) );
  76
  77			wp_localize_script( $customers_autocomplete_handle, 'SFCustomersAc', array(
  78				'ajax_url' => add_query_arg( 'action', SupportFlow()->extend->jsonapi->action, admin_url( 'admin-ajax.php' ) )
  79			) );
  80
  81			wp_localize_script( $ticket_attachment_handle, 'SFTicketAttachments', array(
  82				'frame_title'       => __( 'Attach files', 'supportflow' ),
  83				'button_title'      => __( 'Insert as attachment', 'supportflow' ),
  84				'remove_attachment' => __( 'Remove', 'supportflow' ),
  85				'sure_remove'       => __( 'Are you sure want to remove this attachment?', 'supportflow' ),
  86			) );
  87
  88			wp_localize_script( $supportflow_tickets_handle, 'SFTickets', array(
  89				'no_title_msg'      => __( 'You must need to specify the subject of the ticket', 'supportpress' ),
  90				'no_customer_msg'   => __( 'You must need to add atleast one customer', 'supportpress' ),
  91				'pagenow'           => $pagenow,
  92				'send_msg'          => __( 'Send Message', 'supportflow' ),
  93				'add_private_note'  => __( 'Add Private Note', 'supportflow' ),
  94			) );
  95
  96			wp_localize_script( $auto_save_handle, 'SFAutoSave', array(
  97				'ticket_id' => get_the_ID(),
  98			) );
  99
 100		}
 101
 102		if ( 'post.php' == $pagenow ) {
 103			$email_conversation_handle = SupportFlow()->enqueue_script( 'supportflow-email-conversation', 'email_conversation.js' );
 104
 105			wp_localize_script( $email_conversation_handle, 'SFEmailConversation', array(
 106				'post_id'                   => get_the_ID(),
 107				'sending_emails'            => __( 'Please wait while sending E-Mail(s)', 'supportflow' ),
 108				'failed_sending'            => __( 'Failed sending E-Mails', 'supportflow' ),
 109				'_email_conversation_nonce' => wp_create_nonce( 'sf_email_conversation' ),
 110			) );
 111		}
 112	}
 113
 114	/**
 115	 *
 116	 */
 117	public function action_wp_ajax_sf_email_conversation() {
 118		if ( false === check_ajax_referer( 'sf_email_conversation', '_email_conversation_nonce', false ) ) {
 119			_e( 'Invalid request. Please try refreshing the page.', 'supportflow' );
 120			die;
 121		}
 122
 123		if ( ! isset( $_REQUEST['email_ids'] ) || ! isset( $_REQUEST['post_id'] ) ) {
 124			_e( 'Invalid request. Please try refreshing the page.', 'supportflow' );
 125			die;
 126		}
 127
 128		$email_ids = SupportFlow()->extract_email_ids( $_REQUEST['email_ids'] );
 129		$ticket_id = (int) $_REQUEST['post_id'];
 130
 131		if ( ! current_user_can( 'edit_post', $ticket_id ) ) {
 132			_e( 'You are not allowed to edit this item.' );
 133			die;
 134		}
 135
 136		if ( empty( $email_ids ) ) {
 137			_e( 'No valid E-Mail ID found', 'supportflow' );
 138			die;
 139		}
 140
 141		SupportFlow()->extend->emails->email_conversation( $ticket_id, $email_ids );
 142
 143		_e( 'Successfully sented E-Mails', 'supportflow' );
 144		exit;
 145
 146	}
 147
 148	/**
 149	 * Add random characters to attachment uploaded through SupportFlow web UI
 150	 *
 151	 * @todo Conversion to a better way to determine if attachment if uploaded through SF web UI rather than HTTP referer
 152	 */
 153	function action_add_attachment( $attachment_id ) {
 154		if ( empty( $_SERVER['HTTP_REFERER'] ) ) {
 155			return;
 156		}
 157
 158		$post_type = SupportFlow()->post_type;
 159		$referer   = $_SERVER['HTTP_REFERER'];
 160
 161		$url  = parse_url( $referer );
 162		$path = $url['scheme'] . '://' . $url['host'] . $url['path'];
 163		parse_str( $url['query'], $query );
 164
 165		// Check if referred by SupportFlow ticket page
 166		if ( admin_url( 'post-new.php' ) == $path ) {
 167			if ( empty( $query['post_type'] ) || $query['post_type'] != $post_type ) {
 168				return;
 169			}
 170		} elseif ( admin_url( 'post.php' ) == $path ) {
 171			if ( empty( $query['post'] ) || get_post_type( (int) $query['post'] ) != $post_type ) {
 172				return;
 173			}
 174		} else {
 175			return;
 176		}
 177
 178		SupportFlow()->extend->attachments->secure_attachment_file( $attachment_id );
 179	}
 180
 181	/**
 182	 * Filter the messages that appear to the user after they perform an action on a ticket
 183	 */
 184	public function filter_post_updated_messages( $messages ) {
 185		global $post;
 186
 187		$messages[SupportFlow()->post_type] = array(
 188			0  => '', // Unused. Messages start at index 1.
 189			1  => __( 'Ticket updated.', 'supportflow' ),
 190			2  => __( 'Custom field updated.', 'supportflow' ),
 191			3  => __( 'Custom field deleted.', 'supportflow' ),
 192			4  => __( 'Ticket updated.', 'supportflow' ),
 193			/* translators: %s: date and time of the revision */
 194			5  => isset( $_GET['revision'] ) ? sprintf( __( 'Ticket restored to revision from %s', 'supportflow' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
 195			6  => __( 'Ticket updated.', 'supportflow' ),
 196			7  => __( 'Ticket updated.', 'supportflow' ),
 197			8  => __( 'Ticket updated.', 'supportflow' ),
 198			9  => __( 'Ticket updated.', 'supportflow' ),
 199			10 => __( 'Ticket updated.', 'supportflow' ),
 200		);
 201
 202		return $messages;
 203	}
 204
 205	public function filter_heartbeat_received( $response, $data ) {
 206		if (
 207			isset( $data['supportflow-autosave'] ) &&
 208			is_array( $data['supportflow-autosave'] ) &&
 209			isset( $data['supportflow-autosave']['ticket_id'] ) &&
 210			current_user_can( 'edit_post', (int) $data['supportflow-autosave']['ticket_id'] )
 211		) {
 212			// Save data received from client to the database as post meta
 213
 214			$ticket_id = (int) $data['supportflow-autosave']['ticket_id'];
 215			unset( $data['supportflow-autosave']['ticket_id'] );
 216
 217			if ( 'auto-draft' == get_post_status( $ticket_id ) ) {
 218				wp_update_post( array( 'ID' => $ticket_id, 'post_status' => 'draft' ) );
 219			}
 220
 221			foreach ( $data['supportflow-autosave'] as $element_id => $element_value ) {
 222				update_post_meta( $ticket_id, "_sf_autosave_$element_id", $element_value );
 223			}
 224
 225			echo $data['supportflow-autosave']['post_title'];
 226			if ( ! empty( $data['supportflow-autosave']['post_title'] ) ) {
 227				wp_update_post( array( 'ID' => $ticket_id, 'post_title' => $data['supportflow-autosave']['post_title'] ) );
 228			}
 229		}
 230
 231		return $response;
 232	}
 233
 234
 235	/**
 236	 *
 237	 */
 238	public function filter_views( $views ) {
 239		$post_type    = SupportFlow()->post_type;
 240		$statuses     = SupportFlow()->post_statuses;
 241		$status_slugs = array();
 242
 243		foreach ( $statuses as $status => $status_data ) {
 244			if ( true == $status_data['show_tickets'] ) {
 245				$status_slugs[] = $status;
 246			}
 247		}
 248
 249		$wp_query    = new WP_Query( array(
 250			'post_type'      => $post_type,
 251			'post_parent'    => 0,
 252			'posts_per_page' => 1,
 253			'post_status'    => $status_slugs,
 254		) );
 255		$total_posts = $wp_query->found_posts;
 256
 257		$class    = empty( $class ) && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['show_sticky'] ) ? ' class="current"' : '';
 258		$view_all = "<a href='edit.php?post_type=$post_type'$class>" . sprintf( _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . '</a>';
 259
 260		$post_statuses = SupportFlow()->post_statuses;
 261		array_pop( $post_statuses );
 262		$post_statuses = "'" . implode( "','", array_map( 'sanitize_key', array_keys( $post_statuses ) ) ) . "'";
 263
 264		// @todo Only show "Mine" if the user is an agent
 265		$mine_args = array(
 266			'post_type' => SupportFlow()->post_type,
 267			'author'    => get_current_user_id(),
 268		);
 269		$wp_query  = new WP_Query( array(
 270			'post_type'      => SupportFlow()->post_type,
 271			'author'         => get_current_user_id(),
 272			'post_status'    => $post_statuses,
 273			'posts_per_page' => 1,
 274		) );
 275
 276		$my_posts  = $wp_query->found_posts;
 277		$view_mine = '<a href="' . add_query_arg( $mine_args, admin_url( 'edit.php' ) ) . '">' . sprintf( _nx( 'Mine <span class="count">(%s)</span>', 'Mine <span class="count">(%s)</span>', $my_posts, 'posts' ), number_format_i18n( $my_posts ) ) . '</a>';
 278
 279		$unassigned_args = array(
 280			'post_type' => SupportFlow()->post_type,
 281			'author'    => 0,
 282		);
 283		$wp_query        = new WP_Query( array(
 284			'post_type'      => SupportFlow()->post_type,
 285			'author'         => 0,
 286			'post_status'    => $post_statuses,
 287			'posts_per_page' => 1,
 288		) );
 289
 290		$unassigned_posts = $wp_query->found_posts;
 291		$view_unassigned  = '<a href="' . add_query_arg( $unassigned_args, admin_url( 'edit.php' ) ) . '">' . sprintf( _nx( 'Unassigned <span class="count">(%s)</span>', 'Unassigned <span class="count">(%s)</span>', $unassigned_posts, 'posts' ), number_format_i18n( $unassigned_posts ) ) . '</a>';
 292
 293		// Put 'All' and 'Mine' at the beginning of the array
 294		array_shift( $views );
 295		$views               = array_reverse( $views );
 296		$views['unassigned'] = $view_unassigned;
 297		$views['mine']       = $view_mine;
 298		$views['all']        = $view_all;
 299		$views               = array_reverse( $views );
 300
 301		// Remove private option from filter links as they are just private replies to ticket
 302		unset( $views['private'] );
 303
 304		return $views;
 305	}
 306
 307	/**
 308	 * Add custom filters for the Manage Tickets view
 309	 */
 310	public function action_restrict_manage_posts() {
 311
 312		// Filter to specific agents
 313		$agent_dropdown_args = array(
 314			'show_option_all' => __( 'Show all agents', 'supportflow' ),
 315			'name'            => 'author',
 316			'selected'        => ( ! empty( $_REQUEST['author'] ) ) ? (int) $_REQUEST['author'] : false,
 317			'who'             => 'authors',
 318		);
 319		$agent_dropdown_args = apply_filters( 'supportflow_admin_agent_dropdown_args', $agent_dropdown_args );
 320		wp_dropdown_users( $agent_dropdown_args );
 321
 322		// Filter to specify tag
 323		$tax_slug = SupportFlow()->tags_tax;
 324		$terms    = get_terms( 'sf_tags', array( 'hide_empty' => false ) );
 325
 326		echo "<select name='" . esc_attr( $tax_slug ) . "' id='" . esc_attr( $tax_slug ) . "' class='postform'>";
 327		echo "<option value=''>" . __( 'Show All tags', 'supportflow' ) . "</option>";
 328		foreach ( $terms as $term ) {
 329			$selected = selected( isset( $_REQUEST[$tax_slug] ) && ( $_REQUEST[$tax_slug] == $term->slug ), true, false );
 330			echo "<option value='" . esc_attr( $term->slug ) . "' $selected>" . esc_html( $term->name ) . '</option>';
 331		}
 332		echo "</select>";
 333
 334
 335		// Filter to specify E-Mail account
 336		$email_accounts = SupportFlow()->extend->email_accounts->get_email_accounts( true );
 337		echo "<select name='email_account' id='email_account' class='postform'>";
 338		echo "<option value=''>" . __( 'Show All Accounts', 'supportflow' ) . "</option>";
 339		foreach ( $email_accounts as $id => $email_account ) {
 340			$selected = selected( isset( $_REQUEST['email_account'] ) && ( $_REQUEST['email_account'] == $id ), true, false );
 341			echo "<option value='" . esc_attr( $id ) . "'$selected>" . esc_html( $email_account['username'] ) . '</option>';
 342		}
 343		echo "</select>";
 344
 345	}
 346
 347	/**
 348	 * Filter the actions available to the agent on the post type
 349	 */
 350	function filter_post_row_actions( $row_actions, $post ) {
 351
 352		// Rename these actions
 353		if ( isset( $row_actions['edit'] ) ) {
 354			$row_actions['edit'] = str_replace( __( 'Edit' ), __( 'View', 'supportflow' ), str_replace( __( 'Edit this item' ), __( 'View Ticket', 'supportflow' ), $row_actions['edit'] ) );
 355		}
 356
 357		// Save the trash action for the end
 358		if ( isset( $row_actions['trash'] ) ) {
 359			$trash_action = $row_actions['trash'];
 360			unset( $row_actions['trash'] );
 361		} else {
 362			$trash_action = false;
 363		}
 364
 365		// Allow an agent to easily close a ticket
 366		$statuses     = SupportFlow()->post_statuses;
 367		$status_slugs = array_keys( $statuses );
 368		$last_status  = array_pop( $status_slugs );
 369		if ( ! in_array( get_query_var( 'post_status' ), array( 'trash' ) ) ) {
 370
 371			if ( $last_status == get_post_status( $post->ID ) ) {
 372				$change_to = $status_slugs[2];
 373			} else {
 374				$change_to = $last_status;
 375			}
 376
 377			$args        = array(
 378				'action'      => 'change_status',
 379				'sf_nonce'    => wp_create_nonce( 'sf-change-status' ),
 380				'post_status' => $change_to,
 381				'ticket_id'   => $post->ID,
 382				'post_type'   => SupportFlow()->post_type,
 383			);
 384			$action_link = add_query_arg( $args, admin_url( 'edit.php' ) );
 385			if ( $last_status == $change_to ) {
 386				$title_attr  = esc_attr__( 'Close Ticket', 'supportflow' );
 387				$action_text = esc_html__( 'Close', 'supportflow' );
 388			} else {
 389				$title_attr  = esc_attr__( 'Reopen Ticket', 'supportflow' );
 390				$action_text = esc_html__( 'Reopen', 'supportflow' );
 391			}
 392
 393			if ( current_user_can( 'edit_post', $post->ID ) ) {
 394				$row_actions['change_status'] = '<a href="' . esc_url( $action_link ) . '" title="' . $title_attr . '">' . $action_text . '</a>';
 395			}
 396		}
 397
 398		// Actions we don't want
 399		unset( $row_actions['inline hide-if-no-js'] );
 400		unset( $row_actions['view'] );
 401
 402		if ( $trash_action ) {
 403			$row_actions['trash'] = $trash_action;
 404		}
 405
 406		return $row_actions;
 407	}
 408
 409	/**
 410	 * Remove the 'edit' bulk action. Doesn't do much for us
 411	 */
 412	public function filter_bulk_actions( $actions ) {
 413		unset( $actions['edit'] );
 414
 415		return $actions;
 416	}
 417
 418	/**
 419	 * Handle which tickets are show on the Manage Tickets view when
 420	 */
 421	function action_pre_get_posts( $query ) {
 422		global $pagenow;
 423
 424		if ( 'edit.php' != $pagenow || ! $query->is_main_query() ) {
 425			return;
 426		}
 427
 428		$statuses     = SupportFlow()->post_statuses;
 429		$status_slugs = array();
 430
 431		foreach ( $statuses as $status => $status_data ) {
 432			if ( true == $status_data['show_tickets'] ) {
 433				$status_slugs[] = $status;
 434			}
 435		}
 436
 437		// Order posts by post_modified if there's no orderby set
 438		if ( ! $query->get( 'orderby' ) ) {
 439			$query->set( 'orderby', 'modified' );
 440			$query->set( 'order', 'DESC' );
 441		}
 442
 443		// Do our own custom search handling so we can search against reply text
 444		if ( $search = $query->get( 's' ) ) {
 445
 446			// Get all replies that match our results
 447			$args             = array(
 448				'search' => $search,
 449				'status' => 'any',
 450			);
 451			$matching_replies = SupportFlow()->get_replies( $args );
 452			$post_ids         = wp_list_pluck( $matching_replies, 'post_parent' );
 453
 454			$args       = array(
 455				's'                      => $search,
 456				'post_type'              => SupportFlow()->post_type,
 457				'no_found_rows'          => true,
 458				'update_post_meta_cache' => false,
 459				'update_post_term_cache' => false,
 460				'fields'                 => 'ids',
 461			);
 462			$post_query = new WP_Query( $args );
 463			if ( ! is_wp_error( $post_query ) ) {
 464				$post_ids = array_merge( $post_ids, $post_query->posts );
 465			}
 466
 467			$query->set( 'post__in', $post_ids );
 468			// Ignore the original search query
 469			add_filter( 'posts_search', array( $this, 'filter_posts_search' ) );
 470		}
 471
 472		// Only show tickets with the last status if the last status is set
 473		$post_status = $query->get( 'post_status' );
 474		if ( ! $query->get( 's' ) && empty( $post_status ) ) {
 475			$query->set( 'post_status', $status_slugs );
 476		}
 477
 478		add_action( 'posts_clauses', array( $this, 'filter_author_clause' ), 10, 2 );
 479
 480		if ( isset( $_GET['email_account'] ) && ! empty( $_GET['email_account'] ) ) {
 481			$query->set( 'meta_key', 'email_account' );
 482			$query->set( 'meta_value', (int) $_GET['email_account'] );
 483		}
 484	}
 485
 486	/*
 487	 * Show unassigned tickets when query author is 0
 488	 */
 489	public function filter_author_clause( $clauses, $query ) {
 490
 491		if ( isset( $query->query['author'] ) && 0 == $query->query['author'] ) {
 492			$clauses['where'] .= ' AND post_author = 0 ';
 493		}
 494
 495		return $clauses;
 496	}
 497
 498	/**
 499	 * Sometimes we want to ignore the original search query because we do our own
 500	 */
 501	public function filter_posts_search( $posts_search ) {
 502		return '';
 503	}
 504
 505	/**
 506	 * Handle $_GET actions in the admin
 507	 */
 508	function handle_action_change_status() {
 509
 510		if ( ! isset( $_GET['action'], $_GET['sf_nonce'], $_GET['post_status'], $_GET['ticket_id'] ) ) {
 511			return;
 512		}
 513
 514		if ( ! wp_verify_nonce( $_GET['sf_nonce'], 'sf-change-status' ) ) {
 515			wp_die( __( "Doin' something phishy, huh?", 'supportflow' ) );
 516		}
 517
 518		$ticket_id = (int) $_GET['ticket_id'];
 519
 520		if ( ! current_user_can( 'edit_post', $ticket_id ) ) {
 521			wp_die( __( 'You are not allowed to edit this item.' ) );
 522		}
 523
 524		$post_status = sanitize_key( $_GET['post_status'] );
 525		$new_ticket  = array(
 526			'ID'          => $ticket_id,
 527			'post_status' => $post_status,
 528		);
 529		wp_update_post( $new_ticket );
 530		wp_safe_redirect( wp_get_referer() );
 531		exit;
 532	}
 533
 534	/**
 535	 * Manipulate the meta boxes appearing on the edit post view
 536	 *
 537	 * When creating a new ticket, you should be able to:
 538	 *
 539	 * When updating an existing ticket, you should be able to:
 540	 *
 541	 */
 542	public function action_add_meta_boxes() {
 543		global $pagenow;
 544
 545		if ( ! $this->is_edit_screen() ) {
 546			return;
 547		}
 548
 549		$customers_box = 'tagsdiv-' . SupportFlow()->customers_tax;
 550		remove_meta_box( 'submitdiv', SupportFlow()->post_type, 'side' );
 551		remove_meta_box( $customers_box, SupportFlow()->post_type, 'side' );
 552		remove_meta_box( 'slugdiv', SupportFlow()->post_type, 'normal' );
 553
 554		add_meta_box( 'supportflow-details', __( 'Details', 'supportflow' ), array( $this, 'meta_box_details' ), SupportFlow()->post_type, 'side' );
 555		add_meta_box( 'supportflow-subject', __( 'Subject', 'supportflow' ), array( $this, 'meta_box_subject' ), SupportFlow()->post_type, 'normal' );
 556		add_meta_box( 'supportflow-customers', __( 'Customers', 'supportflow' ), array( $this, 'meta_box_customers' ), SupportFlow()->post_type, 'normal' );
 557		add_meta_box( 'supportflow-cc-bcc', __( 'CC and BCC', 'supportflow' ), array( $this, 'meta_box_cc_bcc' ), SupportFlow()->post_type, 'normal' );
 558		add_meta_box( 'supportflow-replies', __( 'Replies', 'supportflow' ), array( $this, 'meta_box_replies' ), SupportFlow()->post_type, 'normal' );
 559
 560		if ( 'post.php' == $pagenow ) {
 561			add_meta_box( 'supportflow-other-customers-tickets', __( 'Customer(s) recent Tickets', 'supportflow' ), array( $this, 'meta_box_other_customers_tickets' ), SupportFlow()->post_type, 'side' );
 562			add_meta_box( 'supportflow-forward_conversation', __( 'Forward this conversation', 'supportflow' ), array( $this, 'meta_box_email_conversation' ), SupportFlow()->post_type, 'side' );
 563		}
 564	}
 565
 566	public function meta_box_other_customers_tickets() {
 567		$ticket_customers = SupportFlow()->get_ticket_customers( get_the_ID(), array( 'fields' => 'slugs' ) );
 568		$statuses         = SupportFlow()->post_statuses;
 569		$status_slugs     = array_keys($statuses);
 570
 571		$table = new SupportFlow_Table( '', false, false );
 572
 573		if ( empty( $ticket_customers ) ) {
 574			$tickets = array();
 575
 576		} else {
 577			$args = array(
 578				'post_type'    => SupportFlow()->post_type,
 579				'post_parent'  => 0,
 580				'post_status'  => $status_slugs,
 581				'numberposts'  => 10,
 582				'post__not_in' => array( get_the_id() ),
 583				'tax_query'    => array(
 584					array(
 585						'taxonomy' => SupportFlow()->customers_tax,
 586						'field'    => 'slug',
 587						'terms'    => $ticket_customers,
 588					),
 589				),
 590			);
 591
 592			$wp_query = new WP_Query( $args );
 593			$tickets  = $wp_query->posts;
 594		}
 595
 596		$no_items = __( 'No recent tickets found.', 'supportflow' );
 597		$table->set_no_items( $no_items );
 598
 599		$table->set_columns( array(
 600			'title'  => __( 'Subject', 'supportflow' ),
 601			'status' => __( 'Status', 'supportflow' ),
 602		) );
 603
 604		$data = array();
 605		foreach ( $tickets as $ticket ) {
 606			$post_date     = strtotime( $ticket->post_date );
 607			$post_modified = strtotime( $ticket->post_modified );
 608			$title         = '<b>' . esc_html( $ticket->post_title ) . '</b>';
 609			$title         = "<a href='post.php?post=" . $ticket->ID . "&action=edit'>" . $title . "</a>";
 610			$data[]        = array(
 611				'title'  => $title,
 612				'status' => $statuses[$ticket->post_status]['label'],
 613			);
 614		}
 615		$table->set_data( $data );
 616		$table->display();
 617	}
 618
 619	public function meta_box_email_conversation() {
 620		?>
 621		<p class="description"><?php _e( "Please enter E-Mail address seperated by comma to whom you want to send this conversation.", 'supportflow' ) ?></p>
 622		<br />
 623		<input type="text" id="email_conversation_to" />
 624		<?php submit_button( __( 'Send', 'supportflow' ), '', 'email_conversation_submit', false ); ?>
 625		<p id="email_conversation_status"></p>
 626	<?php
 627	}
 628
 629	/**
 630	 * Show details about the ticket, and allow the post status and agent to be changed
 631	 */
 632	public function meta_box_details() {
 633		global $pagenow;
 634
 635		// Get post creation and last update time
 636		if ( 'post.php' == $pagenow ) {
 637			$opened        = get_the_date() . ' ' . get_the_time();
 638			$modified_gmt  = get_post_modified_time( 'U', true, get_the_ID() );
 639			$last_activity = sprintf( __( '%s ago', 'supportflow' ), human_time_diff( $modified_gmt ) );
 640		}
 641
 642
 643		// Get post status
 644		$post_statuses     = SupportFlow()->post_statuses;
 645		$current_status_id = get_post_status( get_the_ID() );
 646
 647		if ( ! isset( $post_statuses[$current_status_id] ) ) {
 648			$post_statuses_key = array_keys( $post_statuses );
 649			$current_status_id = $post_statuses_key[0];
 650		}
 651
 652		$current_status_label = $post_statuses[$current_status_id]['label'];
 653
 654		// Get post authors
 655		$post_author_id = get_post( get_the_ID() )->post_author;
 656
 657		// WP change owner to current user if $post_author_id is 0 (returned when ticket is unassigned)
 658		if ( 0 == $post_author_id ) {
 659			$post_author_id = - 1;
 660		}
 661
 662		if ( 0 < $post_author_id ) {
 663			$post_author_label = get_userdata( $post_author_id )->data->user_nicename;
 664		} else {
 665			$post_author_label = __( '-- Unassigned --', 'supportflow' );
 666		}
 667		$args                  = array(
 668			'show_option_none' => __( '-- Unassigned --', 'supportflow' ),
 669			'selected'         => $post_author_id,
 670			'id'               => '',
 671			'name'             => '',
 672			'who'              => 'author',
 673			'class'            => 'meta-item-dropdown',
 674			'echo'             => false
 675		);
 676		$post_authors_dropdown = wp_dropdown_users( $args );
 677
 678
 679		// Get post E-Mail account
 680		$email_accounts = SupportFlow()->extend->email_accounts->get_email_accounts( true );
 681
 682		$user_permissions = SupportFlow()->extend->permissions->get_user_permissions_data( get_current_user_id() );
 683		$user_permissions = $user_permissions['email_accounts'];
 684
 685		$email_account_id = get_post_meta( get_the_id(), 'email_account', true );
 686
 687		if ( '' == $email_account_id ) {
 688			$email_account_dropdown = '<select class="meta-item-dropdown">';
 689			foreach ( $email_accounts as $id => $email_account ) {
 690				if ( empty( $email_account ) || ( ! current_user_can( 'manage_options' ) && ! in_array( $id, $user_permissions ) ) ) {
 691					continue;
 692				}
 693				$email_account_dropdown .= '<option value="' . esc_attr( $id ) . '" ' . '>' . esc_html( $email_account['username'] ) . '</option>';
 694			}
 695			$email_account_dropdown .= '</select>';
 696
 697			$email_account_keys  = array_keys( $email_accounts );
 698			$email_account_first = $email_account_keys[0];
 699			$email_account_label = $email_accounts[$email_account_first]['username'];
 700		}
 701
 702		// Get E-Mail notification settings
 703		$notification_id          = 0;
 704		$notification_label       = 'Default';
 705		$notification_label_title = 'Choose default if you want to receive E-Mail notifications based on what you set in `E-Mail notification` page. Choose Enable/Disable if you want to override those settings';
 706		$notification_dropdown    = '';
 707		$notification_dropdown .= '<select class="meta-item-dropdown">';
 708
 709		if ( 'post-new.php' == $pagenow ) {
 710			$notification_dropdown .= '<option value="default">' . __( 'Default', 'supportflow' ) . '</option>';
 711			$notification_dropdown .= '<option value="enable">' . __( 'Subscribed', 'supportflow' ) . '</option>';
 712			$notification_dropdown .= '<option value="disable">' . __( 'Unsubscribed', 'supportflow' ) . '</option>';
 713		} elseif ( 'post.php' == $pagenow ) {
 714			$email_notifications_override = get_post_meta( get_the_ID(), 'email_notifications_override', true );
 715			$current_user_id              = get_current_user_id();
 716
 717			if ( isset( $email_notifications_override[$current_user_id] ) ) {
 718				$override_status = $email_notifications_override[$current_user_id];
 719				if ( 'enable' == $override_status ) {
 720					$notification_label = 'Subscribed';
 721					$notification_id    = 1;
 722				} elseif ( 'disable' == $override_status ) {
 723					$notification_label = 'Unsubscribed';
 724					$notification_id    = 2;
 725				}
 726			}
 727
 728			$notification_dropdown .= '<option value="default"' . selected( $notification_id, 0, false ) . '>' . __( 'Default', 'supportflow' ) . '</option>';
 729			$notification_dropdown .= '<option value="enable"' . selected( $notification_id, 1, false ) . '>' . __( 'Subscribed', 'supportflow' ) . '</option>';
 730			$notification_dropdown .= '<option value="disable"' . selected( $notification_id, 2, false ) . '>' . __( 'Unsubscribed', 'supportflow' ) . '</option>';
 731		}
 732
 733		$notification_dropdown .= '</select>';
 734
 735		$close_ticket_label = __( 'Close ticket', 'supportflow' );
 736
 737		// Get submit button label
 738		if ( 'post-new.php' == $pagenow ) {
 739			$submit_text = __( 'Start Ticket', 'supportflow' );
 740		} else {
 741			$submit_text = __( 'Update Ticket', 'supportflow' );
 742		}
 743		?>
 744
 745		<div id="minor-publishing">
 746			<div id="misc-publishing-actions">
 747
 748				<?php if ( '' == $email_account_id ) : ?>
 749					<div class="misc-pub-section meta-item">
 750						<label class="meta-item-toggle-button"><?php _e( 'Account', 'supportflow' ) ?>:</label>
 751						<span class="meta-item-label"><?php _e( $email_account_label, 'supportflow' ) ?></span>
 752						<a href="#" class="meta-item-toggle-button meta-item-toggle-content hide-if-no-js">
 753							<span aria-hidden="true"><?php _e( 'Edit' ) ?></span>
 754						</a>
 755						<input name="post_email_account" class="meta-item-name" value="<?php echo $email_account_first ?>" type="hidden" />
 756
 757						<div class="meta-item-toggle-content hide-if-js">
 758							<?php echo $email_account_dropdown ?>
 759							<a href="#" class="hide-if-no-js button meta-item-ok-button meta-item-toggle-button"><?php _e( 'OK' ) ?></a>
 760							<a href="#" class="hide-if-no-js button-cancel meta-item-cancel-button meta-item-toggle-button"><?php _e( 'Cancel' ) ?></a>
 761						</div>
 762					</div>
 763				<?php endif; ?>
 764
 765				<!--Ticket opening date/time-->
 766				<?php if ( 'post.php' == $pagenow ) : ?>
 767					<div class="misc-pub-section meta-item">
 768						<label><?php _e( 'Opened', 'supportflow' ) ?>:</label>
 769						<span class="meta-item-label"><?php esc_html_e( $opened ) ?></span>
 770					</div>
 771
 772					<!--Last ticket update time-->
 773					<div class="misc-pub-section meta-item">
 774						<label><?php _e( 'Last Activity', 'supportflow' ) ?>:</label>
 775						<span class="meta-item-label"><?php esc_html_e( $last_activity ) ?></span>
 776					</div>
 777				<?php endif; ?>
 778
 779				<!--Ticket status box-->
 780				<div class="misc-pub-section meta-item">
 781					<label class="meta-item-toggle-button"><?php _e( 'Status', 'supportflow' ) ?>:</label>
 782					<span class="meta-item-label"><?php esc_html_e( $current_status_label, 'supportflow' ) ?></span>
 783					<a href="#" class="meta-item-toggle-button meta-item-toggle-content hide-if-no-js">
 784						<span aria-hidden="true"><?php _e( 'Edit' ) ?></span>
 785					</a>
 786					<input name="post_status" class="meta-item-name" value="<?php esc_attr_e( $current_status_id ) ?>" type="hidden" />
 787
 788					<div class="meta-item-toggle-content hide-if-js">
 789						<select class="meta-item-dropdown">
 790							<?php foreach ( $post_statuses as $slug => $post_status ) : ?>
 791								<option value="<?php esc_attr_e( $slug ) ?>"<?php selected( $current_status_id, $slug ) ?>><?php esc_html_e( $post_status['label'] ) ?></option>;
 792							<?php endforeach; ?>
 793						</select>
 794						<a href="#" class="hide-if-no-js button meta-item-ok-button meta-item-toggle-button"><?php _e( 'OK' ) ?></a>
 795						<a href="#" class="hide-if-no-js button-cancel meta-item-cancel-button meta-item-toggle-button"><?php _e( 'Cancel' ) ?></a>
 796					</div>
 797				</div>
 798
 799				<div class="misc-pub-section meta-item">
 800					<label class="meta-item-toggle-button"><?php _e( 'Owner', 'supportflow' ) ?>:</label>
 801					<span class="meta-item-label"><?php _e( $post_author_label, 'supportflow' ) ?></span>
 802					<a href="#" class="meta-item-toggle-button meta-item-toggle-content hide-if-no-js">
 803						<span aria-hidden="true"><?php _e( 'Edit' ) ?></span>
 804					</a>
 805					<input name="post_author" class="meta-item-name" value="<?php esc_attr_e( $post_author_id ) ?>" type="hidden" />
 806
 807					<div class="meta-item-toggle-content hide-if-js">
 808						<?php echo $post_authors_dropdown ?>
 809						<a href="#" class="hide-if-no-js button meta-item-ok-button meta-item-toggle-button"><?php _e( 'OK' ) ?></a>
 810						<a href="#" class="hide-if-no-js button-cancel meta-item-cancel-button meta-item-toggle-button"><?php _e( 'Cancel' ) ?></a>
 811					</div>
 812				</div>
 813
 814				<div class="misc-pub-section meta-item">
 815					<label class="meta-item-toggle-button" title="<?php _e( $notification_label_title, 'supportflow' ) ?>"><?php _e( 'E-Mail Notifications', 'supportflow' ) ?>:</label>
 816					<span class="meta-item-label"><?php esc_html_e( $notification_label, 'supportflow' ) ?></span>
 817					<a href="#" class="meta-item-toggle-button meta-item-toggle-content hide-if-no-js">
 818						<span aria-hidden="true"><?php _e( 'Edit' ) ?></span>
 819					</a>
 820					<input name="post_email_notifications_override" class="meta-item-name" value="<?php echo $notification_id ?>" type="hidden" />
 821
 822					<div class="meta-item-toggle-content hide-if-js">
 823						<?php echo $notification_dropdown ?>
 824						<a href="#" class="hide-if-no-js button meta-item-ok-button meta-item-toggle-button"><?php _e( 'OK' ) ?></a>
 825						<a href="#" class="hide-if-no-js button-cancel meta-item-cancel-button meta-item-toggle-button"><?php _e( 'Cancel' ) ?></a>
 826					</div>
 827				</div>
 828
 829			</div>
 830			<div class="clear"></div>
 831		</div>
 832
 833		<div id="major-publishing-actions">
 834			<?php if ( 'post.php' == $pagenow && $current_status_id != 'sf_closed' ) : ?>
 835				<div id="delete-action">
 836					<?php submit_button( $close_ticket_label, '', 'close-ticket-submit', false, array( 'id' => 'close-ticket-submit' ) ); ?>
 837				</div>
 838			<?php endif; ?>
 839			<div id="publishing-action">
 840				<?php submit_button( $submit_text, 'save-button primary', 'update-ticket', false ); ?>
 841			</div>
 842			<div class="clear"></div>
 843		</div>
 844	<?php
 845	}
 846
 847	/**
 848	 * A box that appears at the top
 849	 */
 850	public function meta_box_subject() {
 851
 852		$placeholder = __( 'What is your conversation about?', 'supportflow' );
 853		echo '<h4>' . __( 'Subject', 'supportflow' ) . '</h4>';
 854		echo '<input type="text" id="subject" name="post_title" class="sf_autosave" placeholder="' . $placeholder . '" value="' . get_the_title() . '" autocomplete="off" />';
 855		echo '<p class="description">' . __( 'Please describe what this ticket is about in several words', 'supportflow' ) . '</p>';
 856
 857	}
 858
 859	/**
 860	 * Add a form element where the user can change the customers
 861	 */
 862	public function meta_box_customers() {
 863
 864		$placeholder = __( 'Who are you starting a conversation with?', 'supportflow' );
 865		if ( 'draft' == get_post_status( get_the_ID() ) ) {
 866			$customers_string = get_post_meta( get_the_ID(), '_sf_autosave_customers', true );
 867		} else {
 868			$customers        = SupportFlow()->get_ticket_customers( get_the_ID(), array( 'fields' => 'emails' ) );
 869			$customers_string = implode( ', ', $customers );
 870			$customers_string .= empty( $customers_string ) ? '' : ', ';
 871		}
 872		echo '<h4>' . __( 'Customer(s)', 'supportflow' ) . '</h4>';
 873		echo '<input type="text" id="customers" name="customers" class="sf_autosave" placeholder="' . $placeholder . '" value="' . esc_attr( $customers_string ) . '" autocomplete="off" />';
 874		echo '<p class="description">' . __( 'Enter each customer email address, separated with a comma', 'supportflow' ) . '</p>';
 875	}
 876
 877	/**
 878	 * Add a form element where you can choose cc and bcc receiver of reply
 879	 */
 880	public function meta_box_cc_bcc() {
 881		$cc_value = esc_attr( get_post_meta( get_the_ID(), '_sf_autosave_cc', true ) );
 882		$bcc      = esc_attr( get_post_meta( get_the_ID(), '_sf_autosave_bcc', true ) );
 883		?>
 884		<p class="description"> <?php _e( "Please add all the E-Mail ID's seperated by comma.", 'supportflow' ) ?></p>
 885		<h4 class="inline"><?php _e( "CC: ", 'supportflow' ) ?></h4>
 886		<input type="text" class="sf_autosave" id="cc" name="cc" value="<?php echo $cc_value ?>" />
 887		<h4 class="inline"> <?php _e( "BCC: ", 'supportflow' ) ?></h4>
 888		<input type="text" class="sf_autosave" id="bcc" name="bcc" value="<?php echo $bcc ?>" />
 889	<?php
 890	}
 891
 892	/**
 893	 * Standard listing of replies includes a form at the top
 894	 * and any existing replies listed in reverse chronological order
 895	 */
 896	public function meta_box_replies() {
 897		global $pagenow;
 898
 899		$predefined_replies = get_posts( array( 'post_type' => 'sf_predefs' ) );
 900		$pre_defs           = array( array( 'title' => __( 'Pre-defined Replies', 'supportflow' ), 'content' => '' ) );
 901
 902		foreach ( $predefined_replies as $predefined_reply ) {
 903			$content = $predefined_reply->post_content;
 904
 905			if ( ! empty( $predefined_reply->post_title ) ) {
 906				$title = $predefined_reply->post_title;
 907			} else {
 908				$title = $predefined_reply->post_content;
 909			}
 910
 911			// Limit size to 75 characters
 912			if ( strlen( $title ) > 75 ) {
 913				$title = substr( $title, 0, 75 - 3 ) . '...';
 914			}
 915
 916			if ( 0 != strlen( $content ) ) {
 917				$pre_defs[] = array( 'title' => $title, 'content' => $content );
 918			}
 919		}
 920
 921		$email_account_id = get_post_meta( get_the_ID(), 'email_account', true );
 922		$email_account    = SupportFlow()->extend->email_accounts->get_email_account( $email_account_id );
 923
 924		$ticket_lock       = ( null == $email_account && '' != $email_account_id );
 925		$disabled_attr     = $ticket_lock ? 'disabled' : '';
 926		$submit_attr_array = $ticket_lock ? array( 'disabled' => 'true' ) : array();
 927
 928		if ( $ticket_lock ) {
 929			$placeholder = __( "Ticket is locked permanently because E-Mail account associated with it is deleted. Please create a new ticket now. You can't now reply to it.", 'supportflow' );
 930		} else {
 931			$placeholders = array(
 932				__( "What's burning?", 'supportflow' ),
 933				__( 'What do you need to get off your chest?', 'supportflow' ),
 934			);
 935			$rand         = array_rand( $placeholders );
 936			$placeholder  = $placeholders[$rand];
 937		}
 938
 939		echo '<div class="alignleft"><h4>' . __( 'Conversation', 'supportflow' ) . '</h4></div>';
 940		echo '<div class="alignright">';
 941		echo '<select id="predefs" ' . $disabled_attr . ' class="predefined_replies_dropdown">';
 942		foreach ( $pre_defs as $pre_def ) {
 943			echo '<option class="predef" data-content="' . esc_attr( $pre_def['content'] ) . '">' . esc_html( $pre_def['title'] ) . "</option>\n";
 944		}
 945		echo '</select></div>';
 946
 947		echo '<div id="ticket-reply-box">';
 948		echo "<textarea id='reply' name='reply' $disabled_attr class='ticket-reply sf_autosave' rows='4' placeholder='" . esc_attr( $placeholder ) . "'>";
 949		echo esc_html( get_post_meta( get_the_ID(), '_sf_autosave_reply', true ) );
 950		echo "</textarea>";
 951
 952		echo '<div id="message-tools">';
 953		echo '<div id="replies-attachments-wrap">';
 954		echo '<div class="drag-drop-buttons">';
 955		echo '<input id="reply-attachment-browse-button" ' . $disabled_attr . ' type="button" value="' . esc_attr( __( 'Attach files', 'supportflow' ) ) . '" class="button" />';
 956		echo '</div>';
 957		echo '<ul id="replies-attachments-list">';
 958		echo '</ul>';
 959		echo '<input type="hidden" id="reply-attachments" name="reply-attachments" value="," />';
 960		echo '</div>';
 961		echo '<div id="submit-action">';
 962		$signature_label_title = __( 'Append your signature at the bottom of the reply. Signature can be removed or changed in preferences page', 'supportflow' );
 963		echo '<input type="checkbox" ' . $disabled_attr . '	checked="checked" id="insert-signature" name="insert-signature" />';
 964		echo "<label for='insert-signature' title='$signature_label_title'>" . __( 'Insert signature', 'supportflow' ) . '</label>';
 965		echo '<input type="checkbox" ' . $disabled_attr . ' id="mark-private" name="mark-private" />';
 966		echo '<label for="mark-private">' . __( 'Mark private', 'supportflow' ) . '</label>';
 967		if ( 'post-new.php' == $pagenow ) {
 968			$submit_text = __( 'Start Ticket', 'supportflow' );
 969		} else {
 970			$submit_text = __( 'Send Message', 'supportflow' );
 971		}
 972		submit_button( $submit_text, 'primary save-button', 'insert-reply', false, $submit_attr_array );
 973		echo '</div>';
 974		echo '</div>';
 975
 976		echo '</div>';
 977
 978		echo '<div class="clear"></div>';
 979
 980		$this->display_ticket_replies();
 981	}
 982
 983	public function display_ticket_replies() {
 984		$private_replies = SupportFlow()->get_ticket_replies( get_the_ID(), array( 'status' => 'private' ) );
 985
 986		if ( ! empty( $private_replies ) ) {
 987			echo '<ul class="private-replies">';
 988			foreach ( $private_replies as $reply ) {
 989				echo '<li>';
 990				echo '<div class="ticket-reply">';
 991				$post_content = wpautop( stripslashes( $reply->post_content ) );
 992				// Make link clickable
 993				$post_content = make_clickable( $post_content );
 994				$post_content = $this->hide_quoted_text( $post_content );
 995				echo $post_content;
 996				if ( $attachment_ids = get_post_meta( $reply->ID, 'sf_attachments' ) ) {
 997					echo '<ul class="ticket-reply-attachments">';
 998					foreach ( $attachment_ids as $attachment_id ) {
 999						$attachment_link = SupportFlow()->extend->attachments->get_attachment_url( $attachment_id );
1000						echo '<li><a target="_blank" href="' . esc_url( $attachment_link ) . '">' . esc_html( get_the_title( $attachment_id ) ) . '</a></li>';
1001					}
1002					echo '</ul>';
1003				}
1004				echo '</div>';
1005				$reply_author    = get_post_meta( $reply->ID, 'reply_author', true );
1006				$reply_timestamp = sprintf( __( 'Noted by %1$s on %2$s at %3$s', 'supportflow' ), $reply_author, get_the_date(), get_the_time() );
1007				$modified_gmt    = get_post_modified_time( 'U', true, get_the_ID() );
1008				$last_activity   = sprintf( __( '%s ago', 'supportflow' ), human_time_diff( $modified_gmt ) );
1009				echo '<div class="ticket-meta"><span class="reply-timestamp">' . esc_html( $reply_timestamp ) . ' (' . $last_activity . ')' . '</span></div>';
1010				echo '</li>';
1011			}
1012			echo '</ul>';
1013		}
1014
1015		$replies = SupportFlow()->get_ticket_replies( get_the_ID(), array( 'status' => 'public' ) );
1016		if ( ! empty( $replies ) ) {
1017			echo '<ul class="ticket-replies">';
1018			foreach ( $replies as $reply ) {
1019				$reply_author       = get_post_meta( $reply->ID, 'reply_author', true );
1020				$reply_author_email = get_post_meta( $reply->ID, 'reply_author_email', true );
1021				echo '<li>';
1022				echo '<div class="reply-avatar">' . get_avatar( $reply_author_email, 72 );
1023				echo '<p class="reply-author">' . esc_html( $reply_author ) . '</p>';
1024				echo '</div>';
1025				echo '<div class="ticket-reply">';
1026				$post_content = wpautop( stripslashes( $reply->post_content ) );
1027				// Make link clickable
1028				$post_content = make_clickable( $post_content );
1029				$post_content = $this->hide_quoted_text( $post_content );
1030				echo $post_content;
1031				if ( $attachment_ids = get_post_meta( $reply->ID, 'sf_attachments' ) ) {
1032					echo '<ul class="ticket-reply-attachments">';
1033					foreach ( $attachment_ids as $attachment_id ) {
1034						$attachment_link = SupportFlow()->extend->attachments->get_attachment_url( $attachment_id );
1035						echo '<li><a target="_blank" href="' . esc_url( $attachment_link ) . '">' . esc_html( get_the_title( $attachment_id ) ) . '</a></li>';
1036					}
1037					echo '</ul>';
1038				}
1039				echo '</div>';
1040				$reply_timestamp = sprintf( __( '%s at %s', 'supportflow' ), get_the_date(), get_the_time() );
1041				$modified_gmt    = get_post_modified_time( 'U', true, get_the_ID() );
1042				$last_activity   = sprintf( __( '%s ago', 'supportflow' ), human_time_diff( $modified_gmt ) );
1043				echo '<div class="ticket-meta"><span class="reply-timestamp">' . esc_html( $reply_timestamp ) . ' (' . $last_activity . ')' . '</span></div>';
1044				echo '</li>';
1045			}
1046			echo '</ul>';
1047		}
1048
1049		echo '<div class="clear"></div>';
1050
1051	}
1052
1053	/**
1054	 * Modifications to the columns appearing in the All Tickets view
1055	 */
1056	public function filter_manage_post_columns( $columns ) {
1057
1058		$new_columns = array(
1059			'cb'          => $columns['cb'],
1060			'updated'     => __( 'Updated', 'supportflow' ),
1061			'title'       => __( 'Subject', 'supportflow' ),
1062			'sf_excerpt'  => __( 'Excerpt', 'supportflow' ),
1063			'customers' => __( 'Customers', 'supportflow' ),
1064			'status'      => __( 'Status', 'supportflow' ),
1065			'author'      => __( 'Agent', 'supportflow' ),
1066			'sf_replies'  => '<span title="' . __( 'Reply count', 'supportflow' ) . '" class="comment-grey-bubble"></span>',
1067			'email'       => __( 'E-Mail account', 'supportflow' ),
1068			'created'     => __( 'Created', 'support' ),
1069		);
1070
1071		return $new_columns;
1072	}
1073
1074	/**
1075	 * Make some other columns sortable too
1076	 */
1077	public function manage_sortable_columns( $columns ) {
1078		$columns['updated'] = 'modified';
1079		$columns['created'] = 'date';
1080
1081		return $columns;
1082	}
1083
1084	/**
1085	 * Use the most recent public reply as the post excerpt
1086	 * on the Manage Tickets view so mode=excerpt works well
1087	 */
1088	public function filter_get_the_excerpt( $orig ) {
1089		if ( $reply = array_pop( SupportFlow()->get_ticket_replies( get_the_ID() ) ) ) {
1090			$reply_author = get_post_meta( $reply->ID, 'reply_author' );
1091
1092			return $reply_author . ': "' . wp_trim_excerpt( $reply->post_content ) . '"';
1093		} else {
1094			return $orig;
1095		}
1096	}
1097
1098	/**
1099	 * Produce the column values for the custom columns we created
1100	 */
1101	function action_manage_posts_custom_column( $column_name, $ticket_id ) {
1102
1103		switch ( $column_name ) {
1104			case 'updated':
1105				$modified_gmt = get_post_modified_time( 'U', true, $ticket_id );
1106				echo sprintf( __( '%s ago', 'supportflow' ), human_time_diff( $modified_gmt ) );
1107				break;
1108			case 'sf_excerpt':
1109				$replies = SupportFlow()->get_ticket_replies( $ticket_id, array( 'numberposts' => 1, 'order' => 'ASC' ) );
1110				if ( ! isset( $replies[0] ) ) {
1111					echo '—';
1112					break;
1113				}
1114				$first_reply = $replies[0]->post_content;
1115				if ( strlen( $first_reply ) > 50 ) {
1116					$first_reply = substr( $first_reply, 0, 50 );
1117				}
1118				echo $first_reply;
1119				break;
1120			case 'customers':
1121				$customers = SupportFlow()->get_ticket_customers( $ticket_id, array( 'fields' => 'emails' ) );
1122				if ( empty( $customers ) ) {
1123					echo '—';
1124					break;
1125				}
1126				foreach ( $customers as $key => $customer_email ) {
1127					$args              = array(
1128						SupportFlow()->customers_tax => SupportFlow()->get_email_hash( $customer_email ),
1129						'post_type'                    => SupportFlow()->post_type,
1130					);
1131					$customer_photo  = get_avatar( $customer_email, 16 );
1132					$customer_link   = '<a class="customer_link" href="' . esc_url( add_query_arg( $args, admin_url( 'edit.php' ) ) ) . '">' . $customer_email . '</a>';
1133					$customers[$key] = $customer_photo . '&nbsp;' . $customer_link;
1134				}
1135				echo implode( '<br />', $customers );
1136				break;
1137			case 'status':
1138				$post_status = get_post_status( $ticket_id );
1139				$args        = array(
1140					'post_type'   => SupportFlow()->post_type,
1141					'post_status' => $post_status,
1142				);
1143				$status_name = get_post_status_object( $post_status )->label;
1144				$filter_link = add_query_arg( $args, admin_url( 'edit.php' ) );
1145				echo '<a href="' . esc_url( $filter_link ) . '">' . esc_html( $status_name ) . '</a>';
1146				break;
1147			case 'email':
1148				$email_account_id = get_post_meta( $ticket_id, 'email_account', true );
1149				$email_accounts   = SupportFlow()->extend->email_accounts->get_email_accounts();
1150				$args             = array(
1151					'post_type'     => SupportFlow()->post_type,
1152					'email_account' => $email_account_id,
1153				);
1154				if ( ! isset( $email_accounts[$email_account_id] ) ) {
1155					echo '—';
1156					break;
1157				}
1158				$email_account_username = $email_accounts[$email_account_id]['username'];
1159				$filter_link            = add_query_arg( $args, admin_url( 'edit.php' ) );
1160				echo '<a href="' . esc_url( $filter_link ) . '">' . esc_html( $email_account_username ) . '</a>';
1161				break;
1162			case 'sf_replies':
1163				$replies = SupportFlow()->get_ticket_replies_count( $ticket_id );
1164				echo '<div class="post-com-count-wrapper">';
1165				echo "<span class='replies-count'>{$replies}</span>";
1166				echo '</div>';
1167				break;
1168			case 'created':
1169				$created_time = get_the_time( get_option( 'time_format' ) . ' T', $ticket_id );
1170				$created_date = get_the_time( get_option( 'date_format' ), $ticket_id );
1171				echo sprintf( __( '%s<br />%s', 'supportflow' ), $created_time, $created_date );
1172				break;
1173		}
1174	}
1175
1176	/**
1177	 * Whether or not we're on a view for creating or updating a ticket
1178	 *
1179	 * @return string $pagenow Return the context for the screen we're in
1180	 */
1181	public function is_edit_screen() {
1182		global $pagenow;
1183
1184		if ( in_array( $pagenow, array( 'edit.php', 'post-new.php' ) ) && ! empty( $_GET['post_type'] ) && $_GET['post_type'] == SupportFlow()->post_type ) {
1185			return $pagenow;
1186		} elseif ( 'post.php' == $pagenow && ! empty( $_GET['action'] ) && 'edit' == $_GET['action'] && ! empty( $_GET['post'] ) ) {
1187			$the_post = get_post( absint( $_GET['post'] ) );
1188
1189			return ( is_a( $the_post, 'WP_Post' ) && $the_post->post_type == SupportFlow()->post_type ) ? $pagenow : false;
1190		} else {
1191			return false;
1192		}
1193
1194	}
1195
1196	/**
1197	 * When a ticket is saved or updated, make sure we save the customer
1198	 * and new reply data
1199	 */
1200	public function action_save_post( $ticket_id ) {
1201		$email_account_id = get_post_meta( $ticket_id, 'email_account', true );
1202		$email_account    = SupportFlow()->extend->email_accounts->get_email_account( $email_account_id );
1203		$ticket_lock      = ( null == $email_account && '' != $email_account );
1204
1205		if ( SupportFlow()->post_type != get_post_type( $ticket_id ) ) {
1206			return;
1207		}
1208
1209		if ( isset( $_POST['customers'] ) ) {
1210			$customers = array_map( 'sanitize_email', explode( ',', $_POST['customers'] ) );
1211			SupportFlow()->update_ticket_customers( $ticket_id, $customers );
1212		}
1213
1214		if ( isset( $_POST['post_email_account'] ) && is_numeric( $_POST['post_email_account'] ) && '' == $email_account_id ) {
1215			$email_account = (int) $_POST['post_email_account'];
1216			update_post_meta( $ticket_id, 'email_account', $email_account );
1217		}
1218
1219		if ( isset( $_POST['post_email_notifications_override'] ) && in_array( $_POST['post_email_notifications_override'], array( 'default', 'enable', 'disable' ) ) ) {
1220			$email_notifications_override                        = get_post_meta( $ticket_id, 'email_notifications_override', true );
1221			$email_notifications_override[get_current_user_id()] = $_POST['post_email_notifications_override'];
1222			update_post_meta( $ticket_id, 'email_notifications_override', $email_notifications_override );
1223		}
1224
1225		if ( isset( $_POST['reply'] ) && ! empty( $_POST['reply'] ) && ! $ticket_lock ) {
1226			$reply = $_POST['reply'];
1227
1228			if ( isset( $_POST['insert-signature'] ) && 'on' == $_POST['insert-signature'] ) {
1229				$agent_signature = get_user_meta( get_current_user_id(), 'sf_user_signature', true );
1230				if ( ! empty( $agent_signature ) ) {
1231					$reply .= "\n\n-----\n$agent_signature";
1232				}
1233			}
1234
1235			$reply = SupportFlow()->sanitize_ticket_reply( $reply );
1236
1237			$visibility = ( ! empty( $_POST['mark-private'] ) ) ? 'private' : 'public';
1238			if ( ! empty( $_POST['reply-attachments'] ) ) {
1239				$attachements   = explode( ',', trim( $_POST['reply-attachments'], ',' ) );
1240				// Remove same attachment added more than once
1241				$attachements   = array_unique($attachements);
1242				// Remove non-int attachment ID's from array
1243				$attachements   = array_filter( $attachements, function ( $val ) {
1244					return (string) (int) $val === (string) $val;
1245				} );
1246				$attachment_ids = array_map( 'intval', $attachements );
1247			} else {
1248				$attachment_ids = '';
1249			}
1250			$cc  = ( ! empty( $_POST['cc'] ) ) …

Large files files are truncated, but you can click here to view the full file