| @@ -62,8 +62,17 @@ | ||
| 62 | 62 | $type = $request->getSafe('type', 'sanitize_text_field', 'to-do'); |
| 63 | 63 | |
| 64 | 64 | $order = $request->getSafe('order', 'sanitize_text_field', 'created_at'); |
| 65 | 65 | $orderBy = $request->getSafe('orderBy', 'sanitize_text_field', 'DESC'); |
| 66 | + $order = is_string($order) ? sanitize_sql_orderby($order) : false; | |
| 67 | + if (!in_array($order, ['created_at', 'title', 'updated_at', 'id'], true)) { | |
| 68 | + $order = 'created_at'; | |
| 69 | + } | |
| 70 | + $orderBy = is_string($orderBy) ? strtoupper($orderBy) : 'DESC'; | |
| 71 | + if (!in_array($orderBy, ['ASC', 'DESC'], true)) { | |
| 72 | + $orderBy = 'DESC'; | |
| 73 | + } | |
| 74 | + | |
| 66 | 75 | $searchInput = $request->getSafe('searchInput', 'sanitize_text_field'); |
| 67 | 76 | |
| 68 | 77 | $option = $request->getSafe('option', 'sanitize_text_field'); |
| 69 | 78 | $folderId = $request->getSafe('fid', 'intval'); // Get folder ID from request |
| @@ -85,10 +94,10 @@ | ||
| 85 | 94 | |
| 86 | 95 | // Scope pinned before pagination, from the same id source as getBoardCounts(), |
| 87 | 96 | // so the pinned page and board_counts.pinned always agree. Filtering pinned |
| 88 | 97 | // after pagination would drop pinned boards that fall on a later active page. |
| 98 | + $pinnedIds = $this->boardService->getPinnedBoardIds(); | |
| 89 | 99 | if ($option == 'pinned') { |
| 90 | - $pinnedIds = $this->boardService->getPinnedBoardIds(); | |
| 91 | 100 | |
| 92 | 101 | $relatedBoardsQuery = $pinnedIds |
| 93 | 102 | ? $relatedBoardsQuery->whereIn('id', $pinnedIds) |
| 94 | 103 | : $relatedBoardsQuery->where('id', 0); |
| @@ -110,15 +119,15 @@ | ||
| 110 | 119 | } |
| 111 | 120 | |
| 112 | 121 | $relatedBoards = $relatedBoardsQuery->orderBy($order, $orderBy) |
| 113 | 122 | ->withCount('completedTasks') |
| 114 | - ->with('stages', 'users') | |
| 123 | + ->with('stages', 'users', 'boardMeta', 'viewerMembership') | |
| 115 | 124 | ->paginate($per_page); |
| 116 | 125 | |
| 117 | 126 | foreach ($relatedBoards as $relatedBoard) { |
| 118 | 127 | $relatedBoard->description = DescriptionMarkdownConverter::normalize($relatedBoard->description); |
| 119 | 128 | $relatedBoard->users = Helper::sanitizeUserCollections($relatedBoard->users); |
| 120 | - $relatedBoard->is_pinned = $this->boardService->isPinned($relatedBoard->id); | |
| 129 | + $relatedBoard->is_pinned = in_array((int) $relatedBoard->id, $pinnedIds, true); | |
| 121 | 130 | } |
| 122 | 131 | |
| 123 | 132 | $response = [ |
| 124 | 133 | 'boards' => $relatedBoards, |
| @@ -535,10 +544,12 @@ | ||
| 535 | 544 | return $this->sendError($e->getMessage(), 404); |
| 536 | 545 | } |
| 537 | 546 | } |
| 538 | 547 | |
| 548 | + /** Return board details with a cursor covering changes made during retrieval. */ | |
| 539 | 549 | public function find(Request $request, $board_id) |
| 540 | 550 | { |
| 551 | + $syncStartedAt = current_time('mysql'); | |
| 541 | 552 | $board = Board::findOrFail($board_id); |
| 542 | 553 | $board->description = DescriptionMarkdownConverter::normalize($board->description); |
| 543 | 554 | $includeArchived = filter_var($request->get('include_archived', false), FILTER_VALIDATE_BOOLEAN); |
| 544 | 555 | $board->background = maybe_unserialize($board->background); |
| @@ -578,9 +589,9 @@ | ||
| 578 | 589 | $board = apply_filters('fluent_boards/board_find', $board); |
| 579 | 590 | |
| 580 | 591 | return [ |
| 581 | 592 | 'board' => $board, |
| 582 | - 'synced_at' => current_time('mysql') | |
| 593 | + 'synced_at' => $syncStartedAt | |
| 583 | 594 | ]; |
| 584 | 595 | } |
| 585 | 596 | |
| 586 | 597 | public function update(Request $request, $board_id) |
| @@ -867,8 +878,9 @@ | ||
| 867 | 878 | |
| 868 | 879 | |
| 869 | 880 | return [ |
| 870 | 881 | 'message' => __('Member added successfully', 'fluent-boards'), |
| 882 | + 'is_admin' => PermissionManager::isAdmin($memberId), | |
| 871 | 883 | 'member' => Helper::sanitizeUserCollections($member) |
| 872 | 884 | ]; |
| 873 | 885 | } |
| 874 | 886 | |
| @@ -1221,8 +1233,11 @@ | ||
| 1221 | 1233 | |
| 1222 | 1234 | return (bool) $permissionManager::currentUserCan('fcrm_read_contacts'); |
| 1223 | 1235 | } |
| 1224 | 1236 | |
| 1237 | + /** | |
| 1238 | + * Duplicate a board with the selected board data options. | |
| 1239 | + */ | |
| 1225 | 1240 | public function duplicateBoard(Request $request, $board_id) |
| 1226 | 1241 | { |
| 1227 | 1242 | $boardData = $this->taskSanitizeAndValidate($request->get('board'), [ |
| 1228 | 1243 | 'title' => 'required|string' |
| @@ -1231,8 +1246,10 @@ | ||
| 1231 | 1246 | $boardData['source_board_id'] = $board_id; |
| 1232 | 1247 | |
| 1233 | 1248 | $isWithLabels = $request->getSafe('isWithLabels'); |
| 1234 | 1249 | $isWithTasks = $request->getSafe('isWithTasks'); |
| 1250 | + $isWithMembers = $request->getSafe('isWithMembers'); | |
| 1251 | + $isWithCustomFields = $request->getSafe('isWithCustomFields', 'sanitize_text_field', 'yes'); | |
| 1235 | 1252 | $isWithTemplates = $request->getSafe('isWithTemplates'); |
| 1236 | 1253 | |
| 1237 | 1254 | try { |
| 1238 | 1255 | if(!PermissionManager::isAdmin()) { |
| @@ -1240,9 +1257,14 @@ | ||
| 1240 | 1257 | throw new \Exception(esc_html($errorMessage), 400); |
| 1241 | 1258 | } |
| 1242 | 1259 | //create board |
| 1243 | 1260 | $newBoard = $this->boardService->copyBoard($boardData); |
| 1261 | + $customFieldMap = []; | |
| 1244 | 1262 | |
| 1263 | + if ($isWithCustomFields == 'yes') { | |
| 1264 | + $customFieldMap = $this->boardService->copyCustomFieldsOfBoard($board_id, $newBoard); | |
| 1265 | + } | |
| 1266 | + | |
| 1245 | 1267 | //label copy |
| 1246 | 1268 | $labelMap = []; |
| 1247 | 1269 | |
| 1248 | 1270 | if ($isWithLabels == 'yes') { |
| @@ -1253,9 +1275,13 @@ | ||
| 1253 | 1275 | $stageMapForCopyingTask = $this->stageService->copyStagesOfBoard($newBoard, $board_id, $isWithTemplates); |
| 1254 | 1276 | |
| 1255 | 1277 | //copy tasks of selected stages |
| 1256 | 1278 | if ($isWithTasks == 'yes') { |
| 1257 | - $this->taskService->copyTasks($board_id, $stageMapForCopyingTask, $newBoard, $labelMap,$isWithTemplates); | |
| 1279 | + $this->taskService->copyTasks($board_id, $stageMapForCopyingTask, $newBoard, $labelMap, $isWithTemplates, $customFieldMap); | |
| 1280 | + } | |
| 1281 | + | |
| 1282 | + if ($isWithMembers == 'yes') { | |
| 1283 | + $this->boardService->copyMembersOfBoard($board_id, $newBoard); | |
| 1258 | 1284 | } |
| 1259 | 1285 | |
| 1260 | 1286 | return $this->sendSuccess([ |
| 1261 | 1287 | 'board' => $newBoard, |