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/Http/Controllers/UserController.php +178 -0 1.91.62.1.0 View file →
@@ -80,8 +80,10 @@
80 80 $boardIds = [];
81 81 if (is_array($rawBoardIds)) {
82 82 $boardIds = array_filter(array_map('intval', $rawBoardIds));
83 83 }
84 +
85 + $perPage = max(1, min(absint($request->getSafe('per_page', 'intval', 15)), 50));
84 86
85 87 $requestData = [
86 88 'page' => $request->getSafe('page', 'intval', 1),
87 89 'taskType' => $request->getSafe('taskType', 'sanitize_text_field'),
@@ -87,8 +89,9 @@
87 89 'taskType' => $request->getSafe('taskType', 'sanitize_text_field'),
88 90 'boardIds' => $boardIds,
89 91 'orderBy' => $request->getSafe('orderBy', 'sanitize_text_field'),
90 92 'order' => $request->getSafe('order', 'sanitize_text_field'),
93 + 'per_page' => $perPage
91 94 ];
92 95 try {
93 96 return $this->sendSuccess(
94 97 $this->userService->getMemberAssociatedTasks($user_id, $requestData)
@@ -97,8 +100,28 @@
97 100 return $this->sendError($e->getMessage(), 404);
98 101 }
99 102 }
100 103
104 + /**
105 + * Return profile task counts using the same categories shown on the dashboard.
106 + */
107 + public function getMemberTaskCounts(Request $request, $user_id)
108 + {
109 + $user_id = absint($user_id);
110 + $rawBoardIds = $request->getSafe('boardIds');
111 + $boardIds = is_array($rawBoardIds)
112 + ? array_filter(array_map('intval', $rawBoardIds))
113 + : [];
114 +
115 + try {
116 + return $this->sendSuccess([
117 + 'counts' => $this->userService->getMemberTaskCounts($user_id, $boardIds),
118 + ], 200);
119 + } catch (\Exception $e) {
120 + return $this->sendError($e->getMessage(), 404);
121 + }
122 + }
123 +
101 124 public function getMemberRelatedAcitivies(Request $request, $user_id)
102 125 {
103 126 $user_id = absint($user_id);
104 127 $page = $request->getSafe('page', 'intval', 1);
@@ -154,5 +177,160 @@
154 177 } catch (\Exception $e) {
155 178 return $this->sendError($e->getMessage(), 404);
156 179 }
157 180 }
181 +
182 + public function getMemberStats($user_id)
183 + {
184 + $user_id = absint($user_id);
185 +
186 + return $this->sendSuccess(
187 + $this->userService->getMemberStats($user_id),
188 + 200
189 + );
190 + }
191 +
192 + public function updateDisplayName(Request $request, $user_id)
193 + {
194 + $user_id = absint($user_id);
195 + $currentUserId = get_current_user_id();
196 +
197 + if ($currentUserId !== $user_id && !PermissionManager::isAdmin($currentUserId)) {
198 + return $this->sendError(
199 + __('You do not have permission to update this display name', 'fluent-boards'),
200 + 403
201 + );
202 + }
203 +
204 + $displayName = $request->getSafe('display_name', 'sanitize_text_field');
205 +
206 + if(!$displayName) {
207 + return $this->sendError('Display name is required', 400);
208 + }
209 +
210 + $updateResult = wp_update_user([
211 + 'ID' => $user_id,
212 + 'display_name' => $displayName,
213 + ]);
214 +
215 + if (is_wp_error($updateResult)) {
216 + return $this->sendError(
217 + $updateResult->get_error_message(),
218 + 400
219 + );
220 + }
221 +
222 + $user = User::findOrFail($user_id);
223 + $user = Helper::sanitizeUserCollections($user);
224 + $user->fbs_role = PermissionManager::isFluentBoardsAdmin($user_id) ? 'fbs_admin' : 'member';
225 + $user->is_wp_admin = user_can($user_id, 'manage_options') ? 'yes' : 'no';
226 +
227 + if (defined('FLUENTCRM')) {
228 + $subscriber = Subscriber::where('user_id', $user_id)->first();
229 + $user->fluentcrm_subscriber = $subscriber ?? null;
230 + }
231 +
232 + return $this->sendSuccess([
233 + 'message' => __('Display name has been updated', 'fluent-boards'),
234 + 'user' => $user,
235 + ], 200);
236 +
237 + }
238 +
239 + public function updateProfilePhoto(Request $request, $user_id)
240 +{
241 + $user_id = absint($user_id);
242 + $currentUserId = get_current_user_id();
243 +
244 + // Only the user themself OR an admin can change the profile picture
245 + if ($currentUserId !== $user_id && !PermissionManager::isAdmin($currentUserId)) {
246 + return $this->sendError(
247 + __('You do not have permission to update this profile photo', 'fluent-boards'),
248 + 403
249 + );
250 + }
251 +
252 + // We’ll use native WordPress upload handling
253 + if (empty($_FILES['photo']) || !empty($_FILES['photo']['error'])) {
254 + return $this->sendError(
255 + __('No photo uploaded or upload error', 'fluent-boards'),
256 + 400
257 + );
258 + }
259 +
260 + // Limit file size to 2MB for profile photos
261 + $maxSize = 2 * 1024 * 1024;
262 + if ($_FILES['photo']['size'] > $maxSize) {
263 + return $this->sendError(
264 + __('Photo must be under 2MB', 'fluent-boards'),
265 + 400
266 + );
267 + }
268 +
269 + $file = $_FILES['photo'];
270 +
271 + // Validate MIME type server-side (client-sent type is spoofable)
272 + $fileType = wp_check_filetype($file['name'], [
273 + 'jpg|jpeg|jpe' => 'image/jpeg',
274 + 'gif' => 'image/gif',
275 + 'png' => 'image/png',
276 + 'webp' => 'image/webp',
277 + ]);
278 +
279 + if (!$fileType['type']) {
280 + return $this->sendError(
281 + __('Invalid image type', 'fluent-boards'),
282 + 400
283 + );
284 + }
285 +
286 + // Load WordPress upload helpers
287 + if (!function_exists('wp_handle_upload')) {
288 + require_once ABSPATH . 'wp-admin/includes/file.php';
289 + }
290 +
291 + $overrides = [
292 + 'test_form' => false,
293 + 'mimes' => [
294 + 'jpg|jpeg|jpe' => 'image/jpeg',
295 + 'gif' => 'image/gif',
296 + 'png' => 'image/png',
297 + 'webp' => 'image/webp',
298 + ],
299 + ];
300 +
301 + $uploaded = wp_handle_upload($file, $overrides);
302 +
303 + if (isset($uploaded['error'])) {
304 + return $this->sendError(
305 + $uploaded['error'],
306 + 400
307 + );
308 + }
309 +
310 + $photoUrl = esc_url_raw($uploaded['url']);
311 +
312 + // Store custom profile photo in user meta
313 + update_user_meta($user_id, 'fbs_profile_photo', $photoUrl);
314 +
315 + // Reload user and sanitize same as in getMemberInfo()
316 + $user = User::findOrFail($user_id);
317 + $user = Helper::sanitizeUserCollections($user);
318 +
319 + // Override photo field if your sanitizer does not already use the meta
320 + $user->photo = $photoUrl;
321 +
322 + $user->fbs_role = PermissionManager::isFluentBoardsAdmin($user_id) ? 'fbs_admin' : 'member';
323 + $user->is_wp_admin = user_can($user_id, 'manage_options') ? 'yes' : 'no';
324 +
325 + if (defined('FLUENTCRM')) {
326 + $subscriber = Subscriber::where('user_id', $user_id)->first();
327 + $user->fluentcrm_subscriber = $subscriber ?? null;
328 + }
329 +
330 + return $this->sendSuccess([
331 + 'message' => __('Profile photo has been updated', 'fluent-boards'),
332 + 'photo_url' => $photoUrl,
333 + 'user' => $user,
334 + ], 200);
335 +}
158 336 }