nodebb-plugin-ezoic-infinite 1.6.87 → 1.6.88

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,58 +2,64 @@
2
2
 
3
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
6
 
6
7
  const SETTINGS_KEY = 'ezoic-infinite';
7
8
  const plugin = {};
8
9
 
9
10
  async function getSettings() {
10
- return await meta.settings.get(SETTINGS_KEY) || {};
11
+ return await meta.settings.get(SETTINGS_KEY);
12
+ }
13
+
14
+ async function isUserExcluded(uid, excludedGroups) {
15
+ if (!uid || !excludedGroups || !excludedGroups.length) return false;
16
+ const userGroups = await groups.getUserGroupsNames([uid]);
17
+ return excludedGroups.some(g => userGroups[0].includes(g));
18
+ }
19
+
20
+ async function getAllGroups() {
21
+ let names = await db.getSortedSetRange('groups:createtime', 0, -1);
22
+ const data = await groups.getGroupsData(names);
23
+ return data.filter(g => g && g.name).map(g => ({ name: g.name }));
11
24
  }
12
25
 
13
26
  plugin.init = async ({ router, middleware }) => {
14
- const renderAdmin = async (req, res) => {
15
- const settings = await getSettings();
16
- const db = require.main.require('./src/database');
17
- const names = await db.getSortedSetRange('groups:createtime', 0, -1);
18
- const groupsData = await groups.getGroupsData(names);
19
- const allGroups = groupsData.filter(g => g && g.name).map(g => ({ name: g.name }));
20
-
21
- res.render('admin/plugins/ezoic-infinite', {
22
- ...settings,
23
- allGroups,
24
- enableBetweenAds_checked: settings.enableBetweenAds === 'on' ? 'checked' : '',
25
- enableMessageAds_checked: settings.enableMessageAds === 'on' ? 'checked' : ''
26
- });
27
- };
28
-
29
- router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, renderAdmin);
30
- router.get('/api/admin/plugins/ezoic-infinite', renderAdmin);
31
-
32
- router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
33
- const settings = await getSettings();
34
- let excluded = false;
35
- if (req.uid && settings.excludedGroups) {
36
- const groupsList = Array.isArray(settings.excludedGroups) ? settings.excludedGroups : [settings.excludedGroups];
37
- excluded = await groups.isMemberOfGroups(req.uid, groupsList);
38
- }
39
-
40
- res.json({
41
- excluded,
42
- enableBetweenAds: settings.enableBetweenAds === 'on',
43
- showFirstTopicAd: settings.showFirstTopicAd === 'on',
44
- placeholderIds: settings.placeholderIds || '',
45
- intervalPosts: settings.intervalPosts || 10,
46
- enableMessageAds: settings.enableMessageAds === 'on',
47
- showFirstMessageAd: settings.showFirstMessageAd === 'on',
48
- messagePlaceholderIds: settings.messagePlaceholderIds || '',
49
- messageIntervalPosts: settings.messageIntervalPosts || 10,
50
- });
27
+ async function render(req, res) {
28
+ const settings = await getSettings();
29
+ const allGroups = await getAllGroups();
30
+ res.render('admin/plugins/ezoic-infinite', {
31
+ ...settings,
32
+ allGroups,
33
+ });
34
+ }
35
+
36
+ router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
37
+ router.get('/api/admin/plugins/ezoic-infinite', render);
38
+
39
+ router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
40
+ const settings = await getSettings();
41
+ const excluded = await isUserExcluded(req.uid, settings.excludedGroups || []);
42
+ res.json({
43
+ excluded,
44
+ enableBetweenAds: settings.enableBetweenAds === 'on',
45
+ showFirstTopicAd: settings.showFirstTopicAd === 'on',
46
+ placeholderIds: settings.placeholderIds,
47
+ intervalPosts: settings.intervalPosts || 10,
48
+ enableMessageAds: settings.enableMessageAds === 'on',
49
+ showFirstMessageAd: settings.showFirstMessageAd === 'on',
50
+ messagePlaceholderIds: settings.messagePlaceholderIds,
51
+ messageIntervalPosts: settings.messageIntervalPosts || 10,
51
52
  });
53
+ });
52
54
  };
53
55
 
