PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.0
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
← All changes | app/Http/Controllers/AgentController.php +166 -235 1.5.52.4.0 View file →
@@ -2,21 +2,14 @@
2 2
3 3 namespace FluentSupport\App\Http\Controllers;
4 4
5 5 use FluentSupport\App\Models\Agent;
6 -use FluentSupport\App\Models\Attachment;
7 -use FluentSupport\App\Models\Person;
8 -use FluentSupport\App\Models\Product;
9 -use FluentSupport\App\Models\Conversation;
10 -use FluentSupport\App\Models\Ticket;
11 -use FluentSupport\App\Modules\Reporting\Reporting;
12 6 use FluentSupport\App\Modules\StatModule;
7 +use FluentSupport\App\Services\AvatarUploder;
13 8 use FluentSupport\App\Services\Helper;
14 -use FluentSupport\App\Services\Includes\FileSystem;
15 -use FluentSupport\App\Services\TicketHelper;
16 -use FluentSupport\Framework\Request\Request;
9 +use FluentSupport\Framework\Http\Request\Request;
17 10 use FluentSupport\App\Modules\PermissionManager;
18 -use FluentSupport\Framework\Support\Arr;
11 +use FluentSupport\App\Http\Requests\AgentCreateRequest;
19 12
20 13 /**
21 14 * AgentController class for REST API
22 15 * This class is responsible for getting data for all request related to agent
@@ -26,319 +19,257 @@
26 19 * @version 1.0.0
27 20 */
28 21 class AgentController extends Controller
29 22 {
30 - public function index(Request $request)
23 + public function index(Request $request, Agent $agent)
31 24 {
32 - $agents = Agent::orderBy('id', 'DESC')
33 - ->searchBy($request->get('search'))
34 - ->paginate();
35 -
36 - foreach ($agents as $agent) {
37 - $agent->permissions = PermissionManager::getUserPermissions($agent->user_id);
38 - if ($agent->user_id) {
39 - $agent->user_profile = admin_url('user-edit.php?user_id=' . $agent->user_id);
40 - }
41 - $agent->replies_count = Conversation::where('person_id', $agent->id)->count();
42 - $agent->interactions_count = Conversation::where('person_id', $agent->id)->groupBy('ticket_id')->get()->count();
43 -
44 - $agent->telegram_chat_id = $agent->getMeta('telegram_chat_id');
45 - $agent->slack_user_id = $agent->getMeta('slack_user_id');
46 - $agent->whatsapp_number = $agent->getMeta('whatsapp_number');
47 - }
48 -
49 25 return [
50 - 'agents' => $agents,
51 - 'permissions' => PermissionManager::getReadablePermissionGroups()
26 + 'agents' => $agent->getAgents($request->getSafe('search', 'sanitize_text_field')),
27 + 'permissions' => PermissionManager::getReadablePermissionGroups(),
28 + 'businessBoxes' => PermissionManager::getMailboxesForRestriction(),
52 29 ];
53 30 }
54 31
55 32 /**
56 33 * addAgent method will add new agent in person table
57 - * @param Request $request
58 - * @return array
34 + * @param AgentCreateRequest $request
35 + * @return \WP_REST_Response | array
59 36 * @throws \FluentSupport\Framework\Validator\ValidationException
60 37 */
61 - public function addAgent(Request $request)
38 + public function addAgent(AgentCreateRequest $request, Agent $agent)
62 39 {
63 - $data = $request->all();
64 - $this->validate($data, [
65 - 'email' => 'required|email'
66 - ]);
40 + $data = $this->sanitizeAgentPayload($request, true);
67 41
68 - $email = $data['email'];
69 -
70 - $user = get_user_by('email', $email);
71 -
72 - if (!$user || is_wp_error($user)) {
42 + try {
43 + return [
44 + 'message' => __('Support Staff has been added', 'fluent-support'),
45 + 'agent' => $agent->createAgent($data)
46 + ];
47 + } catch (\Exception $e) {
73 48 return $this->sendError([
74 - 'message' => __('Sorry, Connected user could not be found with the provided email address', 'fluent-support')
49 + 'message' => Helper::getSafeErrorMessage($e)
75 50 ]);
76 51 }
52 + }
77 53
78 - if (empty($data['first_name'])) {
79 - $data['first_name'] = $user->first_name;
80 - }
54 + /**
55 + * updateAgent method will update the information of an exiting agent
56 + * @param AgentCreateRequest $request
57 + * @param Agent $agent
58 + * @param $agent_id
59 + * @return \WP_REST_Response | array
60 + * @throws \FluentSupport\Framework\Validator\ValidationException
61 + */
62 + public function updateAgent(AgentCreateRequest $request, Agent $agent, $agent_id)
63 + {
64 + $agent = $agent::findOrFail($agent_id);
65 + $data = $this->sanitizeAgentPayload($request);
81 66
82 - if (empty($data['last_name'])) {
83 - $data['last_name'] = $user->last_name;
67 + if (!$agent->user_id && ($user = get_user_by('email', $agent->email))) {
68 + $agent->user_id = $user->ID;
84 69 }
85 70
86 - // check if another agent has same email address
87 - $exist = Person::where('email', $email)->first();
88 - if ($exist) {
71 + try {
72 + return [
73 + 'message' => __('Support Staff has been updated', 'fluent-support'),
74 + 'agent' => $agent->updateAgent($data, $agent)
75 + ];
76 + } catch (\Exception $e) {
89 77 return $this->sendError([
90 - 'message' => __('Sorry, Another agent/person exist with the same email address. Please use a different email address', 'fluent-support')
78 + 'message' => Helper::getSafeErrorMessage($e)
91 79 ]);
92 80 }
93 -
94 - $data['user_id'] = $user->ID;
95 - $agent = Agent::create($data);
96 - PermissionManager::attachPermissions($user, Arr::get($data, 'permissions', []));
97 -
98 - if (!empty($data['telegram_chat_id'])) {
99 - $chatId = sanitize_text_field($data['telegram_chat_id']);
100 - $agent->updateMeta('telegram_chat_id', $chatId);
101 - $agent->telegram_chat_id = $chatId;
102 - }
103 -
104 - if (!empty($data['slack_user_id'])) {
105 - $chatId = sanitize_text_field($data['slack_user_id']);
106 - $agent->updateMeta('slack_user_id', $chatId);
107 - $agent->slack_user_id = $chatId;
108 - }
109 -
110 - if (!empty($data['whatsapp_number'])) {
111 - $whatsappNumber = sanitize_text_field($data['whatsapp_number']);
112 - $agent->updateMeta('whatsapp_number', $whatsappNumber);
113 - $agent->whatsapp_number = $whatsappNumber;
114 - }
115 -
116 - return [
117 - 'message' => __('Support Staff has been added', 'fluent-support'),
118 - 'agent' => $agent
119 - ];
120 81 }
121 82
122 83 /**
123 - * updateAgent method will update the information of an exiting agent
84 + * deleteAgent will delete an exiting agent and add an alternative agent as replacement
124 85 * @param Request $request
125 - * @param $agentId
126 - * @return array
86 + * @param Agent $agent
87 + * @param $agent_id
88 + * @return \WP_REST_Response | array
127 89 * @throws \FluentSupport\Framework\Validator\ValidationException
128 90 */
129 - public function updateAgent(Request $request, $agentId)
91 + public function deleteAgent(Request $request, Agent $agent, $agent_id)
130 92 {
131 - $agent = Agent::findOrFail($agentId);
132 - $data = $request->all();
133 - $this->validate($data, [
134 - 'email' => 'required|email',
135 - 'first_name' => 'required',
136 - 'last_name' => 'required',
137 - ]);
93 + try {
94 + $agent->deleteAgent($request->getSafe('fallback_agent_id', 'intval'), $agent_id);
138 95
139 - $user = get_user_by('ID', $agent->user_id);
140 -
141 - if (!$user || is_wp_error($user)) {
96 + return [
97 + 'message' => __('Support Staff has been deleted', 'fluent-support')
98 + ];
99 + } catch (\Exception $e) {
142 100 return $this->sendError([
143 - 'message' => __('Sorry, Connected user could not be found with the provided email address', 'fluent-support')
101 + 'message' => Helper::getSafeErrorMessage($e)
144 102 ]);
145 103 }
104 + }
146 105
147 - PermissionManager::attachPermissions($user, Arr::get($data, 'permissions', []));
106 + /**
107 + * @param Request $request
108 + * @return \WP_REST_Response | array
109 + */
110 + public function myStats(Request $request)
111 + {
112 + // Get logged in agent information.
113 + $agent = Helper::getAgentByUserId();
148 114
149 - $updateData = Arr::only($data, ['first_name', 'last_name', 'title']);
150 - $updateData['email'] = $user->user_email;
115 + try {
116 + // Get ticket statistics.
117 + $stats = StatModule::getAgentStat($agent->id);
118 + $with = $request->get('with', []);
119 + $with = is_array($with) ? map_deep($with, 'sanitize_text_field') : [];
151 120
152 - Agent::where('id', $agent->id)
153 - ->update($updateData);
121 + $response = (new Agent())->getAgentStat($stats, $with, $agent->id);
154 122
155 - PermissionManager::attachPermissions($user, Arr::get($data, 'permissions', []));
123 + if (defined('FLUENTSUPPORTPRO')) {
124 + $response['dashboard_notice'] = apply_filters('fluent_support/dashboard_notice', '', $agent);
125 + }
156 126
157 - $agent = Agent::findOrFail($agentId);
158 -
159 - if (isset($data['telegram_chat_id'])) {
160 - $chatId = sanitize_text_field($data['telegram_chat_id']);
161 - $agent->updateMeta('telegram_chat_id', $chatId);
162 - $agent->telegram_chat_id = $chatId;
127 + return $response;
128 + } catch (\Exception $e) {
129 + return $this->sendError([
130 + 'message' => Helper::getSafeErrorMessage($e)
131 + ]);
163 132 }
133 + }
164 134
165 - if (isset($data['slack_user_id'])) {
166 - $chatId = sanitize_text_field($data['slack_user_id']);
167 - $agent->updateMeta('slack_user_id', $chatId);
168 - $agent->slack_user_id = $chatId;
169 - }
135 + public function agentPerformance()
136 + {
137 + try {
138 + if (PermissionManager::currentUserCan('fst_agent_today_performance')) {
139 + $agentTodayStats = StatModule::getAgentTodayStats();
170 140
171 - if (isset($data['whatsapp_number'])) {
172 - $whatsappNumber = sanitize_text_field($data['whatsapp_number']);
173 - $agent->updateMeta('whatsapp_number', $whatsappNumber);
174 - $agent->whatsapp_number = $whatsappNumber;
141 + return ['agent_today_stats' => $agentTodayStats];
142 + }
143 + } catch (\Exception $e) {
144 + return $this->sendError([
145 + 'message' => Helper::getSafeErrorMessage($e)
146 + ]);
175 147 }
176 148
177 - return [
178 - 'message' => __('Support Staff has been updated', 'fluent-support'),
179 - 'agent' => $agent
180 - ];
149 + return [];
181 150 }
182 151
183 152 /**
184 - * deleteAgent will delete an exiting agent and add an alternative agent as replacement
153 + * addOrUpdateProfileImage method will upload profile picture for a given agent id
154 + * For a successful upload it's required to send file object, agent id and the user type(agent)
185 155 * @param Request $request
186 - * @param $agentId
187 - * @return array
156 + * @param AvatarUploder $avatarUploder
157 + * @return \WP_REST_Response | array
188 158 */
189 - public function deleteAgent(Request $request, $agentId)
159 + public function addOrUpdateProfileImage(Request $request, AvatarUploder $avatarUploder)
190 160 {
191 - $fallBackAgentId = $request->get('fallback_agent_id');
192 -
193 - if ($fallBackAgentId == $agentId) {
161 + try {
162 + return $avatarUploder->addOrUpdateProfileImage($request->files(), $request->getSafe('agent_id', 'intval'), 'agent');
163 + } catch (\Exception $e) {
194 164 return $this->sendError([
195 - 'message' => __('Old Agent and New agent is same person', 'fluent-support')
165 + 'message' => Helper::getSafeErrorMessage($e)
196 166 ]);
197 167 }
198 -
199 - $agent = Agent::findOrFail($agentId);
200 -
201 - PermissionManager::attachPermissions($agent->user_id, []);
202 -
203 - $newAgent = Agent::findOrFail($fallBackAgentId);
204 -
205 - Attachment::where('person_id', $agentId)->update([
206 - 'person_id' => $newAgent->id
207 - ]);
208 -
209 - Conversation::where('person_id', $agentId)->update([
210 - 'person_id' => $newAgent->id
211 - ]);
212 -
213 - Product::where('created_by', $agentId)->update([
214 - 'created_by' => $newAgent->id
215 - ]);
216 -
217 - Ticket::where('agent_id', $agentId)->update([
218 - 'agent_id' => $newAgent->id
219 - ]);
220 -
221 - $agent->deleteAllMeta();
222 -
223 - Agent::where('id', $agentId)->delete();
224 -
225 - return [
226 - 'message' => __('Selected support staff has been deleted', 'fluent-support')
227 - ];
228 -
229 168 }
230 169
231 170 /**
232 - * @param Request $request
171 + * resetAvatar method will restore a Support Staff avatar
172 + * For a successful upload it's required to send file object, Support Staff id and the user type(Support Staff)
173 + *
174 + * No Agent type-hint here: route-model binding resolves inside the
175 + * permission callback, before any policy runs, which lets unauthenticated
176 + * callers probe agent ID existence (FS-PERM-001). Resolve after auth.
177 + * @param int|string $agent
233 178 * @return array
234 179 */
235 - public function myStats(Request $request)
180 + public function resetAvatar($agent)
236 181 {
237 - $agent = Helper::getAgentByUserId();//Get logged in agent information
238 - //Get the statistics and responses by the agent
239 - $response = $this->getAgentStat($request, $agent->id);
182 + try {
183 + $agent = Agent::findOrFail((int) $agent);
184 + $agent->restoreAvatar();
240 185
241 - if (defined('FLUENTSUPPORTPRO')) {
242 - $response['dashboard_notice'] = apply_filters('fluent_support/dashboard_notice', '', $agent);
186 + return [
187 + 'message' => __('Support Staff avatar reset to gravatar default', 'fluent-support')
188 + ];
189 + } catch (\Exception $e) {
190 + return $this->sendError([
191 + 'message' => Helper::getSafeErrorMessage($e)
192 + ]);
243 193 }
194 + }
244 195
245 - return $response;
196 + public function getAgentInsights(Request $request, Agent $agent)
197 + {
198 + return [
199 + 'agents' => $agent->agentInsights($request->getSafe('search', 'sanitize_text_field')),
200 + ];
201 + }
246 202
203 + public function ping(Request $request)
204 + {
205 + return [
206 + 'ping' => 'pong',
207 + ];
247 208 }
248 209
249 210 /**
250 - * getAgentStat method will return ticket statistics by an agent id
211 + * Sanitize payload data for create/update operations.
251 212 *
252 - * @param Request $request
253 - * @param $agentId
213 + * @param AgentCreateRequest $request
214 + * @param bool $includeEmail
254 215 * @return array
255 216 */
256 - public function getAgentStat(Request $request, $agentId)
217 + private function sanitizeAgentPayload(AgentCreateRequest $request, $includeEmail = false)
257 218 {
258 - $agent = Agent::findOrFail($agentId);//Get agent information
259 - $stats = StatModule::getAgentStat($agentId);//Get ticket statistics
260 -
261 - //If the logged-in user has permission to manage unassigned tickets, get number of unassigned tickets
262 - if (PermissionManager::currentUserCan('fst_manage_unassigned_tickets')) {
263 - $stats['unassigned_tickets'] = [
264 - 'title' => __('Unassigned Tickets', 'fluent-support'),
265 - 'count' => Ticket::whereNull('agent_id')->where('status', '!=', 'closed')->count()
266 - ];
267 - }
268 -
269 219 $data = [
270 - 'stats' => $stats
220 + 'first_name' => $request->getSafe('first_name', 'sanitize_text_field'),
221 + 'last_name' => $request->getSafe('last_name', 'sanitize_text_field'),
222 + 'title' => $request->getSafe('title', 'sanitize_text_field'),
223 + 'permissions' => is_array($request->get('permissions')) ? array_map('sanitize_key', $request->get('permissions')) : [],
224 + 'telegram_chat_id' => $request->getSafe('telegram_chat_id', 'sanitize_text_field'),
225 + 'slack_user_id' => $request->getSafe('slack_user_id', 'sanitize_text_field'),
226 + 'whatsapp_number' => $request->getSafe('whatsapp_number', 'sanitize_text_field'),
227 + 'restrictions' => $this->sanitizeRestrictions($request->get('restrictions')),
271 228 ];
272 229
273 - $with = $request->get('with', []);
274 -
275 - //If the request come with suggested_tickets
276 - if (in_array('suggested_tickets', $with)) {
277 - //Get suggested tickets from ticketHelper
278 - $data['suggested_tickets'] = TicketHelper::getSuggestedTickets($agent->id);
230 + if ($request->has('agent_signature')) {
231 + // Already unslashed at the request boundary; sanitize only, so literal
232 + // backslashes in the signature survive.
233 + $data['agent_signature'] = wp_kses_post($request->get('agent_signature', ''));
279 234 }
280 235
281 - //If the request come with overall_stats
282 - if (in_array('overall_stats', $with)) {
283 - //Get overall status
284 - $data['overall_stats'] = (new Reporting())->getActiveStats();
236 + if ($request->has('agent_signature_enabled')) {
237 + $data['agent_signature_enabled'] = $request->get('agent_signature_enabled') === 'yes' ? 'yes' : 'no';
285 238 }
286 239
287 - //If the request come with individual_stat
288 - if (in_array('individual_stat', $with)) {
289 - //get overall statistics by agent id
290 - $data['individual_stat'] = (new Reporting())->getActiveStatByAgent($agent->id);
240 + if ($includeEmail) {
241 + $data['email'] = $request->getSafe('email', 'sanitize_email');
291 242 }
292 - //If the request come with my_overall_stats
293 - if (in_array('my_overall_stats', $with)) {
294 - //Get the overall statistics by the agent
295 - $data['my_overall_stats'] = StatModule::getAgentOverallStats($agent->id);
296 - }
297 243
298 244 return $data;
299 245 }
300 246
301 247 /**
302 - * addOrUpdateProfileImage method will upload profile picture for a given agent id
303 - * @param Request $request
248 + * Sanitize restrictions data for agent
249 + *
250 + * @param mixed $restrictions
304 251 * @return array
305 252 */
306 - public function addOrUpdateProfileImage(Request $request)
253 + private function sanitizeRestrictions($restrictions)
307 254 {
308 - $allowExtension = [
309 - 'jpeg', 'jpe', 'jpg', 'png'
310 - ];
311 -
312 - $agent_id = $request->get('agent_id');
313 - $file = $request->files();
314 -
315 - $ext = $file['file']->getClientOriginalExtension();
316 -
317 - if(!in_array($ext, $allowExtension)){
318 - return $this->sendError([
319 - 'message' => __('Unsupported file submitted, please select an image file', 'fluent-support')
320 - ]);
255 + if (!is_array($restrictions)) {
256 + return [
257 + 'restrictedBusinessBoxes' => [],
258 + 'businessBoxRestrictions' => false,
259 + ];
321 260 }
322 261
323 - $agent = Agent::findOrFail($agent_id);
262 + $restrictedBusinessBoxes = isset($restrictions['restrictedBusinessBoxes']) && is_array($restrictions['restrictedBusinessBoxes'])
263 + ? array_map('absint', $restrictions['restrictedBusinessBoxes'])
264 + : [];
324 265
325 - $uploadedImage = FileSystem::setSubDir('agents_avatars')->put($file);
266 + $businessBoxRestrictions = isset($restrictions['businessBoxRestrictions'])
267 + ? rest_sanitize_boolean($restrictions['businessBoxRestrictions'])
268 + : false;
326 269
327 - if($avatar = $uploadedImage[0]['url']){
328 - $agent->avatar = $avatar;
329 - $agent->save();
330 -
331 - return[
332 - 'message' => __('Profile picture has been updated successfully', 'fluent-support'),
333 - 'image' => $agent->avatar,
334 - 'agent' => $agent
335 - ];
336 - }
337 -
338 - else{
339 - return $this->sendError([
340 - 'message' => __('Something went wrong while updating the profile picture', 'fluent-support')
341 - ]);
342 - }
270 + return [
271 + 'restrictedBusinessBoxes' => $restrictedBusinessBoxes,
272 + 'businessBoxRestrictions' => $businessBoxRestrictions,
273 + ];
343 274 }
344 275 }