1
0
mirror of https://github.com/freescout-helpdesk/freescout.git synced 2024-11-24 11:22:42 +01:00

Merge branch 'optimize'

This commit is contained in:
FreeScout 2018-11-02 05:38:01 -07:00
commit 1ca18a0e3b
22 changed files with 207 additions and 94 deletions

View File

@ -10,7 +10,7 @@
</div>
**FreeScout** is the super lightweight free open source help desk and shared inbox written in PHP7 (Laravel 5.5 framework) self hosted clone of HelpScout. Now you can enjoy [free Zendesk & Help Scout](https://freescout.net) without giving up privacy or locking you into a service you don't control.
**FreeScout** is the super lightweight free open source help desk and shared inbox written in PHP7 (Laravel 5.5 framework) self hosted clone of HelpScout. Now you can enjoy [free Zendesk & Help Scout](https://freescout.net) without giving up privacy or locking yourself into a service you don't control.
FreeScout has been developed from scratch and is not using any copyrighted Help Scout materials to comply with GitHub's [DMCA Policy](https://help.github.com/articles/dmca-takedown-policy/) (trademark, logos, icons, CSS, JS, HTML files are copyrighted; idea, look and feel cannot be copyrighted).

View File

@ -73,8 +73,7 @@ class ModuleInstall extends Command
$this->call('module:migrate "'.$module->getName().'"');
$this->createModulePublicSymlink($module);
}
$this->call('cache:clear');
$this->call('queue:restart');
$this->call('freescout:clear-cache');
}
public function createModulePublicSymlink($module)

View File

@ -551,6 +551,9 @@ class ConversationsController extends Controller
if ($attachments_info['has_attachments']) {
$thread->has_attachments = true;
}
if (!empty($request->saved_reply_id)) {
$thread->saved_reply_id = $request->saved_reply_id;
}
$thread->save();
// If thread has been created from draft, remove the draft

View File

@ -29,9 +29,9 @@ class MailboxesController extends Controller
*/
public function mailboxes()
{
$this->authorize('create', 'App\Mailbox');
$mailboxes = Mailbox::all();
//$this->authorize('create', 'App\Mailbox');
//$mailboxes = Mailbox::all();
$mailboxes = auth()->user()->mailboxesCanView();
return view('mailboxes/mailboxes', ['mailboxes' => $mailboxes]);
}
@ -88,11 +88,19 @@ class MailboxesController extends Controller
public function update($id)
{
$mailbox = Mailbox::findOrFail($id);
$this->authorize('update', $mailbox);
//$this->authorize('update', $mailbox);
if (!auth()->user()->can('update', $mailbox)) {
$accessible_route = \Eventy::filter('mailbox.accessible_settings_route', '', auth()->user(), $mailbox);
if ($accessible_route) {
return redirect()->route($accessible_route, ['id' => $mailbox->id]);
} else {
\Helper::denyAccess();
}
}
$mailboxes = Mailbox::all()->except($id);
//$mailboxes = Mailbox::all()->except($id);
return view('mailboxes/update', ['mailbox' => $mailbox, 'mailboxes' => $mailboxes, 'flashes' => $this->mailboxActiveWarning($mailbox)]);
return view('mailboxes/update', ['mailbox' => $mailbox, 'flashes' => $this->mailboxActiveWarning($mailbox)]);
}
/**

View File

@ -360,7 +360,7 @@ class UsersController extends Controller
if ($reset_result == Password::RESET_LINK_SENT) {
$response['status'] = 'success';
$response['success_msg'] = __('Password reset email has been sent');
$response['msg_success'] = __('Password reset email has been sent');
}
}
break;

View File

@ -641,4 +641,9 @@ class Helper
{
return 'Error: '.$e->getMessage().'; File: '.$e->getFile().' ('.$e->getLine().')';
}
public static function denyAccess()
{
abort(403, 'This action is unauthorized.');
}
}

View File

@ -60,7 +60,7 @@ class ConversationPolicy
if ($user->isAdmin()) {
return true;
} else {
return $user->hasPermission(User::USER_PERM_DELETE_CONVERSATIONS);
return $user->hasPermission(User::PERM_DELETE_CONVERSATIONS);
}
}
}

View File

@ -91,4 +91,20 @@ class UserPolicy
return false;
}
}
/**
* Determine whether the user can view mailboxes menu.
*
* @param \App\User $user
*
* @return mixed
*/
public function viewMailboxMenu(User $user)
{
if ($user->isAdmin() || \Eventy::filter('user.can_view_mailbox_menu', false, $user)) {
return true;
} else {
return false;
}
}
}

