PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.3.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.3.0
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 2.3.0, at app/Services/Integrations/FluentBoards/Bootstrap.php

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