PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.21
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.21
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
fluent-booking / app / Services / Integrations / FluentBoards / Bootstrap.php

Bootstrap.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.5.21, at app/Services/Integrations/FluentBoards/Bootstrap.php

457 lines 17.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\App\Services\Integrations\FluentBoards;
4
5 use FluentBooking\App\App;
6 use FluentBoards\App\Models\Label;
7 use FluentBoards\App\Models\Stage;
8 use FluentBoards\App\Models\Task;
9 use FluentBoards\App\Models\Board;
10 use FluentBoards\App\Models\User;
11 use FluentBoards\App\Services\Constant;
12 use FluentBoards\App\Services\NotificationService;
13 use FluentBoards\App\Services\TaskService;
14 use FluentBooking\Framework\Support\Arr;
15 use FluentBooking\App\Http\Controllers\IntegrationManagerController;
16
17 class Bootstrap extends IntegrationManagerController
18 {
19 public $hasGlobalMenu = false;
20
21 public $disableGlobalSettings = 'yes';
22
23 public function __construct()
24 {
25 parent::__construct(
26 __('FluentBoards', 'fluent-booking'),
27 'fluentboards',
28 'fluent_booking_fluentboards_configurations',
29 'fluentboards_feeds',
30 10
31 );
32
33 $this->logo = App::getInstance('url.assets') . 'images/fluentboards.png';
34
35 $this->description = __('Connect FluentBoards with Fluent Booking and create tasks with booking fields.', 'fluent-booking');
36
37 $this->registerAdminHooks();
38 }
39
40 public function pushIntegration($integrations, $calendarEventId)
41 {
42 $integrations[$this->integrationKey] = [
43 'title' => $this->title . ' ' . __('Integration', 'fluent-booking'),
44 'logo' => $this->logo,
45 'is_active' => $this->isConfigured(),
46 'configure_title' => __('Configuration required!', 'fluent-booking'),
47 'global_configure_url' => '#',
48 'configure_message' => __('FluentBoards is not configured yet! Please configure your FluentBoards api first', 'fluent-booking'),
49 'configure_button_text' => __('Set FluentBoards', 'fluent-booking'),
50 ];
51
52 return $integrations;
53 }
54
55 public function isConfigured()
56 {
57 return true;
58 }
59
60 public function isEnabled()
61 {
62 return true;
63 }
64
65 public function getIntegrationDefaults($settings, $calendarEventId)
66 {
67 return [
68 'name' => '',
69 'board_config' => [
70 'board_id' => '',
71 'stage_id' => '',
72 'label_ids' => '',
73 'member_ids' => [],
74 'priority' => 'low'
75 ],
76 'task_title' => '',
77 'author_name' => '',
78 'email' => '',
79 'description' => '',
80 'position' => 'bottom',
81 'due_at_type' => 'booking',
82 'due_at_days' => 0,
83 'enabled' => true
84 ];
85 }
86
87 public function getSettingsFields($settings, $calendarEventId)
88 {
89 $fields = [
90 [
91 'key' => 'name',
92 'label' => __('Feed Name', 'fluent-booking'),
93 'required' => true,
94 'placeholder' => __('Your Feed Name', 'fluent-booking'),
95 'component' => 'text',
96 ],
97 [
98 'key' => 'board_config',
99 'label' => __('Fluent Boards Configuration', 'fluent-booking'),
100 'required' => true,
101 'component' => 'chained_select',
102 'primary_key' => 'board_id',
103 'fields_options' => [
104 'board_id' => [],
105 'stage_id' => [],
106 'label_ids' => [],
107 'member_ids' => [],
108 'priority' => []
109 ],
110 'options_labels' => [
111 'board_id' => [
112 'label' => __('Select Board', 'fluent-booking'),
113 'type' => 'select',
114 'placeholder' => __('Select Board', 'fluent-booking')
115 ],
116 'stage_id' => [
117 'label' => __('Select Stage', 'fluent-booking'),
118 'type' => 'select',
119 'placeholder' => __('Select Stage', 'fluent-booking')
120 ],
121 'label_ids' => [
122 'label' => __('Select Labels', 'fluent-booking'),
123 'type' => 'multi-select',
124 'placeholder' => __('Select Labels', 'fluent-booking')
125 ],
126 'member_ids' => [
127 'label' => __('Select Assignees', 'fluent-booking'),
128 'type' => 'multi-select',
129 'placeholder' => __('Select Assignees', 'fluent-booking')
130 ],
131 'priority' => [
132 'label' => __('Select Priority', 'fluent-booking'),
133 'type' => 'select',
134 'placeholder' => __('Priority', 'fluent-booking')
135 ]
136 ]
137 ],
138 [
139 'key' => 'task_title',
140 'label' => __('Task Title', 'fluent-booking'),
141 'required' => true,
142 'placeholder' => __('Task Title', 'fluent-booking'),
143 'component' => 'value_text'
144 ],
145 [
146 'key' => 'description',
147 'label' => __('Description', 'fluent-booking'),
148 'required' => false,
149 'placeholder' => __('Describe your task', 'fluent-booking'),
150 'component' => 'wp_editor',
151 ],
152 [
153 'key' => 'author_name',
154 'label' => __('Submitter Name', 'fluent-booking'),
155 'required' => true,
156 'placeholder' => __('Submitter Name', 'fluent-booking'),
157 'component' => 'value_text'
158 ],
159 [
160 'key' => 'email',
161 'label' => __('Submitter Email', 'fluent-booking'),
162 'required' => true,
163 'placeholder' => __('Submitter Email', 'fluent-booking'),
164 'component' => 'value_text'
165 ],
166 [
167 'key' => 'due_at_type',
168 'label' => __('Due Type', 'fluent-booking'),
169 'tips' => __('Choose “Booking Date” to set the due date relative to when the booking was made or “Meeting Date” to set it relative to the scheduled meeting date.', 'fluent-booking'),
170 'component' => 'radio_choice',
171 'options' => [
172 'booking' => __('Booking Date', 'fluent-booking'),
173 'meeting' => __('Meeting Date', 'fluent-booking')
174 ]
175 ],
176 [
177 'key' => 'due_at_days',
178 'label' => __('Due Date', 'fluent-booking'),
179 'tips' => __('Set the due date by entering a number relative to the booking or meeting date. Positive for days after and negative for days before the booking or meeting date.', 'fluent-booking'),
180 'component' => 'number'
181 ],
182 [
183 'key' => 'position',
184 'label' => __('Task Position', 'fluent-booking'),
185 'placeholder' => __('Position', 'fluent-booking'),
186 'component' => 'radio_choice',
187 'options' => [
188 'bottom' => __('Bottom', 'fluent-booking'),
189 'top' => __('Top', 'fluent-booking')
190 ]
191 ],
192 [
193 'require_list' => false,
194 'required' => true,
195 'key' => 'event_trigger',
196 'options' => $this->getEventTriggerOptions(),
197 'tips' => __('Select in which booking stage you want to trigger this feed', 'fluent-booking'),
198 'label' => __('Event Trigger', 'fluent-booking'),
199 'component' => 'checkbox-multiple-text',
200 'checkbox_label' => __('Event Trigger For This Feed', 'fluent-booking'),
201 ],
202 [
203 'require_list' => false,
204 'key' => 'enabled',
205 'label' => __('Status', 'fluent-booking'),
206 'component' => 'checkbox-single',
207 'checkbox_label' => __('Enable This feed', 'fluent-booking'),
208 ]
209 ];
210
211 return [
212 'fields' => $fields,
213 'button_require_list' => false,
214 'integration_title' => $this->title,
215 ];
216 }
217
218 public function getMergeFields($list, $listId, $slotId)
219 {
220 return [];
221 }
222
223 public function getEventTriggerOptions()
224 {
225 return [
226 'after_booking_scheduled' => __('Booking Confirmed', 'fluent-booking'),
227 'booking_schedule_completed' => __('Booking Completed', 'fluent-booking'),
228 'booking_schedule_cancelled' => __('Booking Cancelled', 'fluent-booking'),
229 ];
230 }
231
232 public function getConfigFieldOptions($settings, $calendarEventId)
233 {
234 $boardId = Arr::get($settings, 'board_config.board_id');
235
236 $data = [
237 'board_id' => $this->getBoards(),
238 'stage_id' => $boardId ? $this->getStages($boardId) : [],
239 'label_ids' => $boardId ? $this->getBoardLabels($boardId) : [],
240 'member_ids' => $boardId ? $this->getBoardMembers($boardId) : [],
241 'priority' => $this->getBoardPriorities()
242 ];
243
244 $data = apply_filters('fluent_booking/fluent_board_config_field_options', $data, $boardId);
245
246 return $data;
247 }
248
249 private function getBoards()
250 {
251 $boards = Board::whereNull('archived_at')
252 ->select('id', 'title')
253 ->get();
254
255 $formattedBoards = $boards->mapWithKeys(function ($board) {
256 return [$board->id => $board->title];
257 })->toArray();
258
259 return $formattedBoards;
260 }
261
262 private function getStages($boardId)
263 {
264 $stages = Stage::where('board_id', $boardId)
265 ->whereNull('archived_at')
266 ->select('id', 'title')
267 ->get();
268
269 $formattedStages = $stages->mapWithKeys(function ($stage) {
270 return [$stage->id => $stage->title];
271 })->toArray();
272
273 return $formattedStages;
274 }
275
276 private function getBoardLabels($boardId)
277 {
278 $labels = Label::where('board_id', $boardId)
279 ->whereNull('archived_at')
280 ->orderBy('position', 'asc')
281 ->get(['id', 'title', 'slug']);
282
283 $formattedLabels = $labels->mapWithKeys(function ($label) {
284 return [$label->id => $label->title ?? $label->slug];
285 })->toArray();
286
287 return $formattedLabels;
288 }
289
290 private function getBoardMembers($boardId)
291 {
292 $board = Board::with(['users'])->findOrFail($boardId);
293
294 $formattedBoardUsers = $board->users->mapWithKeys(function ($user) {
295 return [$user->ID => "{$user->user_login} ({$user->user_email})"];
296 })->toArray();
297
298 return $formattedBoardUsers;
299 }
300
301 private function getBoardPriorities()
302 {
303 return [
304 'low' => __('Low', 'fluent-booking'),
305 'medium' => __('Medium', 'fluent-booking'),
306 'high' => __('High', 'fluent-booking')
307 ];
308 }
309
310 private function getLastPositionOfStageTask($boardId, $stageId)
311 {
312 $lastPosition = Task::query()
313 ->where('board_id', $boardId)
314 ->where('parent_id', null)
315 ->where('stage_id', $stageId)
316 ->whereNull('archived_at')
317 ->orderBy('position', 'desc')
318 ->pluck('position')
319 ->first();
320
321 return $lastPosition + 1;
322 }
323
324 private function convertDueDate($dueTime, $dueType, $bookingStartTime)
325 {
326 $timeStamp = strtotime($bookingStartTime);
327 $currentTime = current_time('timestamp');
328
329 if ($dueType != 'meeting') {
330 $timeStamp = $currentTime;
331 $dueTime = max(0, $dueTime);
332 }
333
334 $adjustSign = $dueTime < 0 ? '-' : '+';
335 $dateAdjustment = $adjustSign . abs($dueTime) . ' day';
336 $dueDate = gmdate('Y-m-d H:i:s', strtotime($dateAdjustment, $timeStamp)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
337
338 if (strtotime($dueDate) < $currentTime) {
339 $dueDate = gmdate('Y-m-d H:i:s', $currentTime); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
340 }
341
342 $wpTimestamp = current_time('timestamp');
343 $utcTimeStamp = time();
344
345 $diff = $wpTimestamp - $utcTimeStamp;
346
347 if (!$diff) {
348 return $dueDate;
349 }
350
351 return gmdate('Y-m-d H:i:s', strtotime($dueDate) + $diff); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
352 }
353
354 public function notify($feed, $booking, $calendarEvent)
355 {
356 $validData = ['task_title', 'description', 'board_config', 'author_name', 'email', 'position', 'due_at_days', 'due_at_type'];
357 $data = Arr::only($feed['processedValues'], $validData);
358
359 $boardId = intval(Arr::get($data, 'board_config.board_id'));
360 $stageId = intval(Arr::get($data, 'board_config.stage_id'));
361 $priority = sanitize_text_field(Arr::get($data, 'board_config.priority'));
362 $assignees = array_map('intval', Arr::get($data, 'board_config.member_ids', []));
363 $boardLabels = array_map('intval', Arr::get($data, 'board_config.label_ids', []));
364 $taskTitle = sanitize_text_field(Arr::get($data, 'task_title'));
365 $description = wp_kses_post(Arr::get($data, 'description'));
366 $position = sanitize_text_field(Arr::get($data, 'position'));
367 $dueAtDays = intval(Arr::get($data, 'due_at_days'));
368 $dueAtType = sanitize_text_field(Arr::get($data, 'due_at_type'));
369 $authorName = sanitize_text_field(Arr::get($data, 'author_name'));
370 $authorEmail = sanitize_email(Arr::get($data, 'email'));
371
372 if (!$booking->id || !$boardId || !$stageId || !$taskTitle) {
373 return false;
374 }
375
376 $board = Board::find($boardId);
377 if (!$board) {
378 return false;
379 }
380
381 $data = [
382 'title' => $taskTitle,
383 'board_id' => $boardId,
384 'stage_id' => $stageId,
385 'priority' => $priority,
386 'description' => $description,
387 'position' => $this->getLastPositionOfStageTask($boardId, $stageId),
388 'due_at' => $this->convertDueDate($dueAtDays, $dueAtType, $booking->start_time),
389 'source' => 'FluentBooking'
390 ];
391
392 $data['started_at'] = $data['due_at'] ? gmdate('Y-m-d H:i:s', strtotime(current_time('mysql'))) : null; // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
393
394 $existingUser = User::where('user_email', $authorEmail)->first();
395 if ($existingUser) {
396 $data['created_by'] = $existingUser->ID;
397 }
398
399 if (!$existingUser) {
400 $data['settings']['author'] = [
401 'name' => $authorName,
402 'email' => $authorEmail,
403 'subtask_count' => 0,
404 'cover' => [
405 'backgroundColor' => ''
406 ]
407 ];
408 }
409
410 if (defined('FLUENTCRM')) {
411 $crmConatct = \FluentCrm\App\Models\Subscriber::where('email', $authorEmail)->first();
412 if ($crmConatct) {
413 $data['crm_contact_id'] = $crmConatct->id;
414 }
415 }
416
417 $task = (new Task())->createTask($data);
418 if (!$task) {
419 return false;
420 }
421
422 do_action('fluent_boards/task_added_from_fluent_booking', $task, $booking, $calendarEvent, $feed);
423
424 if ($position == 'top') {
425 $task->moveToNewPosition(1);
426 }
427
428 foreach ($assignees as $assignee) {
429 $task->addOrRemoveAssignee($assignee);
430 $task->load('assignees');
431 $task->updated_at = current_time('mysql');
432 $task->save();
433
434 $isEmailEnabled = (new NotificationService())->checkIfEmailEnable($assignee, Constant::BOARD_EMAIL_TASK_ASSIGN, $task->board_id);
435 if ($isEmailEnabled) {
436 (new TaskService())->sendMailAfterTaskModify('add_assignee', $assignee, $task->id);
437 }
438
439 do_action('fluent_boards/task_assignee_changed', $task, $assignee, 'added');
440 }
441
442 foreach ($boardLabels as $label) {
443 $task->labels()->syncWithoutDetaching([$label => ['object_type' => Constant::OBJECT_TYPE_TASK_LABEL]]);
444 }
445
446 $taskUrl = admin_url("admin.php?page=fluent-boards#/boards/$boardId/tasks/{$task->id}");
447
448 $this->addLog(
449 $feed['settings']['name'],
450 sprintf(__('Task has been created in FluentBoards. You can %s to view the task.', 'fluent-booking'), '<a target="_blank" href="' . $taskUrl . '">' . __('click here', 'fluent-booking') . '</a>'),
451 $booking->id,
452 'success'
453 );
454 return true;
455 }
456 }
457