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/Services/NotificationService.php +168 -13 1.95.22.1.0 View file →
@@ -129,8 +129,91 @@
129 129
130 130 return $settings;
131 131 }
132 132
133 + /**
134 + * Return board notification preferences with defaults and global watch fallbacks applied.
135 + */
136 + public function getBoardNotificationSettingsWithDefaults($boardId, $userId)
137 + {
138 + $settings = $this->getDefaultBoardNotificationSettings($userId);
139 +
140 + $boardSettings = $this->getBoardNotificationSettingsOfUser($boardId, $userId);
141 + if ($boardSettings && $boardSettings->preferences) {
142 + $preferences = maybe_unserialize($boardSettings->preferences);
143 + if (is_array($preferences)) {
144 + $settings = array_merge($settings, array_intersect_key($preferences, $settings));
145 + }
146 + }
147 +
148 + foreach ($settings as $key => $value) {
149 + $settings[$key] = $this->normalizePreferenceValue($value);
150 + }
151 +
152 + return $settings;
153 + }
154 +
155 + /**
156 + * Check if a board-scoped auto-watch preference is enabled for a user.
157 + */
158 + public function isBoardAutoWatchEnabled($boardId, $userId, $preferenceKey)
159 + {
160 + $settings = $this->getBoardNotificationSettingsWithDefaults($boardId, $userId);
161 +
162 + return array_key_exists($preferenceKey, $settings) && $this->normalizePreferenceValue($settings[$preferenceKey]);
163 + }
164 +
165 + /**
166 + * Get global watch preferences for fallback when a board has no saved watch keys.
167 + */
168 + private function getGlobalWatchNotificationSettings($userId)
169 + {
170 + $watchSettings = $this->getWatchNotificationDefaults();
171 +
172 + $globalSettings = $this->getGlobalNotificationSettingsOfUser($userId);
173 + if (!$globalSettings || !$globalSettings->value) {
174 + return $watchSettings;
175 + }
176 +
177 + $preferences = maybe_unserialize($globalSettings->value);
178 + if (!is_array($preferences)) {
179 + return $watchSettings;
180 + }
181 +
182 + foreach (array_keys($watchSettings) as $key) {
183 + if (array_key_exists($key, $preferences)) {
184 + $watchSettings[$key] = $this->normalizePreferenceValue($preferences[$key]);
185 + }
186 + }
187 +
188 + return $watchSettings;
189 + }
190 +
191 + private function getDefaultBoardNotificationSettings($userId)
192 + {
193 + return array_merge(
194 + Constant::BOARD_NOTIFICATION_TYPES,
195 + $this->getGlobalWatchNotificationSettings($userId)
196 + );
197 + }
198 +
199 + private function getWatchNotificationDefaults()
200 + {
201 + return [
202 + Constant::GLOBAL_EMAIL_NOTIFICATION_CREATING_TASK => true,
203 + Constant::GLOBAL_EMAIL_NOTIFICATION_COMMENTING => true,
204 + Constant::GLOBAL_EMAIL_NOTIFICATION_ASSIGNING => true,
205 + ];
206 + }
207 +
208 + /**
209 + * Convert stored preference values into strict booleans.
210 + */
211 + private function normalizePreferenceValue($value)
212 + {
213 + return true === $value || 1 === $value || '1' === $value || 'true' === $value;
214 + }
215 +
133 216 public function updateBoardNotificationSettings($newSettings, $id)
134 217 {
135 218 $userId = get_current_user_id();
136 219 $boardSettings = $this->getBoardNotificationSettingsOfUser($id, $userId);
@@ -136,13 +219,23 @@
136 219 $boardSettings = $this->getBoardNotificationSettingsOfUser($id, $userId);
137 220 if(empty($boardSettings)){
138 221 return;
139 222 }
140 - foreach ($newSettings as $index => $setting)
141 - {
142 - $newSettings[$index] = $setting == 'true' ? true : false;
223 +
224 + $allowedSettings = array_merge(
225 + Constant::BOARD_NOTIFICATION_TYPES,
226 + $this->getWatchNotificationDefaults()
227 + );
228 + $filteredSettings = [];
229 + foreach ($newSettings as $index => $setting) {
230 + if (!array_key_exists($index, $allowedSettings)) {
231 + continue;
232 + }
233 +
234 + $filteredSettings[$index] = $this->normalizePreferenceValue($setting);
143 235 }
144 - $boardSettings->preferences = $newSettings;
236 +
237 + $boardSettings->preferences = $filteredSettings;
145 238 $boardSettings->save();
146 239
147 240 }
148 241
@@ -165,8 +258,33 @@
165 258
166 259 return $wathersToSendEmail;
167 260 }
168 261
262 + /**
263 + * Get comment notification recipients as revocable user IDs for async delivery.
264 + *
265 + * @param int $taskId
266 + * @return array
267 + */
268 + public function getCommentRecipientUserIds($taskId)
269 + {
270 + $task = Task::findOrFail(absint($taskId));
271 + $currentUserId = get_current_user_id();
272 + $recipientUserIds = [];
273 +
274 + foreach ($task->watchers as $watcher) {
275 + $watcherId = absint($watcher->ID);
276 + if (
277 + $watcherId !== $currentUserId &&
278 + $this->checkIfEmailEnable($watcherId, Constant::BOARD_EMAIL_COMMENT, $task->board_id)
279 + ) {
280 + $recipientUserIds[] = $watcherId;
281 + }
282 + }
283 +
284 + return array_values(array_unique($recipientUserIds));
285 + }
286 +
169 287 public function checkIfEmailEnable($userId, $emailPurpose, $boardId)
170 288 {
171 289 if(
172 290 $this->checkIfEmailEnabled($boardId, $userId, $emailPurpose)
@@ -194,31 +312,68 @@
194 312
195 313 return false;
196 314 }
197 315
316 + /**
317 + * Return the supplied user IDs that currently belong to a board.
318 + *
319 + * Mention recipients are board-scoped because comment content is private to
320 + * the board, even when a caller supplies a valid WordPress user ID.
321 + *
322 + * @param int $boardId
323 + * @param array $mentionedUserIds
324 + * @return array
325 + */
326 + public function resolveBoardMentionUserIds($boardId, $mentionedUserIds)
327 + {
328 + $boardId = absint($boardId);
329 + $mentionedUserIds = array_values(array_unique(array_filter(array_map('absint', (array) $mentionedUserIds))));
330 +
331 + if (!$boardId || !$mentionedUserIds) {
332 + return [];
333 + }
334 +
335 + $boardMemberIdLookup = [];
336 + $boardRelations = Relation::where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
337 + ->where('object_id', $boardId)
338 + ->whereIn('foreign_id', $mentionedUserIds)
339 + ->get(['foreign_id']);
340 +
341 + foreach ($boardRelations as $boardRelation) {
342 + $boardMemberIdLookup[absint($boardRelation->foreign_id)] = true;
343 + }
344 +
345 + return array_values(array_filter($mentionedUserIds, function ($mentionedUserId) use ($boardMemberIdLookup) {
346 + return isset($boardMemberIdLookup[$mentionedUserId]);
347 + }));
348 + }
349 +
198 350 public function mentionInComment($comment, $mentionedUserIds)
199 351 {
200 - $uniqueIds = array_unique($mentionedUserIds);
352 + $currentUserId = get_current_user_id();
353 + $uniqueIds = array_values(array_unique(array_filter(array_map('absint', (array) $mentionedUserIds))));
354 + $uniqueIds = array_values(array_filter($uniqueIds, function ($mentionedUserId) use ($currentUserId) {
355 + return $mentionedUserId !== $currentUserId;
356 + }));
201 357
202 - $uniqueIds = array_filter($uniqueIds, function($value) {
203 - return (int)$value !== get_current_user_id();
204 - });
358 + if (!$uniqueIds) {
359 + return;
360 + }
205 361
206 - //sending emails to mentioned users
207 - $mentionedUserEmails = User::whereIn('ID', $uniqueIds)->pluck('user_email');
208 - $this->sendMailAfterMention($comment->id, $mentionedUserEmails);
362 + // Queued delivery revalidates membership and preferences before sending.
363 + $this->sendMailAfterMention($comment->id, $uniqueIds);
209 364
210 365 //sending desktop notifications
211 366 do_action('fluent_boards/mention_comment_notification', $comment, $uniqueIds);
212 367 }
213 368
214 - public function sendMailAfterMention($commentId, $usersToSendEmail)
369 + public function sendMailAfterMention($commentId, $recipientUserIds)
215 370 {
216 371 $current_user_id = get_current_user_id();
217 372
218 373 /* this will run in background as soon as possible */
219 374 /* sending Model or Model Instance won't work here */
220 - as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_mention', [$commentId, $usersToSendEmail, $current_user_id], 'fluent-boards');
375 + as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_mention', [$commentId, $recipientUserIds, $current_user_id], 'fluent-boards');
221 376 }
222 377
223 378 public function getUnreadNotificationsOfTasks($task)
224 379 {