PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.1.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.1.0
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / app / Services / Intergrations / FluentFormIntegration / Bootstrap.php

Bootstrap.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 2.1.0, at app/Services/Intergrations/FluentFormIntegration/Bootstrap.php

553 lines 20.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\App\Services\Intergrations\FluentFormIntegration;
4
5 use FluentBoards\App\Hooks\Handlers\AdminMenuHandler;
6 use FluentBoards\App\Models\Label;
7 use FluentBoards\App\Models\Stage;
8 use FluentBoards\App\Models\User;
9 use FluentBoards\App\Services\Constant;
10 use FluentBoards\App\Services\DescriptionMarkdownConverter;
11 use FluentBoards\App\Services\NotificationService;
12 use FluentBoards\App\Services\TaskService;
13 use FluentBoardsPro\App\Models\TaskAttachment;
14 use FluentForm\App\Http\Controllers\IntegrationManagerController;
15 use FluentForm\App\Modules\Acl\Acl;
16 use FluentBoards\Framework\Support\Arr;
17 use FluentBoards\App\Models\Board;
18 use FluentBoards\App\Models\Task;
19 use FluentBoards\App\Services\Helper;
20 use FluentCrm\App\Models\Subscriber;
21 use FluentBoards\App\App;
22
23
24 class Bootstrap extends IntegrationManagerController
25 {
26 public $disableGlobalSettings = 'yes';
27
28 public function __construct($app)
29 {
30 parent::__construct(
31 $app,
32 'Fluent Boards',
33 'fluent_boards',
34 '_fluentform_fluent_boards_settings',
35 'fluentform_fluent_boards_feed',
36 30
37 );
38
39 $fluentBoards = App::getInstance();
40 $this->logo = $fluentBoards['url.assets'] . 'images/icon.png';
41
42 $this->description = 'Connect Fluent Boards with WP Fluent Forms and create a task when a form is submitted.';
43
44 $this->registerAdminHooks();
45
46 add_action('wp_ajax_fluentform_fluent_board_config', array($this, 'getBoardConfigOptions'));
47 // add_filter('fluentform/notifying_async_fluent_boards', '__return_false');
48 }
49
50 public function addGlobalMenu($setting)
51 {
52 return $setting;
53 }
54
55 public function pushIntegration($integrations, $formId)
56 {
57 $integrations[$this->integrationKey] = [
58 'title' => $this->title . ' Integration',
59 'logo' => $this->logo,
60 'disable_global_settings' => 'yes',
61 'category' => '',
62 'is_active' => $this->isConfigured()
63 ];
64
65 return $integrations;
66 }
67
68 public function isConfigured()
69 {
70 return true;
71 }
72
73 public function getIntegrationDefaults($settings, $formId)
74 {
75 return [
76 'name' => '',
77 'list_id' => '',
78 'board_config' => [
79 'board_id' => '',
80 'stage_id' => '',
81 'board_label_id' => [],
82 'member_ids' => [],
83 'crm_contact_id' => '',
84 'priority' => ''
85 ],
86 'task_title' => '',
87 'submitter_name' => '',
88 'submitter_email' => '',
89 'priority' => '',
90 'description' => '',
91 'position' => 'bottom',
92 'due_at_days' => '',
93 'create_crm_contact' => false,
94 'conditionals' => [
95 'conditions' => [],
96 'status' => false,
97 'type' => 'all'
98 ],
99
100
101 'enabled' => true
102 ];
103 }
104
105 /**
106 * Build the Fluent Forms feed fields with a form-scoped board options URL.
107 */
108 public function getSettingsFields($settings, $formId)
109 {
110 $data = [
111 'fields' => [
112 [
113 'key' => 'board_config',
114 'label' => 'Fluent Boards Configuration',
115 'required' => true,
116 'component' => 'chained_select',
117 'primary_key' => 'board_id',
118 'fields_options' => [
119 'board_id' => [],
120 'stage_id' => [],
121 'board_label_id' => [],
122 'member_ids' => [],
123 'priority' => []
124 ],
125 'options_labels' => [
126 'board_id' => [
127 'label' => 'Select Board',
128 'type' => 'select',
129 'placeholder' => 'Select Board'
130 ],
131 'stage_id' => [
132 'label' => 'Select Stage',
133 'type' => 'select',
134 'placeholder' => 'Select Stage'
135 ],
136 'board_label_id' => [
137 'label' => 'Select Labels',
138 'type' => 'multi-select',
139 'placeholder' => 'Select Labels'
140 ],
141 'member_ids' => [
142 'label' => 'Select Assignees',
143 'type' => 'multi-select',
144 'placeholder' => 'Select Assignees'
145 ],
146 'priority' => [
147 'label' => 'Select Priority',
148 'type' => 'select',
149 'placeholder' => 'Priority'
150 ]
151 ],
152 'remote_url' => add_query_arg([
153 'action' => 'fluentform_fluent_board_config',
154 'form_id' => absint($formId)
155 ], admin_url('admin-ajax.php'))
156 ],
157 [
158 'key' => 'task_title',
159 'label' => 'Task Title',
160 'required' => true,
161 'placeholder' => 'Task Title',
162 'component' => 'value_text'
163 ],
164 [
165 'key' => 'description',
166 'label' => 'Description',
167 'required' => false,
168 'placeholder' => 'Describe your task',
169 'component' => 'wp_editor',
170 ],
171
172 [
173 'key' => 'submitter_name',
174 'label' => 'Submitter Name',
175 'required' => true,
176 'placeholder' => 'Submitter Name',
177 'component' => 'value_text'
178 ],
179 [
180 'key' => 'submitter_email',
181 'label' => 'Submitter Email',
182 'required' => true,
183 'placeholder' => 'Submitter Email',
184 'component' => 'value_text'
185 ],
186 [
187 'key' => 'position',
188 'label' => 'Task Position',
189 'required' => true,
190 'placeholder' => 'Position',
191 'component' => 'radio_choice',
192 'options' => [
193 'bottom' => 'Bottom',
194 'top' => 'Top'
195 ]
196 ],
197 [
198 'key' => 'due_at_days',
199 'label' => 'Due Date',
200 'tips' => 'Days after form submission, values less than zero will set due date to null.',
201 'component' => 'number'
202 ],
203 [
204 'key' => 'conditionals',
205 'label' => 'Conditional Logics',
206 'tips' => 'Allow integration conditionally based on your submission values',
207 'component' => 'conditional_block'
208 ],
209 [
210 'key' => 'enabled',
211 'label' => 'Status',
212 'component' => 'checkbox-single',
213 'checkbox_label' => 'Enable This feed'
214 ]
215 ],
216 'button_require_list' => false,
217 'integration_title' => $this->title
218 ];
219 if (function_exists('fluentCrm')) {
220 $addToCrmField = [
221 'key' => 'create_crm_contact',
222 'label' => 'FluentCRM',
223 'tips' => "This will create a FluentCRM Contact, if submitter email doesn't exists",
224 'component' => 'checkbox-single',
225 'checkbox_label' => 'Create FluentCRM Contact',
226 ];
227 array_splice($data['fields'], 7, 0, [$addToCrmField]);
228 }
229 if (defined('FLUENT_BOARDS_PRO_VERSION')) {
230 $mapFilesField = [
231 'key' => 'map_files',
232 'label' => 'Files/Attachments',
233 'tips' => "This will map all the Files/Images from the form submission to the task.",
234 'component' => 'checkbox-single',
235 'checkbox_label' => 'Map Files/Attachments to Task',
236 ];
237 array_splice($data['fields'], -2, 0, [$mapFilesField]);
238 }
239 return $data;
240 }
241
242
243 /**
244 * Return board configuration options to authorized Fluent Forms managers.
245 */
246 public function getBoardConfigOptions()
247 {
248 $formId = absint($this->app->request->get('form_id'));
249 $nonceValid = check_ajax_referer('fluent_forms_admin_nonce', 'fluent_forms_admin_nonce', false);
250
251 if (!$nonceValid || !$formId || !Acl::hasPermission('fluentform_forms_manager', $formId)) {
252 $this->sendBoardConfigForbidden();
253 }
254
255 $requestInfo = $this->app->request->get('settings', []);
256 $boardConfig = Arr::get($requestInfo, 'board_config', []);
257
258 $boardId = absint(Arr::get($boardConfig, 'board_id'));
259 $boards = $this->getBoards();
260
261 if ($boardId && !array_key_exists($boardId, $boards)) {
262 $this->sendBoardConfigForbidden();
263 }
264
265 $data = [
266 'board_id' => $boards,
267 'stage_id' => [],
268 'board_label_id' => [],
269 'member_ids' => [],
270 'priority' => apply_filters('fluent_boards/task_priorities', (new AdminMenuHandler())->getDefaultPriorities()),
271 ];
272
273 if ($boardId) {
274 $data['stage_id'] = $this->getStages($boardId);
275 $data['board_label_id'] = $this->getBoardLabels($boardId);
276 $data['member_ids'] = $this->getBoardMembers($boardId);
277 }
278
279 wp_send_json_success([
280 'fields_options' => $data
281 ], 200);
282 }
283
284 /**
285 * Send a consistent forbidden response for invalid board configuration requests.
286 */
287 private function sendBoardConfigForbidden()
288 {
289 wp_send_json_error([
290 'message' => __('You do not have permission to configure Fluent Boards for this form.', 'fluent-boards')
291 ], 403);
292 }
293
294 /**
295 * Get active boards accessible to the current user.
296 */
297 private function getBoards()
298 {
299 $boards = Board::query()
300 ->whereNull('archived_at')
301 ->byAccessUser(get_current_user_id())
302 ->get()
303 ->toArray();
304
305 $formattedBoards = [];
306 foreach ($boards as $board) {
307 if (is_array($board)) {
308 $formattedBoards[$board['id']] = $board['title'];
309 }
310 }
311
312 return $formattedBoards;
313 }
314
315 private function getStages($boardId)
316 {
317 $stages = Stage::where('board_id', $boardId)->whereNull('archived_at')->get()->toArray();
318
319 $formattedStages = [];
320 foreach ($stages as $stage) {
321 if (is_array($stage)) {
322 $formattedStages[$stage['id']] = $stage['title'];
323 }
324 }
325
326 return $formattedStages;
327 }
328
329
330 /**
331 * Prepare Fluent Boards forms for feed field.
332 *
333 * @return array
334 */
335
336 /*
337 * Submission Broadcast Handler
338 */
339
340 public function notify($feed, $formData, $entry, $form)
341 {
342 if (function_exists('fluentBoards')) {
343 $feedData = $feed['processedValues'];
344 $taskName = trim(Arr::get($feedData, 'task_title'));
345 $boardId = Arr::get($feedData, 'board_config.board_id');
346 $stageId = Arr::get($feedData, 'board_config.stage_id');
347 $boardLabels = Arr::get($feedData, 'board_config.board_label_id');
348 $assignees = Arr::get($feedData, 'board_config.member_ids');
349 $priority = Arr::get($feedData, 'board_config.priority');
350 $description = DescriptionMarkdownConverter::normalize(Arr::get($feedData, 'description'));
351 $position = Arr::get($feedData, 'position');
352 $crmContactId = Arr::get($feedData, 'board_config.crm_contact_id');
353 $submitterName = trim(Arr::get($feedData, 'submitter_name'));
354 $submitterEmail = Arr::get($feedData, 'submitter_email');
355 $due_at_days = Arr::get($feedData, 'due_at_days');
356 $mapFiles = Arr::get($feedData, 'map_files');
357 $watcher = Arr::get($feedData, 'watcher_id');
358 $crateCRMContact = Arr::get($feedData, 'create_crm_contact');
359
360 if (!$boardId) {
361 do_action('fluentform/integration_action_result', $feed, 'failed', 'Board is required');
362 return;
363 }
364 $board = Board::find($boardId);
365 if (!$board) {
366 do_action('fluentform/integration_action_result', $feed, 'failed', "Board doesn't exist");
367 return;
368 }
369 if (!$stageId) {
370 do_action('fluentform/integration_action_result', $feed, 'failed', "Stage is required");
371 return;
372 }
373 $stage = Stage::find($stageId);
374 if (!$stage) {
375 do_action('fluentform/integration_action_result', $feed, 'failed', "Stage doesn't exist");
376 return;
377 }
378 if (!$taskName) {
379 do_action('fluentform/integration_action_result', $feed, 'failed', "Task title is required");
380 return;
381 } else if (!is_string($taskName)) {
382 do_action('fluentform/integration_action_result', $feed, 'failed', "Invalid Task title is required");
383 return;
384 }
385
386 $data = [
387 'title' => $taskName,
388 'board_id' => $boardId,
389 'stage_id' => $stageId,
390 'priority' => $priority,
391 'description' => $description,
392 'crm_contact_id' => $crmContactId,
393 'position' => $this->getLastPositionOfStageTask($boardId, $stageId),
394 'due_at' => $this->dueDateConvertion($due_at_days, 'day'),
395 'source' => 'FluentForm'
396
397 ];
398
399 $formSubmitter = User::where('user_email', $submitterEmail)->first();
400 if (isset($formSubmitter['ID'])) {
401 $data['created_by'] = $formSubmitter->ID;
402 } else {
403 $data['settings']['author'] = [
404 'name' => sanitize_text_field($submitterName),
405 'email' => sanitize_email($submitterEmail),
406 'cover' => [
407 'backgroundColor' => ''
408 ],
409 'subtask_count' => 0
410 ];
411 }
412
413 if (function_exists('fluentCrm')) {
414 $crmConatct = Subscriber::where('email', $submitterEmail)->first();
415 if ($crmConatct) {
416 $data['crm_contact_id'] = $crmConatct->id;
417 } else {
418 if ($crateCRMContact) {
419 // create crm contact
420 }
421 }
422 }
423
424 $data = Helper::sanitizeTask($data);
425 $task = (new Task())->createTask($data);
426
427 if ($task->id) {
428 do_action('fluent_boards/task_added_from_fluent_form', $task);
429 if ($position == 'top') {
430 $task->moveToNewPosition(1);
431 }
432
433 if ($assignees) {
434 foreach ($assignees as $assignee) {
435 $operation = $task->addOrRemoveAssignee($assignee);
436 $task->load('assignees');
437 $task->updated_at = current_time('mysql');
438
439 $task->save();
440
441 if ($operation == 'added') {
442 if ((new NotificationService())->checkIfEmailEnable($assignee, Constant::BOARD_EMAIL_TASK_ASSIGN, $task->board_id)) {
443 (new TaskService())->sendMailAfterTaskModify('add_assignee', $assignee, $task->id);
444 }
445 do_action('fluent_boards/task_assignee_added', $task, $assignee);
446 }
447 }
448 }
449
450 if ($boardLabels) {
451 foreach ($boardLabels as $label) {
452 $task->labels()->syncWithoutDetaching([$label => ['object_type' => Constant::OBJECT_TYPE_TASK_LABEL]]);
453 }
454 }
455
456 if ($mapFiles && defined('FLUENT_BOARDS_PRO_VERSION')) {
457 $results = [];
458
459 // Loop through the array to find keys starting with 'image-upload' or 'file-upload'
460 foreach ($formData as $key => $value) {
461 if (preg_match('/^(image-upload|file-upload)/', $key)) {
462 foreach ($value as $file_url) {
463 $results[] = $file_url;
464 }
465 }
466 }
467
468
469 foreach ($results as $attachment) {
470 $urlMeta = [
471 'title' => 'Uploaded from FluentForm',
472 'type' => 'meta_data',
473 'url' => esc_url($attachment)
474 ];
475 $urlData['settings'] = ['meta' => $urlMeta];
476 // creating new attachment object
477 $attachment = new TaskAttachment();
478 $attachment->object_id = $task->id;
479 $attachment->object_type = \FluentBoards\App\Services\Constant::TASK_ATTACHMENT;
480 $attachment->attachment_type = 'url';
481 $attachment->title = 'Uploaded File from Fluent Form';
482 $attachment->full_url = $urlMeta['url'];
483 $attachment->settings = $urlData['settings'] ?? '';
484 $attachment->driver = 'local';
485 $attachment->save();
486 }
487 }
488
489 $successMessage = __('Fluent Boards feed has been successfully initialized and data has been pushed.', 'fluent-boards');
490 do_action('fluentform/integration_action_result', $feed, 'success', $successMessage);
491 } else {
492 $error = __('Error when submitting data to Fluent Board server.', 'fluent-boards');
493 do_action('fluentform/integration_action_result', $feed, 'failed', $error);
494 }
495 } else {
496 do_action('fluentform/integration_action_result', $feed, 'failed', "Fluent Board doesn't exists.");
497 }
498 }
499
500 public function getMergeFields($list, $listId, $formId)
501 {
502 return false;
503 }
504
505 private function getLastPositionOfStageTask($board_id, $stage_id)
506 {
507 $lastPosition = Task::query()
508 ->where('board_id', $board_id)
509 ->where('parent_id', null)
510 ->where('stage_id', $stage_id)
511 ->whereNull('archived_at')
512 ->orderBy('position', 'desc')
513 ->pluck('position')
514 ->first();
515
516 return $lastPosition + 1;
517 }
518
519 private function getBoardLabels($boardId)
520 {
521 $labels = Label::where('board_id', $boardId)->whereNull('archived_at')->orderBy('position', 'asc')->get();
522
523 $formattedLabels = [];
524 foreach ($labels as $label) {
525 $formattedLabels[$label['id']] = $label['title'] ?? $label['slug'];
526 }
527
528 return $formattedLabels;
529 }
530
531 private function getBoardMembers($boardId)
532 {
533 $board = Board::findOrFail($boardId);
534 $boardUsers = $board->users;
535 $formattedBoardUsers = [];
536 foreach ($boardUsers as $boardUser) {
537 $formattedBoardUsers[$boardUser['ID']] = $boardUser['user_login'] . " (" . $boardUser['user_email'] . ")";
538 }
539 return $formattedBoardUsers;
540 }
541
542 private function dueDateConvertion($due_time, $unit)
543 {
544 if ($due_time > 0) {
545 $currentTime = current_time('mysql');
546 $readyString = '+' . $due_time . ' ' . $unit;
547 return gmdate('Y-m-d H:i:s', strtotime($readyString, strtotime($currentTime)));
548 }
549 return null;
550 }
551
552 }
553