← All changes
|
app/Services/Intergrations/FluentFormIntegration/Bootstrap.php
+146
-26
1.12
→
2.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 | } |