View File

@ -65,16 +65,16 @@ class User extends Authenticatable
/**
* Global user permissions.
*/
const USER_PERM_DELETE_CONVERSATIONS = 1;
const USER_PERM_EDIT_CONVERSATIONS = 2;
const USER_PERM_EDIT_SAVED_REPLIES = 3;
const USER_PERM_EDIT_TAGS = 4;
const PERM_DELETE_CONVERSATIONS = 1;
const PERM_EDIT_CONVERSATIONS = 2;
const PERM_EDIT_SAVED_REPLIES = 3;
const PERM_EDIT_TAGS = 4;
public static $user_permissions = [
self::USER_PERM_DELETE_CONVERSATIONS,
self::USER_PERM_EDIT_CONVERSATIONS,
self::USER_PERM_EDIT_SAVED_REPLIES,
self::USER_PERM_EDIT_TAGS,
self::PERM_DELETE_CONVERSATIONS,
self::PERM_EDIT_CONVERSATIONS,
self::PERM_EDIT_SAVED_REPLIES,
self::PERM_EDIT_TAGS,
];
const WEBSITE_NOTIFICATIONS_PAGE_SIZE = 25;
@ -361,10 +361,10 @@ class User extends Authenticatable
public static function getUserPermissionName($user_permission)
{
$user_permission_names = [
self::USER_PERM_DELETE_CONVERSATIONS => __('Users are allowed to delete notes/conversations'),
self::USER_PERM_EDIT_CONVERSATIONS => __('Users are allowed to edit notes/threads'),
self::USER_PERM_EDIT_SAVED_REPLIES => __('Users are allowed to edit/delete saved replies'),
self::USER_PERM_EDIT_TAGS => __('Users are allowed to manage tags'),
self::PERM_DELETE_CONVERSATIONS => __('Users are allowed to delete notes/conversations'),
self::PERM_EDIT_CONVERSATIONS => __('Users are allowed to edit notes/threads'),
self::PERM_EDIT_SAVED_REPLIES => __('Users are allowed to edit/delete saved replies'),
self::PERM_EDIT_TAGS => __('Users are allowed to manage tags'),
];
if (!empty($user_permission_names[$user_permission])) {

View File

@ -45,10 +45,6 @@ class CreateMailboxesTable extends Migration
$table->text('auto_reply_message')->nullable();
// todo
$table->boolean('office_hours_enabled')->default(false);
// todo: permissions
// todo: custom fields
// todo: saved replies
// todo: workflows
$table->boolean('ratings')->default(false);
$table->unsignedTinyInteger('ratings_placement')->default(Mailbox::RATINGS_PLACEMENT_ABOVE);
$table->text('ratings_text')->nullable();

View File

@ -1272,7 +1272,7 @@ h1,
}
h2,
.h2 {
font-size: 30px;
font-size: 24px;
}
h3,
.h3 {

10
public/css/style.css vendored
View File

@ -886,6 +886,12 @@ a h4 {
border-bottom: 1px solid #ddd;
color: #394956;
}
.note-toolbar .note-btn-group .note-btn {
border-radius: 0;
}
.note-toolbar .note-btn-group.open .note-btn {
border-color: #e6e6e6;
}
@media (max-width:390px) {
.note-bottom-div {
display: none;
@ -2817,11 +2823,11 @@ a.help-icon:hover {
top: -2px;
font-style: normal;
}
.accordion .panel-default > .panel-heading {
.accordion > .panel-default > .panel-heading {
background-color: #fff;
padding: 0;
}
.accordion .panel-default > .panel-heading:hover {
.accordion > .panel-default > .panel-heading:hover {
background-color: #f1f3f5;
}
.accordion-status {

143
public/js/main.js vendored
View File

@ -8,6 +8,11 @@ var fs_editor_change_timeout = -1;
var fs_keep_conversation_notes = 30; // days
var fs_draft_autosave_period = 12; // seconds
var fs_reply_changed = false;
var fs_conv_editor_buttons = {};
var fs_conv_editor_toolbar = [
['style', ['attachment', 'bold', 'italic', 'underline', 'ul', 'ol', 'link', 'picture', 'codeview']],
['actions', ['savedraft', 'discard']],
];
// Ajax based notifications;
var poly;
@ -75,21 +80,6 @@ var EditorAttachmentButton = function (context) {
return button.render(); // return button as jquery object
}
var EditorSavedRepliesButton = function (context) {
var ui = $.summernote.ui;
// create button
var button = ui.button({
contents: '<i class="glyphicon glyphicon-comment"></i>',
tooltip: Lang.get("messages.saved_replies"),
container: 'body',
click: function () {
alert('todo: implement saved replies');
}
});
return button.render(); // return button as jquery object
}
var EditorSaveDraftButton = function (context) {
var ui = $.summernote.ui;
@ -188,6 +178,7 @@ $(document).ready(function(){
polycastInit();
webNotificationsInit();
initAccordionHeading();
});
function triggersInit()
@ -202,7 +193,7 @@ function triggersInit()
// Modal windows
$('a[data-trigger="modal"]').click(function(e) {
showModal($(this));
triggerModal($(this));
e.preventDefault();
});
}
@ -256,9 +247,12 @@ function deleteMailboxModal(modal)
// https://stackoverflow.com/questions/21628222/summernote-image-upload
// https://www.kerneldev.com/2018/01/11/using-summernote-wysiwyg-editor-with-laravel/
// https://gist.github.com/abr4xas/22caf07326a81ecaaa195f97321da4ae
function summernoteInit(selector)
function summernoteInit(selector, new_options)
{
$(selector).summernote({
if (typeof(new_options) == "undefined") {
new_options = {};
}
options = {
minHeight: 120,
dialogsInBody: true,
disableResizeEditor: true,
@ -275,7 +269,7 @@ function summernoteInit(selector)
callbacks: {
onInit: function() {
// Remove statusbar
$('.note-statusbar').remove();
$(selector).parent().children().find('.note-statusbar').remove();
// Insert variables
$(selector).parent().children().find('.summernote-inservar:first').on('change', function(event) {
@ -284,7 +278,11 @@ function summernoteInit(selector)
});
}
}
});
};
$.extend(options, new_options);
$(selector).summernote(options);
}
function permissionsInit()
@ -818,23 +816,20 @@ function getGlobalAttr(attr)
// Initialize conversation body editor
function convEditorInit()
{
$.extend(fs_conv_editor_buttons, {
attachment: EditorAttachmentButton,
savedraft: EditorSaveDraftButton,
discard: EditorDiscardButton
});
$('#body').summernote({
minHeight: 120,
dialogsInBody: true,
dialogsFade: true,
disableResizeEditor: true,
followingToolbar: false,
toolbar: [
// [groupName, [list of button]]
['style', ['attachment', 'bold', 'italic', 'underline', 'ul', 'ol', 'link', 'picture', 'codeview', 'savedreplies']],
['actions', ['savedraft', 'discard']],
],
buttons: {
attachment: EditorAttachmentButton,
savedreplies: EditorSavedRepliesButton,
savedraft: EditorSaveDraftButton,
discard: EditorDiscardButton
},
toolbar: fs_conv_editor_toolbar,
buttons: fs_conv_editor_buttons,
callbacks: {
onImageUpload: function(files) {
if (!files) {
@ -1087,7 +1082,7 @@ function newConversationInit()
// After send
$('.after-send-change').click(function(e) {
showModal($(this));
triggerModal($(this));
});
// Send reply, new conversation or note
@ -1216,7 +1211,14 @@ function getQueryParam(name, qs) {
}
// Show bootstrap modal
function showModal(a, params)
function showModal(params)
{
triggerModal(null, params);
}
// Show bootstrap modal from link
// Use showModal instead of this
function triggerModal(a, params)
{
if (typeof(params) == "undefined") {
params = {};
@ -1227,6 +1229,7 @@ function showModal(a, params)
a = $(document.createElement('a'));
}
// Title
var title = a.attr('data-modal-title');
if (typeof(params.title) != "undefined") {
title = params.title;
@ -1237,10 +1240,16 @@ function showModal(a, params)
if (!title) {
title = a.text();
}
// Remote
var remote = a.attr('data-remote');
if (!remote) {
remote = a.attr('href');
}
if (typeof(params.remote) != "undefined") {
remote = params.remote;
}
var body = a.attr('data-modal-body');
if (typeof(params.body) != "undefined") {
body = params.body;
@ -1286,6 +1295,15 @@ function showModal(a, params)
modal_class = '';
}
// Convert bool to string
for (param in params) {
if (params[param] === true) {
params[param] = 'true';
} else if (params[param] === true) {
params[param] = 'false';
}
}
var html = [
'<div class="modal '+(params.no_fade == 'true' ? '' : 'fade')+'" tabindex="-1" role="dialog" aria-labelledby="jsmodal-label" aria-hidden="true">',
'<div class="modal-dialog '+modal_class+'">',
@ -1500,7 +1518,7 @@ function changeCustomerInit()
'</div>'+
'</div>';
showModal(null, {
triggerModal(null, {
body: confirm_html,
width_auto: 'true',
no_header: 'true',
@ -1536,6 +1554,26 @@ function changeCustomerInit()
});
}
// Show confirmation dialog
function showModalConfirm(text, ok_class, options, ok_text)
{
if (typeof(ok_text) == "undefined") {
ok_text = 'OK';
}
var confirm_html = '<div>'+
'<div class="text-center">'+
'<div class="text-larger margin-top-10">'+text+'</div>'+
'<div class="form-group margin-top">'+
'<button class="btn btn-primary '+ok_class+'">'+ok_text+'</button>'+
'<button class="btn btn-link" data-dismiss="modal">'+Lang.get("messages.cancel")+'</button>'+
'</div>'+
'</div>'+
'</div>';
showModalDialog(confirm_html, options);
}
// Show modal dialog
function showModalDialog(body, options)
{
var standard_options = {
@ -1551,7 +1589,7 @@ function showModalDialog(body, options)
}
options = Object.assign(standard_options, options);
showModal(null, options);
triggerModal(null, options);
}
/*function showSelect2Loader(input)
@ -1734,8 +1772,8 @@ function showAjaxResult(response)
loaderHide();
if (typeof(response.status) != "undefined" && response.status == 'success') {
if (typeof(response['success_msg']) != "undefined") {
showFloatingAlert('success', response['success_msg']);
if (typeof(response['msg_success']) != "undefined") {
showFloatingAlert('success', response['msg_success']);
}
} else {
showAjaxError(response);
@ -2172,6 +2210,11 @@ function hideReplyEditor()
$(".conv-action").removeClass('inactive');
}
function getReplyBody(text)
{
return $("#body").val();
}
function setReplyBody(text)
{
$(".conv-reply-block :input[name='body']:first").val(text);
@ -2381,9 +2424,33 @@ function stripTags(html)
return text;
}
function htmlDecode(input){
function htmlEscape(text)
{
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function htmlDecode(input)
{
var e = document.createElement('div');
e.innerHTML = input;
// handle case of empty input
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
// Change accordion heading background color on open
function initAccordionHeading()
{
$(".panel-default .collapse").on('shown.bs.collapse', function(e){
// change heading background when expanded
$(e.target).parent().children('.panel-heading:first').css('background-color', '#f1f3f5');
});
$(".collapse").on('hidden.bs.collapse', function(e){
// change heading background when hide
$(e.target).parent().children('.panel-heading:first').css('background-color', '');
});
}

View File

@ -98,6 +98,7 @@
{{ csrf_field() }}
<input type="hidden" name="conversation_id" value="{{ $conversation->id }}"/>
<input type="hidden" name="mailbox_id" value="{{ $mailbox->id }}"/>
<input type="hidden" name="saved_reply_id" value=""/>
{{-- For drafts --}}
<input type="hidden" name="thread_id" value=""/>
<input type="hidden" name="is_note" value=""/>
@ -158,6 +159,7 @@
</div>
<div class="clearfix"></div>
@include('conversations/editor_bottom_toolbar')
@action('reply_form.after', $conversation)
</div>
</div>
</div>

View File

@ -103,9 +103,11 @@
{{--<li><a href="#">{{ __('Apps') }} (todo)</a></li>--}}
<li class="{{ \App\Misc\Helper::menuSelectedHtml('settings') }}"><a href="{{ route('settings') }}">{{ __('Settings') }}</a></li>
{{--<li><a href="#">{{ __('Docs') }} (todo)</a></li>--}}
@endif
@if (Auth::user()->can('viewMailboxMenu', Auth::user()))
<li class="{{ \App\Misc\Helper::menuSelectedHtml('mailboxes') }}"><a href="{{ route('mailboxes') }}">{{ __('Mailboxes') }}</a></li>
@endif
<li class="{{ \App\Misc\Helper::menuSelectedHtml('tags') }}"><a href="#">{{ __('Tags') }} (todo)</a></li>
{{--<li class="{{ \App\Misc\Helper::menuSelectedHtml('tags') }}"><a href="#">{{ __('Tags') }} (todo)</a></li>--}}
@if (Auth::user()->isAdmin())
{{--<li><a href="#">{{ __('Teams') }} (todo)</a></li>--}}
<li class="{{ \App\Misc\Helper::menuSelectedHtml('users') }}"><a href="{{ route('users') }}">{{ __('Users') }}</a></li>

View File

@ -194,6 +194,7 @@
</div>
</div>
</div>
<hr class="clearfix"/>
</div>
<div class="form-group">

View File

@ -1,8 +1,10 @@
<li @if (Route::currentRouteName() == 'mailboxes.update')class="active"@endif><a href="{{ route('mailboxes.update', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-pencil"></i> {{ __('Edit Mailbox') }}</a></li>
<li @if (Route::currentRouteName() == 'mailboxes.connection' || Route::currentRouteName() == 'mailboxes.connection.incoming')class="active"@endif><a href="{{ route('mailboxes.connection', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-transfer"></i> {{ __('Connection Settings') }}</a></li>
<li @if (Route::currentRouteName() == 'mailboxes.permissions')class="active"@endif><a href="{{ route('mailboxes.permissions', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-ok"></i> {{ __('Permissions') }}</a></li>
{{--<li @if (Route::currentRouteName() == 'mailboxes.fields')class="active"@endif><a href="{{ route('mailboxes.update', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-list"></i> {{ __('Custom Fields') }} (todo)</a></li>--}}
<li @if (Route::currentRouteName() == 'mailboxes.auto_reply')class="active"@endif><a href="{{ route('mailboxes.auto_reply', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-share"></i> {{ __('Auto Reply') }}</a></li>
<li @if (Route::currentRouteName() == 'mailboxes.savedreplies')class="active"@endif><a href="{{ route('mailboxes.update', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-comment"></i> {{ __('Saved Replies') }} (todo)</a></li>
@if (Auth::user()->can('update', $mailbox))
<li @if (Route::currentRouteName() == 'mailboxes.update')class="active"@endif><a href="{{ route('mailboxes.update', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-pencil"></i> {{ __('Edit Mailbox') }}</a></li>
<li @if (Route::currentRouteName() == 'mailboxes.connection' || Route::currentRouteName() == 'mailboxes.connection.incoming')class="active"@endif><a href="{{ route('mailboxes.connection', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-transfer"></i> {{ __('Connection Settings') }}</a></li>
<li @if (Route::currentRouteName() == 'mailboxes.permissions')class="active"@endif><a href="{{ route('mailboxes.permissions', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-ok"></i> {{ __('Permissions') }}</a></li>
{{--<li @if (Route::currentRouteName() == 'mailboxes.fields')class="active"@endif><a href="{{ route('mailboxes.update', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-list"></i> {{ __('Custom Fields') }} (todo)</a></li>--}}
<li @if (Route::currentRouteName() == 'mailboxes.auto_reply')class="active"@endif><a href="{{ route('mailboxes.auto_reply', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-share"></i> {{ __('Auto Reply') }}</a></li>
@endif
@action('mailboxes.settings.menu', $mailbox)
{{--<li @if (Route::currentRouteName() == 'mailboxes.workflows')class="active"@endif><a href="{{ route('mailboxes.update', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-random"></i> {{ __('Workflows') }} (todo)</a></li>
<li @if (Route::currentRouteName() == 'mailboxes.ratings')class="active"@endif><a href="{{ route('mailboxes.update', ['id'=>$mailbox->id]) }}"><i class="glyphicon glyphicon-thumbs-up"></i> {{ __('Sat. Ratings') }} (todo)</a></li>--}}

View File

@ -1,11 +1,14 @@
<div class="dropdown sidebar-title">
@php
$menu_mailboxes = auth()->user()->mailboxesCanView();
@endphp
<span class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false" aria-haspopup="true" v-pre>
{{ $mailbox->name }} @if (isset($mailboxes) && count($mailboxes))<span class="caret"></span>@endif
{{ $mailbox->name }} @if (count($menu_mailboxes))<span class="caret"></span>@endif
</span>
@if (isset($mailboxes) && count($mailboxes))
@if (count($menu_mailboxes))
<ul class="dropdown-menu">
@foreach ($mailboxes as $mailbox_item)
<li @if ($mailbox_item->id == $mailbox->id)class="active"@endif><a href="{{ route('mailboxes.update', ['id'=>$mailbox_item->id]) }}">{{ $mailbox_item->name }}</a></li>
@foreach ($menu_mailboxes as $mailbox_item)
<li @if ($mailbox_item->id == $mailbox->id)class="active"@endif><a href="{{ route(Route::currentRouteName(), ['id'=>$mailbox_item->id]) }}">{{ $mailbox_item->name }}</a></li>
@endforeach
</ul>
@endif

View File

@ -24,7 +24,7 @@
@endforeach
</ul>
<div class="sidebar-buttons btn-group btn-group-justified">
@if (Auth::user()->can('update', $mailbox))
@if (Auth::user()->can('viewMailboxMenu', Auth::user()))
<div class="btn-group dropdown" data-toggle="tooltip" title="{{ __("Mailbox Settings") }}">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><i class="glyphicon glyphicon-cog"></i> <b class="caret"></b></a>
<ul class="dropdown-menu" role="menu">

View File

@ -1,5 +1,8 @@
<div class="empty-content">
<i class="glyphicon @if (!empty($icon))glyphicon-{{ $icon }}@else glyphicon-ok @endif"></i>
@if (!empty($empty_header))
<h2>{{ $empty_header }}</h2>
@endif
@if (!empty($empty_text))
<p>{{ $empty_text }}</p>
@endif

View File

@ -39,7 +39,7 @@
<a href="{{ route('conversations.create', ['mailbox_id' => $mailbox->id]) }}" class="glyphicon glyphicon-envelope" data-toggle="tooltip" title="{{ __("New Conversation") }}"></a>
</div>
@if (Auth::user()->can('update', $mailbox))
@if (Auth::user()->can('viewMailboxMenu', Auth::user()))
<div class="btn-group dropdown dropup" data-toggle="tooltip" title="{{ __("Mailbox Settings") }}">
<a class="glyphicon glyphicon-cog dropdown-toggle" data-toggle="dropdown" href="#"></a>
<ul class="dropdown-menu dropdown-menu-right" role="menu">

View File

@ -58,21 +58,21 @@ Route::get('/search', 'ConversationsController@search')->name('conversations.sea
Route::get('/conversation/undo-reply/{thread_id}', 'ConversationsController@undoReply')->name('conversations.undo');
// Mailboxes
Route::get('/settings/mailboxes', ['uses' => 'MailboxesController@mailboxes', 'laroute' => true])->name('mailboxes');
Route::get('/settings/mailbox-new', 'MailboxesController@create')->name('mailboxes.create');
Route::post('/settings/mailbox-new', 'MailboxesController@createSave');
Route::get('/settings/mailbox/{id}', 'MailboxesController@update')->name('mailboxes.update');
Route::post('/settings/mailbox/{id}', 'MailboxesController@updateSave');
Route::get('/settings/permissions/{id}', 'MailboxesController@permissions')->name('mailboxes.permissions');
Route::post('/settings/permissions/{id}', 'MailboxesController@permissionsSave');
Route::get('/mailboxes', ['uses' => 'MailboxesController@mailboxes', 'laroute' => true])->name('mailboxes');
Route::get('/mailbox/new', 'MailboxesController@create')->name('mailboxes.create');
Route::post('/mailbox/new', 'MailboxesController@createSave');
Route::get('/mailbox/settings/{id}', 'MailboxesController@update')->name('mailboxes.update');
Route::post('/mailbox/settings/{id}', 'MailboxesController@updateSave');
Route::get('/mailbox/permissions/{id}', 'MailboxesController@permissions')->name('mailboxes.permissions');
Route::post('/mailbox/permissions/{id}', 'MailboxesController@permissionsSave');
Route::get('/mailbox/{id}', 'MailboxesController@view')->name('mailboxes.view');
Route::get('/mailbox/{id}/{folder_id}', 'MailboxesController@view')->name('mailboxes.view.folder');
Route::get('/settings/connection-settings/{id}/outgoing', 'MailboxesController@connectionOutgoing')->name('mailboxes.connection');
Route::post('/settings/connection-settings/{id}/outgoing', 'MailboxesController@connectionOutgoingSave');
Route::get('/settings/connection-settings/{id}/incoming', 'MailboxesController@connectionIncoming')->name('mailboxes.connection.incoming');
Route::post('/settings/connection-settings/{id}/incoming', 'MailboxesController@connectionIncomingSave');
Route::get('/settings/mailbox/{id}/auto-reply', 'MailboxesController@autoReply')->name('mailboxes.auto_reply');
Route::post('/settings/mailbox/{id}/auto-reply', 'MailboxesController@autoReplySave');
Route::get('/mailbox/connection-settings/{id}/outgoing', 'MailboxesController@connectionOutgoing')->name('mailboxes.connection');
Route::post('/mailbox/connection-settings/{id}/outgoing', 'MailboxesController@connectionOutgoingSave');
Route::get('/mailbox/connection-settings/{id}/incoming', 'MailboxesController@connectionIncoming')->name('mailboxes.connection.incoming');
Route::post('/mailbox/connection-settings/{id}/incoming', 'MailboxesController@connectionIncomingSave');
Route::get('/mailbox/settings/{id}/auto-reply', 'MailboxesController@autoReply')->name('mailboxes.auto_reply');
Route::post('/mailbox/settings/{id}/auto-reply', 'MailboxesController@autoReplySave');
Route::post('/mailbox/ajax', ['uses' => 'MailboxesController@ajax', 'laroute' => true])->name('mailboxes.ajax');
// Customers