Files
wekan/models/lib/dueNotificationConfig.js
T
Lauri Ojansivu c0bbacaf94 Due-date reminders: honour hour 0 (midnight) and notify card assignees (#3192).
Two source bugs kept due-date reminder mail from reaching people. (1) The reminder hour was parsed as parseInt(NOTIFY_DUE_AT_HOUR_OF_DAY, 10) || 8, so a configured hour of 0 (midnight) silently became 8 (0 is falsy) and out-of-range hours were accepted; parseNotifyDueHour now keeps 0 and rejects anything outside 0..23. (2) A due-card activity's notification participants were only the card CREATOR and its MEMBERS (server/models/activities.js) -- a user ASSIGNED a card but not a member was never notified about it, including its due date; card.assignees are now included too (still gated downstream by the board tracking level, so nobody who is not tracking is newly emailed). The NOTIFY_DUE_* parsing is extracted to a pure, unit-tested models/lib/dueNotificationConfig.js. Note: the built-in reminder still requires NOTIFY_DUE_DAYS_BEFORE_AND_AFTER to be set, and members/assignees are emailed per their board watch/tracking level.

Tests: tests/dueNotificationConfig.test.cjs (12 checks) covers the midnight-hour case, valid/invalid/out-of-range hours, the day-offset window parsing and negatives, plus source guards that cards.js uses parseNotifyDueHour and that assignees are in the notification participants.

Thanks to SirConfigMgr and xet7 !

Fixes #3192,
2026-07-22 11:19:39 +03:00

39 lines
1.6 KiB
JavaScript

'use strict';
// Pure parsers for the due-date reminder configuration (NOTIFY_DUE_* env vars),
// extracted from models/cards.js so they are unit-testable without Meteor.
// Related to #3192 (due-date reminder mail).
// Parse NOTIFY_DUE_DAYS_BEFORE_AND_AFTER: a comma-separated list of day offsets
// (positive = days before due, 0 = due today, negative = days past due). Each is
// validated to the -14..14 window; invalid/out-of-range entries are dropped.
// Returns an array of integers (possibly empty).
function parseNotifyDueDays(envValue) {
if (!envValue || typeof envValue !== 'string') return [];
return envValue
.split(',')
.map(value => {
const iValue = parseInt(value, 10);
if (Number.isNaN(iValue) || iValue < -14 || iValue > 14) return false;
return iValue;
})
.filter(v => v !== false);
}
// Parse NOTIFY_DUE_AT_HOUR_OF_DAY: the hour of day (0..23) at which to send the
// reminders. Returns the parsed hour, or `defaultHour` when the value is missing,
// non-numeric, or out of range.
//
// #3192: the previous `parseInt(env, 10) || defaultHour` silently turned a valid
// hour of 0 (midnight) into the default (8), because 0 is falsy — so midnight
// reminders were impossible. It also accepted out-of-range hours (e.g. 25, -1).
// Number.isNaN + a 0..23 range check fixes both while keeping the "parse error ->
// default" behaviour.
function parseNotifyDueHour(envValue, defaultHour = 8) {
const hour = parseInt(envValue, 10);
if (Number.isNaN(hour) || hour < 0 || hour > 23) return defaultHour;
return hour;
}
module.exports = { parseNotifyDueDays, parseNotifyDueHour };