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/Modules/MCP/Helpers/MCPHelper.php +55 -8 1.95.2 → 2.1.0 View file →
@@ -5,8 +5,9 @@
5 5 use FluentBoards\App\Models\Board;
6 6 use FluentBoards\App\Models\Stage;
7 7 use FluentBoards\App\Models\Task;
8 8 use FluentBoards\App\Services\Constant;
9 +use FluentBoards\App\Services\DescriptionMarkdownConverter;
9 10 use FluentBoards\App\Services\Helper;
10 11 use FluentBoards\App\Services\PermissionManager;
11 12
12 13 /**
@@ -14,8 +15,9 @@
14 15 */
15 16 class MCPHelper
16 17 {
17 18 const TASK_HISTORY_LIMIT = 20;
19 + const MAX_MARKDOWN_DESCRIPTION_LENGTH = 65535;
18 20
19 21 public static function error($code, $message, $data = [])
20 22 {
21 23 return new \WP_Error($code, $message, $data);
@@ -31,8 +33,25 @@
31 33 'per_page' => max(1, min($maxPerPage, $perPage)),
32 34 ];
33 35 }
34 36
37 + public static function sanitizeMarkdown($value)
38 + {
39 + $value = wp_check_invalid_utf8((string) $value);
40 + $value = str_replace(["\r\n", "\r", "\0"], ["\n", "\n", ""], $value);
41 + $value = self::descriptionToMarkdown($value);
42 +
43 + if (strlen($value) <= self::MAX_MARKDOWN_DESCRIPTION_LENGTH) {
44 + return $value;
45 + }
46 +
47 + if (function_exists('mb_strcut')) {
48 + return mb_strcut($value, 0, self::MAX_MARKDOWN_DESCRIPTION_LENGTH);
49 + }
50 +
51 + return substr($value, 0, self::MAX_MARKDOWN_DESCRIPTION_LENGTH);
52 + }
53 +
35 54 public static function resolveBoard($params)
36 55 {
37 56 $boardId = isset($params['board_id']) ? absint($params['board_id']) : 0;
38 57 if (!$boardId) {
@@ -122,9 +141,9 @@
122 141 {
123 142 return [
124 143 'id' => (int) $board->id,
125 144 'title' => $board->title,
126 - 'description' => $board->description,
145 + 'description' => self::descriptionToMarkdown($board->description),
127 146 'type' => $board->type,
128 147 'currency' => $board->currency,
129 148 'created_by' => (int) $board->created_by,
130 149 'archived_at' => self::toIso8601($board->archived_at),
@@ -140,9 +159,9 @@
140 159 {
141 160 $data = self::formatBoardSummary($board);
142 161 $data['stages'] = self::formatStageList($board->stages ?? []);
143 162 $data['labels'] = self::formatLabelList($board->labels ?? []);
144 - $data['members'] = self::formatUserList($board->users ?? []);
163 + $data['members'] = self::formatUserList($board->users ?? [], $board->id);
145 164
146 165 if ($includeTasks) {
147 166 $tasks = Task::with(['stage', 'labels', 'assignees'])
148 167 ->where('board_id', $board->id)
@@ -180,9 +199,9 @@
180 199 'created_at' => self::toIso8601($task->created_at),
181 200 'updated_at' => self::toIso8601($task->updated_at),
182 201 'stage' => $task->stage ? self::formatStage($task->stage) : null,
183 202 'labels' => self::formatLabelList($task->labels ?? []),
184 - 'assignees' => self::formatUserList($task->assignees ?? []),
203 + 'assignees' => self::formatUserList($task->assignees ?? [], $task->board_id),
185 204 ];
186 205 }
187 206
188 207 public static function formatTask($task)
@@ -187,12 +206,12 @@
187 206
188 207 public static function formatTask($task)
189 208 {
190 209 $data = self::formatTaskSummary($task);
191 - $data['description'] = $task->description;
210 + $data['description'] = self::descriptionToMarkdown($task->description);
192 211 $data['settings'] = $task->settings;
193 212 $data['board'] = $task->board ? self::formatBoardSummary($task->board) : null;
194 - $data['watchers'] = self::formatUserList($task->watchers ?? []);
213 + $data['watchers'] = self::formatUserList($task->watchers ?? [], $task->board_id);
195 214 $data['comments'] = self::formatCommentList(self::limitItems($task->comments ?? [], self::TASK_HISTORY_LIMIT));
196 215 $data['comments_limited_to'] = self::TASK_HISTORY_LIMIT;
197 216 $data['activities'] = self::formatActivityList(self::limitItems($task->activities ?? [], self::TASK_HISTORY_LIMIT));
198 217 $data['activities_limited_to'] = self::TASK_HISTORY_LIMIT;
@@ -199,8 +218,13 @@
199 218
200 219 return $data;
201 220 }
202 221
222 + public static function descriptionToMarkdown($description)
223 + {
224 + return DescriptionMarkdownConverter::normalize($description);
225 + }
226 +
203 227 public static function formatTaskList($tasks)
204 228 {
205 229 $items = [];
206 230 foreach ($tasks as $task) {
@@ -241,8 +265,12 @@
241 265 'title' => $label->title,
242 266 'slug' => $label->slug,
243 267 'color' => $label->color,
244 268 'bg_color' => $label->bg_color,
269 + 'color_preset' => \FluentBoards\Framework\Support\Arr::get(
270 + (array) $label->settings,
271 + \FluentBoards\App\Services\Constant::LABEL_COLOR_PRESET_SETTING
272 + ),
245 273 'position' => isset($label->position) ? (float) $label->position : null,
246 274 'archived_at' => self::toIso8601($label->archived_at),
247 275 ];
248 276 }
@@ -248,9 +276,9 @@
248 276 }
249 277 return $items;
250 278 }
251 279
252 - public static function formatUserList($users)
280 + public static function formatUserList($users, $boardId)
253 281 {
254 282 $items = [];
255 283 foreach ($users as $user) {
256 284 $name = trim((string) ($user->display_name ?? ''));
@@ -261,12 +289,31 @@
261 289 $items[] = [
262 290 'id' => isset($user->ID) ? (int) $user->ID : (int) ($user->id ?? 0),
263 291 'display_name' => $name,
264 292 'email' => $user->user_email ?? '',
265 - 'avatar' => !empty($user->user_email) ? fluent_boards_user_avatar($user->user_email, $name) : '',
293 + 'avatar' => '',
266 294 ];
267 295 }
268 - return $items;
296 + // Reuse the board permission lookup across user lists in this request.
297 + static $boardManagerResults = [];
298 + $isBoardManager = null;
299 + if (!current_user_can('list_users')) {
300 + $cacheKey = get_current_blog_id() . ':' . get_current_user_id() . ':' . (int) $boardId;
301 + if (!array_key_exists($cacheKey, $boardManagerResults)) {
302 + $boardManagerResults[$cacheKey] = PermissionManager::isBoardManager($boardId);
303 + }
304 + $isBoardManager = $boardManagerResults[$cacheKey];
305 + }
306 + $sanitizedItems = Helper::sanitizeUsersArray($items, $boardId, $isBoardManager);
307 + foreach ($sanitizedItems as $index => &$item) {
308 + // Only generate avatars when the policy permits disclosure of the email.
309 + if ($item['email'] !== '' && $item['email'] === $items[$index]['email']) {
310 + $item['avatar'] = fluent_boards_user_avatar($item['email'], $item['display_name']);
311 + }
312 + }
313 + unset($item);
314 +
315 + return $sanitizedItems;
269 316 }
270 317
271 318 public static function formatCommentList($comments)
272 319 {