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 +202 -165 1.5.02.4.0 View file →
@@ -2,237 +2,274 @@
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\TicketHelper;
15 -use FluentSupport\Framework\Request\Request;
9 +use FluentSupport\Framework\Http\Request\Request;
16 10 use FluentSupport\App\Modules\PermissionManager;
17 -use FluentSupport\Framework\Support\Arr;
11 +use FluentSupport\App\Http\Requests\AgentCreateRequest;
18 12
13 +/**
14 + * AgentController class for REST API
15 + * This class is responsible for getting data for all request related to agent
16 + *
17 + * @package FluentSupport\App\Http\Controllers
18 + *
19 + * @version 1.0.0
20 + */
19 21 class AgentController extends Controller
20 22 {
21 - public function index(Request $request)
23 + public function index(Request $request, Agent $agent)
22 24 {
23 - $agents = Agent::orderBy('id', 'DESC')
24 - //->whereNotNull('user_id')
25 - ->searchBy($request->get('search'))
26 - ->paginate();
27 -
28 - foreach ($agents as $agent) {
29 - $agent->permissions = PermissionManager::getUserPermissions($agent->user_id);
30 - if ($agent->user_id) {
31 - $agent->user_profile = admin_url('user-edit.php?user_id=' . $agent->user_id);
32 - }
33 - $agent->replies_count = Conversation::where('person_id', $agent->id)->count();
34 - $agent->interactions_count = Conversation::where('person_id', $agent->id)->groupBy('ticket_id')->get()->count();
35 - $agent->telegram_chat_id = $agent->getMeta('telegram_chat_id');
36 - $agent->slack_user_id = $agent->getMeta('slack_user_id');
37 - }
38 -
39 25 return [
40 - 'agents' => $agents,
41 - 'permissions' => PermissionManager::getReadablePermissionGroups()
26 + 'agents' => $agent->getAgents($request->getSafe('search', 'sanitize_text_field')),
27 + 'permissions' => PermissionManager::getReadablePermissionGroups(),
28 + 'businessBoxes' => PermissionManager::getMailboxesForRestriction(),
42 29 ];
43 30 }
44 31
45 - public function addAgent(Request $request)
32 + /**
33 + * addAgent method will add new agent in person table
34 + * @param AgentCreateRequest $request
35 + * @return \WP_REST_Response | array
36 + * @throws \FluentSupport\Framework\Validator\ValidationException
37 + */
38 + public function addAgent(AgentCreateRequest $request, Agent $agent)
46 39 {
47 - $data = $request->all();
48 - $this->validate($data, [
49 - 'email' => 'required|email'
50 - ]);
40 + $data = $this->sanitizeAgentPayload($request, true);
51 41
52 - $email = $data['email'];
53 -
54 - $user = get_user_by('email', $email);
55 -
56 - 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) {
57 48 return $this->sendError([
58 - 'message' => __('Sorry, Connected user could not be found with the provided email address', 'fluent-support')
49 + 'message' => Helper::getSafeErrorMessage($e)
59 50 ]);
60 51 }
52 + }
61 53
62 - if (empty($data['first_name'])) {
63 - $data['first_name'] = $user->first_name;
64 - }
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);
65 66
66 - if (empty($data['last_name'])) {
67 - $data['last_name'] = $user->last_name;
67 + if (!$agent->user_id && ($user = get_user_by('email', $agent->email))) {
68 + $agent->user_id = $user->ID;
68 69 }
69 70
70 - // check if another agent has same email address
71 - $exist = Person::where('email', $email)->first();
72 - 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) {
73 77 return $this->sendError([
74 - 'message' => __('Sorry, Another agent/person exist with the same email address. Please use a different email address', 'fluent-support')
78 + 'message' => Helper::getSafeErrorMessage($e)
75 79 ]);
76 80 }
77 -
78 - $data['user_id'] = $user->ID;
79 - $agent = Agent::create($data);
80 - PermissionManager::attachPermissions($user, Arr::get($data, 'permissions', []));
81 -
82 - if(!empty($data['telegram_chat_id'])) {
83 - $chatId = sanitize_text_field($data['telegram_chat_id']);
84 - $agent->updateMeta('telegram_chat_id', $chatId);
85 - $agent->telegram_chat_id = $chatId;
86 - }
87 -
88 - if(!empty($data['slack_user_id'])) {
89 - $chatId = sanitize_text_field($data['slack_user_id']);
90 - $agent->updateMeta('slack_user_id', $chatId);
91 - $agent->slack_user_id = $chatId;
92 - }
93 -
94 - return [
95 - 'message' => __('Support Staff has been added', 'fluent-support'),
96 - 'agent' => $agent
97 - ];
98 81 }
99 82
100 - public function updateAgent(Request $request, $agentId)
83 + /**
84 + * deleteAgent will delete an exiting agent and add an alternative agent as replacement
85 + * @param Request $request
86 + * @param Agent $agent
87 + * @param $agent_id
88 + * @return \WP_REST_Response | array
89 + * @throws \FluentSupport\Framework\Validator\ValidationException
90 + */
91 + public function deleteAgent(Request $request, Agent $agent, $agent_id)
101 92 {
102 - $agent = Agent::findOrFail($agentId);
103 - $data = $request->all();
104 - $this->validate($data, [
105 - 'email' => 'required|email',
106 - 'first_name' => 'required',
107 - 'last_name' => 'required',
108 - ]);
93 + try {
94 + $agent->deleteAgent($request->getSafe('fallback_agent_id', 'intval'), $agent_id);
109 95
110 - $user = get_user_by('ID', $agent->user_id);
111 -
112 - if (!$user || is_wp_error($user)) {
96 + return [
97 + 'message' => __('Support Staff has been deleted', 'fluent-support')
98 + ];
99 + } catch (\Exception $e) {
113 100 return $this->sendError([
114 - 'message' => __('Sorry, Connected user could not be found with the provided email address', 'fluent-support')
101 + 'message' => Helper::getSafeErrorMessage($e)
115 102 ]);
116 103 }
104 + }
117 105
118 - 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();
119 114
120 - $updateData = Arr::only($data, ['first_name', 'last_name', 'title']);
121 - $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') : [];
122 120
123 - Agent::where('id', $agent->id)
124 - ->update($updateData);
121 + $response = (new Agent())->getAgentStat($stats, $with, $agent->id);
125 122
126 - PermissionManager::attachPermissions($user, Arr::get($data, 'permissions', []));
123 + if (defined('FLUENTSUPPORTPRO')) {
124 + $response['dashboard_notice'] = apply_filters('fluent_support/dashboard_notice', '', $agent);
125 + }
127 126
128 - $agent = Agent::findOrFail($agentId);
127 + return $response;
128 + } catch (\Exception $e) {
129 + return $this->sendError([
130 + 'message' => Helper::getSafeErrorMessage($e)
131 + ]);
132 + }
133 + }
129 134
130 - if(isset($data['telegram_chat_id'])) {
131 - $chatId = sanitize_text_field($data['telegram_chat_id']);
132 - $agent->updateMeta('telegram_chat_id', $chatId);
133 - $agent->telegram_chat_id = $chatId;
134 - }
135 + public function agentPerformance()
136 + {
137 + try {
138 + if (PermissionManager::currentUserCan('fst_agent_today_performance')) {
139 + $agentTodayStats = StatModule::getAgentTodayStats();
135 140
136 - if(isset($data['slack_user_id'])) {
137 - $chatId = sanitize_text_field($data['slack_user_id']);
138 - $agent->updateMeta('slack_user_id', $chatId);
139 - $agent->slack_user_id = $chatId;
141 + return ['agent_today_stats' => $agentTodayStats];
142 + }
143 + } catch (\Exception $e) {
144 + return $this->sendError([
145 + 'message' => Helper::getSafeErrorMessage($e)
146 + ]);
140 147 }
141 148
142 - return [
143 - 'message' => __('Support Staff has been updated', 'fluent-support'),
144 - 'agent' => $agent
145 - ];
149 + return [];
146 150 }
147 151
148 - public function deleteAgent(Request $request, $agentId)
152 + /**
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)
155 + * @param Request $request
156 + * @param AvatarUploder $avatarUploder
157 + * @return \WP_REST_Response | array
158 + */
159 + public function addOrUpdateProfileImage(Request $request, AvatarUploder $avatarUploder)
149 160 {
150 - $fallBackAgentId = $request->get('fallback_agent_id');
151 -
152 - if ($fallBackAgentId == $agentId) {
161 + try {
162 + return $avatarUploder->addOrUpdateProfileImage($request->files(), $request->getSafe('agent_id', 'intval'), 'agent');
163 + } catch (\Exception $e) {
153 164 return $this->sendError([
154 - 'message' => __('Old Agent and New agent is same person', 'fluent-support')
165 + 'message' => Helper::getSafeErrorMessage($e)
155 166 ]);
156 167 }
168 + }
157 169
158 - $agent = Agent::findOrFail($agentId);
170 + /**
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
178 + * @return array
179 + */
180 + public function resetAvatar($agent)
181 + {
182 + try {
183 + $agent = Agent::findOrFail((int) $agent);
184 + $agent->restoreAvatar();
159 185
160 - PermissionManager::attachPermissions($agent->user_id, []);
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 + ]);
193 + }
194 + }
161 195
162 - $newAgent = Agent::findOrFail($fallBackAgentId);
196 + public function getAgentInsights(Request $request, Agent $agent)
197 + {
198 + return [
199 + 'agents' => $agent->agentInsights($request->getSafe('search', 'sanitize_text_field')),
200 + ];
201 + }
163 202
164 - Attachment::where('person_id', $agentId)->update([
165 - 'person_id' => $newAgent->id
166 - ]);
167 -
168 - Conversation::where('person_id', $agentId)->update([
169 - 'person_id' => $newAgent->id
170 - ]);
171 -
172 - Product::where('created_by', $agentId)->update([
173 - 'created_by' => $newAgent->id
174 - ]);
175 -
176 - Ticket::where('agent_id', $agentId)->update([
177 - 'agent_id' => $newAgent->id
178 - ]);
179 -
180 - $agent->deleteAllMeta();
181 -
182 - Agent::where('id', $agentId)->delete();
183 -
203 + public function ping(Request $request)
204 + {
184 205 return [
185 - 'message' => __('Selected support staff has been deleted', 'fluent-support')
206 + 'ping' => 'pong',
186 207 ];
187 -
188 208 }
189 209
190 - public function myStats(Request $request)
210 + /**
211 + * Sanitize payload data for create/update operations.
212 + *
213 + * @param AgentCreateRequest $request
214 + * @param bool $includeEmail
215 + * @return array
216 + */
217 + private function sanitizeAgentPayload(AgentCreateRequest $request, $includeEmail = false)
191 218 {
192 - $agent = Helper::getAgentByUserId();
193 - $response = $this->getAgentStat($request, $agent->id);
219 + $data = [
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')),
228 + ];
194 229
195 - if(defined('FLUENTSUPPORTPRO')) {
196 - $response['dashboard_notice'] = apply_filters('fluent_support/dashboard_notice', '', $agent);
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', ''));
197 234 }
198 235
199 - return $response;
236 + if ($request->has('agent_signature_enabled')) {
237 + $data['agent_signature_enabled'] = $request->get('agent_signature_enabled') === 'yes' ? 'yes' : 'no';
238 + }
200 239
240 + if ($includeEmail) {
241 + $data['email'] = $request->getSafe('email', 'sanitize_email');
242 + }
243 +
244 + return $data;
201 245 }
202 246
203 - public function getAgentStat(Request $request, $agentId)
247 + /**
248 + * Sanitize restrictions data for agent
249 + *
250 + * @param mixed $restrictions
251 + * @return array
252 + */
253 + private function sanitizeRestrictions($restrictions)
204 254 {
205 - $agent = Agent::findOrFail($agentId);
206 - $stats = StatModule::getAgentStat($agentId);
207 -
208 - if (PermissionManager::currentUserCan('fst_manage_unassigned_tickets')) {
209 - $stats['unassigned_tickets'] = [
210 - 'title' => __('Unassigned Tickets', 'fluent-support'),
211 - 'count' => Ticket::whereNull('agent_id')->where('status', '!=', 'closed')->count()
255 + if (!is_array($restrictions)) {
256 + return [
257 + 'restrictedBusinessBoxes' => [],
258 + 'businessBoxRestrictions' => false,
212 259 ];
213 260 }
214 261
215 - $data = [
216 - 'stats' => $stats
217 - ];
262 + $restrictedBusinessBoxes = isset($restrictions['restrictedBusinessBoxes']) && is_array($restrictions['restrictedBusinessBoxes'])
263 + ? array_map('absint', $restrictions['restrictedBusinessBoxes'])
264 + : [];
218 265
219 - $with = $request->get('with', []);
266 + $businessBoxRestrictions = isset($restrictions['businessBoxRestrictions'])
267 + ? rest_sanitize_boolean($restrictions['businessBoxRestrictions'])
268 + : false;
220 269
221 - if (in_array('suggested_tickets', $with)) {
222 - $data['suggested_tickets'] = TicketHelper::getSuggestedTickets($agent->id);
223 - }
224 -
225 - if(in_array('overall_stats', $with)) {
226 - $data['overall_stats'] = (new Reporting())->getActiveStats();
227 - }
228 -
229 - if(in_array('individual_stat', $with)) {
230 - $data['individual_stat'] = (new Reporting())->getActiveStatByAgent($agent->id);
231 - }
232 - if(in_array('my_overall_stats', $with)){
233 - $data['my_overall_stats'] = StatModule::getAgentOverallStats($agent->id);
234 - }
235 -
236 - return $data;
270 + return [
271 + 'restrictedBusinessBoxes' => $restrictedBusinessBoxes,
272 + 'businessBoxRestrictions' => $businessBoxRestrictions,
273 + ];
237 274 }
238 275 }