2018-07-14 03:23:37 +02:00
< ? php
namespace App ;
2018-09-14 10:24:04 +02:00
use App\Attachment ;
2018-07-14 03:23:37 +02:00
use Illuminate\Database\Eloquent\Model ;
class Thread extends Model
{
/**
2018-07-25 19:30:43 +02:00
* By whom action performed ( source_via ) .
2018-07-14 03:23:37 +02:00
*/
const PERSON_CUSTOMER = 1 ;
const PERSON_USER = 2 ;
2018-07-25 19:30:43 +02:00
public static $persons = [
2018-07-14 03:23:37 +02:00
self :: PERSON_CUSTOMER => 'customer' ,
2018-07-25 19:30:43 +02:00
self :: PERSON_USER => 'user' ,
];
2018-07-14 03:23:37 +02:00
/**
2018-07-25 19:30:43 +02:00
* Thread types .
2018-07-14 03:23:37 +02:00
*/
// Email from customer
const TYPE_CUSTOMER = 1 ;
// Thead created by user
const TYPE_MESSAGE = 2 ;
const TYPE_NOTE = 3 ;
// Thread status change
const TYPE_LINEITEM = 4 ;
const TYPE_PHONE = 5 ;
// Forwarded threads
const TYPE_FORWARDPARENT = 6 ;
const TYPE_FORWARDCHILD = 7 ;
const TYPE_CHAT = 8 ;
public static $types = [
2018-07-20 08:19:25 +02:00
// Thread by customer
2018-07-25 19:30:43 +02:00
self :: TYPE_CUSTOMER => 'customer' ,
2018-07-20 08:19:25 +02:00
// Thread by user
2018-07-25 19:30:43 +02:00
self :: TYPE_MESSAGE => 'message' ,
self :: TYPE_NOTE => 'note' ,
2018-07-20 08:19:25 +02:00
// lineitem represents a change of state on the conversation. This could include, but not limited to, the conversation was assigned, the status changed, the conversation was moved from one mailbox to another, etc. A line item won’ t have a body, to/cc/bcc lists, or attachments.
2018-07-25 19:30:43 +02:00
self :: TYPE_LINEITEM => 'lineitem' ,
self :: TYPE_PHONE => 'phone' ,
2018-07-20 08:19:25 +02:00
// When a conversation is forwarded, a new conversation is created to represent the forwarded conversation.
// forwardparent is the type set on the thread of the original conversation that initiated the forward event.
2018-07-25 19:30:43 +02:00
self :: TYPE_FORWARDPARENT => 'forwardparent' ,
2018-07-20 08:19:25 +02:00
// forwardchild is the type set on the first thread of the new forwarded conversation.
2018-07-25 19:30:43 +02:00
self :: TYPE_FORWARDCHILD => 'forwardchild' ,
self :: TYPE_CHAT => 'chat' ,
2018-07-14 03:23:37 +02:00
];
/**
2018-07-27 15:19:25 +02:00
* Statuses ( code must be equal to conversations statuses ) .
2018-07-14 03:23:37 +02:00
*/
const STATUS_ACTIVE = 1 ;
2018-07-27 15:19:25 +02:00
const STATUS_PENDING = 2 ;
const STATUS_CLOSED = 3 ;
const STATUS_SPAM = 4 ;
const STATUS_NOCHANGE = 6 ;
2018-07-14 03:23:37 +02:00
2018-07-25 19:30:43 +02:00
public static $statuses = [
self :: STATUS_ACTIVE => 'active' ,
self :: STATUS_CLOSED => 'closed' ,
self :: STATUS_NOCHANGE => 'nochange' ,
self :: STATUS_PENDING => 'pending' ,
self :: STATUS_SPAM => 'spam' ,
];
2018-07-14 03:23:37 +02:00
/**
2018-07-25 19:30:43 +02:00
* States .
2018-07-14 03:23:37 +02:00
*/
const STATE_DRAFT = 1 ;
const STATE_PUBLISHED = 2 ;
const STATE_HIDDEN = 3 ;
2018-08-02 18:17:13 +02:00
// A state of review means the thread has been stopped by Traffic Cop and is waiting
// to be confirmed (or discarded) by the person that created the thread.
2018-07-14 03:23:37 +02:00
const STATE_REVIEW = 4 ;
2018-07-25 19:30:43 +02:00
public static $states = [
self :: STATE_DRAFT => 'draft' ,
self :: STATE_PUBLISHED => 'published' ,
self :: STATE_HIDDEN => 'hidden' ,
self :: STATE_REVIEW => 'review' ,
];
2018-07-14 03:23:37 +02:00
/**
2018-07-25 19:30:43 +02:00
* Action associated with the line item .
2018-07-14 03:23:37 +02:00
*/
2018-07-25 19:25:00 +02:00
// Conversation's status changed
const ACTION_TYPE_STATUS_CHANGED = 1 ;
// Conversation's assignee changed
const ACTION_TYPE_USER_CHANGED = 2 ;
2018-07-14 03:23:37 +02:00
// The conversation was moved from another mailbox
2018-07-25 19:25:00 +02:00
const ACTION_TYPE_MOVED_FROM_MAILBOX = 3 ;
2018-07-14 03:23:37 +02:00
// Another conversation was merged with this conversation
2018-07-25 19:25:00 +02:00
const ACTION_TYPE_MERGED = 4 ;
2018-07-14 03:23:37 +02:00
// The conversation was imported (no email notifications were sent)
2018-07-25 19:25:00 +02:00
const ACTION_TYPE_IMPORTED = 5 ;
2018-07-14 03:23:37 +02:00
// A workflow was run on this conversation (either automatic or manual)
2018-07-25 19:25:00 +02:00
const ACTION_TYPE_WORKFLOW_MANUAL = 6 ;
const ACTION_TYPE_WORKFLOW_AUTO = 7 ;
2018-07-14 03:23:37 +02:00
// The ticket was imported from an external Service
2018-07-25 19:25:00 +02:00
const ACTION_TYPE_IMPORTED_EXTERNAL = 8 ;
2018-08-29 08:45:15 +02:00
// Conversation customer changed
const ACTION_TYPE_CUSTOMER_CHANGED = 9 ;
2018-07-14 03:23:37 +02:00
// The ticket was deleted
2018-07-25 19:25:00 +02:00
const ACTION_TYPE_DELETED_TICKET = 10 ;
2018-07-14 03:23:37 +02:00
// The ticket was restored
2018-07-25 19:25:00 +02:00
const ACTION_TYPE_RESTORE_TICKET = 11 ;
2018-07-14 03:23:37 +02:00
// Describes an optional action associated with the line item
// todo: values need to be checked via HelpScout API
public static $action_types = [
2018-07-25 19:30:43 +02:00
self :: ACTION_TYPE_STATUS_CHANGED => 'changed-ticket-status' ,
self :: ACTION_TYPE_USER_CHANGED => 'changed-ticket-assignee' ,
self :: ACTION_TYPE_MOVED_FROM_MAILBOX => 'moved-from-mailbox' ,
self :: ACTION_TYPE_MERGED => 'merged' ,
self :: ACTION_TYPE_IMPORTED => 'imported' ,
self :: ACTION_TYPE_WORKFLOW_MANUAL => 'manual-workflow' ,
self :: ACTION_TYPE_WORKFLOW_AUTO => 'automatic-workflow' ,
self :: ACTION_TYPE_IMPORTED_EXTERNAL => 'imported-external' ,
2018-08-29 08:45:15 +02:00
self :: ACTION_TYPE_CUSTOMER_CHANGED => 'changed-ticket-customer' ,
2018-07-25 19:30:43 +02:00
self :: ACTION_TYPE_DELETED_TICKET => 'deleted-ticket' ,
self :: ACTION_TYPE_RESTORE_TICKET => 'restore-ticket' ,
2018-07-14 03:23:37 +02:00
];
2018-07-25 19:30:43 +02:00
/**
* Source types ( equal to thread source types ) .
2018-07-14 03:23:37 +02:00
*/
const SOURCE_TYPE_EMAIL = 1 ;
const SOURCE_TYPE_WEB = 2 ;
const SOURCE_TYPE_API = 3 ;
2018-07-25 19:30:43 +02:00
2018-07-14 03:23:37 +02:00
public static $source_types = [
2018-07-25 19:30:43 +02:00
self :: SOURCE_TYPE_EMAIL => 'email' ,
self :: SOURCE_TYPE_WEB => 'web' ,
self :: SOURCE_TYPE_API => 'api' ,
2018-07-14 03:23:37 +02:00
];
/**
2018-07-25 19:30:43 +02:00
* The user assigned to this thread ( assignedTo ) .
2018-07-14 03:23:37 +02:00
*/
public function user ()
{
return $this -> belongsTo ( 'App\User' );
}
2018-09-05 10:05:00 +02:00
/**
2018-09-07 15:08:34 +02:00
* The user assigned to this thread ( cached ) .
2018-09-05 10:05:00 +02:00
*/
public function user_cached ()
{
return $this -> user () -> rememberForever ();
}
2018-07-14 03:23:37 +02:00
/**
2018-07-25 19:30:43 +02:00
* Get the thread customer .
2018-07-14 03:23:37 +02:00
*/
public function customer ()
{
return $this -> belongsTo ( 'App\Customer' );
}
2018-09-07 15:08:34 +02:00
/**
* Get the thread customer ( cached ) .
*/
public function customer_cached ()
{
return $this -> customer () -> rememberForever ();
}
2018-07-14 03:23:37 +02:00
/**
2018-07-25 19:30:43 +02:00
* Get conversation .
2018-07-14 03:23:37 +02:00
*/
public function conversation ()
{
return $this -> belongsTo ( 'App\Conversation' );
}
2018-07-20 08:19:25 +02:00
2018-08-04 12:50:55 +02:00
/**
* Get thread attachmets .
*/
public function attachments ()
{
2018-08-15 08:10:05 +02:00
return $this -> hasMany ( 'App\Attachment' ) -> where ( 'embedded' , false );
//return $this->hasMany('App\Attachment');
}
/**
* Get thread embedded attachments .
*/
public function embeds ()
{
return $this -> hasMany ( 'App\Attachment' ) -> where ( 'embedded' , true );
2018-08-04 12:50:55 +02:00
}
2018-09-14 10:24:04 +02:00
/**
* All kinds of attachments including embedded .
*/
public function all_attachments ()
{
return $this -> hasMany ( 'App\Attachment' );
}
2018-07-20 08:19:25 +02:00
/**
2018-07-25 19:25:00 +02:00
* Get user who created the thread .
*/
public function created_by_user ()
{
return $this -> belongsTo ( 'App\User' );
}
2018-09-05 10:05:00 +02:00
/**
* Get user who created the thread ( cached )
*/
public function created_by_user_cached ()
{
2018-09-07 15:08:34 +02:00
return $this -> created_by_user () -> rememberForever ();
2018-09-05 10:05:00 +02:00
}
2018-07-25 19:25:00 +02:00
/**
* Get customer who created the thread .
*/
public function created_by_customer ()
{
return $this -> belongsTo ( 'App\Customer' );
}
2018-09-14 10:24:04 +02:00
/**
* Get user who edited draft .
*/
public function edited_by_user ()
{
return $this -> belongsTo ( 'App\User' );
}
2018-07-25 19:25:00 +02:00
/**
2018-07-25 19:30:43 +02:00
* Get sanitized body HTML .
*
2018-07-20 08:19:25 +02:00
* @ return string
*/
public function getCleanBody ()
{
2018-08-15 08:10:05 +02:00
$body = \Purifier :: clean ( $this -> body );
// Remove all kinds of spaces after tags
// https://stackoverflow.com/questions/3230623/filter-all-types-of-whitespace-in-php
$body = preg_replace ( " /^(.*)>[ \r \n ]* \ s+/mu " , '$1>' , $body );
return $body ;
2018-07-20 08:19:25 +02:00
}
/**
* Get thread recipients .
2018-07-25 19:30:43 +02:00
*
2018-07-20 08:19:25 +02:00
* @ return array
*/
2018-08-27 10:08:20 +02:00
public function getToArray ( $exclude_array = [])
2018-07-20 08:19:25 +02:00
{
if ( $this -> to ) {
2018-08-27 10:08:20 +02:00
$to_array = json_decode ( $this -> to );
if ( $to_array && $exclude_array ) {
$to_array = array_diff ( $to_array , $exclude_array );
}
return $to_array ;
2018-07-20 08:19:25 +02:00
} else {
return [];
}
}
2018-09-14 10:24:04 +02:00
public function getToString ( $exclude_array = [])
{
return implode ( ', ' , $this -> getToArray ( $exclude_array ));
}
/**
* Get first address from the To list .
*/
public function getToFirst ()
{
$to = $this -> getToArray ();
return array_shift ( $to );
}
2018-08-18 14:21:14 +02:00
/**
* Get type name .
*/
public function getTypeName ()
{
return self :: $types [ $this -> type ];
}
2018-07-20 08:19:25 +02:00
/**
* Get thread CC recipients .
2018-07-25 19:30:43 +02:00
*
2018-07-20 08:19:25 +02:00
* @ return array
*/
2018-08-27 10:08:20 +02:00
public function getCcArray ( $exclude_array = [])
2018-07-20 08:19:25 +02:00
{
if ( $this -> cc ) {
2018-08-27 10:08:20 +02:00
$cc_array = json_decode ( $this -> cc );
if ( $cc_array && $exclude_array ) {
$cc_array = array_diff ( $cc_array , $exclude_array );
}
return $cc_array ;
2018-07-20 08:19:25 +02:00
} else {
return [];
}
}
2018-09-14 10:24:04 +02:00
public function getCcString ( $exclude_array = [])
{
return implode ( ', ' , $this -> getCcArray ( $exclude_array ));
}
2018-07-20 08:19:25 +02:00
/**
* Get thread BCC recipients .
2018-07-25 19:30:43 +02:00
*
2018-07-20 08:19:25 +02:00
* @ return array
*/
2018-08-27 10:08:20 +02:00
public function getBccArray ( $exclude_array = [])
2018-07-20 08:19:25 +02:00
{
if ( $this -> bcc ) {
2018-08-27 10:08:20 +02:00
$bcc_array = json_decode ( $this -> bcc );
if ( $bcc_array && $exclude_array ) {
$bcc_array = array_diff ( $bcc_array , $exclude_array );
}
return $bcc_array ;
2018-07-20 08:19:25 +02:00
} else {
return [];
}
}
2018-09-14 10:24:04 +02:00
public function getBccString ( $exclude_array = [])
{
return implode ( ', ' , $this -> getBccArray ( $exclude_array ));
}
2018-07-30 17:11:35 +02:00
/**
* Set to as JSON .
*/
public function setTo ( $emails )
{
$emails_array = Conversation :: sanitizeEmails ( $emails );
if ( $emails_array ) {
2018-08-04 17:33:45 +02:00
$emails_array = array_unique ( $emails_array );
2018-07-30 17:11:35 +02:00
$this -> to = json_encode ( $emails_array );
} else {
$this -> to = null ;
}
}
public function setCc ( $emails )
{
$emails_array = Conversation :: sanitizeEmails ( $emails );
if ( $emails_array ) {
2018-08-04 17:33:45 +02:00
$emails_array = array_unique ( $emails_array );
2018-07-30 17:11:35 +02:00
$this -> cc = json_encode ( $emails_array );
} else {
$this -> cc = null ;
}
}
public function setBcc ( $emails )
{
$emails_array = Conversation :: sanitizeEmails ( $emails );
if ( $emails_array ) {
2018-08-04 17:33:45 +02:00
$emails_array = array_unique ( $emails_array );
2018-07-30 17:11:35 +02:00
$this -> bcc = json_encode ( $emails_array );
} else {
$this -> bcc = null ;
}
}
2018-07-20 08:19:25 +02:00
/**
2018-07-25 19:25:00 +02:00
* Get thread ' s status name .
2018-07-25 19:30:43 +02:00
*
2018-07-24 08:34:28 +02:00
* @ return string
2018-07-20 08:19:25 +02:00
*/
2018-07-25 19:25:00 +02:00
public function getStatusName ()
{
return self :: statusCodeToName ( $this -> status );
}
/**
* Get status name . Made as a function to allow status names translation .
2018-07-25 19:30:43 +02:00
*
* @ param int $status
*
* @ return string
2018-07-25 19:25:00 +02:00
*/
public static function statusCodeToName ( $status )
2018-07-20 08:19:25 +02:00
{
switch ( $status ) {
case self :: STATUS_ACTIVE :
2018-07-25 19:30:43 +02:00
return __ ( 'Active' );
2018-07-20 08:19:25 +02:00
break ;
case self :: STATUS_PENDING :
2018-07-25 19:30:43 +02:00
return __ ( 'Pending' );
2018-07-20 08:19:25 +02:00
break ;
case self :: STATUS_CLOSED :
2018-07-25 19:30:43 +02:00
return __ ( 'Closed' );
2018-07-20 08:19:25 +02:00
break ;
case self :: STATUS_SPAM :
2018-07-25 19:30:43 +02:00
return __ ( 'Spam' );
2018-07-20 08:19:25 +02:00
break ;
case self :: STATUS_NOCHANGE :
2018-07-25 19:30:43 +02:00
return __ ( 'Not changed' );
2018-07-20 08:19:25 +02:00
break ;
default :
return '' ;
break ;
}
}
2018-07-27 08:01:34 +02:00
/**
* Get text for the assignee .
2018-07-27 15:30:50 +02:00
*
2018-07-27 08:01:34 +02:00
* @ return string
*/
2018-09-10 17:24:20 +02:00
public function getAssigneeName ( $ucfirst = false , $user = null , $self = true )
2018-07-27 08:01:34 +02:00
{
if ( ! $this -> user_id ) {
2018-08-08 09:52:53 +02:00
if ( $ucfirst ) {
return __ ( 'Anyone' );
} else {
return __ ( 'anyone' );
}
2018-09-10 11:50:53 +02:00
} elseif (( $user && $this -> user_id == $user -> id ) || ( ! $user && auth () -> user () && $this -> user_id == auth () -> user () -> id )) {
2018-09-10 17:24:20 +02:00
// Not using mb_ucfirst to avoid possible problems with encoding
2018-08-08 09:52:53 +02:00
if ( $ucfirst ) {
2018-09-10 17:24:20 +02:00
if ( $self ) {
$name = __ ( 'Yourself' );
} else {
$name = __ ( 'You' );
}
2018-08-08 09:52:53 +02:00
} else {
2018-09-10 17:24:20 +02:00
if ( $self ) {
$name = __ ( 'yourself' );
} else {
$name = __ ( 'you' );
}
2018-08-08 09:52:53 +02:00
}
2018-09-10 17:24:20 +02:00
return $name ;
2018-07-27 08:01:34 +02:00
} else {
return $this -> user -> getFullName ();
}
}
2018-08-02 18:17:13 +02:00
/**
* Get user or customer who created the thead .
*/
public function getCreatedBy ()
{
if ( ! empty ( $this -> created_by_user_id )) {
return $this -> created_by_user ;
} else {
return $this -> created_by_customer ;
}
}
2018-09-07 15:08:34 +02:00
/**
* Get creator of the thread .
*/
public function getPerson ( $cached = false )
{
if ( $this -> type == Thread :: TYPE_CUSTOMER ) {
if ( $cached ) {
return $this -> customer_cached ;
} else {
return $this -> customer ;
}
} else {
if ( $cached ) {
return $this -> created_by_user_cached ;
} else {
return $this -> created_by_user ;
}
}
}
/**
* Description of what happened .
*/
public function getActionDescription ( $conversation_number , $escape = true )
{
$person = '' ;
$did_this = '' ;
// Person
if ( $this -> type == Thread :: TYPE_CUSTOMER ) {
$person = $this -> customer -> getFullName ( true );
2018-09-14 10:24:04 +02:00
} elseif ( $this -> state == Thread :: STATE_DRAFT && ! empty ( $this -> edited_by_user_id )) {
// Draft
if ( auth () -> user () && $this -> edited_by_user_id == auth () -> user () -> id ) {
$person = __ ( " you " );
} else {
$person = $this -> edited_by_user -> getFullName ();
}
2018-09-07 15:08:34 +02:00
} else {
2018-09-10 11:50:53 +02:00
if ( $this -> created_by_user_id && auth () -> user () && $this -> created_by_user_cached -> id == auth () -> user () -> id ) {
2018-09-07 15:08:34 +02:00
$person = __ ( " you " );
} else {
$person = $this -> created_by_user_cached -> getFullName ();
}
}
// Did this
if ( $this -> type == Thread :: TYPE_LINEITEM ) {
if ( $this -> action_type == Thread :: ACTION_TYPE_STATUS_CHANGED ) {
$did_this = __ ( " marked as :status_name conversation #:conversation_number " , [ 'status_name' => $this -> getStatusName (), 'conversation_number' => $conversation_number ]);
} elseif ( $this -> action_type == Thread :: ACTION_TYPE_USER_CHANGED ) {
2018-09-10 17:24:20 +02:00
$did_this = __ ( " assigned :assignee convsersation #:conversation_number " , [ 'assignee' => $this -> getAssigneeName ( false , null , false ), 'conversation_number' => $conversation_number ]);
2018-09-07 15:08:34 +02:00
} elseif ( $this -> action_type == Thread :: ACTION_TYPE_CUSTOMER_CHANGED ) {
$did_this = __ ( " changed the customer to :customer in conversation #:conversation_number " , [ 'customer' => $this -> customer -> getFullName ( true ), 'conversation_number' => $conversation_number ]);
}
2018-09-14 10:24:04 +02:00
} elseif ( $this -> state == Thread :: STATE_DRAFT ) {
if ( empty ( $this -> edited_by_user_id )) {
$did_this = __ ( " created a draft " );
} else {
$did_this = __ ( " edited :creator's draft " , [ 'creator' => $this -> created_by_user_cached -> getFirstName ()]);
}
2018-09-07 15:08:34 +02:00
} else {
if ( $this -> first ) {
$did_this = __ ( " started a new conversation #:conversation_number " , [ 'conversation_number' => $conversation_number ]);
} elseif ( $this -> type == Thread :: TYPE_NOTE ) {
$did_this = __ ( " added a note to conversation #:conversation_number " , [ 'conversation_number' => $conversation_number ]);
} else {
$did_this = __ ( " replied to conversation #:conversation_number " , [ 'conversation_number' => $conversation_number ]);
}
}
$description = ':person_tag_start:person:person_tag_end :did_this' ;
if ( $escape ) {
$description = htmlspecialchars ( $description );
}
return __ ( $description , [
'person' => $person ,
'person_tag_start' => '<strong>' ,
'person_tag_end' => '</strong>' ,
'did_this' => $did_this
]);
}
2018-09-14 10:24:04 +02:00
/**
* Get thread state name .
*/
public function getStateName ()
{
return self :: $states [ $this -> state ];
}
public function deleteThread ()
{
$this -> deteleAttachments ();
$this -> delete ();
}
/**
* Delete thread attachments .
*/
public function deteleAttachments ()
{
Attachment :: deleteByIds ( $this -> all_attachments () -> pluck ( 'id' ) -> toArray ());
}
2018-07-14 03:23:37 +02:00
}