nodebb-plugin-onekite-calendar 2.0.57 → 2.0.59
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/api.js +47 -0
- package/library.js +2 -0
- package/nodebb-plugin-onekite-calendar-2.0.40.tgz +0 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/public/client.js +68 -6
package/lib/api.js
CHANGED
|
@@ -996,6 +996,29 @@ api.joinSpecialEvent = async function (req, res) {
|
|
|
996
996
|
});
|
|
997
997
|
};
|
|
998
998
|
|
|
999
|
+
api.leaveSpecialEvent = async function (req, res) {
|
|
1000
|
+
const settings = await getSettings();
|
|
1001
|
+
const uid = req.uid;
|
|
1002
|
+
if (!uid) return res.status(401).json({ error: 'not-logged-in' });
|
|
1003
|
+
const ok = await canCreateSpecial(uid, settings);
|
|
1004
|
+
if (!ok) return res.status(403).json({ error: 'not-allowed' });
|
|
1005
|
+
|
|
1006
|
+
const eid = String(req.params.eid || '').replace(/^special:/, '').trim();
|
|
1007
|
+
if (!eid) return res.status(400).json({ error: 'bad-id' });
|
|
1008
|
+
|
|
1009
|
+
const ev = await dbLayer.getSpecialEvent(eid);
|
|
1010
|
+
if (!ev) return res.status(404).json({ error: 'not-found' });
|
|
1011
|
+
|
|
1012
|
+
const list = normalizeUidList(ev.participants);
|
|
1013
|
+
const sUid = String(uid);
|
|
1014
|
+
const next = list.filter((x) => String(x) !== sUid);
|
|
1015
|
+
ev.participants = JSON.stringify(next);
|
|
1016
|
+
await dbLayer.saveSpecialEvent(ev);
|
|
1017
|
+
|
|
1018
|
+
realtime.emitCalendarUpdated({ kind: 'specialEvent', action: 'participants', eid });
|
|
1019
|
+
return res.json({ ok: true, participants: next, participantsUsernames: await usernamesByUids(next) });
|
|
1020
|
+
};
|
|
1021
|
+
|
|
999
1022
|
api.getCapabilities = async function (req, res) {
|
|
1000
1023
|
const settings = await getSettings();
|
|
1001
1024
|
const uid = req.uid || 0;
|
|
@@ -1163,6 +1186,30 @@ api.joinOuting = async function (req, res) {
|
|
|
1163
1186
|
return res.json({ ok: true, participants: list, participantsUsernames: await usernamesByUids(list) });
|
|
1164
1187
|
};
|
|
1165
1188
|
|
|
1189
|
+
api.leaveOuting = async function (req, res) {
|
|
1190
|
+
const settings = await getSettings();
|
|
1191
|
+
const uid = req.uid;
|
|
1192
|
+
if (!uid) return res.status(401).json({ error: 'not-logged-in' });
|
|
1193
|
+
// Outings share the same rights as reservations/locations.
|
|
1194
|
+
const ok = await canRequest(uid, settings, Date.now());
|
|
1195
|
+
if (!ok) return res.status(403).json({ error: 'not-allowed' });
|
|
1196
|
+
|
|
1197
|
+
const oid = String(req.params.oid || '').replace(/^outing:/, '').trim();
|
|
1198
|
+
if (!oid) return res.status(400).json({ error: 'bad-id' });
|
|
1199
|
+
|
|
1200
|
+
const o = await dbLayer.getOuting(oid);
|
|
1201
|
+
if (!o) return res.status(404).json({ error: 'not-found' });
|
|
1202
|
+
|
|
1203
|
+
const list = normalizeUidList(o.participants);
|
|
1204
|
+
const sUid = String(uid);
|
|
1205
|
+
const next = list.filter((x) => String(x) !== sUid);
|
|
1206
|
+
o.participants = JSON.stringify(next);
|
|
1207
|
+
await dbLayer.saveOuting(o);
|
|
1208
|
+
|
|
1209
|
+
realtime.emitCalendarUpdated({ kind: 'outing', action: 'participants', oid });
|
|
1210
|
+
return res.json({ ok: true, participants: next, participantsUsernames: await usernamesByUids(next) });
|
|
1211
|
+
};
|
|
1212
|
+
|
|
1166
1213
|
api.createOuting = async function (req, res) {
|
|
1167
1214
|
const settings = await getSettings();
|
|
1168
1215
|
if (!req.uid) return res.status(401).json({ error: 'not-logged-in' });
|
package/library.js
CHANGED
|
@@ -84,6 +84,7 @@ Plugin.init = async function (params) {
|
|
|
84
84
|
router.delete('/api/v3/plugins/calendar-onekite/special-events/:eid', ...publicExpose, api.deleteSpecialEvent);
|
|
85
85
|
// Participants (self-join) for special events
|
|
86
86
|
router.post('/api/v3/plugins/calendar-onekite/special-events/:eid/participants', ...publicExpose, api.joinSpecialEvent);
|
|
87
|
+
router.delete('/api/v3/plugins/calendar-onekite/special-events/:eid/participants', ...publicExpose, api.leaveSpecialEvent);
|
|
87
88
|
|
|
88
89
|
// Outings (prévisions de sortie)
|
|
89
90
|
router.post('/api/v3/plugins/calendar-onekite/outings', ...publicExpose, api.createOuting);
|
|
@@ -91,6 +92,7 @@ Plugin.init = async function (params) {
|
|
|
91
92
|
router.delete('/api/v3/plugins/calendar-onekite/outings/:oid', ...publicExpose, api.deleteOuting);
|
|
92
93
|
// Participants (self-join) for outings
|
|
93
94
|
router.post('/api/v3/plugins/calendar-onekite/outings/:oid/participants', ...publicExpose, api.joinOuting);
|
|
95
|
+
router.delete('/api/v3/plugins/calendar-onekite/outings/:oid/participants', ...publicExpose, api.leaveOuting);
|
|
94
96
|
|
|
95
97
|
// Calendar export (ICS)
|
|
96
98
|
// Note: reservations are protected by a signature in the querystring.
|
|
Binary file
|
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1675,10 +1675,15 @@ function toDatetimeLocalValue(date) {
|
|
|
1675
1675
|
const hasCoords = Number.isFinite(lat) && Number.isFinite(lon);
|
|
1676
1676
|
const notes = String(p.notes || '').trim();
|
|
1677
1677
|
const participants = Array.isArray(p.participantsUsernames) ? p.participantsUsernames : [];
|
|
1678
|
+
const eidPlain = escapeHtml(String(p.eid || '').replace(/^special:/, ''));
|
|
1679
|
+
// Use real buttons with visible + / - text to avoid relying on icon fonts.
|
|
1678
1680
|
const joinBtn = (p.canJoin && !p.isParticipant)
|
|
1679
|
-
? `<
|
|
1681
|
+
? `<button type="button" class="btn btn-sm btn-success ms-2 onekite-join-special" data-eid="${eidPlain}" title="S'ajouter" aria-label="S'ajouter">+</button>`
|
|
1680
1682
|
: '';
|
|
1681
|
-
const
|
|
1683
|
+
const leaveBtn = (p.canJoin && p.isParticipant)
|
|
1684
|
+
? `<button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-special" data-eid="${eidPlain}" title="Se retirer" aria-label="Se retirer">−</button>`
|
|
1685
|
+
: '';
|
|
1686
|
+
const participantsHtml = `<div class="mb-2" id="onekite-participants-special"><strong>Participants</strong>${joinBtn}${leaveBtn}<br>` +
|
|
1682
1687
|
(participants.length
|
|
1683
1688
|
? participants.map((name) => {
|
|
1684
1689
|
const u = String(name || '').trim();
|
|
@@ -1755,12 +1760,38 @@ function toDatetimeLocalValue(date) {
|
|
|
1755
1760
|
return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
|
|
1756
1761
|
}).filter(Boolean).join('')
|
|
1757
1762
|
: `<span class="text-muted">Aucun</span>`;
|
|
1758
|
-
box.html(`<strong>Participants</strong><br>${links}`);
|
|
1763
|
+
box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-special" data-eid="${escapeHtml(eid)}" title="Se retirer" aria-label="Se retirer">−</button><br>${links}`);
|
|
1759
1764
|
}
|
|
1760
1765
|
} catch (e3) {
|
|
1761
1766
|
showAlert('error', "Impossible de s'ajouter.");
|
|
1762
1767
|
}
|
|
1763
1768
|
});
|
|
1769
|
+
|
|
1770
|
+
dlg.find('.onekite-leave-special').off('click').on('click', async (e2) => {
|
|
1771
|
+
e2.preventDefault();
|
|
1772
|
+
const btn = e2.currentTarget;
|
|
1773
|
+
if (!btn) return;
|
|
1774
|
+
try {
|
|
1775
|
+
btn.classList.add('disabled');
|
|
1776
|
+
const eid = String(btn.getAttribute('data-eid') || '').trim();
|
|
1777
|
+
if (!eid) return;
|
|
1778
|
+
const r = await fetchJson(`/api/v3/plugins/calendar-onekite/special-events/${encodeURIComponent(eid)}/participants`, { method: 'DELETE' });
|
|
1779
|
+
const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
|
|
1780
|
+
const box = dlg.find('#onekite-participants-special');
|
|
1781
|
+
if (box && box.length) {
|
|
1782
|
+
const links = names.length
|
|
1783
|
+
? names.map((name) => {
|
|
1784
|
+
const u = String(name || '').trim();
|
|
1785
|
+
if (!u) return '';
|
|
1786
|
+
return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
|
|
1787
|
+
}).filter(Boolean).join('')
|
|
1788
|
+
: `<span class="text-muted">Aucun</span>`;
|
|
1789
|
+
box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-success ms-2 onekite-join-special" data-eid="${escapeHtml(eid)}" title="S'ajouter" aria-label="S'ajouter">+</button><br>${links}`);
|
|
1790
|
+
}
|
|
1791
|
+
} catch (e3) {
|
|
1792
|
+
showAlert('error', "Impossible de se retirer.");
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1764
1795
|
});
|
|
1765
1796
|
} catch (e) {}
|
|
1766
1797
|
try {
|
|
@@ -1783,10 +1814,15 @@ function toDatetimeLocalValue(date) {
|
|
|
1783
1814
|
const hasCoords = Number.isFinite(lat) && Number.isFinite(lon);
|
|
1784
1815
|
const notes = String(p.notes || '').trim();
|
|
1785
1816
|
const participants = Array.isArray(p.participantsUsernames) ? p.participantsUsernames : [];
|
|
1817
|
+
const oidPlain = escapeHtml(String(p.oid || '').replace(/^outing:/, ''));
|
|
1818
|
+
// Use real buttons with visible + / - text to avoid relying on icon fonts.
|
|
1786
1819
|
const joinBtn = (p.canJoin && !p.isParticipant)
|
|
1787
|
-
? `<
|
|
1820
|
+
? `<button type="button" class="btn btn-sm btn-success ms-2 onekite-join-outing" data-oid="${oidPlain}" title="S'ajouter" aria-label="S'ajouter">+</button>`
|
|
1788
1821
|
: '';
|
|
1789
|
-
const
|
|
1822
|
+
const leaveBtn = (p.canJoin && p.isParticipant)
|
|
1823
|
+
? `<button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-outing" data-oid="${oidPlain}" title="Se retirer" aria-label="Se retirer">−</button>`
|
|
1824
|
+
: '';
|
|
1825
|
+
const participantsHtml = `<div class="mb-2" id="onekite-participants-outing"><strong>Participants</strong>${joinBtn}${leaveBtn}<br>` +
|
|
1790
1826
|
(participants.length
|
|
1791
1827
|
? participants.map((name) => {
|
|
1792
1828
|
const u = String(name || '').trim();
|
|
@@ -1863,12 +1899,38 @@ function toDatetimeLocalValue(date) {
|
|
|
1863
1899
|
return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
|
|
1864
1900
|
}).filter(Boolean).join('')
|
|
1865
1901
|
: `<span class="text-muted">Aucun</span>`;
|
|
1866
|
-
box.html(`<strong>Participants</strong><br>${links}`);
|
|
1902
|
+
box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-outing" data-oid="${escapeHtml(oid)}" title="Se retirer" aria-label="Se retirer">−</button><br>${links}`);
|
|
1867
1903
|
}
|
|
1868
1904
|
} catch (e3) {
|
|
1869
1905
|
showAlert('error', "Impossible de s'ajouter.");
|
|
1870
1906
|
}
|
|
1871
1907
|
});
|
|
1908
|
+
|
|
1909
|
+
dlg.find('.onekite-leave-outing').off('click').on('click', async (e2) => {
|
|
1910
|
+
e2.preventDefault();
|
|
1911
|
+
const btn = e2.currentTarget;
|
|
1912
|
+
if (!btn) return;
|
|
1913
|
+
try {
|
|
1914
|
+
btn.classList.add('disabled');
|
|
1915
|
+
const oid = String(btn.getAttribute('data-oid') || '').trim();
|
|
1916
|
+
if (!oid) return;
|
|
1917
|
+
const r = await fetchJson(`/api/v3/plugins/calendar-onekite/outings/${encodeURIComponent(oid)}/participants`, { method: 'DELETE' });
|
|
1918
|
+
const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
|
|
1919
|
+
const box = dlg.find('#onekite-participants-outing');
|
|
1920
|
+
if (box && box.length) {
|
|
1921
|
+
const links = names.length
|
|
1922
|
+
? names.map((name) => {
|
|
1923
|
+
const u = String(name || '').trim();
|
|
1924
|
+
if (!u) return '';
|
|
1925
|
+
return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
|
|
1926
|
+
}).filter(Boolean).join('')
|
|
1927
|
+
: `<span class="text-muted">Aucun</span>`;
|
|
1928
|
+
box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-success ms-2 onekite-join-outing" data-oid="${escapeHtml(oid)}" title="S'ajouter" aria-label="S'ajouter">+</button><br>${links}`);
|
|
1929
|
+
}
|
|
1930
|
+
} catch (e3) {
|
|
1931
|
+
showAlert('error', "Impossible de se retirer.");
|
|
1932
|
+
}
|
|
1933
|
+
});
|
|
1872
1934
|
});
|
|
1873
1935
|
} catch (e) {}
|
|
1874
1936
|
try {
|