54
56
  plugin.addAdminNavigation = async (header) => {
55
- header.plugins.push({ route: '/plugins/ezoic-infinite', icon: 'fa-ad', name: 'Ezoic Infinite' });
56
- return header;
57
+ header.plugins.push({
58
+ route: '/plugins/ezoic-infinite',
59
+ icon: 'fa-ad',
60
+ name: 'Ezoic Infinite'
61
+ });
62
+ return header;
57
63
  };
58
64
 
59
65
  module.exports = plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.87",
3
+ "version": "1.6.88",
4
4
  "description": "Production-ready Ezoic infinite ads integration for NodeBB 4.x",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -9,7 +9,8 @@
9
9
 
10
10
  let config = null;
11
11
  let isInternalChange = false;
12
- let activePlaceholders = new Set();
12
+ let definedIds = new Set();
13
+ let ezoicEnabledOnThisPage = false;
13
14
 
14
15
  function getPool() {
15
16
  let p = document.getElementById(POOL_ID);
@@ -22,25 +23,32 @@
22
23
  return p;
23
24
  }
24
25
 
25
- // Utilisation stricte de la file d'attente CMD du zip
26
26
  function callEzoic(id) {
27
27
  if (typeof window.ezstandalone === 'undefined') return;
28
-
28
+ const pid = parseInt(id, 10);
29
+ if (isNaN(pid)) return;
30
+
29
31
  window.ezstandalone.cmd.push(function() {
30
- // Si l'ID est déjà affiché, on ne fait rien pour éviter le spam de refresh
31
- if (activePlaceholders.has(id)) {
32
- window.ezstandalone.refresh();
33
- return;
32
+ // On définit l'ID s'il est nouveau
33
+ if (!definedIds.has(pid)) {
34
+ window.ezstandalone.define(pid);
35
+ definedIds.add(pid);
34
36
  }
35
37
 
36
- window.ezstandalone.define(id);
37
- activePlaceholders.add(id);
38
-
39
- if (!window.ezstandalone.enabled) {
38
+ // Protection contre l'erreur "cannot call refresh on same page enable"
39
+ if (!window.ezstandalone.enabled && !ezoicEnabledOnThisPage) {
40
40
  window.ezstandalone.enable();
41
41
  window.ezstandalone.display();
42
+ ezoicEnabledOnThisPage = true;
42
43
  } else {
43
- window.ezstandalone.refresh();
44
+ // Délai de sécurité de 500ms pour laisser Ezoic finir son cycle initial
45
+ setTimeout(function() {
46
+ try {
47
+ window.ezstandalone.refresh();
48
+ } catch (e) {
49
+ console.debug('[Ezoic] Refresh deferred');
50
+ }
51
+ }, 500);
44
52
  }
45
53
  });
46
54
  }
@@ -48,22 +56,20 @@
48
56
  function redistribute() {
49
57
  if (!config || config.excluded) return;
50
58
 
51
- // Utilisation des sélecteurs exacts du zip V17 (Harmony/NodeBB 4)
59
+ // Sélecteurs V17 (Harmony)
52
60
  const topicItems = document.querySelectorAll('li[component="category/topic"]');
53
- const postItems = document.querySelectorAll('[component="post"][data-pid]');
61
+ const postItems = document.querySelectorAll('[component="post"]');
54
62
 
55
- // Injection pour la page d'accueil / catégories
56
63
  if (topicItems.length > 0 && config.enableBetweenAds) {
57
- inject(Array.from(topicItems), 'between', config.intervalPosts, config.showFirstTopicAd);
64
+ processItems(Array.from(topicItems), 'between', config.intervalPosts, config.showFirstTopicAd);
58
65
  }
59
-
60
- // Injection pour les messages (topics)
66
+
61
67
  if (postItems.length > 0 && config.enableMessageAds) {
62
- inject(Array.from(postItems), 'message', config.messageIntervalPosts, config.showFirstMessageAd);
68
+ processItems(Array.from(postItems), 'message', config.messageIntervalPosts, config.showFirstMessageAd);
63
69
  }
64
70
  }
65
71
 
66
- function inject(items, kind, interval, showFirst) {
72
+ function processItems(items, kind, interval, showFirst) {
67
73
  const int = parseInt(interval, 10) || 10;
68
74
  items.forEach((item, index) => {
69
75
  const pos = index + 1;
@@ -73,15 +79,10 @@
73
79
  if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
74
80
  const pool = getPool();
75
81
  const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
76
-
77
82
  if (available) {
78
83
  isInternalChange = true;
79
- // On insère l'élément comme dans le zip original
80
84
  item.parentNode.insertBefore(available, item.nextSibling);
81
-
82
- const pid = parseInt(available.getAttribute('data-placeholder-id'), 10);
83
- callEzoic(pid);
84
-
85
+ callEzoic(available.getAttribute('data-placeholder-id'));
85
86
  setTimeout(() => { isInternalChange = false; }, 100);
86
87
  }
87
88
  }
@@ -94,8 +95,7 @@
94
95
  .then(data => {
95
96
  config = data;
96
97
  const pool = getPool();
97
-
98
- const setupPool = (raw, kind) => {
98
+ const setup = (raw, kind) => {
99
99
  if (!raw) return;
100
100
  raw.split(/[\s,]+/).filter(Boolean).forEach(id => {
101
101
  if (!document.querySelector(`[data-placeholder-id="${id}"]`)) {
@@ -103,19 +103,16 @@
103
103
  d.className = WRAP_CLASS;
104
104
  d.setAttribute('data-kind', kind);
105
105
  d.setAttribute('data-placeholder-id', id);
106
- // Structure HTML propre
107
- d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}" class="ezoic-ad"></div>`;
106
+ d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}"></div>`;
108
107
  pool.appendChild(d);
109
108
  }
110
109
  });
111
110
  };
112
-
113
- setupPool(config.placeholderIds, 'between');
114
- setupPool(config.messagePlaceholderIds, 'message');
111
+ setup(config.placeholderIds, 'between');
112
+ setup(config.messagePlaceholderIds, 'message');
115
113
 
116
114
  redistribute();
117
115
 
118
- // Observer pour l'infinite scroll (reprise du zip)
119
116
  const observer = new MutationObserver(() => {
120
117
  if (!isInternalChange) redistribute();
121
118
  });
@@ -123,12 +120,15 @@
123
120
  });
124
121
  }
125
122
 
126
- // Événements NodeBB pour gérer la navigation sans rechargement
127
- if (window.jQuery) {
128
- window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function() {
129
- setTimeout(redistribute, 500);
130
- });
131
- }
123
+ // Reset lors de la navigation NodeBB (SPA)
124
+ window.addEventListener('action:ajaxify.start', function() {
125
+ ezoicEnabledOnThisPage = false;
126
+ definedIds.clear();
127
+ });
128
+
129
+ window.addEventListener('action:ajaxify.end', function() {
130
+ setTimeout(redistribute, 600);
131
+ });
132
132
 
133
133
  if (document.readyState === 'loading') {
134
134
  document.addEventListener('DOMContentLoaded', init);
package/public/style.css CHANGED
@@ -1,20 +1,29 @@
1
+ /* Container global */
1
2
  .nodebb-ezoic-wrap {
2
- display: block !important;
3
- width: 100% !important;
4
- margin: 30px 0 !important;
5
- min-height: 250px;
6
- clear: both;
3
+ display: block !important;
4
+ width: 100% !important;
5
+ min-width: 100% !important;
6
+ margin: 20px 0 !important;
7
+ min-height: 250px;
8
+ clear: both;
9
+ text-align: center;
7
10
  }
8
11
 
9
- /* Fix pour les listes de l'accueil Harmony */
12
+ /* Fix spécifique pour la liste des topics (Accueil Harmony) */
10
13
  li[component="category/topic"] + .nodebb-ezoic-wrap {
11
- list-style: none;
12
- padding: 15px;
13
- background: rgba(0,0,0,0.02);
14
- border-radius: 8px;
14
+ list-style: none !important;
15
+ margin-left: 0 !important;
16
+ padding: 15px 0;
17
+ border-bottom: 1px solid rgba(0,0,0,0.05);
15
18
  }
16
19
 
17
- /* Éviter les trous si la pub ne charge pas */
20
+ /* Si le bloc est vide ou n'a pas encore chargé */
18
21
  .nodebb-ezoic-wrap:empty {
19
- min-height: 1px;
22
+ min-height: 1px;
23
+ }
24
+
25
+ /* Neutralisation des marges internes d'Ezoic */
26
+ .ezoic-ad {
27
+ margin: 0 auto !important;
28
+ display: inline-block !important;
20
29
  }