nodebb-plugin-onekite-calendar 1.0.3 → 1.0.4

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/CHANGELOG.md CHANGED
@@ -1,4 +1,29 @@
1
- # Changelog – calendar-onekite
1
+ # Changelog
2
+
3
+ ## 1.1.0
4
+ - Ultra perf clean : JS chargé uniquement sur les pages/plugin concernées (plus de scripts globaux)
5
+ - Suppression legacy supplémentaire : clés DB renommées en onekite-calendar:* (aucune donnée conservée)
6
+ - Nettoyages internes (variables globales JS, routes/paramètres uniformisés)
7
+
8
+
9
+ ## 1.0.8
10
+ - Suppression du legacy (namespace unique onekite-calendar, ACP/API/widgets)
11
+ - Suppression des fallbacks inutiles
12
+
13
+
14
+ ## 1.0.6
15
+ - Refactor interne : centralisation de l’envoi d’emails et du logging (opt-in)
16
+ - Nettoyage : suppression des alias legacy (API + ACP)
17
+
18
+ ## 1.0.5
19
+ - Widget : points colorés selon le type (mêmes couleurs que le calendrier)
20
+ - Widget mobile : swipe horizontal pour naviguer semaine par semaine
21
+
22
+ ## 1.0.4
23
+ - Widget : affichage des évènements sous forme de points (sans texte)
24
+ - Widget : survol/clic sur un point affiche le contenu (tooltip + popup)
25
+ - Widget : renommage « Calendrier (2 semaines) » en « Calendrier »
26
+ - Texte : remplacement de toutes les occurrences « OneKite » par « Onekite »
2
27
 
3
28
  ## 1.0.3
4
29
  - Suppression du texte d’archivage dans le toast de purge (plus de « 0 archivés »)
@@ -18,7 +43,7 @@
18
43
  - Correctifs divers de stabilité/performance (dont crash au démarrage)
19
44
 
20
45
  ## 1.0.0
21
- - Première version stable du plugin calendar-onekite
46
+ - Première version stable du plugin onekite-calendar
22
47
  - Gestion des réservations de matériel avec contrôle de disponibilité
23
48
  - Calendrier FullCalendar via CDN
24
49
  - Validation / refus des demandes depuis l’ACP
package/lib/admin.js CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  const meta = require.main.require('./src/meta');
4
4
  const user = require.main.require('./src/user');
5
- const emailer = require.main.require('./src/emailer');
6
5
  const nconf = require.main.require('nconf');
7
6
 
