| @@ -508,14 +508,26 @@ | ||
| 508 | 508 | |
| 509 | 509 | public function moveAllTasks($oldStageId, $newStageId) |
| 510 | 510 | { |
| 511 | 511 | $tasks = Task::where('stage_id', $oldStageId)->whereNull('parent_id')->whereNull('archived_at')->get(); |
| 512 | + $sourceStage = Stage::findOrFail($oldStageId); | |
| 513 | + $targetStage = Stage::findOrFail($newStageId); | |
| 512 | 514 | |
| 515 | + // Reuse task-user relations unless default assignments change them. | |
| 516 | + $tasks->load(['assignees', 'watchers']); | |
| 517 | + | |
| 513 | 518 | // get the last position available of that stage |
| 514 | - $position = (new TaskService())->getLastPositionOfTasks($newStageId); | |
| 519 | + $taskService = new TaskService(); | |
| 520 | + $position = $taskService->getLastPositionOfTasks($newStageId); | |
| 521 | + $notificationService = new NotificationService(); | |
| 522 | + $currentUserId = get_current_user_id(); | |
| 523 | + // Stage-email eligibility per watcher, resolved in batches and shared across tasks. | |
| 524 | + $emailEligibility = []; | |
| 515 | 525 | |
| 516 | 526 | // update tasks stage and position |
| 517 | 527 | foreach ($tasks as $key => $task) { |
| 528 | + $taskOldStageId = $task->stage_id; | |
| 529 | + | |
| 518 | 530 | // Clean up archived_by_stage meta when moving to different stage |
| 519 | 531 | TaskMeta::where('task_id', $task->id) |
| 520 | 532 | ->where('key', Constant::META_KEY_ARCHIVED_BY_STAGE) |
| 521 | 533 | ->delete(); |
| @@ -521,10 +533,55 @@ | ||
| 521 | 533 | ->delete(); |
| 522 | 534 | |
| 523 | 535 | $task->stage_id = $newStageId; |
| 524 | 536 | $task->position = $position + $key; |
| 537 | + $task->setRelation('stage', $targetStage); | |
| 525 | 538 | $task->save(); |
| 539 | + | |
| 540 | + if ((int) $taskOldStageId !== (int) $newStageId) { | |
| 541 | + $assignmentsChanged = $taskService->manageDefaultAssignees($task, $newStageId, $targetStage); | |
| 542 | + if ($targetStage->defaultTaskStatus() === 'closed' && $task->status !== 'closed') { | |
| 543 | + $task = $task->close(); | |
| 544 | + } | |
| 545 | + // Assignment hooks may change assignees and watchers before notification. | |
| 546 | + if ($assignmentsChanged) { | |
| 547 | + $task->load(['assignees', 'watchers']); | |
| 548 | + } | |
| 549 | + | |
| 550 | + do_action('fluent_boards/task_stage_updated', $task, $taskOldStageId, [ | |
| 551 | + 'source' => $sourceStage, | |
| 552 | + 'target' => $targetStage, | |
| 553 | + ]); | |
| 554 | + | |
| 555 | + $watcherIds = array_diff(array_map('intval', $task->watchers->pluck('ID')->toArray()), [$currentUserId]); | |
| 556 | + $unknownIds = array_diff($watcherIds, array_keys($emailEligibility)); | |
| 557 | + if ($unknownIds) { | |
| 558 | + $emailEligibility += array_fill_keys($unknownIds, false); | |
| 559 | + $enabledIds = $notificationService->getEmailEnabledUserIds($task->board_id, $unknownIds, Constant::BOARD_EMAIL_STAGE_CHANGE); | |
| 560 | + foreach ($enabledIds as $enabledId) { | |
| 561 | + $emailEligibility[$enabledId] = true; | |
| 562 | + } | |
| 563 | + } | |
| 564 | + $recipients = []; | |
| 565 | + foreach ($task->watchers as $watcher) { | |
| 566 | + if (!empty($emailEligibility[(int) $watcher->ID])) { | |
| 567 | + $recipients[] = $watcher->user_email; | |
| 568 | + } | |
| 569 | + } | |
| 570 | + $taskService->sendMailAfterTaskModify('stage_change', $recipients, $task->id); | |
| 571 | + } | |
| 526 | 572 | } |
| 573 | + | |
| 574 | + if ($tasks->count() && (int) $sourceStage->id !== (int) $targetStage->id) { | |
| 575 | + do_action( | |
| 576 | + 'fluent_boards/tasks_moved_between_stages', | |
| 577 | + $sourceStage->board_id, | |
| 578 | + $sourceStage, | |
| 579 | + $targetStage, | |
| 580 | + $tasks->count() | |
| 581 | + ); | |
| 582 | + } | |
| 583 | + | |
| 527 | 584 | return $tasks; |
| 528 | 585 | } |
| 529 | 586 | public function archiveAllTasksInStage($stage_id, $boardId = null) |
| 530 | 587 | { |