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
← All changes | app/Services/Intergrations/FluentFormIntegration/Bootstrap.php +146 -26 1.122.1.0 View file →
@@ -1,15 +1,19 @@
1 1 <?php
2 2
3 3 namespace FluentBoards\App\Services\Intergrations\FluentFormIntegration;
4 4
5 +use FluentBoards\App\Hooks\Handlers\AdminMenuHandler;
5 6 use FluentBoards\App\Models\Label;
6 7 use FluentBoards\App\Models\Stage;
7 8 use FluentBoards\App\Models\User;
8 9 use FluentBoards\App\Services\Constant;
10 +use FluentBoards\App\Services\DescriptionMarkdownConverter;
9 11 use FluentBoards\App\Services\NotificationService;
10 12 use FluentBoards\App\Services\TaskService;
13 +use FluentBoardsPro\App\Models\TaskAttachment;
11 14 use FluentForm\App\Http\Controllers\IntegrationManagerController;
15 +use FluentForm\App\Modules\Acl\Acl;
12 16 use FluentBoards\Framework\Support\Arr;
13 17 use FluentBoards\App\Models\Board;
14 18 use FluentBoards\App\Models\Task;
15 19 use FluentBoards\App\Services\Helper;
@@ -39,9 +43,9 @@
39 43
40 44 $this->registerAdminHooks();
41 45
42 46 add_action('wp_ajax_fluentform_fluent_board_config', array($this, 'getBoardConfigOptions'));
43 - add_filter('fluentform/notifying_async_fluent_boards', '__return_false');
47 +// add_filter('fluentform/notifying_async_fluent_boards', '__return_false');
44 48 }
45 49
46 50 public function addGlobalMenu($setting)
47 51 {
@@ -84,8 +88,10 @@
84 88 'submitter_email' => '',
85 89 'priority' => '',
86 90 'description' => '',
87 91 'position' => 'bottom',
92 + 'due_at_days' => '',
93 + 'create_crm_contact' => false,
88 94 'conditionals' => [
89 95 'conditions' => [],
90 96 'status' => false,
91 97 'type' => 'all'
@@ -95,11 +101,14 @@
95 101 'enabled' => true
96 102 ];
97 103 }
98 104
105 + /**
106 + * Build the Fluent Forms feed fields with a form-scoped board options URL.
107 + */
99 108 public function getSettingsFields($settings, $formId)
100 109 {
101 - return [
110 + $data = [
102 111 'fields' => [
103 112 [
104 113 'key' => 'board_config',
105 114 'label' => 'Fluent Boards Configuration',
@@ -139,9 +148,12 @@
139 148 'type' => 'select',
140 149 'placeholder' => 'Priority'
141 150 ]
142 151 ],
143 - 'remote_url' => admin_url('admin-ajax.php?action=fluentform_fluent_board_config')
152 + 'remote_url' => add_query_arg([
153 + 'action' => 'fluentform_fluent_board_config',
154 + 'form_id' => absint($formId)
155 + ], admin_url('admin-ajax.php'))
144 156 ],
145 157 [
146 158 'key' => 'task_title',
147 159 'label' => 'Task Title',
@@ -182,8 +194,14 @@
182 194 'top' => 'Top'
183 195 ]
184 196 ],
185 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 + [
186 204 'key' => 'conditionals',
187 205 'label' => 'Conditional Logics',
188 206 'tips' => 'Allow integration conditionally based on your submission values',
189 207 'component' => 'conditional_block'
@@ -197,34 +215,66 @@
197 215 ],
198 216 'button_require_list' => false,
199 217 'integration_title' => $this->title
200 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;
201 240 }
202 241
203 242
243 + /**
244 + * Return board configuration options to authorized Fluent Forms managers.
245 + */
204 246 public function getBoardConfigOptions()
205 247 {
206 - $requestInfo = $this->app->request->get('settings');
207 - $boardConfig = Arr::get($requestInfo, 'board_config');
248 + $formId = absint($this->app->request->get('form_id'));
249 + $nonceValid = check_ajax_referer('fluent_forms_admin_nonce', 'fluent_forms_admin_nonce', false);
208 250
209 - $boardId = Arr::get($boardConfig, 'board_id');
251 + if (!$nonceValid || !$formId || !Acl::hasPermission('fluentform_forms_manager', $formId)) {
252 + $this->sendBoardConfigForbidden();
253 + }
210 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 +
211 265 $data = [
212 - 'board_id' => $this->getBoards(),
266 + 'board_id' => $boards,
213 267 'stage_id' => [],
214 268 'board_label_id' => [],
215 269 'member_ids' => [],
216 - 'priority' => [
217 - 'low' => 'Low',
218 - 'medium' => 'Medium',
219 - 'high' => 'High'
220 - ]
270 + 'priority' => apply_filters('fluent_boards/task_priorities', (new AdminMenuHandler())->getDefaultPriorities()),
221 271 ];
222 272
223 273 if ($boardId) {
224 274 $data['stage_id'] = $this->getStages($boardId);
225 275 $data['board_label_id'] = $this->getBoardLabels($boardId);
226 - $data['member_ids'] = $this->getBoardMemmbers($boardId);
276 + $data['member_ids'] = $this->getBoardMembers($boardId);
227 277 }
228 278
229 279 wp_send_json_success([
230 280 'fields_options' => $data
@@ -230,12 +280,29 @@
230 280 'fields_options' => $data
231 281 ], 200);
232 282 }
233 283
234 - protected function getBoards()
284 + /**
285 + * Send a consistent forbidden response for invalid board configuration requests.
286 + */
287 + private function sendBoardConfigForbidden()
235 288 {
236 - $boards = Board::query()->whereNull('archived_at')->get()->toArray();
289 + wp_send_json_error([
290 + 'message' => __('You do not have permission to configure Fluent Boards for this form.', 'fluent-boards')
291 + ], 403);
292 + }
237 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 +
238 305 $formattedBoards = [];
239 306 foreach ($boards as $board) {
240 307 if (is_array($board)) {
241 308 $formattedBoards[$board['id']] = $board['title'];
@@ -244,9 +311,9 @@
244 311
245 312 return $formattedBoards;
246 313 }
247 314
248 - protected function getStages($boardId)
315 + private function getStages($boardId)
249 316 {
250 317 $stages = Stage::where('board_id', $boardId)->whereNull('archived_at')->get()->toArray();
251 318
252 319 $formattedStages = [];
@@ -279,19 +346,23 @@
279 346 $stageId = Arr::get($feedData, 'board_config.stage_id');
280 347 $boardLabels = Arr::get($feedData, 'board_config.board_label_id');
281 348 $assignees = Arr::get($feedData, 'board_config.member_ids');
282 349 $priority = Arr::get($feedData, 'board_config.priority');
283 - $description = Arr::get($feedData, 'description');
350 + $description = DescriptionMarkdownConverter::normalize(Arr::get($feedData, 'description'));
284 351 $position = Arr::get($feedData, 'position');
285 352 $crmContactId = Arr::get($feedData, 'board_config.crm_contact_id');
286 - $submitterName = Arr::get($feedData, 'submitter_name');
353 + $submitterName = trim(Arr::get($feedData, 'submitter_name'));
287 354 $submitterEmail = Arr::get($feedData, 'submitter_email');
288 - $watcher = trim(Arr::get($feedData, 'watcher_id'));
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 +
289 360 if (!$boardId) {
290 361 do_action('fluentform/integration_action_result', $feed, 'failed', 'Board is required');
291 362 return;
292 363 }
293 - $board = Board::find($boardId)->toArray();
364 + $board = Board::find($boardId);
294 365 if (!$board) {
295 366 do_action('fluentform/integration_action_result', $feed, 'failed', "Board doesn't exist");
296 367 return;
297 368 }
@@ -298,9 +369,9 @@
298 369 if (!$stageId) {
299 370 do_action('fluentform/integration_action_result', $feed, 'failed', "Stage is required");
300 371 return;
301 372 }
302 - $stage = Stage::find($stageId)->toArray();
373 + $stage = Stage::find($stageId);
303 374 if (!$stage) {
304 375 do_action('fluentform/integration_action_result', $feed, 'failed', "Stage doesn't exist");
305 376 return;
306 377 }
@@ -319,8 +390,9 @@
319 390 'priority' => $priority,
320 391 'description' => $description,
321 392 'crm_contact_id' => $crmContactId,
322 393 'position' => $this->getLastPositionOfStageTask($boardId, $stageId),
394 + 'due_at' => $this->dueDateConvertion($due_at_days, 'day'),
323 395 'source' => 'FluentForm'
324 396
325 397 ];
326 398
@@ -341,8 +413,12 @@
341 413 if (function_exists('fluentCrm')) {
342 414 $crmConatct = Subscriber::where('email', $submitterEmail)->first();
343 415 if ($crmConatct) {
344 416 $data['crm_contact_id'] = $crmConatct->id;
417 + } else {
418 + if ($crateCRMContact) {
419 + // create crm contact
420 + }
345 421 }
346 422 }
347 423
348 424 $data = Helper::sanitizeTask($data);
@@ -365,9 +441,9 @@
365 441 if ($operation == 'added') {
366 442 if ((new NotificationService())->checkIfEmailEnable($assignee, Constant::BOARD_EMAIL_TASK_ASSIGN, $task->board_id)) {
367 443 (new TaskService())->sendMailAfterTaskModify('add_assignee', $assignee, $task->id);
368 444 }
369 - do_action('fluent_boards/task_assignee_changed', $task, $assignee, $operation);
445 + do_action('fluent_boards/task_assignee_added', $task, $assignee);
370 446 }
371 447 }
372 448 }
373 449
@@ -376,8 +452,41 @@
376 452 $task->labels()->syncWithoutDetaching([$label => ['object_type' => Constant::OBJECT_TYPE_TASK_LABEL]]);
377 453 }
378 454 }
379 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 +
380 489 $successMessage = __('Fluent Boards feed has been successfully initialized and data has been pushed.', 'fluent-boards');
381 490 do_action('fluentform/integration_action_result', $feed, 'success', $successMessage);
382 491 } else {
383 492 $error = __('Error when submitting data to Fluent Board server.', 'fluent-boards');
@@ -387,14 +496,14 @@
387 496 do_action('fluentform/integration_action_result', $feed, 'failed', "Fluent Board doesn't exists.");
388 497 }
389 498 }
390 499
391 - function getMergeFields($list, $listId, $formId)
500 + public function getMergeFields($list, $listId, $formId)
392 501 {
393 502 return false;
394 503 }
395 504
396 - function getLastPositionOfStageTask($board_id, $stage_id)
505 + private function getLastPositionOfStageTask($board_id, $stage_id)
397 506 {
398 507 $lastPosition = Task::query()
399 508 ->where('board_id', $board_id)
400 509 ->where('parent_id', null)
@@ -406,9 +515,9 @@
406 515
407 516 return $lastPosition + 1;
408 517 }
409 518
410 - function getBoardLabels($boardId)
519 + private function getBoardLabels($boardId)
411 520 {
412 521 $labels = Label::where('board_id', $boardId)->whereNull('archived_at')->orderBy('position', 'asc')->get();
413 522
414 523 $formattedLabels = [];
@@ -418,9 +527,9 @@
418 527
419 528 return $formattedLabels;
420 529 }
421 530
422 - function getBoardMemmbers($boardId)
531 + private function getBoardMembers($boardId)
423 532 {
424 533 $board = Board::findOrFail($boardId);
425 534 $boardUsers = $board->users;
426 535 $formattedBoardUsers = [];
@@ -428,5 +537,16 @@
428 537 $formattedBoardUsers[$boardUser['ID']] = $boardUser['user_login'] . " (" . $boardUser['user_email'] . ")";
429 538 }
430 539 return $formattedBoardUsers;
431 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 +
432 552 }