nodebb-plugin-ezoic-infinite 1.6.94 → 1.6.96

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
@@ -7,33 +7,24 @@ const db = require.main.require('./src/database');
7
7
  const SETTINGS_KEY = 'ezoic-infinite';
8
8
  const plugin = {};
9
9
 
10
- /**
11
- * Récupère les paramètres du plugin
12
- */
13
10
  async function getSettings() {
14
11
  return await meta.settings.get(SETTINGS_KEY);
15
12
  }
16
13
 
17
14
  /**
18
- * Vérifie si l'utilisateur actuel fait partie d'un groupe exclu
15
+ * Vérifie l'exclusion par groupe avec gestion du format NodeBB
19
16
  */
20
17
  async function isUserExcluded(uid, excludedGroups) {
21
18
  if (!uid || uid <= 0) return false;
22
- if (!excludedGroups || !excludedGroups.length) return false;
19
+ if (!excludedGroups || (Array.isArray(excludedGroups) && excludedGroups.length === 0)) return false;
23
20
 
24
- // S'assurer que excludedGroups est un tableau
25
21
  const excludedList = Array.isArray(excludedGroups) ? excludedGroups : [excludedGroups];
26
-
27
- // Récupérer les groupes de l'utilisateur
28
22
  const userGroups = await groups.getUserGroupsNames([uid]);
29
23
 
30
- // Vérifier s'il y a une intersection entre les groupes de l'utilisateur et les exclus
24
+ // userGroups[0] contient le tableau des noms de groupes de l'utilisateur
31
25
  return excludedList.some(g => userGroups[0].includes(g));
32
26
  }
33
27
 
