From 5fad34c6b67dc310ed2773b8e559480b728a57ca Mon Sep 17 00:00:00 2001 From: Antoine Le Gonidec Date: Fri, 5 Jul 2024 17:46:34 +0200 Subject: [PATCH] Restrict the Customers shown in the search page --- routes/web.php | 7 +- src/Customer.php | 18 ++ .../Controllers/ConversationsController.php | 173 ++++++++++++++++++ src/Http/Controllers/CustomersController.php | 2 +- 4 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 src/Http/Controllers/ConversationsController.php diff --git a/routes/web.php b/routes/web.php index 386c705..244db90 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,18 +5,22 @@ */ use Illuminate\Support\Facades\Route; +use MMF\FreescoutRestrictedCustomers\Http\Controllers\ConversationsController; use MMF\FreescoutRestrictedCustomers\Http\Controllers\CrmController; use MMF\FreescoutRestrictedCustomers\Http\Controllers\CustomersController; // FIXME: Routes are not correctly exposed to the main application, // routes/web.php and Modules/Crm/Http/routes.php must be manually edited. +// Customers Route::get('/customers/{id}/edit', CustomersController::class . '@update')->name('customers.update'); Route::post('/customers/{id}/edit', CustomersController::class . '@updateSave'); Route::get('/customers/{id}/', CustomersController::class . '@conversations')->name('customers.conversations'); Route::get('/customers/ajax-search', ['uses' => CustomersController::class . '@ajaxSearch', 'laroute' => true])->name('customers.ajax_search'); Route::post('/customers/ajax', ['uses' => CustomersController::class . '@ajax', 'laroute' => true])->name('customers.ajax'); - +// Conversations +Route::get('/search', ConversationsController::class . '@search')->name('conversations.search'); +// Crm module Route::group([ 'roles' => ['user', 'admin'] ], function() { Route::get('/customers/new', CrmController::class . '@createCustomer')->name('freescout-restricted-customers.create_customer'); // The Crm module initialization will crash if no route named "crm.create_customer" is set. @@ -26,7 +30,6 @@ Route::group([ 'roles' => ['user', 'admin'] ], function() { Route::get('/customers/fields/ajax-search', ['uses' => CrmController::class . '@ajaxSearch', 'laroute' => true])->name('crm.ajax_search'); Route::post('/crm/ajax', ['uses' => CrmController::class . '@ajax', 'laroute' => true])->name('crm.ajax'); }); - Route::group([ 'roles' => ['admin'] ], function() { Route::post('/customers/export', ['uses' => CrmController::class . '@export'])->name('crm.export'); Route::post('/crm/ajax-admin', ['uses' => CrmController::class . '@ajaxAdmin', 'laroute' => true])->name('crm.ajax_admin'); diff --git a/src/Customer.php b/src/Customer.php index 23b116f..589ec5f 100644 --- a/src/Customer.php +++ b/src/Customer.php @@ -64,4 +64,22 @@ class Customer extends BaseCustomer { return parent::setData($data, $replace_data, $save); } + + /** + * Only return a Customer instance if it is available to the current User. + * + * @param int $id + * @param array $columns + * @return mixed|static + */ + public static function find($id, $columns = ['*']) { + // Get the list of Mailboxes the current User has access to. + $user = auth()->user(); + $mailboxes = $user->mailboxesIdsCanView(); + + $customer = self + ::where('id', '=', $id) + ->whereIn('mailbox_id', $mailboxes) + ->first($columns); + } } diff --git a/src/Http/Controllers/ConversationsController.php b/src/Http/Controllers/ConversationsController.php new file mode 100644 index 0000000..ff699b3 --- /dev/null +++ b/src/Http/Controllers/ConversationsController.php @@ -0,0 +1,173 @@ + + */ + +namespace MMF\FreescoutRestrictedCustomers\Http\Controllers; + +use App\Conversation; +use Illuminate\Http\Request; +use App\Http\Controllers\ConversationsController as BaseConversationsController; +use MMF\FreescoutRestrictedCustomers\Customer; + +class ConversationsController extends BaseConversationsController { + /** + * Search. + */ + public function search(Request $request) { + $user = auth()->user(); + $conversations = []; + $customers = []; + + $mode = $this->getSearchMode($request); + + // Search query + $q = $this->getSearchQuery($request); + + // Filters. + $filters = $this->getSearchFilters($request); + $filters_data = []; + // Modify filters is needed. + if (!empty($filters['customer'])) { + // Get customer name. + $filters_data['customer'] = Customer::find($filters['customer']); + } + //$filters = \Eventy::filter('search.filters', $filters, $filters_data, $mode, $q); + + // Remember recent query. + $recent_search_queries = session('recent_search_queries') ?? []; + if ($q && !in_array($q, $recent_search_queries)) { + array_unshift($recent_search_queries, $q); + $recent_search_queries = array_slice($recent_search_queries, 0, 4); + session()->put('recent_search_queries', $recent_search_queries); + } + + $conversations = []; + if (\Eventy::filter('search.is_needed', true, 'conversations')) { + $conversations = $this->searchQuery($user, $q, $filters); + } + + // Jump to the conversation if searching by conversation number. + if (count($conversations) == 1 + && $conversations[0]->number == $q + && empty($filters) + && !$request->x_embed + ) { + return redirect()->away($conversations[0]->url($conversations[0]->folder_id)); + } + + $customers = $this->searchCustomers($request, $user); + + // Dummy folder + $folder = $this->getSearchFolder($conversations); + + // List of available filters. + if ($mode == Conversation::SEARCH_MODE_CONV) { + $filters_list = \Eventy::filter('search.filters_list', Conversation::$search_filters, $mode, $filters, $q); + } else { + $filters_list = \Eventy::filter('search.filters_list_customers', Customer::$search_filters, $mode, $filters, $q); + } + + $mailboxes = \Cache::remember('search_filter_mailboxes_'.$user->id, 5, function () use ($user) { + return $user->mailboxesCanView(); + }); + $users = \Cache::remember('search_filter_users_'.$user->id, 5, function () use ($user, $mailboxes) { + return \Eventy::filter('search.assignees', $user->whichUsersCanView($mailboxes), $user, $mailboxes); + }); + $search_mailbox = null; + if (isset($filters['mailbox'])) { + $mailbox_id = (int)$filters['mailbox']; + if ($mailbox_id && in_array($mailbox_id, $mailboxes->pluck('id')->toArray())) { + foreach ($mailboxes as $mailbox_item) { + if ($mailbox_item->id == $mailbox_id) { + $search_mailbox = $mailbox_item; + break; + } + } + } + } elseif (count($mailboxes) == 1) { + $search_mailbox = $mailboxes[0]; + } + + return view('conversations/search', [ + 'folder' => $folder, + 'q' => $request->q, + 'filters' => $filters, + 'filters_list' => $filters_list, + 'filters_data' => $filters_data, + //'filters_list_all' => $filters_list_all, + 'mode' => $mode, + 'conversations' => $conversations, + 'customers' => $customers, + 'recent' => session('recent_search_queries'), + 'users' => $users, + 'mailboxes' => $mailboxes, + 'search_mailbox' => $search_mailbox, + ]); + } + + /** + * Search conversations. + */ + public function searchCustomers($request, $user) { + // Get IDs of mailboxes to which user has access + $mailbox_ids = $user->mailboxesIdsCanView(); + + // Filters + $filters = $this->getSearchFilters($request);; + + // Search query + $q = $this->getSearchQuery($request); + + // Like is case insensitive. + $like = '%'.mb_strtolower($q).'%'; + + // We need to use aggregate function for email to avoid "Grouping error" error in PostgreSQL. + $query_customers = Customer::select(['customers.*', \DB::raw('MAX(emails.email)')]) + ->groupby('customers.id') + ->leftJoin('emails', function ($join) { + $join->on('customers.id', '=', 'emails.customer_id'); + }) + ->where(function ($query) use ($like, $q) { + $like_op = 'like'; + if (\Helper::isPgSql()) { + $like_op = 'ilike'; + } + + $query + ->where('customers.first_name', $like_op, $like) + ->orwhere('customers.last_name', $like_op, $like) + ->orwhere('customers.company', $like_op, $like) + ->orwhere('customers.job_title', $like_op, $like) + ->orwhere('customers.websites', $like_op, $like) + ->orwhere('customers.social_profiles', $like_op, $like) + ->orwhere('customers.address', $like_op, $like) + ->orwhere('customers.city', $like_op, $like) + ->orwhere('customers.state', $like_op, $like) + ->orwhere('customers.zip', $like_op, $like) + ->orwhere('emails.email', $like_op, $like); + + $phone_numeric = \Helper::phoneToNumeric($q); + + if ($phone_numeric) { + $query->orWhere('customers.phones', $like_op, '%"'.$phone_numeric.'"%'); + } + + }) + // Restrict the query to the Customers the current User is allowed to access. + ->whereIn('customers.mailbox_id', $mailbox_ids); + + if (!empty($filters['mailbox']) && in_array($filters['mailbox'], $mailbox_ids)) { + $query_customers->join('conversations', function ($join) use ($filters) { + $join->on('conversations.customer_id', '=', 'customers.id'); + //$join->on('conversations.mailbox_id', '=', $filters['mailbox']); + }); + $query_customers->where('conversations.mailbox_id', '=', $filters['mailbox']); + } + + $query_customers = \Eventy::filter('search.customers.apply_filters', $query_customers, $filters, $q); + + return $query_customers->paginate(50); + } +} diff --git a/src/Http/Controllers/CustomersController.php b/src/Http/Controllers/CustomersController.php index 8fd02cf..53f994d 100644 --- a/src/Http/Controllers/CustomersController.php +++ b/src/Http/Controllers/CustomersController.php @@ -363,7 +363,7 @@ class CustomersController extends BaseCustomersController { // Conversations navigation case 'customers_pagination': - $customers = app('App\Http\Controllers\ConversationsController')->searchCustomers($request, $user); + $customers = app('MMF\FreescoutRestrictedCustomers\Http\Controllers\ConversationsController')->searchCustomers($request, $user); $response['status'] = 'success';