nodebb-plugin-gemach-directory 1.0.1 → 1.0.3
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/library.js +98 -6
- package/package.json +1 -1
package/library.js
CHANGED
|
@@ -7,13 +7,18 @@
|
|
|
7
7
|
* חדש (SocketPlugins.gemachDirectory.submit) - הוא נשמר כ"ממתין" ולא
|
|
8
8
|
* מוצג לאף אחד עד שמנהל מאשר אותו.
|
|
9
9
|
* - כשמוצע גמ"ח חדש, נשלחת התראת NodeBB אמיתית (פעמון) לכל חברי קבוצת
|
|
10
|
-
* "administrators"
|
|
11
|
-
* setNotifyPreference למטה - כל מנהל שולט בעצמו, לא משפיע על אחרים).
|
|
10
|
+
* "administrators" - כדי שמנהל ידע שיש משהו ממתין לאישור.
|
|
12
11
|
* - אישור/דחייה (SocketPlugins.gemachDirectory.approve/reject) פתוחים רק
|
|
13
12
|
* למנהלים (נבדק בשרת, לא רק בממשק) - אישור מעביר את הגמ"ח לרשימה
|
|
14
13
|
* הציבורית באופן מיידי, דחייה מוחקת אותו לגמרי (בלי לצבור "זבל").
|
|
15
14
|
* - הרשימה הציבורית (SocketPlugins.gemachDirectory.listApproved) פתוחה
|
|
16
|
-
* לכולם, כולל גולשים לא-מחוברים.
|
|
15
|
+
* לכולם, כולל גולשים לא-מחוברים. כל גמ"ח מוחזר עם שם המשתמש שהעלה אותו
|
|
16
|
+
* (submittedByUsername/submittedByUserslug) - כדי שהלקוח יוכל להציג
|
|
17
|
+
* קרדיט ("מאת @שם") ולדעת אם המשתמש המחובר הוא הבעלים.
|
|
18
|
+
* - עריכה/מחיקה (SocketPlugins.gemachDirectory.edit/remove) פתוחים למי
|
|
19
|
+
* שהעלה את הגמ"ח (נבדק לפי socket.uid מול השדה submittedBy שנשמר בזמן
|
|
20
|
+
* ההעלאה - אי אפשר לזייף) *או* למנהל - מנהל יכול לערוך/למחוק כל גמ"ח,
|
|
21
|
+
* משתמש רגיל רק את שלו.
|
|
17
22
|
*
|
|
18
23
|
* חלק הלקוח (הצגת הרשימה, הטופס להוספה, פאנל האישור למנהל) *לא* נמצא כאן -
|
|
19
24
|
* הוא קובץ נפרד (gemach-directory-client.js) שמודבק ב-Custom JS של הפורום,
|
|
@@ -36,6 +41,10 @@ const GEMACH_KEY = id => `gemachDirectory:item:${id}`;
|
|
|
36
41
|
// מפה של uid -> '0'/'1' - האם המנהל הזה רוצה לקבל התראות על גמ"חים חדשים.
|
|
37
42
|
// חסר מפתח = כברירת מחדל כן (כדי שמנהלים קיימים לא "יפספסו" בלי לשים לב).
|
|
38
43
|
const NOTIFY_PREFS_OBJECT = 'gemachDirectory:notifyPrefs';
|
|
44
|
+
// הנתיב שאליו התראת "גמ"ח חדש ממתין" מפנה - הפוסט האמיתי של נושא "רשימת
|
|
45
|
+
// גמחים" בפורום הזה. אם הנושא אי-פעם יימחק וייווצר מחדש, צריך לעדכן כאן
|
|
46
|
+
// את מספר הפוסט (רואים אותו ב-URL כשפותחים את הנושא: /post/<המספר>).
|
|
47
|
+
const NOTIFY_TARGET_PATH = '/post/151093';
|
|
39
48
|
|
|
40
49
|
const MAX_LENGTHS = {
|
|
41
50
|
name: 120,
|
|
@@ -142,6 +151,60 @@ function registerSocketHandlers() {
|
|
|
142
151
|
await db.setObjectField(NOTIFY_PREFS_OBJECT, socket.uid, enabled ? '1' : '0');
|
|
143
152
|
return { ok: true };
|
|
144
153
|
};
|
|
154
|
+
|
|
155
|
+
// עריכת גמ"ח קיים - הבעלים (submittedBy) או מנהל בלבד. עובד גם על גמ"ח
|
|
156
|
+
// ממתין וגם על גמ"ח מאושר, בלי לשנות את הסטטוס שלו.
|
|
157
|
+
SocketPlugins.gemachDirectory.edit = async function (socket, data) {
|
|
158
|
+
requireLogin(socket);
|
|
159
|
+
const id = data && data.id;
|
|
160
|
+
if (!id) throw new Error('[[error:invalid-data]]');
|
|
161
|
+
|
|
162
|
+
const gemach = await db.getObject(GEMACH_KEY(id));
|
|
163
|
+
if (!gemach) throw new Error('[[error:no-such-gemach]]');
|
|
164
|
+
await requireOwnerOrAdmin(socket, gemach);
|
|
165
|
+
|
|
166
|
+
const name = sanitizeText(data && data.name, MAX_LENGTHS.name);
|
|
167
|
+
const city = sanitizeText(data && data.city, MAX_LENGTHS.city);
|
|
168
|
+
const category = sanitizeText(data && data.category, MAX_LENGTHS.category);
|
|
169
|
+
const contact = sanitizeText(data && data.contact, MAX_LENGTHS.contact);
|
|
170
|
+
const description = sanitizeText(data && data.description, MAX_LENGTHS.description);
|
|
171
|
+
|
|
172
|
+
if (!name || !city || !category || !contact) {
|
|
173
|
+
throw new Error('[[error:invalid-data]]');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
await db.setObject(GEMACH_KEY(id), { name, city, category, contact, description });
|
|
177
|
+
return { ok: true };
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// מחיקת גמ"ח - הבעלים או מנהל בלבד. מוחק לגמרי מכל הרשימות (ממתין/מאושר).
|
|
181
|
+
SocketPlugins.gemachDirectory.remove = async function (socket, data) {
|
|
182
|
+
requireLogin(socket);
|
|
183
|
+
const id = data && data.id;
|
|
184
|
+
if (!id) throw new Error('[[error:invalid-data]]');
|
|
185
|
+
|
|
186
|
+
const gemach = await db.getObject(GEMACH_KEY(id));
|
|
187
|
+
if (!gemach) return { ok: true };
|
|
188
|
+
await requireOwnerOrAdmin(socket, gemach);
|
|
189
|
+
|
|
190
|
+
await db.sortedSetRemove(PENDING_SET, id);
|
|
191
|
+
await db.sortedSetRemove(APPROVED_SET, id);
|
|
192
|
+
await db.delete(GEMACH_KEY(id));
|
|
193
|
+
|
|
194
|
+
return { ok: true };
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// נקרא מהלקוח כשמנהל נכנס לנושא "רשימת גמחים" עצמו - מסמן כ"נקראו" רק
|
|
198
|
+
// את התראות ה"גמ"ח חדש ממתין" (לא נוגע בשום התראה אחרת של המנהל), כי
|
|
199
|
+
// הוא כבר רואה את כל הבקשות הממתינות ממש עכשיו בעמוד.
|
|
200
|
+
SocketPlugins.gemachDirectory.markPendingNotificationsRead = async function (socket) {
|
|
201
|
+
await requireAdmin(socket);
|
|
202
|
+
const ids = await db.getSortedSetRange(PENDING_SET, 0, -1);
|
|
203
|
+
if (!ids.length) return { ok: true };
|
|
204
|
+
const nids = ids.map(id => `gemach-directory:${id}`);
|
|
205
|
+
await notifications.markReadMultiple(nids, socket.uid);
|
|
206
|
+
return { ok: true };
|
|
207
|
+
};
|
|
145
208
|
}
|
|
146
209
|
|
|
147
210
|
async function getGemachsFromSet(setKey, newestFirst) {
|
|
@@ -149,8 +212,27 @@ async function getGemachsFromSet(setKey, newestFirst) {
|
|
|
149
212
|
await db.getSortedSetRevRange(setKey, 0, -1) :
|
|
150
213
|
await db.getSortedSetRange(setKey, 0, -1);
|
|
151
214
|
if (!ids.length) return [];
|
|
152
|
-
const gemachs = await db.getObjects(ids.map(GEMACH_KEY));
|
|
153
|
-
return gemachs
|
|
215
|
+
const gemachs = (await db.getObjects(ids.map(GEMACH_KEY))).filter(Boolean);
|
|
216
|
+
return attachSubmitterInfo(gemachs);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// מוסיף לכל גמ"ח את שם המשתמש (לקרדיט) ואת ה-userslug (לקישור לפרופיל)
|
|
220
|
+
// של מי שהעלה אותו - כדי שהלקוח לא יצטרך שאילתת משתמש נפרדת לכל כרטיס.
|
|
221
|
+
async function attachSubmitterInfo(gemachs) {
|
|
222
|
+
const uids = gemachs.map(g => g.submittedBy).filter(Boolean);
|
|
223
|
+
if (!uids.length) return gemachs;
|
|
224
|
+
|
|
225
|
+
const users = await user.getUsersFields(uids, ['uid', 'username', 'userslug']);
|
|
226
|
+
const byUid = {};
|
|
227
|
+
users.forEach((u) => { byUid[u.uid] = u; });
|
|
228
|
+
|
|
229
|
+
return gemachs.map((g) => {
|
|
230
|
+
const submitter = byUid[g.submittedBy];
|
|
231
|
+
return Object.assign({}, g, {
|
|
232
|
+
submittedByUsername: submitter ? submitter.username : null,
|
|
233
|
+
submittedByUserslug: submitter ? submitter.userslug : null,
|
|
234
|
+
});
|
|
235
|
+
});
|
|
154
236
|
}
|
|
155
237
|
|
|
156
238
|
async function notifyAdmins(gemach) {
|
|
@@ -167,7 +249,7 @@ async function notifyAdmins(gemach) {
|
|
|
167
249
|
// nid כולל את מזהה הגמ"ח - כך שכל הצעה חדשה היא התראה "חדשה" נפרדת
|
|
168
250
|
nid: `gemach-directory:${gemach.id}`,
|
|
169
251
|
bodyShort: `גמ"ח חדש ממתין לאישור: ${gemach.name} (${gemach.city})`,
|
|
170
|
-
path:
|
|
252
|
+
path: NOTIFY_TARGET_PATH,
|
|
171
253
|
from: gemach.submittedBy,
|
|
172
254
|
});
|
|
173
255
|
await notifications.push(notification, targetUids);
|
|
@@ -187,6 +269,16 @@ async function requireAdmin(socket) {
|
|
|
187
269
|
}
|
|
188
270
|
}
|
|
189
271
|
|
|
272
|
+
// מרשה גישה רק למי שהעלה את הגמ"ח הזה (submittedBy) או למנהל.
|
|
273
|
+
async function requireOwnerOrAdmin(socket, gemach) {
|
|
274
|
+
requireLogin(socket);
|
|
275
|
+
if (String(gemach.submittedBy) === String(socket.uid)) return;
|
|
276
|
+
const isAdmin = await user.isAdministrator(socket.uid);
|
|
277
|
+
if (!isAdmin) {
|
|
278
|
+
throw new Error('[[error:no-privileges]]');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
190
282
|
// חיתוך אורך + הסרת תווי בקרה - הגנת שרת בסיסית. ההגנה האמיתית מפני
|
|
191
283
|
// XSS היא ב-escaping בצד הלקוח בזמן הצגה (ראו gemach-directory-client.js),
|
|
192
284
|
// כי הנתונים האלה מוזרקים שם ישירות ל-innerHTML.
|
package/package.json
CHANGED