34
- /**
35
- * Récupère la liste de tous les groupes pour l'affichage dans l'admin
36
- */
37
28
  async function getAllGroups() {
38
29
  let names = await db.getSortedSetRange('groups:createtime', 0, -1);
39
30
  if (!names || !names.length) {
@@ -46,9 +37,6 @@ async function getAllGroups() {
46
37
  return valid;
47
38
  }
48
39
 
49
- /**
50
- * Initialisation du plugin (Routes et API)
51
- */
52
40
  plugin.init = async ({ router, middleware }) => {
53
41
  async function render(req, res) {
54
42
  const settings = await getSettings();
@@ -57,10 +45,10 @@ plugin.init = async ({ router, middleware }) => {
57
45
  res.render('admin/plugins/ezoic-infinite', {
58
46
  title: 'Ezoic Infinite Ads',
59
47
  ...settings,
60
- // On force les booléens pour les cases à cocher dans le template
61
- enableCategoryAds_checked: settings.enableCategoryAds === 'on' || settings.enableCategoryAds === true ? 'checked' : '',
62
- enableBetweenAds_checked: settings.enableBetweenAds === 'on' || settings.enableBetweenAds === true ? 'checked' : '',
63
- enableMessageAds_checked: settings.enableMessageAds === 'on' || settings.enableMessageAds === true ? 'checked' : '',
48
+ // On s'assure que les checkbox sont bien cochées dans l'admin
49
+ enableCategoryAds_checked: settings.enableCategoryAds === 'on' ? 'checked' : '',
50
+ enableBetweenAds_checked: settings.enableBetweenAds === 'on' ? 'checked' : '',
51
+ enableMessageAds_checked: settings.enableMessageAds === 'on' ? 'checked' : '',
64
52
  allGroups,
65
53
  });
66
54
  }
@@ -68,40 +56,34 @@ plugin.init = async ({ router, middleware }) => {
68
56
  router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
69
57
  router.get('/api/admin/plugins/ezoic-infinite', render);
70
58
 
71
- // API de configuration appelée par client.js
59
+ // API consommée par le client.js
72
60
  router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
73
61
  const settings = await getSettings();
74
-
75
- // Vérification de l'exclusion
76
- const excludedGroups = settings.excludedGroups ? (Array.isArray(settings.excludedGroups) ? settings.excludedGroups : [settings.excludedGroups]) : [];
77
- const excluded = await isUserExcluded(req.uid, excludedGroups);
62
+ const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
78
63
 
79
64
  res.json({
80
65
  excluded,
81
- // 1. Accueil (Catégories)
82
- enableCategoryAds: settings.enableCategoryAds === 'on' || settings.enableCategoryAds === true,
83
- showFirstCategoryAd: settings.showFirstCategoryAd === 'on' || settings.showFirstCategoryAd === true,
66
+ // Pool Accueil
67
+ enableCategoryAds: settings.enableCategoryAds === 'on',
68
+ showFirstCategoryAd: settings.showFirstCategoryAd === 'on',
84
69
  categoryPlaceholderIds: settings.categoryPlaceholderIds || "",
85
70
  intervalCategories: parseInt(settings.intervalCategories, 10) || 5,
86
71
 
87
- // 2. Liste des Topics (Page catégorie)
88
- enableBetweenAds: settings.enableBetweenAds === 'on' || settings.enableBetweenAds === true,
89
- showFirstTopicAd: settings.showFirstTopicAd === 'on' || settings.showFirstTopicAd === true,
72
+ // Pool Topics (Catégories)
73
+ enableBetweenAds: settings.enableBetweenAds === 'on',
74
+ showFirstTopicAd: settings.showFirstTopicAd === 'on',
90
75
  placeholderIds: settings.placeholderIds || "",
91
76
  intervalPosts: parseInt(settings.intervalPosts, 10) || 10,
92
77
 
93
- // 3. Messages (Dans un post)
94
- enableMessageAds: settings.enableMessageAds === 'on' || settings.enableMessageAds === true,
95
- showFirstMessageAd: settings.showFirstMessageAd === 'on' || settings.showFirstMessageAd === true,
78
+ // Pool Messages (Topics)
79
+ enableMessageAds: settings.enableMessageAds === 'on',
80
+ showFirstMessageAd: settings.showFirstMessageAd === 'on',
96
81
  messagePlaceholderIds: settings.messagePlaceholderIds || "",
97
82
  messageIntervalPosts: parseInt(settings.messageIntervalPosts, 10) || 10,
98
83
  });
99
84
  });
100
85
  };
101
86
 
102
- /**
103
- * Ajoute le lien dans le menu de navigation de l'administration
104
- */
105
87
  plugin.addAdminNavigation = async (header) => {
106
88
  header.plugins.push({
107
89
  route: '/plugins/ezoic-infinite',
@@ -111,12 +93,9 @@ plugin.addAdminNavigation = async (header) => {
111
93
  return header;
112
94
  };
113
95
 
114
- /**
115
- * Gère la sauvegarde des paramètres
116
- */
117
96
  plugin.onSettingsSet = async (data) => {
118
97
  if (data.plugin === 'ezoic-infinite') {
119
- // Logique optionnelle à l'enregistrement
98
+ // Optionnel : purger un cache si nécessaire
120
99
  }
121
100
  };
122
101
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.94",
3
+ "version": "1.6.96",
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
@@ -5,76 +5,83 @@
5
5
  window.ezInfiniteInjected = true;
6
6
 
7
7
  const WRAP_CLASS = 'nodebb-ezoic-wrap';
8
+ const POOL_ID = 'nodebb-ezoic-placeholder-pool';
8
9
  let config = null;
9
10
  let isInternalChange = false;
10
- let triggerTimer = null;
11
11
 
12
- // On récupère les IDs déjà présents dans la page (générés par ton intégration de base)
13
- // pour éviter de tenter de les redéfinir.
14
- function redistribute() {
15
- if (!config || config.excluded) return;
16
-
17
- const selectors = {
18
- 'home': '.category-item, [component="categories/category"]',
19
- 'topic-list': 'li[component="category/topic"]',
20
- 'message': '[component="post"]'
21
- };
22
-
23
- if (config.enableCategoryAds) {
24
- process(Array.from(document.querySelectorAll(selectors.home)), 'home', config.intervalCategories, config.showFirstCategoryAd);
25
- }
26
- if (config.enableBetweenAds) {
27
- process(Array.from(document.querySelectorAll(selectors['topic-list'])), 'topic-list', config.intervalPosts, config.showFirstTopicAd);
28
- }
29
- if (config.enableMessageAds) {
30
- process(Array.from(document.querySelectorAll(selectors.message)), 'message', config.messageIntervalPosts, config.showFirstMessageAd);
12
+ function getPool() {
13
+ let p = document.getElementById(POOL_ID);
14
+ if (!p) {
15
+ p = document.createElement('div');
16
+ p.id = POOL_ID;
17
+ p.style.display = 'none';
18
+ document.body.appendChild(p);
31
19
  }
20
+ return p;
32
21
  }
33
22
 
34
- function process(items, kind, interval, showFirst) {
35
- const int = parseInt(interval, 10) || 10;
36
- const pool = document.getElementById('nodebb-ezoic-placeholder-pool');
37
- if (!pool) return;
38
-
39
- items.forEach((item, index) => {
40
- const pos = index + 1;
41
- const shouldHaveAd = (pos === 1 && showFirst) || (pos % int === 0);
42
- const next = item.nextElementSibling;
23
+ // Cette fonction force Ezoic à remplir le placeholder
24
+ function forceFillAd(id) {
25
+ if (typeof window.ezstandalone === 'undefined') return;
43
26
 
44
- // Si on doit mettre une pub et qu'il n'y en a pas déjà une après cet élément
45
- if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
46
- const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
47
-
48
- if (available) {
49
- isInternalChange = true;
50
- item.parentNode.insertBefore(available, item.nextSibling);
51
-
52
- const placeholderId = available.getAttribute('data-placeholder-id');
53
- refreshAd(placeholderId);
54
-
55
- setTimeout(() => { isInternalChange = false; }, 50);
27
+ window.ezstandalone.cmd.push(function() {
28
+ // 1. On s'assure que l'ID est bien dans la liste d'Ezoic
29
+ window.ezstandalone.define(parseInt(id, 10));
30
+
31
+ // 2. On attend un micro-délai pour que le DOM soit stable
32
+ setTimeout(() => {
33
+ try {
34
+ // La doc précise que showAds avec IDs est le mode "Dynamic"
35
+ window.ezstandalone.showAds(parseInt(id, 10));
36
+ // On force un rafraîchissement spécifique si showAds ne suffit pas
37
+ if (window.ezstandalone.refresh) {
38
+ window.ezstandalone.refresh(parseInt(id, 10));
39
+ }
40
+ } catch (e) {
41
+ console.warn('[Ezoic] Fill failed for', id, e);
56
42
  }
57
- }
43
+ }, 200);
58
44
  });
59
45
  }
60
46
 
61
- function refreshAd(id) {
62
- if (typeof window.ezstandalone === 'undefined') return;
47
+ function redistribute() {
48
+ if (!config || config.excluded) return;
63
49
 
64
- clearTimeout(triggerTimer);
65
- triggerTimer = setTimeout(() => {
66
- window.ezstandalone.cmd.push(function() {
67
- // Au lieu de define(), on utilise simplement showAds()
68
- // Ezoic va détecter que le div avec cet ID a bougé et va le remplir.
69
- try {
70
- // On force l'affichage de cet ID spécifique
71
- window.ezstandalone.showAds(parseInt(id, 10));
72
- console.debug('[Ezoic] Displaying ID:', id);
73
- } catch (e) {
74
- console.error('[Ezoic] Error showing ID:', id, e);
50
+ const selectors = {
51
+ 'home': '.category-item, [component="categories/category"]',
52
+ 'topic-list': 'li[component="category/topic"]',
53
+ 'message': '[component="post"]'
54
+ };
55
+
56
+ const process = (items, kind, interval, showFirst) => {
57
+ const int = parseInt(interval, 10) || 10;
58
+ const pool = getPool();
59
+
60
+ items.forEach((item, index) => {
61
+ const pos = index + 1;
62
+ const shouldHaveAd = (pos === 1 && showFirst) || (pos % int === 0);
63
+ const next = item.nextElementSibling;
64
+
65
+ if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
66
+ const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
67
+ if (available) {
68
+ isInternalChange = true;
69
+ // On insère l'élément
70
+ item.parentNode.insertBefore(available, item.nextSibling);
71
+
72
+ // On déclenche le remplissage publicitaire
73
+ const pid = available.getAttribute('data-placeholder-id');
74
+ forceFillAd(pid);
75
+
76
+ setTimeout(() => { isInternalChange = false; }, 50);
77
+ }
75
78
  }
76
79
  });
77
- }, 100);
80
+ };
81
+
82
+ if (config.enableCategoryAds) process(Array.from(document.querySelectorAll(selectors.home)), 'home', config.intervalCategories, config.showFirstCategoryAd);
83
+ if (config.enableBetweenAds) process(Array.from(document.querySelectorAll(selectors['topic-list'])), 'topic-list', config.intervalPosts, config.showFirstTopicAd);
84
+ if (config.enableMessageAds) process(Array.from(document.querySelectorAll(selectors.message)), 'message', config.messageIntervalPosts, config.showFirstMessageAd);
78
85
  }
79
86
 
80
87
  function init() {
@@ -82,27 +89,18 @@
82
89
  .then(r => r.json())
83
90
  .then(data => {
84
91
  config = data;
92
+ const pool = getPool();
85
93
 
86
- // On crée un pool CACHÉ qui contient tous les placeholders déclarés dans l'admin
87
- let pool = document.getElementById('nodebb-ezoic-placeholder-pool');
88
- if (!pool) {
89
- pool = document.createElement('div');
90
- pool.id = 'nodebb-ezoic-placeholder-pool';
91
- pool.style.display = 'none';
92
- document.body.appendChild(pool);
93
- }
94
-
95
94
  const setup = (raw, kind) => {
96
95
  if (!raw) return;
97
96
  raw.split(/[\s,]+/).filter(Boolean).forEach(id => {
98
97
  if (!document.getElementById(`ezoic-pub-ad-placeholder-${id}`)) {
99
- const wrap = document.createElement('div');
100
- wrap.className = WRAP_CLASS;
101
- wrap.setAttribute('data-kind', kind);
102
- wrap.setAttribute('data-placeholder-id', id);
103
- // Structure exacte attendue par Ezoic
104
- wrap.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}"></div>`;
105
- pool.appendChild(wrap);
98
+ const d = document.createElement('div');
99
+ d.className = WRAP_CLASS;
100
+ d.setAttribute('data-kind', kind);
101
+ d.setAttribute('data-placeholder-id', id);
102
+ d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}"></div>`;
103
+ pool.appendChild(d);
106
104
  }
107
105
  });
108
106
  };
@@ -120,7 +118,9 @@
120
118
  });
121
119
  }
122
120
 
123
- window.addEventListener('action:ajaxify.end', () => setTimeout(redistribute, 500));
121
+ window.addEventListener('action:ajaxify.end', () => {
122
+ setTimeout(redistribute, 500);
123
+ });
124
124
 
125
125
  if (document.readyState === 'loading') {
126
126
  document.addEventListener('DOMContentLoaded', init);