nodebb-plugin-ezoic-infinite 1.8.17 → 1.8.19

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 CHANGED
@@ -1,16 +1,24 @@
1
1
  'use strict';
2
2
 
3
- const meta = require.main.require('./src/meta');
3
+ const meta = require.main.require('./src/meta');
4
4
  const groups = require.main.require('./src/groups');
5
- const db = require.main.require('./src/database');
5
+ const db = require.main.require('./src/database');
6
6
 
7
7
  const SETTINGS_KEY = 'ezoic-infinite';
8
8
  const plugin = {};
9
9
 
10
+ // ── Helpers ────────────────────────────────────────────────────────────────
11
+
10
12
  function normalizeExcludedGroups(value) {
11
13
  if (!value) return [];
12
14
  if (Array.isArray(value)) return value;
13
- return String(value).split(',').map(s => s.trim()).filter(Boolean);
15
+ // NodeBB stocke les settings multi-valeurs comme string JSON "[\"group1\",\"group2\"]"
16
+ const s = String(value).trim();
17
+ if (s.startsWith('[')) {
18
+ try { const parsed = JSON.parse(s); if (Array.isArray(parsed)) return parsed.map(String).filter(Boolean); } catch (_) {}
19
+ }
20
+ // Fallback : séparation par virgule
21
+ return s.split(',').map(v => v.trim()).filter(Boolean);
14
22
  }
15
23
 
16
24
  function parseBool(v, def = false) {
@@ -27,14 +35,16 @@ async function getAllGroups() {
27
35
  }
28
36
  const filtered = names.filter(name => !groups.isPrivilegeGroup(name));
29
37
  const data = await groups.getGroupsData(filtered);
30
- // Filter out nulls (groups deleted between the sorted-set read and getGroupsData)
31
38
  const valid = data.filter(g => g && g.name);
32
39
  valid.sort((a, b) => String(a.name).localeCompare(String(b.name), undefined, { sensitivity: 'base' }));
33
40
  return valid;
34
41
  }
35
- let _settingsCache = null;
42
+
43
+ // ── Settings cache (30s TTL) ────────────────────────────────────────────────
44
+
45
+ let _settingsCache = null;
36
46
  let _settingsCacheAt = 0;
37
- const SETTINGS_TTL = 30000; // 30s
47
+ const SETTINGS_TTL = 30_000;
38
48
 
