From 7c57764273fda351aae98ffd5fe18b59eb223b29 Mon Sep 17 00:00:00 2001 From: FreeScout Date: Tue, 30 Oct 2018 23:14:59 -0700 Subject: [PATCH] Optimize code --- README.md | 2 +- app/Console/Commands/ModuleInstall.php | 3 +- .../Controllers/ConversationsController.php | 3 + app/Http/Controllers/MailboxesController.php | 20 ++- app/Http/Controllers/UsersController.php | 2 +- app/Misc/Helper.php | 5 + app/Policies/ConversationPolicy.php | 2 +- app/Policies/UserPolicy.php | 16 ++ app/User.php | 24 +-- ...18_06_25_065719_create_mailboxes_table.php | 4 - public/css/bootstrap.css | 2 +- public/css/style.css | 10 +- public/js/main.js | 143 +++++++++++++----- resources/views/conversations/view.blade.php | 2 + resources/views/layouts/app.blade.php | 4 +- .../views/mailboxes/connection.blade.php | 1 + .../views/mailboxes/settings_menu.blade.php | 14 +- .../views/mailboxes/sidebar_menu.blade.php | 11 +- .../mailboxes/sidebar_menu_view.blade.php | 2 +- resources/views/partials/empty.blade.php | 3 + resources/views/secure/dashboard.blade.php | 2 +- routes/web.php | 26 ++-- 22 files changed, 207 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 1e0939b1..9ad3de56 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ -**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). diff --git a/app/Console/Commands/ModuleInstall.php b/app/Console/Commands/ModuleInstall.php index 8e9277b1..2a851364 100644 --- a/app/Console/Commands/ModuleInstall.php +++ b/app/Console/Commands/ModuleInstall.php @@ -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) diff --git a/app/Http/Controllers/ConversationsController.php b/app/Http/Controllers/ConversationsController.php index 70f4de42..a531dca7 100644 --- a/app/Http/Controllers/ConversationsController.php +++ b/app/Http/Controllers/ConversationsController.php @@ -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 diff --git a/app/Http/Controllers/MailboxesController.php b/app/Http/Controllers/MailboxesController.php index 1c2a9b70..438456a9 100644 --- a/app/Http/Controllers/MailboxesController.php +++ b/app/Http/Controllers/MailboxesController.php @@ -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)]); } /** diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php index 11453a96..d6e7339f 100644 --- a/app/Http/Controllers/UsersController.php +++ b/app/Http/Controllers/UsersController.php @@ -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; diff --git a/app/Misc/Helper.php b/app/Misc/Helper.php index 9e4f1b5b..7af17d78 100644 --- a/app/Misc/Helper.php +++ b/app/Misc/Helper.php @@ -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.'); + } } diff --git a/app/Policies/ConversationPolicy.php b/app/Policies/ConversationPolicy.php index af3b6b03..2645773e 100644 --- a/app/Policies/ConversationPolicy.php +++ b/app/Policies/ConversationPolicy.php @@ -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); } } } diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php index 9dda2a51..1d621bc8 100644 --- a/app/Policies/UserPolicy.php +++ b/app/Policies/UserPolicy.php @@ -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; + } + } } diff --git a/app/User.php b/app/User.php index 50a37d18..72e214d6 100644 --- a/app/User.php +++ b/app/User.php @@ -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])) { diff --git a/database/migrations/2018_06_25_065719_create_mailboxes_table.php b/database/migrations/2018_06_25_065719_create_mailboxes_table.php index b1375142..61f3927d 100644 --- a/database/migrations/2018_06_25_065719_create_mailboxes_table.php +++ b/database/migrations/2018_06_25_065719_create_mailboxes_table.php @@ -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(); diff --git a/public/css/bootstrap.css b/public/css/bootstrap.css index be1c5a97..66b9438a 100644 --- a/public/css/bootstrap.css +++ b/public/css/bootstrap.css @@ -1272,7 +1272,7 @@ h1, } h2, .h2 { - font-size: 30px; + font-size: 24px; } h3, .h3 { diff --git a/public/css/style.css b/public/css/style.css index 8ec0e3d9..6511df2a 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -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 { diff --git a/public/js/main.js b/public/js/main.js index 9c6f58bb..454472c7 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -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: '', - 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 = [ ''; - 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 = '
'+ + '
'+ + '
'+text+'
'+ + '
'+ + ''+ + ''+ + '
'+ + '
'+ + '
'; + + 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, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +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', ''); + }); } \ No newline at end of file diff --git a/resources/views/conversations/view.blade.php b/resources/views/conversations/view.blade.php index a97a0ec0..285747c0 100644 --- a/resources/views/conversations/view.blade.php +++ b/resources/views/conversations/view.blade.php @@ -98,6 +98,7 @@ {{ csrf_field() }} + {{-- For drafts --}} @@ -158,6 +159,7 @@
@include('conversations/editor_bottom_toolbar') + @action('reply_form.after', $conversation) diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 7771780e..1edf1858 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -103,9 +103,11 @@ {{--
  • {{ __('Apps') }} (todo)
  • --}}
  • {{ __('Settings') }}
  • {{--
  • {{ __('Docs') }} (todo)
  • --}} + @endif + @if (Auth::user()->can('viewMailboxMenu', Auth::user()))
  • {{ __('Mailboxes') }}
  • @endif -
  • {{ __('Tags') }} (todo)
  • + {{--
  • {{ __('Tags') }} (todo)
  • --}} @if (Auth::user()->isAdmin()) {{--
  • {{ __('Teams') }} (todo)
  • --}}
  • {{ __('Users') }}
  • diff --git a/resources/views/mailboxes/connection.blade.php b/resources/views/mailboxes/connection.blade.php index 959f7563..db7b0654 100644 --- a/resources/views/mailboxes/connection.blade.php +++ b/resources/views/mailboxes/connection.blade.php @@ -194,6 +194,7 @@ +
    diff --git a/resources/views/mailboxes/settings_menu.blade.php b/resources/views/mailboxes/settings_menu.blade.php index 5af17b7d..ecf163ff 100644 --- a/resources/views/mailboxes/settings_menu.blade.php +++ b/resources/views/mailboxes/settings_menu.blade.php @@ -1,8 +1,10 @@ -
  • {{ __('Edit Mailbox') }}
  • -
  • {{ __('Connection Settings') }}
  • -
  • {{ __('Permissions') }}
  • -{{--
  • {{ __('Custom Fields') }} (todo)
  • --}} -
  • {{ __('Auto Reply') }}
  • -
  • {{ __('Saved Replies') }} (todo)
  • +@if (Auth::user()->can('update', $mailbox)) +
  • {{ __('Edit Mailbox') }}
  • +
  • {{ __('Connection Settings') }}
  • +
  • {{ __('Permissions') }}
  • + {{--
  • {{ __('Custom Fields') }} (todo)
  • --}} +
  • {{ __('Auto Reply') }}
  • +@endif +@action('mailboxes.settings.menu', $mailbox) {{--
  • {{ __('Workflows') }} (todo)
  • {{ __('Sat. Ratings') }} (todo)
  • --}} \ No newline at end of file diff --git a/resources/views/mailboxes/sidebar_menu.blade.php b/resources/views/mailboxes/sidebar_menu.blade.php index 41be95af..449554ae 100644 --- a/resources/views/mailboxes/sidebar_menu.blade.php +++ b/resources/views/mailboxes/sidebar_menu.blade.php @@ -1,11 +1,14 @@