nodebb-plugin-ezoic-infinite 1.0.0 → 1.0.1

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
@@ -2,13 +2,11 @@
2
2
 
3
3
  const meta = require.main.require('./src/meta');
4
4
  const groups = require.main.require('./src/groups');
5
- const routeHelpers = require.main.require('./src/routes/helpers');
6
5
 
7
6
  const Plugin = {};
8
7
 
9
8
  Plugin.init = async function ({ router, middleware }) {
10
- // Official helper ensures correct ACP behavior under ajaxify
11
- routeHelpers.setupAdminPageRoute(router, '/admin/plugins/ezoic-infinite', middleware, [], renderAdmin);
9
+ router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, renderAdmin);
12
10
  router.get('/api/admin/plugins/ezoic-infinite', renderAdmin);
13
11
  };
14
12
 
@@ -32,7 +30,7 @@ async function renderAdmin(req, res) {
32
30
  }
33
31
 
34
32
  groupList = (groupList || [])
35
- .filter(g => g && g.name && typeof g.name === 'string')
33
+ .filter(g => g && g.name)
36
34
  .sort((a, b) => (a.name || '').localeCompare(b.name || '', 'fr', { sensitivity: 'base' }));
37
35
 
38
36
  res.render('admin/plugins/ezoic-infinite', {
@@ -52,4 +50,11 @@ Plugin.addAdminNavigation = async function (header) {
52
50
  return header;
53
51
  };
54
52
 
53
+ // Expose settings to client without any admin API calls
54
+ Plugin.addConfig = async function (config) {
55
+ const settings = await meta.settings.get('ezoic-infinite');
56
+ config.ezoicInfinite = settings || {};
57
+ return config;
58
+ };
59
+
55
60
  module.exports = Plugin;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.0.0",
4
- "description": "Injection de publicités Ezoic entre les topics et entre les messages avec infinite scroll (NodeBB 4.x).",
3
+ "version": "1.0.1",
4
+ "description": "Ezoic ads injection for NodeBB 4.x (rebased to 0.8.3 style).",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
7
7
  "keywords": [
package/plugin.json CHANGED
@@ -11,19 +11,23 @@
11
11
  {
12
12
  "hook": "filter:admin.header.build",
13
13
  "method": "addAdminNavigation"
14
+ },
15
+ {
16
+ "hook": "filter:config.get",
17
+ "method": "addConfig"
14
18
  }
15
19
  ],
16
20
  "staticDirs": {
17
21
  "public": "public"
18
22
  },
23
+ "acpScripts": [
24
+ "public/admin.js"
25
+ ],
19
26
  "scripts": [
20
27
  "public/client.js"
21
28
  ],
22
29
  "css": [
23
30
  "public/style.css"
24
31
  ],
25
- "templates": "public/templates",
26
- "modules": {
27
- "../admin/plugins/ezoic-infinite.js": "public/admin.js"
28
- }
32
+ "templates": "public/templates"
29
33
  }
package/public/admin.js CHANGED
@@ -1,27 +1,52 @@
1
- define('admin/plugins/ezoic-infinite', ['settings', 'alerts'], function (Settings, alerts) {
2
- 'use strict';
3
-
4
- const ACP = {};
5
-
6
- ACP.init = function () {
7
- const $form = $('.ezoic-infinite-settings');
8
-
9
- // Charge les settings dans le formulaire
10
- Settings.load('ezoic-infinite', $form);
11
-
12
- // Le save_button.tpl déclenche un event "click" sur .save
13
- // On écoute l'event standard Settings.
14
- $form.on('click', '.save', function (e) {
15
- e.preventDefault();
16
- Settings.save('ezoic-infinite', $form, function (err) {
17
- if (err) {
18
- alerts.error(err.message || err);
19
- return;
20
- }
21
- alerts.success('Paramètres enregistrés');
1
+ /* global $, app, socket */
2
+ 'use strict';
3
+
4
+ $(document).ready(function () {
5
+ const namespace = 'ezoic-infinite';
6
+
7
+ function load() {
8
+ socket.emit('admin.settings.get', { hash: namespace }, function (err, data) {
9
+ if (err) return;
10
+ data = data || {};
11
+
12
+ const form = $('.ezoic-infinite-settings');
13
+
14
+ form.find('[name="enableBetweenAds"]').prop('checked', data.enableBetweenAds === true || data.enableBetweenAds === 'on');
15
+ form.find('[name="intervalTopics"]').val(parseInt(data.intervalTopics, 10) || 6);
16
+ form.find('[name="placeholderIds"]').val(data.placeholderIds || '');
17
+
18
+ form.find('[name="enableMessageAds"]').prop('checked', data.enableMessageAds === true || data.enableMessageAds === 'on');
19
+ form.find('[name="messageIntervalPosts"]').val(parseInt(data.messageIntervalPosts, 10) || 3);
20
+ form.find('[name="messagePlaceholderIds"]').val(data.messagePlaceholderIds || '');
21
+
22
+ // Do NOT clear groups; server rendered
23
+ const selected = (data.excludedGroups || '').split(',').map(s => s.trim()).filter(Boolean);
24
+ form.find('[name="excludedGroups"] option').each(function () {
25
+ $(this).prop('selected', selected.includes($(this).val()));
22
26
  });
23
27
  });
24
- };
28
+ }
29
+
30
+ function save() {
31
+ const form = $('.ezoic-infinite-settings');
32
+ const payload = {
33
+ enableBetweenAds: form.find('[name="enableBetweenAds"]').is(':checked'),
34
+ intervalTopics: parseInt(form.find('[name="intervalTopics"]').val(), 10) || 6,
35
+ placeholderIds: form.find('[name="placeholderIds"]').val() || '',
36
+
37
+ enableMessageAds: form.find('[name="enableMessageAds"]').is(':checked'),
38
+ messageIntervalPosts: parseInt(form.find('[name="messageIntervalPosts"]').val(), 10) || 3,
39
+ messagePlaceholderIds: form.find('[name="messagePlaceholderIds"]').val() || '',
40
+
41
+ excludedGroups: (form.find('[name="excludedGroups"]').val() || []).join(','),
42
+ };
43
+
44
+ socket.emit('admin.settings.set', { hash: namespace, values: payload }, function (err) {
45
+ if (err) { app.alertError(err.message || err); return; }
46
+ app.alertSuccess('Paramètres enregistrés');
47
+ });
48
+ }
25
49
 
26
- return ACP;
50
+ $('.ezoic-infinite-save').on('click', save);
51
+ load();
27
52
  });
package/public/client.js CHANGED
@@ -1,15 +1,13 @@
1
- /* global $, ajaxify, app */
1
+ /* global $, ajaxify, app, config */
2
2
  'use strict';
3
3
 
4
4
  (function () {
5
5
  if (window.ezoicInfiniteLoaded) return;
6
6
  window.ezoicInfiniteLoaded = true;
7
7
 
8
- const SETTINGS_NS = 'ezoic-infinite';
9
- let settings = null;
8
+ let settings = (window.config && window.config.ezoicInfinite) ? window.config.ezoicInfinite : {};
10
9
  let pageKey = null;
11
10
 
12
- // State per page
13
11
  let usedTopic = new Set();
14
12
  let usedCat = new Set();
15
13
  let fifoTopic = [];
@@ -18,6 +16,10 @@
18
16
  let refreshInFlight = false;
19
17
  let refreshQueued = false;
20
18
 
19
+ function reloadSettingsFromConfig() {
20
+ settings = (window.config && window.config.ezoicInfinite) ? window.config.ezoicInfinite : (settings || {});
21
+ }
22
+
21
23
  function parsePool(text) {
22
24
  return String(text || '')
23
25
  .split(/\r?\n/)
@@ -29,15 +31,13 @@
29
31
 
30
32
  function userExcluded() {
31
33
  try {
32
- const raw = (settings && settings.excludedGroups) ? String(settings.excludedGroups) : '';
34
+ const raw = settings && settings.excludedGroups ? String(settings.excludedGroups) : '';
33
35
  if (!raw) return false;
34
36
  const excluded = raw.split(',').map(s => s.trim()).filter(Boolean);
35
37
  if (!excluded.length) return false;
36
38
  const myGroups = (app.user && app.user.groups) ? app.user.groups : [];
37
39
  return excluded.some(g => myGroups.includes(g));
38
- } catch (e) {
39
- return false;
40
- }
40
+ } catch (e) { return false; }
41
41
  }
42
42
 
43
43
  function getPageKey() {
@@ -61,16 +61,12 @@
61
61
 
62
62
  function cleanupForNewPage() {
63
63
  $('.ezoic-ad').remove();
64
- usedTopic = new Set();
65
- usedCat = new Set();
66
- fifoTopic = [];
67
- fifoCat = [];
64
+ usedTopic = new Set(); usedCat = new Set();
65
+ fifoTopic = []; fifoCat = [];
68
66
  }
69
67
 
70
68
  function pickNextId(pool, usedSet) {
71
- for (const id of pool) {
72
- if (!usedSet.has(id)) return id;
73
- }
69
+ for (const id of pool) if (!usedSet.has(id)) return id;
74
70
  return null;
75
71
  }
76
72
 
@@ -91,74 +87,49 @@
91
87
  function ensureUniquePlaceholder(id) {
92
88
  const existing = document.getElementById('ezoic-pub-ad-placeholder-' + id);
93
89
  if (!existing) return;
94
-
95
90
  const wrap = existing.closest('.ezoic-ad');
96
- if (wrap) {
97
- try { $(wrap).remove(); } catch (e) { wrap.remove(); }
98
- } else {
99
- existing.remove();
100
- }
91
+ if (wrap) $(wrap).remove(); else existing.remove();
101
92
  destroyPlaceholder(id);
102
93
  }
103
94
 
104
95
  function callEzoicSingle(id) {
105
96
  if (!id) return;
106
-
107
97
  const now = Date.now();
108
98
  window.__ezoicLastSingle = window.__ezoicLastSingle || {};
109
- const last = window.__ezoicLastSingle[id] || 0;
110
- if (now - last < 1200) return;
99
+ if (now - (window.__ezoicLastSingle[id] || 0) < 1200) return;
111
100
  window.__ezoicLastSingle[id] = now;
112
101
 
113
- try {
114
- window.ezstandalone = window.ezstandalone || {};
115
- window.ezstandalone.cmd = window.ezstandalone.cmd || [];
102
+ window.ezstandalone = window.ezstandalone || {};
103
+ window.ezstandalone.cmd = window.ezstandalone.cmd || [];
104
+ const run = function () {
105
+ if (typeof window.ezstandalone.showAds === 'function') {
106
+ window.ezstandalone.showAds(id);
107
+ return true;
108
+ }
109
+ return false;
110
+ };
111
+ window.ezstandalone.cmd.push(function () { try { run(); } catch (e) {} });
116
112
 
117
- const run = function () {
118
- try {
119
- if (typeof window.ezstandalone.showAds === 'function') {
120
- window.ezstandalone.showAds(id);
121
- return true;
122
- }
123
- } catch (e) {}
124
- return false;
125
- };
126
-
127
- window.ezstandalone.cmd.push(function () { run(); });
128
-
129
- let tries = 0;
130
- const tick = function () {
131
- tries++;
132
- if (run() || tries >= 8) return;
133
- setTimeout(tick, 800);
134
- };
113
+ let tries = 0;
114
+ (function tick() {
115
+ tries++;
116
+ try { if (run() || tries >= 8) return; } catch (e) {}
135
117
  setTimeout(tick, 800);
136
- } catch (e) {}
118
+ })();
137
119
  }
138
120
 
139
121
  function setupAutoHeightOnce() {
140
122
  if (window.__ezoicAutoHeight) return;
141
123
  window.__ezoicAutoHeight = true;
142
-
143
- const mark = function (wrap) {
144
- if (!wrap) return;
145
- const ph = wrap.querySelector('[id^="ezoic-pub-ad-placeholder-"]');
146
- if (ph && ph.children && ph.children.length) {
147
- wrap.classList.add('ezoic-filled');
148
- }
149
- };
150
-
151
124
  const scan = function () {
152
- document.querySelectorAll('.ezoic-ad').forEach(mark);
125
+ document.querySelectorAll('.ezoic-ad').forEach((wrap) => {
126
+ const ph = wrap.querySelector('[id^="ezoic-pub-ad-placeholder-"]');
127
+ if (ph && ph.children && ph.children.length) wrap.classList.add('ezoic-filled');
128
+ });
153
129
  };
154
-
155
130
  scan();
156
131
  setInterval(scan, 1000);
157
-
158
- try {
159
- const mo = new MutationObserver(scan);
160
- mo.observe(document.body, { childList: true, subtree: true });
161
- } catch (e) {}
132
+ try { new MutationObserver(scan).observe(document.body, { childList: true, subtree: true }); } catch (e) {}
162
133
  }
163
134
 
164
135
  function insertAfter($target, id, cls, afterVal) {
@@ -169,52 +140,27 @@
169
140
  '</div>'
170
141
  );
171
142
  $target.after(wrap);
172
- return wrap;
173
143
  }
174
144
 
175
145
  function recycleTopic($posts) {
176
- fifoTopic.sort((a, b) => a.afterNo - b.afterNo);
146
+ fifoTopic.sort((a,b)=>a.afterNo-b.afterNo);
177
147
  while (fifoTopic.length) {
178
- const old = fifoTopic.shift();
179
- const sel = '.ezoic-ad-topic[data-ezoic-id="' + old.id + '"][data-ezoic-after="' + old.afterNo + '"]';
180
- const $el = $(sel);
148
+ const old=fifoTopic.shift();
149
+ const $el=$('.ezoic-ad-topic[data-ezoic-id="'+old.id+'"][data-ezoic-after="'+old.afterNo+'"]');
181
150
  if (!$el.length) continue;
182
-
183
- try {
184
- const $last = $posts.last();
185
- if ($last.length && $el.prev().is($last)) {
186
- fifoTopic.push(old);
187
- return null;
188
- }
189
- } catch (e) {}
190
-
191
- $el.remove();
192
- usedTopic.delete(old.id);
193
- destroyPlaceholder(old.id);
151
+ $el.remove(); usedTopic.delete(old.id); destroyPlaceholder(old.id);
194
152
  return old.id;
195
153
  }
196
154
  return null;
197
155
  }
198
156
 
199
157
  function recycleCat($items) {
200
- fifoCat.sort((a, b) => a.afterPos - b.afterPos);
158
+ fifoCat.sort((a,b)=>a.afterPos-b.afterPos);
201
159
  while (fifoCat.length) {
202
- const old = fifoCat.shift();
203
- const sel = '.ezoic-ad-between[data-ezoic-id="' + old.id + '"][data-ezoic-after="' + old.afterPos + '"]';
204
- const $el = $(sel);
160
+ const old=fifoCat.shift();
161
+ const $el=$('.ezoic-ad-between[data-ezoic-id="'+old.id+'"][data-ezoic-after="'+old.afterPos+'"]');
205
162
  if (!$el.length) continue;
206
-
207
- try {
208
- const $last = $items.last();
209
- if ($last.length && $el.prev().is($last)) {
210
- fifoCat.push(old);
211
- return null;
212
- }
213
- } catch (e) {}
214
-
215
- $el.remove();
216
- usedCat.delete(old.id);
217
- destroyPlaceholder(old.id);
163
+ $el.remove(); usedCat.delete(old.id); destroyPlaceholder(old.id);
218
164
  return old.id;
219
165
  }
220
166
  return null;
@@ -222,125 +168,82 @@
222
168
 
223
169
  function injectInTopic() {
224
170
  if (!(settings && (settings.enableMessageAds === true || settings.enableMessageAds === 'on'))) return;
225
-
226
- const interval = parseInt(settings.messageIntervalPosts, 10) || 3;
171
+ const interval = parseInt(settings.messageIntervalPosts,10) || 3;
227
172
  const pool = parsePool(settings.messagePlaceholderIds);
228
173
  if (!pool.length) return;
229
174
 
230
175
  const $posts = $('[component="post"][data-pid]');
231
176
  if (!$posts.length) return;
232
177
 
233
- $posts.each(function (idx) {
234
- const postNo = idx + 1;
235
- if (postNo % interval !== 0) return;
236
- if (idx === $posts.length - 1) return;
178
+ $posts.each(function(idx){
179
+ const no=idx+1;
180
+ if (no % interval !== 0) return;
181
+ if (idx === $posts.length-1) return;
237
182
 
238
- const $post = $(this);
239
- const existing = $post.next('.ezoic-ad-topic');
240
- if (existing.length) return;
183
+ const $post=$(this);
184
+ if ($post.next('.ezoic-ad-topic').length) return;
241
185
 
242
- let id = pickNextId(pool, usedTopic);
243
- if (!id) {
244
- id = recycleTopic($posts);
245
- if (!id) return;
246
- }
186
+ let id=pickNextId(pool, usedTopic);
187
+ if (!id) { id=recycleTopic($posts); if (!id) return; }
247
188
 
248
- usedTopic.add(id);
249
- fifoTopic.push({ id, afterNo: postNo });
250
-
251
- insertAfter($post, id, 'ezoic-ad-topic', postNo);
189
+ usedTopic.add(id); fifoTopic.push({id, afterNo:no});
190
+ insertAfter($post, id, 'ezoic-ad-topic', no);
252
191
  callEzoicSingle(id);
253
192
  });
254
193
  }
255
194
 
256
195
  function injectInCategory() {
257
196
  if (!(settings && (settings.enableBetweenAds === true || settings.enableBetweenAds === 'on'))) return;
258
-
259
- const interval = parseInt(settings.intervalTopics, 10) || 6;
197
+ const interval = parseInt(settings.intervalTopics,10) || 6;
260
198
  const pool = parsePool(settings.placeholderIds);
261
199
  if (!pool.length) return;
262
200
 
263
201
  const $items = $('li[component="category/topic"]');
264
202
  if (!$items.length) return;
265
203
 
266
- $items.each(function (idx) {
267
- const pos = idx + 1;
204
+ $items.each(function(idx){
205
+ const pos=idx+1;
268
206
  if (pos % interval !== 0) return;
269
- if (idx === $items.length - 1) return;
207
+ if (idx === $items.length-1) return;
270
208
 
271
- const $li = $(this);
272
- const existing = $li.next('.ezoic-ad-between');
273
- if (existing.length) return;
209
+ const $li=$(this);
210
+ if ($li.next('.ezoic-ad-between').length) return;
274
211
 
275
- let id = pickNextId(pool, usedCat);
276
- if (!id) {
277
- id = recycleCat($items);
278
- if (!id) return;
279
- }
280
-
281
- usedCat.add(id);
282
- fifoCat.push({ id, afterPos: pos });
212
+ let id=pickNextId(pool, usedCat);
213
+ if (!id) { id=recycleCat($items); if (!id) return; }
283
214
 
215
+ usedCat.add(id); fifoCat.push({id, afterPos:pos});
284
216
  insertAfter($li, id, 'ezoic-ad-between', pos);
285
217
  callEzoicSingle(id);
286
218
  });
287
219
  }
288
220
 
289
221
  function refresh() {
222
+ reloadSettingsFromConfig();
290
223
  if (!settings) return;
291
224
  if (userExcluded()) return;
292
225
 
293
226
  if (refreshInFlight) { refreshQueued = true; return; }
294
227
  refreshInFlight = true;
295
228
  try {
296
- const key = getPageKey();
297
- if (pageKey !== key) {
298
- pageKey = key;
299
- cleanupForNewPage();
300
- }
301
-
229
+ const key=getPageKey();
230
+ if (pageKey !== key) { pageKey=key; cleanupForNewPage(); }
302
231
  setupAutoHeightOnce();
303
-
304
232
  if (isTopicPage()) injectInTopic();
305
233
  else if (isCategoryTopicList()) injectInCategory();
306
234
  } finally {
307
- refreshInFlight = false;
308
- if (refreshQueued) { refreshQueued = false; setTimeout(refresh, 50); }
235
+ refreshInFlight=false;
236
+ if (refreshQueued) { refreshQueued=false; setTimeout(refresh, 50); }
309
237
  }
310
238
  }
311
239
 
312
- function loadSettings(cb) {
313
- // Use public settings endpoint to avoid any admin socket calls on frontend
314
- const base = (window.config && window.config.relative_path) ? window.config.relative_path : '';
315
- $.get(base + '/api/admin/settings/' + SETTINGS_NS).done(function (data) {
316
- // If access denied, settings stays null; plugin won't inject
317
- settings = (data && data.settings) ? data.settings : data;
318
- cb && cb();
319
- }).fail(function () {
320
- // Fallback: try window.ajaxify data if available
321
- settings = settings || {};
322
- cb && cb();
323
- });
324
- }
325
-
326
- function boot() {
327
- loadSettings(function () {
328
- refresh();
329
- setTimeout(refresh, 1200);
330
- });
331
- }
240
+ function boot() { refresh(); setTimeout(refresh, 1200); }
332
241
 
333
242
  $(document).ready(boot);
334
243
  $(window).on('action:ajaxify.end', boot);
335
-
336
- $(window).on('action:posts.loaded action:topic.loaded action:topics.loaded action:category.loaded', function () {
337
- refresh();
338
- setTimeout(refresh, 600);
339
- });
340
-
341
- $(window).on('action:ajaxify.start', function () {
342
- pageKey = null;
343
- cleanupForNewPage();
244
+ $(window).on('action:posts.loaded action:topics.loaded action:topic.loaded action:category.loaded', function(){
245
+ refresh(); setTimeout(refresh, 600);
344
246
  });
247
+ $(window).on('action:ajaxify.start', function(){ pageKey=null; cleanupForNewPage(); });
345
248
 
346
249
  })();
package/public/style.css CHANGED
@@ -1,5 +1,3 @@
1
- /* Le conteneur est caché tant qu'Ezoic n'a pas injecté de contenu */
2
1
  .ezoic-ad{min-height:0 !important;height:auto !important;padding:0 !important;margin:0.5rem 0;}
3
2
  .ezoic-ad:not(.ezoic-filled){display:none !important;}
4
3
  .ezoic-ad .ezoic-ad-inner{padding:0;margin:0;}
5
- .ezoic-ad .ezoic-ad-inner > div{margin:0;padding:0;}
@@ -1,11 +1,7 @@
1
1
  <div class="acp-page-container">
2
2
  <h1 class="mb-3">Ezoic - Publicités Infinite Scroll</h1>
3
3
 
4
- <div class="alert alert-info">
5
- Format placeholder&nbsp;: <code>&lt;div id="ezoic-pub-ad-placeholder-XXX"&gt;&lt;/div&gt;</code>
6
- </div>
7
-
8
- <form class="ezoic-infinite-settings" role="form">
4
+ <form role="form" class="ezoic-infinite-settings">
9
5
  <div class="mb-3">
10
6
  <label class="form-label">Groupes exclus (pas de pubs pour ces groupes)</label>
11
7
  <select multiple class="form-select" name="excludedGroups">
@@ -13,53 +9,41 @@
13
9
  <option value="{groups.name}">{groups.name}</option>
14
10
  <!-- END groups -->
15
11
  </select>
16
- <div class="form-text">Maintenez Ctrl/Cmd pour sélectionner plusieurs groupes. Liste triée par ordre alphabétique.</div>
12
+ <div class="form-text">Liste triée par ordre alphabétique.</div>
17
13
  </div>
18
14
 
19
15
  <hr/>
20
16
 
21
17
  <h3>Entre les topics dans une catégorie (liste des sujets)</h3>
22
-
23
18
  <div class="form-check form-switch mb-2">
24
19
  <input class="form-check-input" type="checkbox" name="enableBetweenAds">
25
- <label class="form-check-label">Activer les pubs entre les topics</label>
20
+ <label class="form-check-label">Activer</label>
26
21
  </div>
27
-
28
22
  <div class="mb-3">
29
- <label class="form-label">Intervalle (insérer après chaque N topics)</label>
23
+ <label class="form-label">Intervalle (après chaque N topics)</label>
30
24
  <input type="number" class="form-control" name="intervalTopics" min="1" step="1">
31
25
  </div>
32
-
33
26
  <div class="mb-3">
34
- <label class="form-label">Pool d'IDs de placeholder (un par ligne)</label>
35
- <textarea class="form-control" name="placeholderIds" rows="6"></textarea>
27
+ <label class="form-label">Pool d'IDs placeholder (un par ligne)</label>
28
+ <textarea class="form-control" name="placeholderIds" rows="5"></textarea>
36
29
  </div>
37
30
 
38
31
  <hr/>
39
32
 
40
33
  <h3>Dans les topics (entre les messages)</h3>
41
-
42
34
  <div class="form-check form-switch mb-2">
43
35
  <input class="form-check-input" type="checkbox" name="enableMessageAds">
44
- <label class="form-check-label">Activer les pubs entre les messages</label>
36
+ <label class="form-check-label">Activer</label>
45
37
  </div>
46
-
47
38
  <div class="mb-3">
48
- <label class="form-label">Intervalle (insérer après chaque N messages)</label>
39
+ <label class="form-label">Intervalle (après chaque N messages)</label>
49
40
  <input type="number" class="form-control" name="messageIntervalPosts" min="1" step="1">
50
41
  </div>
51
-
52
42
  <div class="mb-3">
53
- <label class="form-label">Pool d'IDs de placeholder pour messages (un par ligne)</label>
54
- <textarea class="form-control" name="messagePlaceholderIds" rows="6"></textarea>
43
+ <label class="form-label">Pool d'IDs placeholder messages (un par ligne)</label>
44
+ <textarea class="form-control" name="messagePlaceholderIds" rows="5"></textarea>
55
45
  </div>
56
46
 
57
- <!-- IMPORT admin/partials/save_button.tpl -->
47
+ <button type="button" class="btn btn-primary ezoic-infinite-save">Enregistrer</button>
58
48
  </form>
59
49
  </div>
60
-
61
- <script>
62
- require(['admin/plugins/ezoic-infinite'], function (acp) {
63
- acp.init();
64
- });
65
- </script>
package/README.md DELETED
@@ -1,12 +0,0 @@
1
- nodebb-plugin-ezoic-infinite v1.0.0
2
- ==================================
3
-
4
- ACP (NodeBB 4.x official pattern):
5
- - routeHelpers.setupAdminPageRoute
6
- - Settings framework (Settings.load/save)
7
- - server-rendered groups (no dynamic fetch)
8
-
9
- Frontend:
10
- - inject ads between topics list + between posts in topic
11
- - showAds called one placeholder id at a time (no batch)
12
- - FIFO recycling pool on same page