nodebb-plugin-facebook-post 1.0.20 → 1.0.22
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 +90 -11
- package/package.json +1 -1
- package/static/lib/composer.js +61 -5
package/library.js
CHANGED
|
@@ -293,6 +293,39 @@ Plugin.init = async function (params) {
|
|
|
293
293
|
return res.json({ allowed: false, reason: 'error' });
|
|
294
294
|
}
|
|
295
295
|
});
|
|
296
|
+
|
|
297
|
+
router.get('/api/facebook-post/search-place', middleware.ensureLoggedIn, async (req, res) => {
|
|
298
|
+
res.set('Cache-Control', 'no-store');
|
|
299
|
+
try {
|
|
300
|
+
await loadSettings();
|
|
301
|
+
if (!settings.fbPageAccessToken) return res.json({ results: [] });
|
|
302
|
+
const q = trimStr(req.query.q);
|
|
303
|
+
if (q.length < 2) return res.json({ results: [] });
|
|
304
|
+
|
|
305
|
+
const resp = await axios.get(
|
|
306
|
+
`https://graph.facebook.com/${settings.fbGraphVersion}/search`,
|
|
307
|
+
{
|
|
308
|
+
params: {
|
|
309
|
+
type: 'place',
|
|
310
|
+
q,
|
|
311
|
+
fields: 'id,name,location',
|
|
312
|
+
access_token: settings.fbPageAccessToken,
|
|
313
|
+
limit: 7,
|
|
314
|
+
},
|
|
315
|
+
timeout: 8000,
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
const data = (resp.data && resp.data.data) || [];
|
|
319
|
+
const results = data.map(p => ({
|
|
320
|
+
id: p.id,
|
|
321
|
+
name: p.name,
|
|
322
|
+
city: (p.location && (p.location.city || p.location.country)) || '',
|
|
323
|
+
}));
|
|
324
|
+
return res.json({ results });
|
|
325
|
+
} catch {
|
|
326
|
+
return res.json({ results: [] });
|
|
327
|
+
}
|
|
328
|
+
});
|
|
296
329
|
};
|
|
297
330
|
|
|
298
331
|
Plugin.addAdminNavigation = async function (header) {
|
|
@@ -306,44 +339,90 @@ Plugin.addAdminNavigation = async function (header) {
|
|
|
306
339
|
};
|
|
307
340
|
|
|
308
341
|
Plugin.onPostCreate = async function (hookData) {
|
|
342
|
+
const winston = require.main.require('winston');
|
|
309
343
|
try {
|
|
310
|
-
if (!hookData?.post || !hookData?.data)
|
|
344
|
+
if (!hookData?.post || !hookData?.data) {
|
|
345
|
+
winston.verbose('[facebook-post] onPostCreate: hookData manquant (post ou data absent)');
|
|
346
|
+
return hookData;
|
|
347
|
+
}
|
|
311
348
|
hookData.post.fbPostEnabled = bool(hookData.data.fbPostEnabled);
|
|
312
349
|
const fbPlaceId = trimStr(hookData.data.fbPlaceId);
|
|
313
350
|
if (fbPlaceId) hookData.post.fbPlaceId = fbPlaceId;
|
|
314
|
-
|
|
315
|
-
|
|
351
|
+
winston.verbose(`[facebook-post] onPostCreate: pid=${hookData.post.pid} fbPostEnabled=${hookData.post.fbPostEnabled} fbPlaceId=${hookData.post.fbPlaceId || '(none)'}`);
|
|
352
|
+
} catch (e) {
|
|
353
|
+
winston.error(`[facebook-post] onPostCreate erreur: ${e?.message || e}`);
|
|
316
354
|
}
|
|
317
355
|
return hookData;
|
|
318
356
|
};
|
|
319
357
|
|
|
320
358
|
Plugin.onPostSave = async function (hookData) {
|
|
321
359
|
const winston = require.main.require('winston');
|
|
360
|
+
winston.verbose('[facebook-post] onPostSave: hook déclenché');
|
|
322
361
|
try {
|
|
323
362
|
await loadSettings();
|
|
324
|
-
|
|
363
|
+
winston.verbose(`[facebook-post] onPostSave: settings.enabled=${settings.enabled} fbPageId=${settings.fbPageId ? 'défini' : 'MANQUANT'} fbPageAccessToken=${settings.fbPageAccessToken ? 'défini' : 'MANQUANT'}`);
|
|
325
364
|
|
|
326
|
-
|
|
327
|
-
|
|
365
|
+
if (!settings.enabled) {
|
|
366
|
+
winston.verbose('[facebook-post] onPostSave: abandon — plugin désactivé dans les paramètres');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
328
369
|
|
|
329
|
-
const
|
|
330
|
-
|
|
370
|
+
const rawPost = hookData && hookData.post ? hookData.post : hookData;
|
|
371
|
+
winston.verbose(`[facebook-post] onPostSave: données brutes reçues — pid=${rawPost?.pid} fbPostEnabled=${rawPost?.fbPostEnabled}`);
|
|
331
372
|
|
|
332
|
-
|
|
373
|
+
const ctx = await getPostContext(rawPost);
|
|
374
|
+
if (!ctx) {
|
|
375
|
+
winston.warn('[facebook-post] onPostSave: abandon — impossible de récupérer le contexte du post (pid invalide ?)');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
winston.verbose(`[facebook-post] onPostSave: contexte récupéré — pid=${ctx.post.pid} tid=${ctx.topic.tid} cid=${ctx.topic.cid} uid=${ctx.user.uid} isMainPost=${ctx.post.isMainPost} index=${ctx.post.index} reputation=${ctx.user.reputation}`);
|
|
379
|
+
|
|
380
|
+
const allowed = await userIsAllowed(ctx.post.uid);
|
|
381
|
+
if (!allowed) {
|
|
382
|
+
const groups = parseAllowedGroupsList();
|
|
383
|
+
winston.verbose(`[facebook-post] onPostSave: abandon — uid=${ctx.post.uid} n'est pas dans les groupes autorisés: [${groups.join(', ')}]`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
winston.verbose(`[facebook-post] onPostSave: uid=${ctx.post.uid} autorisé par les groupes`);
|
|
387
|
+
|
|
388
|
+
const process = shouldProcessPost(ctx);
|
|
389
|
+
if (!process) {
|
|
390
|
+
// Log détaillé de la raison du refus
|
|
391
|
+
const isFirstPost = (ctx.post.isMainPost === true) || (ctx.post.index === 0);
|
|
392
|
+
const fbEnabled = bool(ctx.post.fbPostEnabled);
|
|
393
|
+
const repOk = (ctx.user.reputation || 0) >= settings.minimumReputation;
|
|
394
|
+
const whitelist = parseCsvInts(settings.categoriesWhitelist);
|
|
395
|
+
const catOk = whitelist.length === 0 || whitelist.includes(parseInt(ctx.topic.cid, 10));
|
|
396
|
+
winston.verbose(`[facebook-post] onPostSave: abandon — shouldProcessPost=false. Détail: isFirstPost=${isFirstPost} fbPostEnabled=${fbEnabled} reputationOk=${repOk}(${ctx.user.reputation}>=${settings.minimumReputation}) categoryOk=${catOk}(cid=${ctx.topic.cid} whitelist=[${whitelist}])`);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
winston.verbose('[facebook-post] onPostSave: shouldProcessPost=true, publication en cours…');
|
|
333
400
|
|
|
334
401
|
const Posts = require.main.require('./src/posts');
|
|
335
402
|
|
|
336
403
|
const already = await Posts.getPostField(ctx.post.pid, 'fbPostedId');
|
|
337
|
-
if (already)
|
|
404
|
+
if (already) {
|
|
405
|
+
winston.verbose(`[facebook-post] onPostSave: abandon — déjà publié (fbPostedId=${already})`);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const imageUrls = extractImageUrlsFromContent(ctx.post.content || '').filter(isForumHosted);
|
|
410
|
+
winston.verbose(`[facebook-post] onPostSave: images hébergées sur le forum trouvées: ${imageUrls.length} [${imageUrls.join(', ')}]`);
|
|
338
411
|
|
|
339
412
|
const fbId = await postToFacebook(ctx);
|
|
340
413
|
if (fbId) {
|
|
341
414
|
await Posts.setPostField(ctx.post.pid, 'fbPostedId', fbId);
|
|
342
415
|
await Posts.setPostField(ctx.post.pid, 'fbPostedAt', Date.now());
|
|
416
|
+
winston.info(`[facebook-post] onPostSave: publication réussie — fbId=${fbId} pid=${ctx.post.pid}`);
|
|
417
|
+
} else {
|
|
418
|
+
winston.warn('[facebook-post] onPostSave: postToFacebook a retourné un fbId vide (pas d\'erreur levée)');
|
|
343
419
|
}
|
|
344
420
|
} catch (e) {
|
|
345
421
|
const detail = e?.response ? JSON.stringify(e.response.data) : (e?.message || e);
|
|
346
|
-
winston.error(`[facebook-post]
|
|
422
|
+
winston.error(`[facebook-post] onPostSave: ERREUR: ${detail}`);
|
|
423
|
+
if (e?.response?.config?.url) {
|
|
424
|
+
winston.error(`[facebook-post] onPostSave: URL appelée: ${e.response.config.url} — status: ${e.response.status}`);
|
|
425
|
+
}
|
|
347
426
|
}
|
|
348
427
|
};
|
|
349
428
|
|
package/package.json
CHANGED
package/static/lib/composer.js
CHANGED
|
@@ -26,13 +26,18 @@
|
|
|
26
26
|
</div>
|
|
27
27
|
|
|
28
28
|
<div class="mb-2" data-fbpost-place-wrap style="display:none;">
|
|
29
|
-
<label class="form-label" style="font-size:
|
|
30
|
-
Lieu
|
|
29
|
+
<label class="form-label" style="font-size:12px;opacity:.8;">
|
|
30
|
+
Lieu – optionnel
|
|
31
31
|
</label>
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
<div style="position:relative;">
|
|
33
|
+
<input type="text" class="form-control" data-fbpost-place-query
|
|
34
|
+
placeholder="Rechercher un lieu (ville, adresse…)" autocomplete="off">
|
|
35
|
+
<ul data-fbpost-place-results
|
|
36
|
+
style="display:none;position:absolute;z-index:9999;background:#fff;
|
|
37
|
+
border:1px solid #ccc;border-radius:4px;list-style:none;
|
|
38
|
+
margin:0;padding:0;width:100%;max-height:200px;overflow-y:auto;"></ul>
|
|
35
39
|
</div>
|
|
40
|
+
<input type="hidden" data-fbpost-place-id>
|
|
36
41
|
</div>
|
|
37
42
|
</div>
|
|
38
43
|
`;
|
|
@@ -56,6 +61,57 @@
|
|
|
56
61
|
$placeWrap.toggle($enabled.is(':checked'));
|
|
57
62
|
});
|
|
58
63
|
|
|
64
|
+
let _placeTimer = null;
|
|
65
|
+
const $query = $composer.find('[data-fbpost-place-query]');
|
|
66
|
+
const $results = $composer.find('[data-fbpost-place-results]');
|
|
67
|
+
const $placeId = $composer.find('[data-fbpost-place-id]');
|
|
68
|
+
|
|
69
|
+
function clearPlace() {
|
|
70
|
+
$placeId.val('');
|
|
71
|
+
}
|
|
72
|
+
function selectPlace(id, name, city) {
|
|
73
|
+
$placeId.val(id);
|
|
74
|
+
$query.val(name + (city ? ' – ' + city : ''));
|
|
75
|
+
$results.hide().empty();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
$query.on('input', function () {
|
|
79
|
+
clearPlace();
|
|
80
|
+
clearTimeout(_placeTimer);
|
|
81
|
+
const q = $query.val().trim();
|
|
82
|
+
if (q.length < 2) { $results.hide().empty(); return; }
|
|
83
|
+
_placeTimer = setTimeout(async function () {
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch('/api/facebook-post/search-place?q=' + encodeURIComponent(q), {
|
|
86
|
+
credentials: 'same-origin', cache: 'no-store',
|
|
87
|
+
});
|
|
88
|
+
if (!res.ok) return;
|
|
89
|
+
const json = await res.json();
|
|
90
|
+
$results.empty();
|
|
91
|
+
if (!json.results || !json.results.length) { $results.hide(); return; }
|
|
92
|
+
json.results.forEach(function (p) {
|
|
93
|
+
const label = p.name + (p.city ? ' – ' + p.city : '');
|
|
94
|
+
$('<li>')
|
|
95
|
+
.text(label)
|
|
96
|
+
.css({ padding: '6px 10px', cursor: 'pointer' })
|
|
97
|
+
.on('mousedown', function (e) {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
selectPlace(p.id, p.name, p.city);
|
|
100
|
+
})
|
|
101
|
+
.appendTo($results);
|
|
102
|
+
});
|
|
103
|
+
$results.show();
|
|
104
|
+
} catch { /* ignore */ }
|
|
105
|
+
}, 350);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
$query.on('blur', function () {
|
|
109
|
+
setTimeout(function () { $results.hide(); }, 200);
|
|
110
|
+
});
|
|
111
|
+
$query.on('focus', function () {
|
|
112
|
+
if ($results.children().length) $results.show();
|
|
113
|
+
});
|
|
114
|
+
|
|
59
115
|
$(window).off('filter:composer.submit.fbpost')
|
|
60
116
|
.on('filter:composer.submit.fbpost', function (ev2, submitData) {
|
|
61
117
|
const enabled = $enabled.is(':checked');
|