diff --git a/Http/Controllers/ConversationsController.php b/Http/Controllers/ConversationsController.php
new file mode 100644
index 0000000..fc59f26
--- /dev/null
+++ b/Http/Controllers/ConversationsController.php
@@ -0,0 +1,742 @@
+
+ */
+
+namespace Modules\MMFRestrictedCustomers\Http\Controllers;
+
+use Validator;
+
+use Illuminate\Http\Request;
+
+use App\Attachment;
+use App\Conversation;
+use App\Events\ConversationStatusChanged;
+use App\Events\ConversationUserChanged;
+use App\Events\UserAddedNote;
+use App\Events\UserCreatedConversation;
+use App\Events\UserReplied;
+use App\Mailbox;
+use App\MailboxUser;
+use App\Thread;
+
+use Modules\MMFRestrictedCustomers\Entities\Customer;
+
+use App\Http\Controllers\ConversationsController as BaseConversationsController;
+
+class ConversationsController extends BaseConversationsController {
+ /**
+ * Conversations ajax controller.
+ */
+ public function ajax(Request $request) {
+ // This override is only required for "send_reply" requests.
+ if ( $request->action != 'send_reply' )
+ return parent::ajax($request);
+
+ $response = [
+ 'status' => 'error',
+ 'msg' => '', // this is error message
+ ];
+
+ $user = auth()->user();
+
+ $mailbox = Mailbox::findOrFail($request->mailbox_id);
+
+ if (!$response['msg'] && !$user->can('view', $mailbox)) {
+ $response['msg'] = __('Not enough permissions');
+ }
+
+ $conversation = null;
+ if (!$response['msg'] && !empty($request->conversation_id)) {
+ $conversation = Conversation::find($request->conversation_id);
+ if ($conversation && !$user->can('view', $conversation)) {
+ $response['msg'] = __('Not enough permissions');
+ }
+ }
+ $new = false;
+ if (empty($request->conversation_id)) {
+ $new = true;
+ }
+
+ $is_note = false;
+ if (!empty($request->is_note)) {
+ $is_note = true;
+ }
+
+ // Conversation type.
+ $type = Conversation::TYPE_EMAIL;
+ if (!empty($request->type)) {
+ $type = (int)$request->type;
+ } elseif ($conversation) {
+ $type = $conversation->type;
+ }
+
+ $is_phone = false;
+ if ($type == Conversation::TYPE_PHONE) {
+ $is_phone = true;
+ }
+
+ $is_custom = false;
+ if ($type == Conversation::TYPE_CUSTOM) {
+ $is_custom = true;
+ }
+
+ $is_create = false;
+ if (!empty($request->is_create)) {
+ //if ($new || ($from_draft && $conversation->threads_count == 1)) {
+ $is_create = $request->is_create;
+ }
+
+ $is_forward = false;
+ if (!empty($request->subtype) && (int)$request->subtype == Thread::SUBTYPE_FORWARD) {
+ $is_forward = true;
+ }
+
+ $is_multiple = false;
+ if (!empty($request->multiple_conversations)) {
+ $is_multiple = true;
+ }
+
+ // If reply is being created from draft, there is already thread created
+ $thread = null;
+ $from_draft = false;
+ if (( ! $is_note || $is_phone || $is_custom ) && ! $response['msg'] && ! empty($request->thread_id)) {
+ $thread = Thread::find($request->thread_id);
+ if ($thread && (!$conversation || $thread->conversation_id != $conversation->id)) {
+ $response['msg'] = __('Incorrect thread');
+ } else {
+ $from_draft = true;
+ }
+ }
+
+ if (!$response['msg']) {
+ if ($thread && $from_draft && $thread->state == Thread::STATE_PUBLISHED) {
+ $response['msg'] = __('Message has been already sent. Please discard this draft.');
+ }
+ }
+
+ // Validate form
+ if (!$response['msg']) {
+ if ($new) {
+ if ($type == Conversation::TYPE_EMAIL) {
+ $validator = Validator::make($request->all(), [
+ 'to' => 'required|array',
+ 'subject' => 'required|string|max:998',
+ 'body' => 'required|string',
+ 'cc' => 'nullable|array',
+ 'bcc' => 'nullable|array',
+ ]);
+ } elseif ($type === Conversation::TYPE_PHONE) {
+ // Phone conversation.
+ $validator = Validator::make($request->all(), [
+ 'name' => 'required|string',
+ 'subject' => 'required|string|max:998',
+ 'body' => 'required|string',
+ 'phone' => 'nullable|string',
+ 'to_email' => 'nullable|string',
+ ]);
+ } elseif ($type === Conversation::TYPE_CUSTOM) {
+ $validation_rules = \Eventy::filter('conversation.custom.validation_rules', [
+ 'body' => 'required|string',
+ 'cc' => 'nullable|array',
+ 'bcc' => 'nullable|array',
+ ], $request);
+ $validator = Validator::make($request->all(), $validation_rules);
+ }
+ } else {
+ $validator = Validator::make($request->all(), [
+ 'body' => 'required|string',
+ 'cc' => 'nullable|array',
+ 'bcc' => 'nullable|array',
+ ]);
+ }
+
+ if ($validator->fails()) {
+ foreach ($validator->errors()->getMessages()as $errors) {
+ foreach ($errors as $field => $message) {
+ $response['msg'] .= $message.' ';
+ }
+ }
+ }
+ }
+
+ $body = $request->body;
+
+ // Replace base64 images with attachment URLs in case text
+ // was copy and pasted into the editor.
+ // https://github.com/freescout-helpdesk/freescout/issues/3057
+ $body = Thread::replaceBase64ImagesWithAttachments($body);
+
+ // List of emails.
+ $to_array = [];
+ if ($is_forward) {
+ $to_array = Conversation::sanitizeEmails($request->to_email);
+ } else {
+ $to_array = Conversation::sanitizeEmails($request->to);
+ }
+ // Check To
+ if (! $response['msg'] && $new && ! $is_phone && ! $is_custom) {
+ if (!$to_array) {
+ $response['msg'] .= __('Incorrect recipients');
+ }
+ }
+
+ // Check max. message size.
+ if (!$response['msg']) {
+
+ $max_message_size = (int)config('app.max_message_size');
+ if ($max_message_size) {
+ // Todo: take into account conversation history.
+ $message_size = mb_strlen($body, '8bit');
+
+ // Calculate attachments size.
+ $attachments_ids = array_merge($request->attachments ?? [], $request->embeds ?? []);
+ $attachments_ids = $this->decodeAttachmentsIds($attachments_ids);
+
+ if (count($attachments_ids)) {
+ $attachments_to_check = Attachment::select('size')->whereIn('id', $attachments_ids)->get();
+ foreach ($attachments_to_check as $attachment) {
+ $message_size += (int)$attachment->size;
+ }
+ }
+
+ if ($message_size > $max_message_size*1024*1024) {
+ $response['msg'] = __('Message is too large — :info. Please shorten your message or remove some attachments.', ['info' => __('Max. Message Size').': '.$max_message_size.' MB']);
+ }
+ }
+ }
+
+ if (!$response['msg']) {
+
+ // Get attachments info
+ // Delete removed attachments.
+ $attachments_info = $this->processReplyAttachments($request);
+
+ // Determine redirect.
+ // Must be done before updating current conversation's status or assignee.
+ // Redirect URL for new no saved yet conversation is determined below.
+ if (!$new) {
+ $response['redirect_url'] = $this->getRedirectUrl($request, $conversation, $user);
+ }
+
+ // Conversation
+ $now = date('Y-m-d H:i:s');
+ $status_changed = false;
+ $user_changed = false;
+ // Chat conversations in chat mode can not be undone.
+ $can_undo = true;
+
+ $request_status = (int)$request->status;
+
+ if ($new) {
+ // New conversation
+ $conversation = new Conversation();
+ $conversation->type = $type;
+ $conversation->subject = $request->subject;
+ $conversation->setPreview($body);
+ $conversation->mailbox_id = $request->mailbox_id;
+ $conversation->created_by_user_id = auth()->user()->id;
+ $conversation->source_via = Conversation::PERSON_USER;
+ $conversation->source_type = Conversation::SOURCE_TYPE_WEB;
+ } else {
+ // Reply or note
+ if ($request_status && $request_status != (int)$conversation->status) {
+ $status_changed = true;
+ }
+ if (!empty($request->subject)) {
+ $conversation->subject = $request->subject;
+ }
+ // When switching from regular message to phone and message sent
+ // without saving a draft type need to be saved here.
+ // Or vise versa.
+ if (($conversation->type == Conversation::TYPE_EMAIL && $type == Conversation::TYPE_PHONE)
+ || ($conversation->type == Conversation::TYPE_PHONE && $type == Conversation::TYPE_EMAIL)
+ ) {
+ $conversation->type = $type;
+ }
+ // Allow to convert phone conversations into email conversations.
+ if ($conversation->isPhone() && !$is_note && $conversation->customer
+ && $customer_email = $conversation->customer->getMainEmail()
+ ) {
+ $conversation->type = Conversation::TYPE_EMAIL;
+ $conversation->customer_email = $customer_email;
+ $is_phone = false;
+ }
+ }
+
+ if ($attachments_info['has_attachments']) {
+ $conversation->has_attachments = true;
+ }
+
+ // Customer can be empty in existing conversation if this is a draft.
+ $customer_email = '';
+ $customer = null;
+
+ if ($is_phone && $is_create) {
+ // Phone.
+ $phone_customer_data = $this->processPhoneCustomer($request);
+
+ $customer_email = $phone_customer_data['customer_email'];
+ $customer = $phone_customer_data['customer'];
+ if (! $conversation->customer_id) {
+ $conversation->customer_id = $customer->id;
+ }
+ } elseif ($is_custom) {
+ // No customer for custom conversations.
+ } else {
+ // Email or reply to a phone conversation.
+ if (!empty($to_array)) {
+ $customer_email = $to_array[0];
+ } elseif (!$conversation->customer_email
+ && ($conversation->isEmail() || $conversation->isPhone())
+ && $conversation->customer_id
+ && $conversation->customer
+ ) {
+ // When replying to a phone conversation, we need to
+ // set 'customer_email' for the conversation.
+ $customer_email = $conversation->customer->getMainEmail();
+ }
+ if (!$conversation->customer_id) {
+ // The new Customer must be linked to a specific Mailbox.
+ $data = [];
+ $data['mailbox_id'] = $conversation->mailbox_id;
+ $customer = Customer::create($customer_email, $data);
+ $conversation->customer_id = $customer->id;
+ } else {
+ $customer = $conversation->customer;
+ }
+ }
+ if ($customer_email && !$is_note && !$is_forward) {
+ $conversation->customer_email = $customer_email;
+ }
+
+ $prev_status = $conversation->status;
+
+ $conversation->status = $request_status ?: $conversation->status;
+
+ if (($prev_status != $conversation->status || $is_create)
+ && $conversation->status == Conversation::STATUS_CLOSED
+ ) {
+ $conversation->closed_by_user_id = $user->id;
+ $conversation->closed_at = date('Y-m-d H:i:s');
+ }
+
+ // We need to set state, as it may have been a draft.
+ $prev_state = $conversation->state;
+ $conversation->state = Conversation::STATE_PUBLISHED;
+
+ // Set assignee
+ $prev_user_id = $conversation->user_id;
+ if ((int) $request->user_id != -1) {
+ // Check if user has access to the current mailbox
+ if ((int) $conversation->user_id != (int) $request->user_id && $mailbox->userHasAccess($request->user_id)) {
+ $conversation->user_id = $request->user_id;
+ $user_changed = true;
+ }
+ } else {
+ $conversation->user_id = null;
+ }
+
+ // To is a single email string.
+ $to = '';
+ // List of emails.
+ $to_list = [];
+ if ($is_forward) {
+ if (empty($request->to_email[0])) {
+ $response['msg'] = __('Please specify a recipient.');
+ return \Response::json($response);
+ }
+ $to = $request->to_email[0];
+ } else {
+ if (!empty($request->to)) {
+ // When creating a new conversation, to is a list of emails.
+ if (is_array($request->to)) {
+ $to = $request->to[0];
+ } else {
+ $to = $request->to;
+ }
+ } else {
+ $to = $conversation->customer_email;
+ }
+ }
+
+ if (!$is_note && !$is_forward) {
+ // Save extra recipients to CC
+ if ($is_create && !$is_multiple && count($to_array) > 1) {
+ $conversation->setCc(array_merge(Conversation::sanitizeEmails($request->cc), $to_array));
+ } else {
+ if (!$is_multiple) {
+ $conversation->setCc(array_merge(Conversation::sanitizeEmails($request->cc), [$to]));
+ } else {
+ $conversation->setCc(Conversation::sanitizeEmails($request->cc));
+ }
+ }
+ $conversation->setBcc($request->bcc);
+ $conversation->last_reply_at = $now;
+ $conversation->last_reply_from = Conversation::PERSON_USER;
+ $conversation->user_updated_at = $now;
+ }
+ if ($conversation->isPhone() && $is_note) {
+ $conversation->last_reply_at = $now;
+ $conversation->last_reply_from = Conversation::PERSON_USER;
+ }
+ $conversation->updateFolder();
+ if ($from_draft) {
+ // Increment number of replies in conversation
+ $conversation->threads_count++;
+ // We need to set preview here as when conversation is created from draft,
+ // ThreadObserver::created() method is not called.
+ $conversation->setPreview($body);
+ }
+ $conversation->save();
+
+ // Redirect URL for new not saved yet conversation must be determined here.
+ if ($new) {
+ $response['redirect_url'] = $this->getRedirectUrl($request, $conversation, $user);
+ }
+
+ // Fire events
+ \Eventy::action('conversation.send_reply_save', $conversation, $request);
+
+ if (!$new) {
+ if ($status_changed) {
+ event(new ConversationStatusChanged($conversation));
+ \Eventy::action('conversation.status_changed', $conversation, $user, $changed_on_reply = true, $prev_status);
+ }
+ if ($user_changed) {
+ event(new ConversationUserChanged($conversation, $user));
+ \Eventy::action('conversation.user_changed', $conversation, $user, $prev_user_id);
+ }
+ }
+
+ if ($conversation->state != $prev_state) {
+ \Eventy::action('conversation.state_changed', $conversation, $user, $prev_state);
+ }
+
+ // Create thread
+ if (!$thread) {
+ $thread = new Thread();
+ $thread->conversation_id = $conversation->id;
+ if ($is_note || $is_forward) {
+ $thread->type = Thread::TYPE_NOTE;
+ } else {
+ $thread->type = Thread::TYPE_MESSAGE;
+ }
+ $thread->source_via = Thread::PERSON_USER;
+ $thread->source_type = Thread::SOURCE_TYPE_WEB;
+ } else {
+ if ($is_forward || $is_phone) {
+ $thread->type = Thread::TYPE_NOTE;
+ } else {
+ $thread->type = Thread::TYPE_MESSAGE;
+ }
+ $thread->created_at = $now;
+ }
+ if ($new) {
+ $thread->first = true;
+ }
+ $thread->user_id = $conversation->user_id;
+ $thread->status = $request_status ?? $conversation->status;
+ $thread->state = Thread::STATE_PUBLISHED;
+ if (!$is_custom) {
+ $thread->customer_id = $customer->id;
+ }
+ $thread->created_by_user_id = auth()->user()->id;
+ $thread->edited_by_user_id = null;
+ $thread->edited_at = null;
+ $thread->body = $body;
+ if ($is_create && !$is_multiple && count($to_array) > 1) {
+ $thread->setTo($to_array);
+ } else {
+ $thread->setTo($to);
+ }
+ // We save CC and BCC as is and filter emails when sending replies
+ $thread->setCc($request->cc);
+ $thread->setBcc($request->bcc);
+ if ($attachments_info['has_attachments'] && !$is_forward) {
+ $thread->has_attachments = true;
+ }
+ if (!empty($request->saved_reply_id)) {
+ $thread->saved_reply_id = $request->saved_reply_id;
+ }
+
+ $forwarded_conversations = [];
+ $forwarded_threads = [];
+
+ if ($is_forward) {
+ // Create forwarded conversations.
+ foreach ($to_array as $recipient_email) {
+ $forwarded_conversation = $conversation->replicate();
+ $forwarded_conversation->type = Conversation::TYPE_EMAIL;
+ $forwarded_conversation->setPreview($thread->body);
+ $forwarded_conversation->created_by_user_id = auth()->user()->id;
+ $forwarded_conversation->source_via = Conversation::PERSON_USER;
+ $forwarded_conversation->source_type = Conversation::SOURCE_TYPE_WEB;
+ $forwarded_conversation->threads_count = 0; // Counter will be incremented in ThreadObserver.
+ $forwarded_customer = Customer::create($recipient_email);
+ $forwarded_conversation->customer_id = $forwarded_customer->id;
+ // Reload customer object, otherwise it stores previous customer.
+ $forwarded_conversation->load('customer');
+ $forwarded_conversation->customer_email = $recipient_email;
+ $forwarded_conversation->subject = 'Fwd: '.$forwarded_conversation->subject;
+ //$forwarded_conversation->setCc(array_merge(Conversation::sanitizeEmails($request->cc), [$to]));
+ $forwarded_conversation->setCc(Conversation::sanitizeEmails($request->cc));
+ $forwarded_conversation->setBcc($request->bcc);
+ $forwarded_conversation->last_reply_at = $now;
+ $forwarded_conversation->last_reply_from = Conversation::PERSON_USER;
+ $forwarded_conversation->user_updated_at = $now;
+ if ($attachments_info['has_attachments']) {
+ $forwarded_conversation->has_attachments = true;
+ }
+ $forwarded_conversation->updateFolder();
+ $forwarded_conversation->save();
+
+ $forwarded_thread = $thread->replicate();
+
+ $forwarded_conversations[] = $forwarded_conversation;
+ $forwarded_threads[] = $forwarded_thread;
+ }
+
+ // Set forwarding meta data.
+ // todo: store array of numbers and IDs.
+ $thread->subtype = Thread::SUBTYPE_FORWARD;
+ $thread->setMeta(Thread::META_FORWARD_CHILD_CONVERSATION_NUMBER, $forwarded_conversation->number);
+ $thread->setMeta(Thread::META_FORWARD_CHILD_CONVERSATION_ID, $forwarded_conversation->id);
+ }
+
+ // Conversation history.
+ if (!empty($request->conv_history)) {
+ if ($request->conv_history != 'global') {
+ if ($is_forward && !empty($forwarded_threads)) {
+ foreach ($forwarded_threads as $forwarded_thread) {
+ $forwarded_thread->setMeta(Thread::META_CONVERSATION_HISTORY, $request->conv_history);
+ }
+ } else {
+ $thread->setMeta(Thread::META_CONVERSATION_HISTORY, $request->conv_history);
+ }
+ }
+ }
+
+ // From (mailbox alias).
+ if (!empty($request->from_alias)) {
+ $thread->from = $request->from_alias;
+ }
+
+ \Eventy::action('thread.before_save_from_request', $thread, $request);
+ $thread->save();
+
+ // Save forwarded thread.
+ if ($is_forward) {
+ foreach ($forwarded_conversations as $i => $forwarded_conversation) {
+ $forwarded_thread = $forwarded_threads[$i];
+
+ $forwarded_thread->conversation_id = $forwarded_conversation->id;
+ $forwarded_thread->type = Thread::TYPE_MESSAGE;
+ $forwarded_thread->subtype = null;
+ if ($attachments_info['has_attachments']) {
+ $forwarded_thread->has_attachments = true;
+ }
+ $forwarded_thread->setMeta(Thread::META_FORWARD_PARENT_CONVERSATION_NUMBER, $conversation->number);
+ $forwarded_thread->setMeta(Thread::META_FORWARD_PARENT_CONVERSATION_ID, $conversation->id);
+ $forwarded_thread->setMeta(Thread::META_FORWARD_PARENT_THREAD_ID, $thread->id);
+ \Eventy::action('send_reply.before_save_forwarded_thread', $forwarded_thread, $request);
+ $forwarded_thread->save();
+ }
+ }
+
+ // If thread has been created from draft, remove the draft
+ // if ($request->thread_id) {
+ // $draft_thread = Thread::find($request->thread_id);
+ // if ($draft_thread) {
+ // $draft_thread->delete();
+ // }
+ // }
+
+ if ($from_draft) {
+ // Remove conversation from drafts folder if needed
+ $conversation->maybeRemoveFromDrafts();
+ }
+
+ // Update folders counters
+ $conversation->mailbox->updateFoldersCounters();
+
+ $response['status'] = 'success';
+
+ // Set thread_id for uploaded attachments
+ if ($attachments_info['attachments']) {
+ if ($is_forward) {
+ // Copy attachments for each thread.
+ if (count($forwarded_threads) > 1) {
+ $attachments = Attachment::whereIn('id', $attachments_info['attachments'])->get();
+ }
+ foreach ($forwarded_threads as $i => $forwarded_thread) {
+ if ($i == 0) {
+ Attachment::whereIn('id', $attachments_info['attachments'])->update(['thread_id' => $forwarded_thread->id]);
+ } else {
+ foreach ($attachments as $attachment) {
+ $attachment->duplicate($forwarded_thread->id);
+ }
+ }
+ }
+ } else {
+ Attachment::whereIn('id', $attachments_info['attachments'])
+ ->where('thread_id', null)
+ ->update(['thread_id' => $thread->id]);
+ }
+ }
+
+ // Follow conversation if it's assigned to someone else.
+ if (!$is_create && !$new && !$is_forward && !$is_note
+ && $conversation->user_id != $user->id
+ ) {
+ $user->followConversation($conversation->id);
+ }
+
+ if ($conversation->isChat() && \Helper::isChatMode()) {
+ $can_undo = false;
+ }
+
+ // When user creates a new conversation it may be saved as draft first.
+ if ($is_create) {
+ // New conversation.
+ event(new UserCreatedConversation($conversation, $thread));
+ \Eventy::action('conversation.created_by_user_can_undo', $conversation, $thread);
+ // After Conversation::UNDO_TIMOUT period trigger final event.
+ \Helper::backgroundAction('conversation.created_by_user', [$conversation, $thread], now()->addSeconds($this->getUndoTimeout($can_undo)));
+ } elseif ($is_forward) {
+ // Forward.
+ // Notifications to users not sent.
+ event(new UserAddedNote($conversation, $thread));
+ foreach ($forwarded_conversations as $i => $forwarded_conversation) {
+ $forwarded_thread = $forwarded_threads[$i];
+
+ // To send email with forwarded conversation.
+ event(new UserReplied($forwarded_conversation, $forwarded_thread));
+ \Eventy::action('conversation.user_forwarded_can_undo', $conversation, $thread, $forwarded_conversation, $forwarded_thread);
+ // After Conversation::UNDO_TIMOUT period trigger final event.
+ \Helper::backgroundAction('conversation.user_forwarded', [$conversation, $thread, $forwarded_conversation, $forwarded_thread], now()->addSeconds(Conversation::UNDO_TIMOUT));
+ }
+ } elseif ($is_note) {
+ // Note.
+ event(new UserAddedNote($conversation, $thread));
+ \Eventy::action('conversation.note_added', $conversation, $thread);
+ } else {
+ // Reply.
+ event(new UserReplied($conversation, $thread));
+ \Eventy::action('conversation.user_replied_can_undo', $conversation, $thread);
+ // After Conversation::UNDO_TIMOUT period trigger final event.
+ \Helper::backgroundAction('conversation.user_replied', [$conversation, $thread], now()->addSeconds($this->getUndoTimeout($can_undo)));
+ }
+
+ // Send new conversation separately to each customer.
+ if ($is_create && count($to_array) > 1 && $is_multiple) {
+ $prev_customers_ids = [];
+ foreach ($to_array as $i => $customer_email) {
+ // Skip first email, as conversation has already been created for it.
+ if ($i == 0) {
+ continue;
+ }
+ // Get customer by email.
+ $customer_tmp = Customer::getByEmail($customer_email);
+ // Skip same customers.
+ if ($customer_tmp && in_array($customer_tmp->id, $prev_customers_ids)) {
+ continue;
+ }
+
+ if (!$customer_tmp) {
+ $customer_tmp = Customer::create($customer_email);
+ }
+
+ $prev_customers_ids[] = $customer_tmp->id;
+
+ // Copy conversation and thread.
+ $conversation_copy = $conversation->replicate();
+ $thread_copy = $thread->replicate();
+
+ // Save conversation.
+ $conversation_copy->threads_count = 0;
+ $conversation_copy->customer_id = $customer_tmp->id;
+ // Reload customer, otherwise all recipients will have the same name.
+ $conversation_copy->load('customer');
+ $conversation_copy->customer_email = $customer_email;
+ $conversation_copy->has_attachments = $conversation->has_attachments;
+ $conversation_copy->push();
+
+ $thread_copy->conversation_id = $conversation_copy->id;
+ $thread_copy->customer_id = $customer_tmp->id;
+ $thread_copy->has_attachments = $conversation->has_attachments;
+ $thread_copy->setTo($customer_email);
+ // Reload the conversation, otherwise Thread observer will be
+ // increasing threads_count for the first conversation.
+ $thread_copy->load('conversation');
+ $thread_copy->push();
+
+ // Copy attachments.
+ if (!empty($attachments_info['attachments'])) {
+ $attachments = Attachment::whereIn('id', $attachments_info['attachments'])->get();
+ foreach ($attachments as $attachment) {
+ $attachment->duplicate($thread_copy->id);
+ }
+ }
+
+ // Events.
+ // todo: allow to undo all emails
+ event(new UserCreatedConversation($conversation_copy, $thread_copy));
+ \Eventy::action('conversation.created_by_user_can_undo', $conversation_copy, $thread_copy);
+ // After Conversation::UNDO_TIMOUT period trigger final event.
+ \Helper::backgroundAction('conversation.created_by_user', [$conversation_copy, $thread_copy], now()->addSeconds($this->getUndoTimeout($can_undo)));
+ }
+ }
+
+ // Compose flash message.
+ $show_view_link = true;
+ if (!empty($request->after_send) && $request->after_send == MailboxUser::AFTER_SEND_STAY) {
+ $show_view_link = false;
+ }
+
+ $flash_vars = ['%tag_start%' => '', '%tag_end%' => '', '%view_start%' => ' ', '%a_end%' => ' ', '%undo_start%' => ' '];
+
+ if ($is_phone) {
+ $flash_type = 'warning';
+ if ($show_view_link) {
+ $flash_text = __(':%tag_start%Conversation created:%tag_end% :%view_start%View:%a_end% or :%undo_start%Undo:%a_end%', $flash_vars);
+ } else {
+ $flash_text = ''.__('Conversation created').'';
+ }
+ } elseif ($is_custom) {
+ $flash_type = 'warning';
+ $identifier = \Eventy::filter('conversation.custom.identifier', __('Custom conversation'), $request);
+ if ($show_view_link) {
+ $flash_text = __(':%tag_start%' . $identifier . ' added:%tag_end% :%view_start%View:%a_end%', $flash_vars);
+ } else {
+ $flash_text = ''.__('%identifier% added',['%identifier%'=>$identifier]).'';
+ }
+ } elseif ($is_note) {
+ $flash_type = 'warning';
+ if ($show_view_link) {
+ $flash_text = __(':%tag_start%Note added:%tag_end% :%view_start%View:%a_end%', $flash_vars);
+ } else {
+ $flash_text = ''.__('Note added').'';
+ }
+ } else {
+ $flash_type = 'success';
+ if ($show_view_link) {
+ $flash_text = __(':%tag_start%Email Sent:%tag_end% :%view_start%View:%a_end% or :%undo_start%Undo:%a_end%', $flash_vars);
+ } else {
+ $flash_text = __(':%tag_start%Email Sent:%tag_end% :%undo_start%Undo:%a_end%', $flash_vars);
+ }
+ }
+
+ if ($can_undo) {
+ \Session::flash('flash_'.$flash_type.'_floating', $flash_text);
+ }
+ }
+
+ if ($response['status'] == 'error' && empty($response['msg'])) {
+ $response['msg'] = 'Unknown error occurred';
+ }
+
+ return \Response::json($response);
+ }
+}
diff --git a/README.md b/README.md
index 2560024..9f90a9a 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,38 @@ Route::get('/customers/ajax-search', ['uses' => '\Modules\MMFRestrictedCustomers
Route::post('/customers/ajax', ['uses' => '\Modules\MMFRestrictedCustomers\Http\Controllers\CustomersController@ajax', 'laroute' => true])->name('customers.ajax');
```
+This other section:
+
+```php
+// Conversations
+Route::get('/conversation/{id}', ['uses' => 'ConversationsController@view', 'laroute' => true])->name('conversations.view');
+Route::post('/conversation/ajax', ['uses' => 'ConversationsController@ajax', 'laroute' => true])->name('conversations.ajax');
+Route::post('/conversation/upload', ['uses' => 'ConversationsController@upload', 'laroute' => true])->name('conversations.upload');
+Route::get('/mailbox/{mailbox_id}/new-ticket', 'ConversationsController@create')->name('conversations.create');
+Route::get('/mailbox/{mailbox_id}/clone-ticket/{from_thread_id}', 'ConversationsController@cloneConversation')->name('conversations.clone_conversation');
+//Route::get('/conversation/draft/{id}', 'ConversationsController@draft')->name('conversations.draft');
+Route::get('/conversation/ajax-html/{action}', ['uses' => 'ConversationsController@ajaxHtml', 'laroute' => true])->name('conversations.ajax_html');
+Route::get('/search', 'ConversationsController@search')->name('conversations.search');
+Route::get('/conversation/undo-reply/{thread_id}', 'ConversationsController@undoReply')->name('conversations.undo');
+Route::get('/mailbox/{mailbox_id}/chats', 'ConversationsController@chats')->name('conversations.chats');
+```
+
+should be replaced with:
+
+```php
+// Conversations
+Route::get('/conversation/{id}', ['uses' => 'ConversationsController@view', 'laroute' => true])->name('conversations.view');
+Route::post('/conversation/ajax', ['uses' => '\Modules\MMFRestrictedCustomers\Http\Controllers\ConversationsController@ajax', 'laroute' => true])->name('conversations.ajax');
+Route::post('/conversation/upload', ['uses' => 'ConversationsController@upload', 'laroute' => true])->name('conversations.upload');
+Route::get('/mailbox/{mailbox_id}/new-ticket', 'ConversationsController@create')->name('conversations.create');
+Route::get('/mailbox/{mailbox_id}/clone-ticket/{from_thread_id}', 'ConversationsController@cloneConversation')->name('conversations.clone_conversation');
+//Route::get('/conversation/draft/{id}', 'ConversationsController@draft')->name('conversations.draft');
+Route::get('/conversation/ajax-html/{action}', ['uses' => 'ConversationsController@ajaxHtml', 'laroute' => true])->name('conversations.ajax_html');
+Route::get('/search', 'ConversationsController@search')->name('conversations.search');
+Route::get('/conversation/undo-reply/{thread_id}', 'ConversationsController@undoReply')->name('conversations.undo');
+Route::get('/mailbox/{mailbox_id}/chats', 'ConversationsController@chats')->name('conversations.chats');
+```
+
### Edit the artisan commands
Console commands set in other modules or in Freescout itself can not be automatically overridden.