39
49
  async function getSettings() {
40
50
  const now = Date.now();
@@ -42,25 +52,19 @@ async function getSettings() {
42
52
  const s = await meta.settings.get(SETTINGS_KEY);
43
53
  _settingsCacheAt = Date.now();
44
54
  _settingsCache = {
45
- // Between-post ads (simple blocks) in category topic list
46
- enableBetweenAds: parseBool(s.enableBetweenAds, true),
47
- showFirstTopicAd: parseBool(s.showFirstTopicAd, false),
48
- placeholderIds: (s.placeholderIds || '').trim(),
49
- intervalPosts: Math.max(1, parseInt(s.intervalPosts, 10) || 6),
50
-
51
- // Home/categories list ads (between categories on / or /categories)
52
- enableCategoryAds: parseBool(s.enableCategoryAds, false),
53
- showFirstCategoryAd: parseBool(s.showFirstCategoryAd, false),
55
+ enableBetweenAds: parseBool(s.enableBetweenAds, true),
56
+ showFirstTopicAd: parseBool(s.showFirstTopicAd, false),
57
+ placeholderIds: (s.placeholderIds || '').trim(),
58
+ intervalPosts: Math.max(1, parseInt(s.intervalPosts, 10) || 6),
59
+ enableCategoryAds: parseBool(s.enableCategoryAds, false),
60
+ showFirstCategoryAd: parseBool(s.showFirstCategoryAd, false),
54
61
  categoryPlaceholderIds: (s.categoryPlaceholderIds || '').trim(),
55
- intervalCategories: Math.max(1, parseInt(s.intervalCategories, 10) || 4),
56
-
57
- // "Ad message" between replies (looks like a post)
58
- enableMessageAds: parseBool(s.enableMessageAds, false),
59
- showFirstMessageAd: parseBool(s.showFirstMessageAd, false),
60
- messagePlaceholderIds: (s.messagePlaceholderIds || '').trim(),
61
- messageIntervalPosts: Math.max(1, parseInt(s.messageIntervalPosts, 10) || 3),
62
-
63
- excludedGroups: normalizeExcludedGroups(s.excludedGroups),
62
+ intervalCategories: Math.max(1, parseInt(s.intervalCategories, 10) || 4),
63
+ enableMessageAds: parseBool(s.enableMessageAds, false),
64
+ showFirstMessageAd: parseBool(s.showFirstMessageAd, false),
65
+ messagePlaceholderIds: (s.messagePlaceholderIds || '').trim(),
66
+ messageIntervalPosts: Math.max(1, parseInt(s.messageIntervalPosts, 10) || 3),
67
+ excludedGroups: normalizeExcludedGroups(s.excludedGroups),
64
68
  };
65
69
  return _settingsCache;
66
70
  }
@@ -71,58 +75,84 @@ async function isUserExcluded(uid, excludedGroups) {
71
75
  return (userGroups[0] || []).some(g => excludedGroups.includes(g.name));
72
76
  }
73
77
 
78
+ // ── Scripts Ezoic ──────────────────────────────────────────────────────────
79
+
80
+ const EZOIC_SCRIPTS = `<script data-cfasync="false" src="https://cmp.gatekeeperconsent.com/min.js"></script>
81
+ <script data-cfasync="false" src="https://the.gatekeeperconsent.com/cmp.min.js"></script>
82
+ <script async src="//www.ezojs.com/ezoic/sa.min.js"></script>
83
+ <script>
84
+ window.ezstandalone = window.ezstandalone || {};
85
+ ezstandalone.cmd = ezstandalone.cmd || [];
86
+ </script>`;
87
+
88
+ // ── Hooks ──────────────────────────────────────────────────────────────────
89
+
74
90
  plugin.onSettingsSet = function (data) {
75
- // Invalider le cache dès que les settings de ce plugin sont sauvegardés via l'ACP
76
- if (data && data.hash === SETTINGS_KEY) {
77
- _settingsCache = null;
78
- }
91
+ if (data && data.hash === SETTINGS_KEY) _settingsCache = null;
79
92
  };
80
93
 
81
94
  plugin.addAdminNavigation = async (header) => {
82
95
  header.plugins = header.plugins || [];
83
- header.plugins.push({
84
- route: '/plugins/ezoic-infinite',
85
- icon: 'fa-ad',
86
- name: 'Ezoic Infinite Ads'
87
- });
96
+ header.plugins.push({ route: '/plugins/ezoic-infinite', icon: 'fa-ad', name: 'Ezoic Infinite Ads' });
88
97
  return header;
89
98
  };
90
99
 
100
+ /**
101
+ * Injecte les scripts Ezoic dans le <head> via templateData.customHTML.
102
+ *
103
+ * NodeBB v4 / thème Harmony : header.tpl contient {{customHTML}} dans le <head>
104
+ * (render.js ligne 232 : templateValues.customHTML = meta.config.customHTML).
105
+ * Le hook filter:middleware.renderHeader reçoit templateData = headerFooterData
106
+ * et est rendu via req.app.renderAsync('header', hookReturn.templateData).
107
+ * On préfixe customHTML pour que nos scripts passent AVANT le customHTML admin,
108
+ * tout en préservant ce dernier.
109
+ */
110
+ plugin.injectEzoicHead = async (data) => {
111
+ try {
112
+ const settings = await getSettings();
113
+ const uid = data.req?.uid ?? 0;
114
+ const excluded = await isUserExcluded(uid, settings.excludedGroups);
115
+ if (!excluded) {
116
+ // Préfixer : nos scripts d'abord, puis le customHTML existant de l'admin
117
+ data.templateData.customHTML = EZOIC_SCRIPTS + (data.templateData.customHTML || '');
118
+ }
119
+ } catch (_) {}
120
+ return data;
121
+ };
122
+
91
123
  plugin.init = async ({ router, middleware }) => {
92
124
  async function render(req, res) {
93
- const settings = await getSettings();
125
+ const settings = await getSettings();
94
126
  const allGroups = await getAllGroups();
95
-
96
127
  res.render('admin/plugins/ezoic-infinite', {
97
128
  title: 'Ezoic Infinite Ads',
98
129
  ...settings,
99
130
  enableBetweenAds_checked: settings.enableBetweenAds ? 'checked' : '',
100
- enableMessageAds_checked: settings.enableMessageAds ? 'checked' : '',
131
+ enableMessageAds_checked: settings.enableMessageAds ? 'checked' : '',
101
132
  allGroups,
102
133
  });
103
134
  }
104
135
 
105
- router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
136
+ router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
106
137
  router.get('/api/admin/plugins/ezoic-infinite', render);
107
138
 
108
139
  router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
109
140
  const settings = await getSettings();
110
141
  const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
111
-
112
142
  res.json({
113
143
  excluded,
114
- enableBetweenAds: settings.enableBetweenAds,
115
- showFirstTopicAd: settings.showFirstTopicAd,
116
- placeholderIds: settings.placeholderIds,
117
- intervalPosts: settings.intervalPosts,
118
- enableCategoryAds: settings.enableCategoryAds,
119
- showFirstCategoryAd: settings.showFirstCategoryAd,
144
+ enableBetweenAds: settings.enableBetweenAds,
145
+ showFirstTopicAd: settings.showFirstTopicAd,
146
+ placeholderIds: settings.placeholderIds,
147
+ intervalPosts: settings.intervalPosts,
148
+ enableCategoryAds: settings.enableCategoryAds,
149
+ showFirstCategoryAd: settings.showFirstCategoryAd,
120
150
  categoryPlaceholderIds: settings.categoryPlaceholderIds,
121
- intervalCategories: settings.intervalCategories,
122
- enableMessageAds: settings.enableMessageAds,
123
- showFirstMessageAd: settings.showFirstMessageAd,
124
- messagePlaceholderIds: settings.messagePlaceholderIds,
125
- messageIntervalPosts: settings.messageIntervalPosts,
151
+ intervalCategories: settings.intervalCategories,
152
+ enableMessageAds: settings.enableMessageAds,
153
+ showFirstMessageAd: settings.showFirstMessageAd,
154
+ messagePlaceholderIds: settings.messagePlaceholderIds,
155
+ messageIntervalPosts: settings.messageIntervalPosts,
126
156
  });
127
157
  });
