| @@ -34,8 +34,47 @@ | ||
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | Helper::createActivity($data); |
| 37 | 37 | } |
| 38 | + /** Record the creation of a board's custom-field definition. */ | |
| 39 | + public function customFieldCreated($customField) | |
| 40 | + { | |
| 41 | + $this->createLogActivity( | |
| 42 | + $customField->board_id, | |
| 43 | + Constant::ACTIVITY_ACTION_CREATED, | |
| 44 | + Constant::ACTIVITY_CUSTOM_FIELD, | |
| 45 | + null, | |
| 46 | + $customField->title, | |
| 47 | + null, | |
| 48 | + ['custom_field_id' => $customField->id, 'custom_field_type' => $customField->settings['custom_field_type']] | |
| 49 | + ); | |
| 50 | + } | |
| 51 | + | |
| 52 | + /** Record changed names and options using snapshots taken before the definition was saved. */ | |
| 53 | + public function customFieldUpdated($customField, $oldCustomField) | |
| 54 | + { | |
| 55 | + $settings = [ | |
| 56 | + 'custom_field_id' => $customField->id, | |
| 57 | + 'custom_field_title' => $customField->title, | |
| 58 | + 'custom_field_type' => $customField->settings['custom_field_type'] | |
| 59 | + ]; | |
| 60 | + if ($oldCustomField->title !== $customField->title) { | |
| 61 | + $this->createLogActivity($customField->board_id, Constant::ACTIVITY_ACTION_UPDATED, | |
| 62 | + Constant::ACTIVITY_CUSTOM_FIELD, $oldCustomField->title, $customField->title, null, $settings); | |
| 63 | + } | |
| 64 | + | |
| 65 | + $oldOptions = $oldCustomField->settings['select_options'] ?? []; | |
| 66 | + $newOptions = $customField->settings['select_options'] ?? []; | |
| 67 | + if ($oldOptions !== $newOptions) { | |
| 68 | + $this->createLogActivity($customField->board_id, Constant::ACTIVITY_ACTION_UPDATED, | |
| 69 | + /* translators: %s is the custom field title */ | |
| 70 | + sprintf(__('custom field options for %s', 'fluent-boards'), $customField->title), | |
| 71 | + $oldOptions ? implode(', ', $oldOptions) : __('empty', 'fluent-boards'), | |
| 72 | + $newOptions ? implode(', ', $newOptions) : __('empty', 'fluent-boards'), | |
| 73 | + null, $settings); | |
| 74 | + } | |
| 75 | + } | |
| 76 | + | |
| 38 | 77 | public function getAllBoards($sortBy = 'ASC') |
| 39 | 78 | { |
| 40 | 79 | $boards = Board::orderBy('created_at', $sortBy)->get(); |
| 41 | 80 | |
| @@ -57,8 +96,40 @@ | ||
| 57 | 96 | $this->createLogActivity($boardId, 'created', 'board', null, null, $board->title); |
| 58 | 97 | $this->updateOnboarding(); |
| 59 | 98 | } |
| 60 | 99 | |
| 100 | + /** | |
| 101 | + * Log a system activity without allowing a non-critical log failure to fail the automation. | |
| 102 | + * | |
| 103 | + * @param Board $board | |
| 104 | + * @return void | |
| 105 | + */ | |
| 106 | + public function boardCreatedFromAutomation($board) | |
| 107 | + { | |
| 108 | + $previousUserId = get_current_user_id(); | |
| 109 | + | |
| 110 | + try { | |
| 111 | + wp_set_current_user(0); | |
| 112 | + | |
| 113 | + $this->createLogActivity( | |
| 114 | + $board->id, | |
| 115 | + 'created', | |
| 116 | + 'board', | |
| 117 | + null, | |
| 118 | + null, | |
| 119 | + null, | |
| 120 | + ['source' => 'fluentcrm_automation'] | |
| 121 | + ); | |
| 122 | + } catch (\Throwable $exception) { | |
| 123 | + error_log(sprintf( | |
| 124 | + 'FluentBoards: Failed to log FluentCRM automation board activity: %s', | |
| 125 | + sanitize_text_field($exception->getMessage()) | |
| 126 | + )); | |
| 127 | + } finally { | |
| 128 | + wp_set_current_user($previousUserId); | |
| 129 | + } | |
| 130 | + } | |
| 131 | + | |
| 61 | 132 | private function updateOnboarding() |
| 62 | 133 | { |
| 63 | 134 | $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first(); |
| 64 | 135 | if($onboarding && $onboarding->value == 'no'){ |
| @@ -194,8 +265,35 @@ | ||
| 194 | 265 | $this->updateBoardTaskCount($task->board_id); |
| 195 | 266 | } |
| 196 | 267 | } |
| 197 | 268 | |
| 269 | + /** | |
| 270 | + * Log one board activity for a stage-level bulk task move. | |
| 271 | + * | |
| 272 | + * @param int $boardId | |
| 273 | + * @param Stage $sourceStage | |
| 274 | + * @param Stage $targetStage | |
| 275 | + * @param int $taskCount | |
| 276 | + * @return void | |
| 277 | + */ | |
| 278 | + public function tasksMovedBetweenStages($boardId, $sourceStage, $targetStage, $taskCount) | |
| 279 | + { | |
| 280 | + $description = sprintf( | |
| 281 | + /* translators: %d is the number of tasks moved */ | |
| 282 | + _n('%d task moved', '%d tasks moved', $taskCount, 'fluent-boards'), | |
| 283 | + $taskCount | |
| 284 | + ); | |
| 285 | + | |
| 286 | + $this->createLogActivity( | |
| 287 | + $boardId, | |
| 288 | + 'moved', | |
| 289 | + 'tasks', | |
| 290 | + $sourceStage->title, | |
| 291 | + $targetStage->title, | |
| 292 | + $description | |
| 293 | + ); | |
| 294 | + } | |
| 295 | + | |
| 198 | 296 | public function boardLabelCreatedActivity($label) |
| 199 | 297 | { |
| 200 | 298 | $column = 'label'; |
| 201 | 299 | $settings = [ |
| @@ -444,8 +542,15 @@ | ||
| 444 | 542 | $board->settings = $settings; |
| 445 | 543 | $board->save(); |
| 446 | 544 | } |
| 447 | 545 | |
| 546 | + /** | |
| 547 | + * Delete a board-owned previous background attachment and log the change. | |
| 548 | + * | |
| 549 | + * @param int $boardId | |
| 550 | + * @param array|string $oldBackground | |
| 551 | + * @return void | |
| 552 | + */ | |
| 448 | 553 | public function backgroundUpdated($boardId, $oldBackground) |
| 449 | 554 | { |
| 450 | 555 | if ( |
| 451 | 556 | is_array($oldBackground) && |
| @@ -451,19 +556,20 @@ | ||
| 451 | 556 | is_array($oldBackground) && |
| 452 | 557 | !empty($oldBackground['is_image']) && |
| 453 | 558 | !empty($oldBackground['image_url']) |
| 454 | 559 | ) { |
| 455 | - // Delete attachment if ID exists | |
| 560 | + // Only the target board's background attachment may be removed by this hook. | |
| 456 | 561 | if (!empty($oldBackground['id'])) { |
| 457 | - $attachment = Attachment::find($oldBackground['id']); | |
| 562 | + $attachment = Attachment::where('id', absint($oldBackground['id'])) | |
| 563 | + ->where('object_id', absint($boardId)) | |
| 564 | + ->where('object_type', Constant::BOARD_BACKGROUND_IMAGE) | |
| 565 | + ->first(); | |
| 566 | + | |
| 458 | 567 | if ($attachment) { |
| 568 | + $deletedAttachment = clone $attachment; | |
| 459 | 569 | $attachment->delete(); |
| 570 | + (new FileHandler())->deleteAttachmentFile($deletedAttachment, absint($boardId)); | |
| 460 | 571 | } |
| 461 | - } | |
| 462 | - | |
| 463 | - // Delete file if full_url exists | |
| 464 | - if (!empty($oldBackground['full_url'])) { | |
| 465 | - (new FileHandler())->deleteFileByUrl($oldBackground['full_url']); | |
| 466 | 572 | } |
| 467 | 573 | } |
| 468 | 574 | |
| 469 | 575 | $this->createLogActivity($boardId, 'changed', 'board background', null, null); |