PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.2.5
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.2.5
2.5.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 All 34 releases
fluent-booking / app / Services / Integrations / FluentBoards / Bootstrap.php

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

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