128
158
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.8.17",
3
+ "version": "1.8.19",
4
4
  "description": "Production-ready Ezoic infinite ads integration for NodeBB 4.x",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/plugin.json CHANGED
@@ -4,30 +4,14 @@
4
4
  "description": "Ezoic ads with infinite scroll using a pool of placeholder IDs",
5
5
  "library": "./library.js",
6
6
  "hooks": [
7
- {
8
- "hook": "static:app.load",
9
- "method": "init"
10
- },
11
- {
12
- "hook": "filter:admin.header.build",
13
- "method": "addAdminNavigation"
14
- },
15
- {
16
- "hook": "action:settings.set",
17
- "method": "onSettingsSet"
18
- }
7
+ { "hook": "static:app.load", "method": "init" },
8
+ { "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
9
+ { "hook": "action:settings.set", "method": "onSettingsSet" },
10
+ { "hook": "filter:middleware.renderHeader","method": "injectEzoicHead" }
19
11
  ],
20
- "staticDirs": {
21
- "public": "public"
22
- },
23
- "acpScripts": [
24
- "public/admin.js"
25
- ],
26
- "scripts": [
27
- "public/client.js"
28
- ],
29
- "templates": "public/templates",
30
- "css": [
31
- "public/style.css"
32
- ]
33
- }
12
+ "staticDirs": { "public": "public" },
13
+ "acpScripts": [ "public/admin.js" ],
14
+ "scripts": [ "public/client.js" ],
15
+ "templates": "public/templates",
16
+ "css": [ "public/style.css" ]
17
+ }