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 +95 -11 1.412.1.0 View file →
@@ -18,11 +18,28 @@
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 40 $app->addCustomAction('task_due_date_changed', 'ActivityHandler@logDueDateActivity', 10, 2);
41 +$app->addCustomAction('task_due_date_removed', 'ActivityHandler@logDueDateRemoveActivity', 10, 1);
25 42 $app->addCustomAction('task_start_date_changed', 'ActivityHandler@logStartDateActivity', 10, 2);
26 43
27 44 $app->addCustomAction('task_priority_changed', 'ActivityHandler@logPriorityChangeActivity', 10, 2);
28 45 $app->addCustomAction('comment_created', 'ActivityHandler@logCommentCreateActivity', 10, 1);
@@ -28,25 +45,33 @@
28 45 $app->addCustomAction('comment_created', 'ActivityHandler@logCommentCreateActivity', 10, 1);
29 46 $app->addCustomAction('comment_updated', 'ActivityHandler@logCommentUpdateActivity', 10, 2);
30 47 $app->addCustomAction('comment_deleted', 'ActivityHandler@logCommentDeleteActivity', 10, 1);
31 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);
32 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);
33 54 $app->addCustomAction('task_completed_activity', 'ActivityHandler@logTaskCompletedOrReopenActivity', 10, 2);
34 -$app->addCustomAction('task_stage_updated', 'ActivityHandler@logTaskStageUpdatedActivity', 10, 2);
55 +$app->addCustomAction('task_stage_updated', 'ActivityHandler@logTaskStageUpdatedActivity', 10, 3);
35 56 $app->addCustomAction('task_assignee_added', 'ActivityHandler@logAssigneeAddedActivity', 10, 2);
36 57 $app->addCustomAction('task_assignee_removed', 'ActivityHandler@logAssigneeRemovedActivity', 10, 2);
37 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);
38 62 $app->addCustomAction('task_created', 'ActivityHandler@logTaskCreationActivity', 10, 1);
39 63 $app->addCustomAction('task_content_updated', 'ActivityHandler@logTaskContentUpdatedActivity', 10, 3);
40 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
41 67
42 68 $app->addCustomAction('board_created', 'BoardHandler@boardCreated', 10, 1);
69 +$app->addCustomAction('board_created_from_automation', 'BoardHandler@boardCreatedFromAutomation', 10, 1);
43 70 $app->addCustomAction('before_board_deleted', 'BoardHandler@beforeBoardDeleted', 10, 2);
44 -$app->addCustomAction('board_deleted', 'BoardHandler@boardDeleted', 10, 1);
45 71 $app->addCustomAction('board_updated', 'BoardHandler@boardUpdated', 10, 2);
46 72 $app->addCustomAction('stage_updated', 'BoardHandler@boardStageUpdated', 10, 3);
47 73 $app->addCustomAction('board_stage_moved', 'BoardHandler@boardStageMoved', 10, 2);
48 -$app->addCustomAction('board_stage_dragged', 'BoardHandler@boardStageDragged', 10, 1);
49 74 $app->addCustomAction('board_stage_added', 'BoardHandler@boardStageAdded', 10, 2);
50 75 $app->addCustomAction('stage_deleted', 'BoardHandler@boardStageDeleted', 10, 2);
51 76 $app->addCustomAction('stage_archived', 'BoardHandler@boardStageArchived', 10, 2);
52 77 $app->addCustomAction('board_stage_restored', 'BoardHandler@boardArchivedStageRestore', 10, 2);
@@ -53,9 +78,11 @@
53 78 $app->addCustomAction('task_created', 'BoardHandler@taskCreatedOnBoard', 10, 1);
54 79 $app->addCustomAction('before_task_deleted', 'BoardHandler@beforeTaskDeleted', 10, 2);
55 80 $app->addCustomAction('task_deleted', 'BoardHandler@taskdeleted', 10, 1);
56 81 $app->addCustomAction('task_moved_from_board', 'BoardHandler@taskMovedFromBoard', 10, 3);
57 -$app->addCustomAction('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);
58 85 $app->addCustomAction('board_member_added', 'BoardHandler@boardMemberAdded', 10, 2);
59 86 $app->addCustomAction('board_viewer_added', 'BoardHandler@boardViewerAdded', 10, 2);
60 87 $app->addCustomAction('board_member_removed', 'BoardHandler@boardMemberRemoved', 10, 2);
61 88 $app->addCustomAction('board_admin_added', 'BoardHandler@boardAdminAdded', 10, 2);
@@ -78,9 +105,9 @@
78 105 $app->addCustomAction('task_start_date_changed', 'NotificationHandler@changeStartDateNotification', 10, 2);
79 106 $app->addCustomAction('task_stage_updated', 'NotificationHandler@changeStageNotification', 10, 2);
80 107 $app->addCustomAction('task_priority_changed', 'NotificationHandler@changePriorityNotification', 10, 2);
81 108 $app->addCustomAction('task_archived', 'NotificationHandler@taskArchiveNotification', 10, 1);
82 -$app->addCustomAction('task_content_updated', 'NotificationHandler@changeTitleOrDescriptionNotification', 10, 3);
109 +$app->addCustomAction('task_content_updated', 'NotificationHandler@changeTitleNotification', 10, 3);
83 110 //$app->addCustomAction('description_changed_notification', 'NotificationHandler@changeDescriptionNotification', 10, 1);
84 111 $app->addCustomAction('board_changed_notification', 'NotificationHandler@changeBoardNotification', 10, 2);
85 112 $app->addCustomAction('task_assignee_added', 'NotificationHandler@assigneeAddedNotification', 10, 2);
86 113 $app->addCustomAction('task_assignee_removed', 'NotificationHandler@assigneeRemovedNotification', 10, 2);
@@ -97,18 +124,19 @@
97 124 $app->addCustomAction('one_time_schedule_send_email_for_removed_from_task', 'ScheduleHandler@sendEmailForChangeStage', 10, 3);
98 125
99 126 $app->addCustomAction('task_attachment_added', 'ActivityHandler@taskAttachmentAdded', 10, 1);
100 127 $app->addCustomAction('task_attachment_deleted', 'ActivityHandler@taskAttachmentDeleted', 10, 1);
101 -$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
102 130 $app->addAction('deleted_user', 'BoardHandler@deleteUserRelatedData', 10, 3);
103 -$app->addAction('delete_attachment', 'FileHandler@mediaFileDeleted', 10, 1);
104 131
105 -$app->addCustomAction('send_invitation', 'ScheduleHandler@sendInvitationViaEmail', 10, 3);
106 132 $app->addCustomAction('task_created', 'TaskHandler@onTaskCreated', 10, 1);
107 133 $app->addCustomAction('comment_created', 'TaskHandler@onCommentCreated', 10, 1);
108 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);
109 138
110 -
111 139 if (defined('WP_CLI') && WP_CLI) {
112 140 \WP_CLI::add_command('fluent_boards', '\FluentBoards\App\Hooks\Cli\Commands');
113 141 }
114 142
@@ -113,22 +141,24 @@
113 141 }
114 142
115 143
116 144 add_action('init', function () {
145 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public routing parameter check, no data modification
117 146 if (!isset($_REQUEST['fbs'])) {
118 147 return;
119 148 }
120 149 });
121 150
122 -
123 151 /*
124 152 * IMPORTANT
125 153 * External Pages Handler
126 154 * Each Request must have fbs=1 as a query parameter, then the plugin will handle the request.
127 155 */
156 +// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public routing logic, security validated in individual handlers
128 157 if(isset($_GET['fbs']) && $_GET['fbs'] == 1) {
129 158
130 159 // For viewing attachment
160 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public routing logic, security validated in handler
131 161 if(isset($_GET['route']) && ($_GET['route'] == 'task')) {
132 162 add_action('init', function() {
133 163 (new \FluentBoardsPro\App\Hooks\Handlers\ExternalPages())->handleTaskWebhook();
134 164 });
@@ -133,10 +163,12 @@
133 163 (new \FluentBoardsPro\App\Hooks\Handlers\ExternalPages())->handleTaskWebhook();
134 164 });
135 165 }
136 166
167 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public file serving endpoint, security validated by hash in handler
137 168 if(isset($_GET['fbs_comment_image'])) {
138 - if(isset($_GET['fbs_type']) == 'public_url') {
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') {
139 171 add_action('init', function() {
140 172 (new \FluentBoards\App\Hooks\Handlers\ExternalPages())->view_uploaded_comment_image();
141 173 });
142 174 } else {
@@ -148,9 +180,11 @@
148 180 }
149 181
150 182 }
151 183
184 +// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public redirect endpoint, no sensitive operations
152 185 if(isset($_GET['redirect']) && $_GET['redirect'] == 'to_task') {
186 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public redirect endpoint, validation performed in handler
153 187 if (isset($_GET['taskId'])) {
154 188 add_action('init', function() {
155 189 (new \FluentBoards\App\Hooks\Handlers\ExternalPages())->redirectToPage();
156 190 });
@@ -155,5 +189,55 @@
155 189 (new \FluentBoards\App\Hooks\Handlers\ExternalPages())->redirectToPage();
156 190 });
157 191 }
158 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);
159 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);
243 +}