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/Hooks/actions.php +143 -26 1.212.1.0 View file →
@@ -18,42 +18,73 @@
18 18 */
19 19
20 20 (new \FluentBoards\App\Hooks\Handlers\AdminMenuHandler())->register();
21 21
22 +/*
23 + * Add custom CSS for fbs_notice.
24 + */
25 +add_action('admin_head', function () {
26 + echo '<style>
27 + .fbs_notice {
28 + background: var(--fbs-primary-bg, #ffffff);
29 + border: 1px solid var(--fbs-primary-border, #E1E4EA);
30 + border-left: 3px solid #FB3748;
31 + padding: 10px 12px !important;
32 + border-radius: 8px;
33 + margin-bottom: 5px;
34 + }
35 + </style>';
36 +});
37 +
22 38 //$app->addCustomAction('task_prop_changed', 'ActivityHandler@logActivity', 10, 3);
23 39 $app->addCustomAction('task_board_changed', 'ActivityHandler@logMoveTaskToAnotherBoardActivity', 10, 2); //will call from service after board change code merged
24 -$app->addCustomAction('task_date_changed', 'ActivityHandler@logDueDateActivity', 10, 3);
40 +$app->addCustomAction('task_due_date_changed', 'ActivityHandler@logDueDateActivity', 10, 2);
41 +$app->addCustomAction('task_due_date_removed', 'ActivityHandler@logDueDateRemoveActivity', 10, 1);
42 +$app->addCustomAction('task_start_date_changed', 'ActivityHandler@logStartDateActivity', 10, 2);
43 +
25 44 $app->addCustomAction('task_priority_changed', 'ActivityHandler@logPriorityChangeActivity', 10, 2);
26 -$app->addCustomAction('task_comment_added', 'ActivityHandler@logCommentCreateActivity', 10, 1);
27 -$app->addCustomAction('task_comment_updated', 'ActivityHandler@logCommentUpdateActivity', 10, 3);
28 -$app->addCustomAction('task_comment_deleted', 'ActivityHandler@logCommentDeleteActivity', 10, 2);
45 +$app->addCustomAction('comment_created', 'ActivityHandler@logCommentCreateActivity', 10, 1);
46 +$app->addCustomAction('comment_updated', 'ActivityHandler@logCommentUpdateActivity', 10, 2);
47 +$app->addCustomAction('comment_deleted', 'ActivityHandler@logCommentDeleteActivity', 10, 1);
29 48 $app->addCustomAction('subtask_added', 'ActivityHandler@logSubtaskAddedActivity', 10, 2);
49 +$app->addCustomAction('subtask_cloned', 'ActivityHandler@logSubtaskCloneActivity', 10, 2);
50 +$app->addCustomAction('subtask_group_created', 'ActivityHandler@logSubtaskGroupAddedActivity', 10, 2);
30 51 $app->addCustomAction('subtask_deleted_activity', 'ActivityHandler@logSubtaskDeletedActivity', 10, 2);
52 +$app->addCustomAction('subtask_group_deleted_activity', 'ActivityHandler@logSubtaskGroupDeletedActivity', 10, 2);
53 +$app->addCustomAction('subtask_group_title_updated', 'ActivityHandler@logSubtaskGroupTitleUpdatedActivity', 10, 2);
31 54 $app->addCustomAction('task_completed_activity', 'ActivityHandler@logTaskCompletedOrReopenActivity', 10, 2);
32 -$app->addCustomAction('task_moved_to_new_stage', 'ActivityHandler@logTaskStageUpdatedActivity', 10, 2);
33 -$app->addCustomAction('task_assignee_changed', 'ActivityHandler@logAssigneeChangeActivity', 10, 3);
55 +$app->addCustomAction('task_stage_updated', 'ActivityHandler@logTaskStageUpdatedActivity', 10, 3);
56 +$app->addCustomAction('task_assignee_added', 'ActivityHandler@logAssigneeAddedActivity', 10, 2);
57 +$app->addCustomAction('task_assignee_removed', 'ActivityHandler@logAssigneeRemovedActivity', 10, 2);
34 58 $app->addCustomAction('task_added_from_fluent_form', 'ActivityHandler@taskAddedFromFluentForms', 10, 1);
59 +$app->addCustomAction('repeat_task_created', 'ActivityHandler@logRepeatTaskCreatedActivity', 10, 2);
60 +$app->addCustomAction('repeat_task_set', 'ActivityHandler@logRepeatTaskSet', 10, 1);
61 +$app->addCustomAction('repeat_task_updated', 'ActivityHandler@logRepeatTaskUpdated', 10, 1);
35 62 $app->addCustomAction('task_created', 'ActivityHandler@logTaskCreationActivity', 10, 1);
36 63 $app->addCustomAction('task_content_updated', 'ActivityHandler@logTaskContentUpdatedActivity', 10, 3);
37 64 $app->addCustomAction('task_deleted', 'TaskHandler@taskDeleted', 10, 1);
65 +$app->addCustomAction('task_archived', 'ActivityHandler@taskArchived', 10, 1);
66 +//$app->addCustomAction('task_custom_field_changed', 'ActivityHandler@logCustomFieldActivity', 10, 5); // commented for now, will implement later
38 67
39 68 $app->addCustomAction('board_created', 'BoardHandler@boardCreated', 10, 1);
69 +$app->addCustomAction('board_created_from_automation', 'BoardHandler@boardCreatedFromAutomation', 10, 1);
40 70 $app->addCustomAction('before_board_deleted', 'BoardHandler@beforeBoardDeleted', 10, 2);
41 -$app->addCustomAction('board_deleted', 'BoardHandler@boardDeleted', 10, 1);
42 71 $app->addCustomAction('board_updated', 'BoardHandler@boardUpdated', 10, 2);
43 -$app->addCustomAction('board_stage_updated', 'BoardHandler@boardStageUpdated', 10, 3);
72 +$app->addCustomAction('stage_updated', 'BoardHandler@boardStageUpdated', 10, 3);
44 73 $app->addCustomAction('board_stage_moved', 'BoardHandler@boardStageMoved', 10, 2);
45 -$app->addCustomAction('board_stage_dragged', 'BoardHandler@boardStageDragged', 10, 1);
46 74 $app->addCustomAction('board_stage_added', 'BoardHandler@boardStageAdded', 10, 2);
47 75 $app->addCustomAction('stage_deleted', 'BoardHandler@boardStageDeleted', 10, 2);
48 -$app->addCustomAction('board_stage_archived', 'BoardHandler@boardStageArchived', 10, 2);
76 +$app->addCustomAction('stage_archived', 'BoardHandler@boardStageArchived', 10, 2);
49 77 $app->addCustomAction('board_stage_restored', 'BoardHandler@boardArchivedStageRestore', 10, 2);
50 78 $app->addCustomAction('task_created', 'BoardHandler@taskCreatedOnBoard', 10, 1);
51 79 $app->addCustomAction('before_task_deleted', 'BoardHandler@beforeTaskDeleted', 10, 2);
52 80 $app->addCustomAction('task_deleted', 'BoardHandler@taskdeleted', 10, 1);
53 81 $app->addCustomAction('task_moved_from_board', 'BoardHandler@taskMovedFromBoard', 10, 3);
54 -$app->addCustomAction('board_task_archived', 'BoardHandler@taskarchivedOnBoard', 10, 1);
82 +$app->addCustomAction('task_moved_from_board', 'ActivityHandler@taskMovedFromBoard', 10, 3);
83 +$app->addCustomAction('task_archived', 'BoardHandler@taskArchivedOnBoard', 10, 1);
84 +$app->addCustomAction('tasks_moved_between_stages', 'BoardHandler@tasksMovedBetweenStages', 10, 4);
55 85 $app->addCustomAction('board_member_added', 'BoardHandler@boardMemberAdded', 10, 2);
86 +$app->addCustomAction('board_viewer_added', 'BoardHandler@boardViewerAdded', 10, 2);
56 87 $app->addCustomAction('board_member_removed', 'BoardHandler@boardMemberRemoved', 10, 2);
57 88 $app->addCustomAction('board_admin_added', 'BoardHandler@boardAdminAdded', 10, 2);
58 89 $app->addCustomAction('board_admin_removed', 'BoardHandler@boardAdminRemoved', 10, 2);
59 90 $app->addCustomAction('associate_user_add_change_remove_activity', 'ActivityHandler@associateUserAddChangeRemoveActivity', 10, 3);
@@ -60,26 +91,30 @@
60 91 $app->addCustomAction('task_assignee_leave', 'ActivityHandler@taskAssigneeLeave', 10, 1);
61 92 $app->addCustomAction('task_assignee_join', 'ActivityHandler@taskAssigneeJoin', 10, 1);
62 93 $app->addCustomAction('task_label', 'ActivityHandler@taskLabelActivity', 10, 3);
63 94 $app->addCustomAction('label_manage_for_task_activity', 'ActivityHandler@labelManageForTaskActivity', 10, 2);
64 -$app->addCustomAction('board_label', 'BoardHandler@boardLabelActivity', 10, 2);
95 +$app->addCustomAction('board_label_created', 'BoardHandler@boardLabelCreatedActivity', 10, 1);
96 +$app->addCustomAction('board_label_updated', 'BoardHandler@boardLabelUpdatedActivity', 10, 1);
97 +$app->addCustomAction('board_label_deleted', 'BoardHandler@boardLabelDeletedActivity', 10, 1);
65 98 $app->addCustomAction('board_stages_reordered', 'BoardHandler@boardStagesReOrdered', 10, 2);
99 +$app->addCustomAction('default_assignees_updated', 'BoardHandler@defaultAssigneesUpdated', 10, 2);
66 100
67 -
68 -$app->addCustomAction('task_comment_added', 'NotificationHandler@addCommentNotification', 10, 1);
69 -$app->addCustomAction('mention_comment_notification', 'NotificationHandler@mentionInCommentNotification', 10, 3);
101 +$app->addCustomAction('comment_created', 'NotificationHandler@addCommentNotification', 10, 1);
102 +$app->addCustomAction('mention_comment_notification', 'NotificationHandler@mentionInCommentNotification', 10, 2);
70 103 $app->addCustomAction('subtask_added', 'NotificationHandler@addSubtaskNotification', 10, 2);
71 -$app->addCustomAction('task_date_changed', 'NotificationHandler@changeDateNotification', 10, 2);
72 -$app->addCustomAction('task_moved_to_new_stage', 'NotificationHandler@changeStageNotification', 10, 2);
104 +$app->addCustomAction('task_due_date_changed', 'NotificationHandler@changeDueDateNotification', 10, 2);
105 +$app->addCustomAction('task_start_date_changed', 'NotificationHandler@changeStartDateNotification', 10, 2);
106 +$app->addCustomAction('task_stage_updated', 'NotificationHandler@changeStageNotification', 10, 2);
73 107 $app->addCustomAction('task_priority_changed', 'NotificationHandler@changePriorityNotification', 10, 2);
74 -$app->addCustomAction('board_task_archived', 'NotificationHandler@taskArchiveNotification', 10, 1);
75 -$app->addCustomAction('task_content_updated', 'NotificationHandler@changeTitleOrDescriptionNotification', 10, 3);
108 +$app->addCustomAction('task_archived', 'NotificationHandler@taskArchiveNotification', 10, 1);
109 +$app->addCustomAction('task_content_updated', 'NotificationHandler@changeTitleNotification', 10, 3);
76 110 //$app->addCustomAction('description_changed_notification', 'NotificationHandler@changeDescriptionNotification', 10, 1);
77 111 $app->addCustomAction('board_changed_notification', 'NotificationHandler@changeBoardNotification', 10, 2);
78 -$app->addCustomAction('task_assignee_changed', 'NotificationHandler@assigneeAddedNotification', 10, 3);
112 +$app->addCustomAction('task_assignee_added', 'NotificationHandler@assigneeAddedNotification', 10, 2);
113 +$app->addCustomAction('task_assignee_removed', 'NotificationHandler@assigneeRemovedNotification', 10, 2);
79 114
80 -
81 115 $app->addCustomAction('one_time_schedule_send_email_for_comment', 'ScheduleHandler@sendEmailForComment', 10, 3);
116 +$app->addCustomAction('one_time_schedule_send_email_for_mention', 'ScheduleHandler@sendEmailForMention', 10, 3);
82 117 $app->addCustomAction('one_time_schedule_send_email_for_add_assignee', 'ScheduleHandler@sendEmailForAddAssignee', 10, 3);
83 118 $app->addCustomAction('one_time_schedule_send_email_for_remove_assignee', 'ScheduleHandler@sendEmailForRemoveAssignee', 10, 3);
84 119 $app->addCustomAction('one_time_schedule_send_email_for_stage_change', 'ScheduleHandler@sendEmailForChangeStage', 10, 3);
85 120
@@ -89,15 +124,19 @@
89 124 $app->addCustomAction('one_time_schedule_send_email_for_removed_from_task', 'ScheduleHandler@sendEmailForChangeStage', 10, 3);
90 125
91 126 $app->addCustomAction('task_attachment_added', 'ActivityHandler@taskAttachmentAdded', 10, 1);
92 127 $app->addCustomAction('task_attachment_deleted', 'ActivityHandler@taskAttachmentDeleted', 10, 1);
93 -$app->addCustomAction('task_attachment_deleted', 'TaskHandler@taskAttachmentDeleted', 10, 1);
128 +$app->addCustomAction('task_attachment_deleted', 'TaskHandler@taskAttachmentDeleted', 10, 2);
129 +// Removed: direct call is made from Comment::deleting to TaskHandler@commentImageDeleted
94 130 $app->addAction('deleted_user', 'BoardHandler@deleteUserRelatedData', 10, 3);
95 -$app->addAction('delete_attachment', 'FileHandler@mediaFileDeleted', 10, 1);
96 131
97 -$app->addCustomAction('send_invitation', 'ScheduleHandler@sendInvitationViaEmail', 10, 3);
132 +$app->addCustomAction('task_created', 'TaskHandler@onTaskCreated', 10, 1);
133 +$app->addCustomAction('comment_created', 'TaskHandler@onCommentCreated', 10, 1);
134 +$app->addCustomAction('assign_another_user', 'TaskHandler@onAssignAnotherUser', 10, 2);
135 +$app->addCustomAction('board_background_updated', 'BoardHandler@backgroundUpdated', 10, 2);
136 +$app->addCustomAction('task_cloned', 'TaskHandler@taskCloned', 10, 2);
137 +$app->addCustomAction('task_cloned_activity', 'ActivityHandler@taskCloned', 10, 2);
98 138
99 -
100 139 if (defined('WP_CLI') && WP_CLI) {
101 140 \WP_CLI::add_command('fluent_boards', '\FluentBoards\App\Hooks\Cli\Commands');
102 141 }
103 142
@@ -102,22 +141,24 @@
102 141 }
103 142
104 143
105 144 add_action('init', function () {
145 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public routing parameter check, no data modification
106 146 if (!isset($_REQUEST['fbs'])) {
107 147 return;
108 148 }
109 149 });
110 150
111 -
112 151 /*
113 152 * IMPORTANT
114 153 * External Pages Handler
115 154 * Each Request must have fbs=1 as a query parameter, then the plugin will handle the request.
116 155 */
156 +// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public routing logic, security validated in individual handlers
117 157 if(isset($_GET['fbs']) && $_GET['fbs'] == 1) {
118 158
119 159 // For viewing attachment
160 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public routing logic, security validated in handler
120 161 if(isset($_GET['route']) && ($_GET['route'] == 'task')) {
121 162 add_action('init', function() {
122 163 (new \FluentBoardsPro\App\Hooks\Handlers\ExternalPages())->handleTaskWebhook();
123 164 });
@@ -122,5 +163,81 @@
122 163 (new \FluentBoardsPro\App\Hooks\Handlers\ExternalPages())->handleTaskWebhook();
123 164 });
124 165 }
125 166
167 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public file serving endpoint, security validated by hash in handler
168 + if(isset($_GET['fbs_comment_image'])) {
169 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public routing check, security validated in handler
170 + if(isset($_GET['fbs_type']) && $_GET['fbs_type'] == 'public_url') {
171 + add_action('init', function() {
172 + (new \FluentBoards\App\Hooks\Handlers\ExternalPages())->view_uploaded_comment_image();
173 + });
174 + } else {
175 + add_action('init', function() {
176 + (new \FluentBoards\App\Hooks\Handlers\ExternalPages())->view_comment_image();
177 + });
178 + }
179 +
180 + }
181 +
182 +}
183 +
184 +// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public redirect endpoint, no sensitive operations
185 +if(isset($_GET['redirect']) && $_GET['redirect'] == 'to_task') {
186 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public redirect endpoint, validation performed in handler
187 + if (isset($_GET['taskId'])) {
188 + add_action('init', function() {
189 + (new \FluentBoards\App\Hooks\Handlers\ExternalPages())->redirectToPage();
190 + });
191 + }
192 +}
193 +// Stage Archive Handler - Archive tasks when stage is archived, restore when stage is restored
194 +$app->addAction('fluent_boards/stage_archived_with_tasks', 'StageArchiveHandler@onStageArchived', 10, 2);
195 +$app->addAction('fluent_boards/stage_restored_with_tasks', 'StageArchiveHandler@onStageRestored', 10, 2);
196 +
197 +/*
198 + * MCP - Register Fluent Boards abilities for the WordPress Abilities API.
199 + *
200 + * This mirrors FluentCRM's MCP architecture: on WordPress installs without the
201 + * Abilities API / WP MCP Adapter, `wp_register_ability` is undefined and the
202 + * integration skips silently. Admins can opt out with the `mcp_enabled` option.
203 + */
204 +add_filter('fluent_kit/mcp_products', [\FluentBoards\App\Modules\MCP\MCPInit::class, 'registerToolkitProduct'], 10, 2);
205 +add_filter('fluent_kit/mcp_toggle_handlers', function ($handlers) {
206 + $handlers['fluent-boards'] = [
207 + 'get_enabled' => function () {
208 + return fluent_boards_get_option('mcp_enabled', 'yes') === 'yes';
209 + },
210 + 'set_enabled' => function ($enabled) {
211 + fluent_boards_update_option('mcp_enabled', $enabled ? 'yes' : 'no');
212 + },
213 + ];
214 +
215 + return $handlers;
216 +});
217 +
218 +add_action('init', function () {
219 + if (!function_exists('wp_register_ability')) {
220 + return;
221 + }
222 +
223 + if (fluent_boards_get_option('mcp_enabled', 'yes') !== 'yes') {
224 + return;
225 + }
226 +
227 + (new \FluentBoards\App\Modules\MCP\MCPInit())->init();
228 +}, 5);
229 +
230 +$invalidateMcpContext = [\FluentBoards\App\Modules\MCP\Tools\ContextTools::class, 'invalidateCache'];
231 +foreach ([
232 + 'fluent_boards/board_created',
233 + 'fluent_boards/board_updated',
234 + 'fluent_boards/before_board_deleted',
235 + 'fluent_boards/board_stage_added',
236 + 'fluent_boards/stage_updated',
237 + 'fluent_boards/stage_deleted',
238 + 'fluent_boards/board_label_created',
239 + 'fluent_boards/board_label_updated',
240 + 'fluent_boards/board_label_deleted',
241 +] as $mcpContextHook) {
242 + add_action($mcpContextHook, $invalidateMcpContext);
126 243 }