7
+ const { sendEmail } = require('./email');
8
+ const log = require('./log');
9
+
8
10
  function forumBaseUrl() {
9
11
  const base = String(nconf.get('url') || '').trim().replace(/\/$/, '');
10
12
  return base;
@@ -18,53 +20,7 @@ function formatFR(tsOrIso) {
18
20
  return `${dd}/${mm}/${yyyy}`;
19
21
  }
20
22
 
21
- async function sendEmail(template, toEmail, subject, data) {
22
- // Prefer sending by uid (NodeBB core expects uid in various places)
23
- const uid = data && Number.isInteger(data.uid) ? data.uid : null;
24
- if (!toEmail && !uid) return;
25
-
26
- const settings = await meta.settings.get('calendar-onekite').catch(() => ({}));
27
- const lang = (settings && settings.defaultLang) || (meta && meta.config && meta.config.defaultLang) || 'fr';
28
- const params = Object.assign({}, data || {}, subject ? { subject } : {});
29
-
30
- // If we have a uid, use the native uid-based sender first.
31
- try {
32
- if (uid && typeof emailer.send === 'function') {
33
- // NodeBB: send(template, uid, params)
34
- if (emailer.send.length >= 3) {
35
- await emailer.send(template, uid, params);
36
- } else {
37
- await emailer.send(template, uid, params);
38
- }
39
- return;
40
- }
41
- } catch (err) {
42
- console.warn('[calendar-onekite] Failed to send email', {
43
- template,
44
- toEmail,
45
- err: err && err.message ? err.message : String(err),
46
- });
47
- }
48
-
49
- try {
50
- if (typeof emailer.sendToEmail === 'function') {
51
- // NodeBB: sendToEmail(template, email, language, params)
52
- if (emailer.sendToEmail.length >= 4) {
53
- await emailer.sendToEmail(template, toEmail, lang, params);
54
- } else {
55
- // Older signature: sendToEmail(template, email, params)
56
- await emailer.sendToEmail(template, toEmail, params);
57
- }
58
- return;
59
- }
60
- } catch (err) {
61
- console.warn('[calendar-onekite] Failed to send email', {
62
- template,
63
- toEmail,
64
- err: err && err.message ? err.message : String(err),
65
- });
66
- }
67
- }
23
+ // Email helper is centralized in lib/email.js.
68
24
 
69
25
  function normalizeCallbackUrl(configured, meta) {
70
26
  const base = (meta && meta.config && (meta.config.url || meta.config['url'])) ? (meta.config.url || meta.config['url']) : '';
@@ -94,18 +50,18 @@ const ADMIN_PRIV = 'admin:settings';
94
50
  const admin = {};
95
51
 
96
52
  admin.renderAdmin = async function (req, res) {
97
- res.render('admin/plugins/calendar-onekite', {
98
- title: 'Calendar OneKite',
53
+ res.render('admin/plugins/onekite-calendar', {
54
+ title: 'Calendar Onekite',
99
55
  });
100
56
  };
101
57
 
102
58
  admin.getSettings = async function (req, res) {
103
- const settings = await meta.settings.get('calendar-onekite');
59
+ const settings = await meta.settings.get('onekite-calendar');
104
60
  res.json(settings || {});
105
61
  };
106
62
 
107
63
  admin.saveSettings = async function (req, res) {
108
- await meta.settings.set('calendar-onekite', req.body || {});
64
+ await meta.settings.set('onekite-calendar', req.body || {});
109
65
  res.json({ ok: true });
110
66
  };
111
67
 
@@ -141,17 +97,13 @@ admin.approveReservation = async function (req, res) {
141
97
  }
142
98
 
143
99
  // Create HelloAsso payment link if configured
144
- const settings = await meta.settings.get('calendar-onekite');
100
+ const settings = await meta.settings.get('onekite-calendar');
145
101
  const env = settings.helloassoEnv || 'prod';
146
102
  const token = await helloasso.getAccessToken({
147
103
  env,
148
104
  clientId: settings.helloassoClientId,
149
105
  clientSecret: settings.helloassoClientSecret,
150
106
  });
151
- if (!token) {
152
-
153
- }
154
-
155
107
  let paymentUrl = null;
156
108
  if (token) {
157
109
  const requester = await user.getUserFields(r.uid, ['username', 'email']);
@@ -159,7 +111,7 @@ admin.approveReservation = async function (req, res) {
159
111
  const totalAmount = Math.max(0, Math.round((Number(r.total) || 0) * 100));
160
112
  const base = forumBaseUrl();
161
113
  const returnUrl = base ? `${base}/calendar` : '';
162
- const webhookUrl = base ? `${base}/plugins/calendar-onekite/helloasso` : '';
114
+ const webhookUrl = base ? `${base}/plugins/onekite-calendar/helloasso` : '';
163
115
  const year = new Date(Number(r.start)).getFullYear();
164
116
  const intent = await helloasso.createCheckoutIntent({
165
117
  env,
@@ -173,7 +125,7 @@ admin.approveReservation = async function (req, res) {
173
125
  // User return/back/error URLs must be real pages; webhook uses the plugin endpoint.
174
126
  callbackUrl: returnUrl,
175
127
  webhookUrl: webhookUrl,
176
- itemName: 'Réservation matériel OneKite',
128
+ itemName: 'Réservation matériel Onekite',
177
129
  containsDonation: false,
178
130
  metadata: { reservationId: String(rid) },
179
131
  });
@@ -188,7 +140,7 @@ admin.approveReservation = async function (req, res) {
188
140
  if (paymentUrl) {
189
141
  r.paymentUrl = paymentUrl;
190
142
  } else {
191
- console.warn('[calendar-onekite] HelloAsso payment link not created (approve ACP)', { rid });
143
+ log.warn('HelloAsso payment link not created (approve ACP)', { rid });
192
144
  }
193
145
 
194
146
  await dbLayer.saveReservation(r);
@@ -202,7 +154,7 @@ admin.approveReservation = async function (req, res) {
202
154
  const mapUrl = (Number.isFinite(latNum) && Number.isFinite(lonNum))
203
155
  ? `https://www.openstreetmap.org/?mlat=${encodeURIComponent(String(latNum))}&mlon=${encodeURIComponent(String(lonNum))}#map=18/${encodeURIComponent(String(latNum))}/${encodeURIComponent(String(lonNum))}`
204
156
  : '';
205
- await sendEmail('calendar-onekite_approved', requester.email, 'Location matériel - Réservation validée', {
157
+ await sendEmail('onekite-calendar_approved', requester.email, 'Location matériel - Réservation validée', {
206
158
  uid: parseInt(r.uid, 10),
207
159
  username: requester.username,
208
160
  itemName: (Array.isArray(r.itemNames) ? r.itemNames.join(', ') : (r.itemName || '')),
@@ -237,7 +189,7 @@ admin.refuseReservation = async function (req, res) {
237
189
  try {
238
190
  const requester = await user.getUserFields(r.uid, ['username', 'email']);
239
191
  if (requester && requester.email) {
240
- await sendEmail('calendar-onekite_refused', requester.email, 'Location matériel - Réservation refusée', {
192
+ await sendEmail('onekite-calendar_refused', requester.email, 'Location matériel - Réservation refusée', {
241
193
  uid: parseInt(r.uid, 10),
242
194
  username: requester.username,
243
195
  itemName: (Array.isArray(r.itemNames) ? r.itemNames.join(', ') : (r.itemName || '')),
@@ -292,98 +244,6 @@ admin.purgeSpecialEventsByYear = async function (req, res) {
292
244
  return res.json({ ok: true, removed: count });
293
245
  };
294
246
 
295
- // Debug endpoint to validate HelloAsso connectivity and item loading
296
-
297
-
298
- admin.debugHelloAsso = async function (req, res) {
299
- const settings = await meta.settings.get('calendar-onekite');
300
- const env = (settings && settings.helloassoEnv) || 'prod';
301
-
302
- // Never expose secrets in debug output
303
- const safeSettings = {
304
- helloassoEnv: env,
305
- helloassoClientId: settings && settings.helloassoClientId ? String(settings.helloassoClientId) : '',
306
- helloassoClientSecret: settings && settings.helloassoClientSecret ? '***' : '',
307
- helloassoOrganizationSlug: settings && settings.helloassoOrganizationSlug ? String(settings.helloassoOrganizationSlug) : '',
308
- helloassoFormType: settings && settings.helloassoFormType ? String(settings.helloassoFormType) : '',
309
- helloassoFormSlug: settings && settings.helloassoFormSlug ? String(settings.helloassoFormSlug) : '',
310
- };
311
-
312
- const out = {
313
- ok: true,
314
- settings: safeSettings,
315
- token: { ok: false },
316
- // Catalog = what you actually want for a shop (available products/material)
317
- catalog: { ok: false, count: 0, sample: [], keys: [] },
318
- // Sold items = items present in orders (can be 0 if no sales yet)
319
- soldItems: { ok: false, count: 0, sample: [] },
320
- };
321
-
322
- try {
323
- const token = await helloasso.getAccessToken({
324
- env,
325
- clientId: settings.helloassoClientId,
326
- clientSecret: settings.helloassoClientSecret,
327
- });
328
- if (!token) {
329
- out.token = { ok: false, error: 'token-null' };
330
- return res.json(out);
331
- }
332
- out.token = { ok: true };
333
-
334
- // Catalog items (via /public)
335
- try {
336
- const y = new Date().getFullYear();
337
- const { publicForm, items } = await helloasso.listCatalogItems({
338
- env,
339
- token,
340
- organizationSlug: settings.helloassoOrganizationSlug,
341
- formType: settings.helloassoFormType,
342
- formSlug: `locations-materiel-${y}`,
343
- });
344
-
345
- const arr = Array.isArray(items) ? items : [];
346
- out.catalog.ok = true;
347
- out.catalog.count = arr.length;
348
- out.catalog.keys = publicForm && typeof publicForm === 'object' ? Object.keys(publicForm) : [];
349
- out.catalog.sample = arr.slice(0, 10).map((it) => ({
350
- id: it.id,
351
- name: it.name,
352
- price: it.price ?? null,
353
- }));
354
- } catch (e) {
355
- out.catalog = { ok: false, error: String(e && e.message ? e.message : e), count: 0, sample: [], keys: [] };
356
- }
357
-
358
- // Sold items
359
- try {
360
- const y2 = new Date().getFullYear();
361
- const items = await helloasso.listItems({
362
- env,
363
- token,
364
- organizationSlug: settings.helloassoOrganizationSlug,
365
- formType: settings.helloassoFormType,
366
- formSlug: `locations-materiel-${y2}`,
367
- });
368
- const arr = Array.isArray(items) ? items : [];
369
- out.soldItems.ok = true;
370
- out.soldItems.count = arr.length;
371
- out.soldItems.sample = arr.slice(0, 10).map((it) => ({
372
- id: it.id || it.itemId || it.reference || it.name,
373
- name: it.name || it.label || it.itemName,
374
- price: it.price || it.amount || it.unitPrice || null,
375
- }));
376
- } catch (e) {
377
- out.soldItems = { ok: false, error: String(e && e.message ? e.message : e), count: 0, sample: [] };
378
- }
379
-
380
- return res.json(out);
381
- } catch (e) {
382
- out.ok = false;
383
- out.token = { ok: false, error: String(e && e.message ? e.message : e) };
384
- return res.json(out);
385
- }
386
- };
387
247
 
388
248
  // Accounting endpoint: aggregates paid reservations so you can contabilize what was rented.
389
249
  // Query params:
@@ -500,7 +360,7 @@ admin.exportAccountingCsv = async function (req, res) {
500
360
  }
501
361
  const csv = lines.join('\n');
502
362
  res.setHeader('Content-Type', 'text/csv; charset=utf-8');
503
- res.setHeader('Content-Disposition', 'attachment; filename="calendar-onekite-accounting.csv"');
363
+ res.setHeader('Content-Disposition', 'attachment; filename="onekite-calendar-accounting.csv"');
504
364
  return res.send(csv);
505
365
  };
506
366