nodebb-plugin-onekite-calendar 2.0.70 → 2.0.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/admin.js CHANGED
@@ -20,10 +20,6 @@ const {
20
20
  autoFormSlugForYear,
21
21
  } = shared;
22
22
 
23
- // Kept for local compatibility in accounting helper
24
- function baseUrl() { return forumBaseUrl(); }
25
-
26
-
27
23
  const dbLayer = require('./db');
28
24
  const helloasso = require('./helloasso');
29
25
 
package/lib/api.js CHANGED
@@ -361,12 +361,6 @@ function eventsForOuting(o) {
361
361
 
362
362
  const api = {};
363
363
 
364
- // baseUrl, hmacSecret, signCalendarLink, ymdToCompact, dtToGCalUtc,
365
- // buildCalendarLinks are imported from shared.js
366
- function baseUrl() {
367
- return forumBaseUrl();
368
- }
369
-
370
364
  function computeEtag(payload) {
371
365
  // Weak ETag is fine here: it is only used to skip identical JSON payloads.
372
366
  const hash = crypto.createHash('sha1').update(JSON.stringify(payload)).digest('hex');
@@ -773,19 +767,18 @@ api.getCapabilities = async function (req, res) {
773
767
  specialEventCategoryCid: 0,
774
768
  });
775
769
  }
776
- const [canMod, canSpecialC, canSpecialD, canOuting, canRes] = await Promise.all([
770
+ const [canMod, canSpecialC, canSpecialD, canReq] = await Promise.all([
777
771
  canValidate(uid, settings),
778
772
  canCreateSpecial(uid, settings),
779
773
  canDeleteSpecial(uid, settings),
780
774
  canRequest(uid, settings, Date.now()),
781
- canRequest(uid, settings, Date.now()),
782
775
  ]);
783
776
  res.json({
784
777
  canModerate: canMod,
785
778
  canCreateSpecial: canSpecialC,
786
779
  canDeleteSpecial: canSpecialD,
787
- canCreateOuting: canOuting,
788
- canCreateReservation: canRes,
780
+ canCreateOuting: canReq,
781
+ canCreateReservation: canReq,
789
782
  specialEventCategoryCid: parseInt(settings && settings.specialEventCategoryId, 10) || 0,
790
783
  });
791
784
  };
@@ -1376,7 +1369,7 @@ api.createReservation = async function (req, res) {
1376
1369
 
1377
1370
  // Notify groups by email (NodeBB emailer config)
1378
1371
  try {
1379
- const notifyGroups = (settings.notifyGroups || '').split(',').map(s => s.trim()).filter(Boolean);
1372
+ const notifyGroups = normalizeAllowedGroups(settings.notifyGroups);
1380
1373
  if (notifyGroups.length) {
1381
1374
  const requester = await user.getUserFields(uid, ['username', 'email']);
1382
1375
  const itemsLabel = (resv.itemNames || []).join(', ');
@@ -1601,18 +1594,7 @@ api.approveReservation = async function (req, res) {
1601
1594
  pickupLon: r.pickupLon || '',
1602
1595
  mapUrl,
1603
1596
  paymentUrl: r.paymentUrl || '',
1604
- icsUrl: (buildCalendarLinks({
1605
- type: 'reservation',
1606
- id: String(r.rid),
1607
- uid: requesterUid,
1608
- title: (Array.isArray(r.itemNames) && r.itemNames.length) ? `Location - ${r.itemNames.join(', ')}` : 'Location',
1609
- details: (Array.isArray(r.itemNames) && r.itemNames.length) ? `Matériel: ${r.itemNames.join(', ')}` : (r.itemName ? `Matériel: ${r.itemName}` : ''),
1610
- location: r.pickupAddress || '',
1611
- allDay: true,
1612
- startYmd: (r.startDate && /^\d{4}-\d{2}-\d{2}$/.test(String(r.startDate))) ? String(r.startDate) : new Date(parseInt(r.start, 10)).toISOString().slice(0, 10),
1613
- endYmd: (r.endDate && /^\d{4}-\d{2}-\d{2}$/.test(String(r.endDate))) ? String(r.endDate) : new Date(parseInt(r.end, 10)).toISOString().slice(0, 10),
1614
- })).icsUrl,
1615
- googleCalUrl: (buildCalendarLinks({
1597
+ ...buildCalendarLinks({
1616
1598
  type: 'reservation',
1617
1599
  id: String(r.rid),
1618
1600
  uid: requesterUid,
@@ -1622,7 +1604,7 @@ api.approveReservation = async function (req, res) {
1622
1604
  allDay: true,
1623
1605
  startYmd: (r.startDate && /^\d{4}-\d{2}-\d{2}$/.test(String(r.startDate))) ? String(r.startDate) : new Date(parseInt(r.start, 10)).toISOString().slice(0, 10),
1624
1606
  endYmd: (r.endDate && /^\d{4}-\d{2}-\d{2}$/.test(String(r.endDate))) ? String(r.endDate) : new Date(parseInt(r.end, 10)).toISOString().slice(0, 10),
1625
- })).googleCalUrl,
1607
+ }),
1626
1608
  validatedBy: r.approvedByUsername || '',
1627
1609
  validatedByUrl: (r.approvedByUsername ? `https://www.onekite.com/user/${encodeURIComponent(String(r.approvedByUsername))}` : ''),
1628
1610
  });
@@ -1972,7 +1954,7 @@ api.getIcs = async function (req, res) {
1972
1954
  allDay: true,
1973
1955
  startYmd,
1974
1956
  endYmd,
1975
- url: `${baseUrl()}/calendar`,
1957
+ url: `${forumBaseUrl()}/calendar`,
1976
1958
  });
1977
1959
  } else if (type === 'special') {
1978
1960
  const ev = await dbLayer.getSpecialEvent(id);
@@ -1987,7 +1969,7 @@ api.getIcs = async function (req, res) {
1987
1969
  allDay: false,
1988
1970
  start,
1989
1971
  end,
1990
- url: `${baseUrl()}/calendar`,
1972
+ url: `${forumBaseUrl()}/calendar`,
1991
1973
  });
1992
1974
  } else if (type === 'outing') {
1993
1975
  const o = await dbLayer.getOuting(id);
@@ -2002,7 +1984,7 @@ api.getIcs = async function (req, res) {
2002
1984
  allDay: false,
2003
1985
  start,
2004
1986
  end,
2005
- url: `${baseUrl()}/calendar`,
1987
+ url: `${forumBaseUrl()}/calendar`,
2006
1988
  });
2007
1989
  } else {
2008
1990
  return res.status(400).send('unknown-type');
package/lib/db.js CHANGED
@@ -21,19 +21,6 @@ const KEY_MAINTENANCE_ZSET = 'calendar-onekite:maintenance:itemIds';
21
21
  const KEY_AUDIT_ZSET = (year) => `calendar-onekite:audit:${year}`;
22
22
  const KEY_AUDIT_OBJ = (id) => `calendar-onekite:audit:entry:${id}`;
23
23
 
24
- // Helpers
25
- function reservationKey(rid) {
26
- return KEY_OBJ(rid);
27
- }
28
-
29
- function specialKey(eid) {
30
- return KEY_SPECIAL_OBJ(eid);
31
- }
32
-
33
- function outingKey(oid) {
34
- return KEY_OUTING_OBJ(oid);
35
- }
36
-
37
24
  async function getReservation(rid) {
38
25
  return await db.getObject(KEY_OBJ(rid));
39
26
  }
@@ -45,7 +32,7 @@ async function getReservation(rid) {
45
32
  async function getReservations(rids) {
46
33
  const ids = Array.isArray(rids) ? rids.filter(Boolean) : [];
47
34
  if (!ids.length) return [];
48
- const keys = ids.map(reservationKey);
35
+ const keys = ids.map(KEY_OBJ);
49
36
  const rows = await db.getObjects(keys);
50
37
  // Ensure rid is present even if older objects were missing it.
51
38
  return (rows || []).map((row, idx) => {
@@ -198,7 +185,7 @@ module.exports = {
198
185
  getSpecialEvents: async (eids) => {
199
186
  const ids = Array.isArray(eids) ? eids.filter(Boolean) : [];
200
187
  if (!ids.length) return [];
201
- const keys = ids.map(specialKey);
188
+ const keys = ids.map(KEY_SPECIAL_OBJ);
202
189
  const rows = await db.getObjects(keys);
203
190
  return (rows || []).map((row, idx) => {
204
191
  if (!row) return null;
@@ -225,7 +212,7 @@ module.exports = {
225
212
  getOutings: async (oids) => {
226
213
  const ids = Array.isArray(oids) ? oids.filter(Boolean) : [];
227
214
  if (!ids.length) return [];
228
- const keys = ids.map(outingKey);
215
+ const keys = ids.map(KEY_OUTING_OBJ);
229
216
  const rows = await db.getObjects(keys);
230
217
  return (rows || []).map((row, idx) => {
231
218
  if (!row) return null;
@@ -14,9 +14,6 @@ const realtime = require('./realtime');
14
14
  const shared = require('./shared');
15
15
  const { formatFR, forumBaseUrl, sendEmail, buildCalendarLinks, signCalendarLink, ymdToCompact } = shared;
16
16
 
17
- function baseUrl() { return forumBaseUrl(); }
18
-
19
-
20
17
  function buildReservationCalendarLinks(r) {
21
18
  const startYmd = (r.startDate && /^\d{4}-\d{2}-\d{2}$/.test(String(r.startDate)))
22
19
  ? String(r.startDate)
package/lib/scheduler.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const nconf = require.main.require('nconf');
4
4
  const db = require.main.require('./src/database');
5
+ const user = require.main.require('./src/user');
5
6
  const dbLayer = require('./db');
6
7
  const discord = require('./discord');
7
8
  const realtime = require('./realtime');
@@ -109,8 +110,6 @@ async function expirePending(preIds, preReservations) {
109
110
  const validatorReminderMins = parseInt(getSetting(settings, 'validatorReminderMinutesPending', '0'), 10) || 0;
110
111
  const now = Date.now();
111
112
 
112
- const user = require.main.require('./src/user');
113
-
114
113
  const adminUrl = (() => {
115
114
  const base = forumBaseUrl();
116
115
  return base ? `${base}/admin/plugins/calendar-onekite` : '/admin/plugins/calendar-onekite';
@@ -214,8 +213,6 @@ async function processAwaitingPayment(preIds, preReservations) {
214
213
  const ids = preIds || await dbLayer.listAllReservationIds(5000);
215
214
  if (!ids || !ids.length) return;
216
215
 
217
- const user = require.main.require('./src/user');
218
-
219
216
  const adminUrl = (() => {
220
217
  const base = forumBaseUrl();
221
218
  return base ? `${base}/admin/plugins/calendar-onekite` : '/admin/plugins/calendar-onekite';
package/lib/shared.js CHANGED
@@ -23,8 +23,8 @@ function forumBaseUrl() {
23
23
  // Try meta.config first (runtime-accurate), then nconf fallback.
24
24
  let base = '';
25
25
  try {
26
- base = (meta && meta.config && (meta.config.url || meta.config['url']))
27
- ? String(meta.config.url || meta.config['url'])
26
+ base = (meta && meta.config && meta.config.url)
27
+ ? String(meta.config.url)
28
28
  : '';
29
29
  } catch (_) { /* ignore */ }
30
30
  if (!base) {
package/library.js CHANGED
@@ -101,28 +101,26 @@ Plugin.init = async function (params) {
101
101
  router.get('/plugins/calendar-onekite/ics/:type/:id', api.getIcs);
102
102
 
103
103
  // Admin API (JSON)
104
- const adminBases = ['/api/v3/admin/plugins/calendar-onekite'];
105
-
106
- adminBases.forEach((base) => {
107
- router.get(`${base}/settings`, ...adminMws, admin.getSettings);
108
- router.put(`${base}/settings`, ...adminMws, admin.saveSettings);
109
-
110
- router.get(`${base}/pending`, ...adminMws, admin.listPending);
111
- router.put(`${base}/reservations/:rid/approve`, ...adminMws, admin.approveReservation);
112
- router.put(`${base}/reservations/:rid/refuse`, ...adminMws, admin.refuseReservation);
113
-
114
- router.post(`${base}/purge`, ...adminMws, admin.purgeByYear);
115
- router.get(`${base}/debug`, ...adminMws, admin.debugHelloAsso);
116
- // Accounting / exports
117
- router.get(`${base}/accounting`, ...adminMws, admin.getAccounting);
118
- router.get(`${base}/accounting.csv`, ...adminMws, admin.exportAccountingCsv);
119
- router.post(`${base}/accounting/purge`, ...adminMws, admin.purgeAccounting);
120
-
121
- // Purge special events by year
122
- router.post(`${base}/special-events/purge`, ...adminMws, admin.purgeSpecialEventsByYear);
123
- // Purge outings by year
124
- router.post(`${base}/outings/purge`, ...adminMws, admin.purgeOutingsByYear);
125
- });
104
+ const adminBase = '/api/v3/admin/plugins/calendar-onekite';
105
+
106
+ router.get(`${adminBase}/settings`, ...adminMws, admin.getSettings);
107
+ router.put(`${adminBase}/settings`, ...adminMws, admin.saveSettings);
108
+
109
+ router.get(`${adminBase}/pending`, ...adminMws, admin.listPending);
110
+ router.put(`${adminBase}/reservations/:rid/approve`, ...adminMws, admin.approveReservation);
111
+ router.put(`${adminBase}/reservations/:rid/refuse`, ...adminMws, admin.refuseReservation);
112
+
113
+ router.post(`${adminBase}/purge`, ...adminMws, admin.purgeByYear);
114
+ router.get(`${adminBase}/debug`, ...adminMws, admin.debugHelloAsso);
115
+ // Accounting / exports
116
+ router.get(`${adminBase}/accounting`, ...adminMws, admin.getAccounting);
117
+ router.get(`${adminBase}/accounting.csv`, ...adminMws, admin.exportAccountingCsv);
118
+ router.post(`${adminBase}/accounting/purge`, ...adminMws, admin.purgeAccounting);
119
+
120
+ // Purge special events by year
121
+ router.post(`${adminBase}/special-events/purge`, ...adminMws, admin.purgeSpecialEventsByYear);
122
+ // Purge outings by year
123
+ router.post(`${adminBase}/outings/purge`, ...adminMws, admin.purgeOutingsByYear);
126
124
 
127
125
  // HelloAsso callback endpoint (hardened)
128
126
  // - Only accepts POST
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-onekite-calendar",
3
- "version": "2.0.70",
3
+ "version": "2.0.72",
4
4
  "description": "FullCalendar-based equipment reservation workflow with admin approval & HelloAsso payment for NodeBB",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -169,6 +169,13 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
169
169
  .replace(/'/g, ''');
170
170
  }
171
171
 
172
+ function linkifyNotes(str) {
173
+ return escapeHtml(str).replace(
174
+ /https?:\/\/[^\s<>"']+/g,
175
+ url => `<a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`
176
+ );
177
+ }
178
+
172
179
  function getCsrfToken() {
173
180
  try {
174
181
  return (
@@ -1809,7 +1816,7 @@ function toDatetimeLocalValue(date) {
1809
1816
  ${calHtml}
1810
1817
  ${participantsHtml}
1811
1818
  ${addr ? `<div class="mb-2"><strong>Adresse</strong><br>${addrHtml}</div>` : ''}
1812
- ${notes ? `<div class="mb-2"><strong>Notes</strong><br>${escapeHtml(notes).replace(/\n/g,'<br>')}</div>` : ''}
1819
+ ${notes ? `<div class="mb-2"><strong>Notes</strong><br>${linkifyNotes(notes).replace(/\n/g,'<br>')}</div>` : ''}
1813
1820
  `;
1814
1821
  const canDel = !!(p.canDeleteSpecial || canDeleteSpecial);
1815
1822
  const canEdit = !!p.canEditSpecial;
@@ -1987,7 +1994,7 @@ function toDatetimeLocalValue(date) {
1987
1994
  ${calHtml}
1988
1995
  ${participantsHtml}
1989
1996
  ${addr ? `<div class="mb-2"><strong>Adresse</strong><br>${addrHtml}</div>` : ''}
1990
- ${notes ? `<div class="mb-2"><strong>Notes</strong><br>${escapeHtml(notes).replace(/\n/g,'<br>')}</div>` : ''}
1997
+ ${notes ? `<div class="mb-2"><strong>Notes</strong><br>${linkifyNotes(notes).replace(/\n/g,'<br>')}</div>` : ''}
1991
1998
  `;
1992
1999
  const canDel = !!(p.canDeleteOuting);
1993
2000
  const canEditOuting = !!p.canEditOuting;
@@ -2160,7 +2167,7 @@ function toDatetimeLocalValue(date) {
2160
2167
  ${pickupAddress ? `${escapeHtml(pickupAddress)}<br>` : ''}
2161
2168
  ${pickupTime ? `Heure : ${escapeHtml(pickupTime)}<br>` : ''}
2162
2169
  ${hasCoords ? `<a href="${osmLink}" target="_blank" rel="noopener">Voir sur la carte</a><br>` : ''}
2163
- ${notes ? `<div class="mt-1"><strong>Notes</strong><br>${escapeHtml(notes)}</div>` : ''}
2170
+ ${notes ? `<div class="mt-1"><strong>Notes</strong><br>${linkifyNotes(notes).replace(/\n/g,'<br>')}</div>` : ''}
2164
2171
  </div>
2165
2172
  `
2166
2173
  : '';