nodebb-plugin-ezoic-infinite 1.6.89 → 1.6.91
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 +97 -39
- package/package.json +1 -1
- package/public/client.js +27 -50
- package/public/style.css +17 -12
package/library.js
CHANGED
|
@@ -7,59 +7,117 @@ 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
|
+
*/
|
|
10
13
|
async function getSettings() {
|
|
11
|
-
|
|
14
|
+
return await meta.settings.get(SETTINGS_KEY);
|
|
12
15
|
}
|
|
13
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Vérifie si l'utilisateur actuel fait partie d'un groupe exclu
|
|
19
|
+
*/
|
|
14
20
|
async function isUserExcluded(uid, excludedGroups) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
if (!uid || uid <= 0) return false;
|
|
22
|
+
if (!excludedGroups || !excludedGroups.length) return false;
|
|
23
|
+
|
|
24
|
+
// S'assurer que excludedGroups est un tableau
|
|
25
|
+
const excludedList = Array.isArray(excludedGroups) ? excludedGroups : [excludedGroups];
|
|
26
|
+
|
|
27
|
+
// Récupérer les groupes de l'utilisateur
|
|
28
|
+
const userGroups = await groups.getUserGroupsNames([uid]);
|
|
29
|
+
|
|
30
|
+
// Vérifier s'il y a une intersection entre les groupes de l'utilisateur et les exclus
|
|
31
|
+
return excludedList.some(g => userGroups[0].includes(g));
|
|
18
32
|
}
|
|
19
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Récupère la liste de tous les groupes pour l'affichage dans l'admin
|
|
36
|
+
*/
|
|
20
37
|
async function getAllGroups() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
38
|
+
let names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
39
|
+
if (!names || !names.length) {
|
|
40
|
+
names = await db.getSortedSetRange('groups:visible:createtime', 0, -1);
|
|
41
|
+
}
|
|
42
|
+
const filtered = names.filter(name => !groups.isPrivilegeGroup(name));
|
|
43
|
+
const data = await groups.getGroupsData(filtered);
|
|
44
|
+
const valid = data.filter(g => g && g.name);
|
|
45
|
+
valid.sort((a, b) => String(a.name).localeCompare(String(b.name), undefined, { sensitivity: 'base' }));
|
|
46
|
+
return valid;
|
|
24
47
|
}
|
|
25
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Initialisation du plugin (Routes et API)
|
|
51
|
+
*/
|
|
26
52
|
plugin.init = async ({ router, middleware }) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
async function render(req, res) {
|
|
54
|
+
const settings = await getSettings();
|
|
55
|
+
const allGroups = await getAllGroups();
|
|
56
|
+
|
|
57
|
+
res.render('admin/plugins/ezoic-infinite', {
|
|
58
|
+
title: 'Ezoic Infinite Ads',
|
|
59
|
+
...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' : '',
|
|
64
|
+
allGroups,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
|
|
69
|
+
router.get('/api/admin/plugins/ezoic-infinite', render);
|
|
70
|
+
|
|
71
|
+
// API de configuration appelée par client.js
|
|
72
|
+
router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
|
|
73
|
+
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);
|
|
78
|
+
|
|
79
|
+
res.json({
|
|
80
|
+
excluded,
|
|
81
|
+
// 1. Accueil (Catégories)
|
|
82
|
+
enableCategoryAds: settings.enableCategoryAds === 'on' || settings.enableCategoryAds === true,
|
|
83
|
+
showFirstCategoryAd: settings.showFirstCategoryAd === 'on' || settings.showFirstCategoryAd === true,
|
|
84
|
+
categoryPlaceholderIds: settings.categoryPlaceholderIds || "",
|
|
85
|
+
intervalCategories: parseInt(settings.intervalCategories, 10) || 5,
|
|
86
|
+
|
|
87
|
+
// 2. Liste des Topics (Page catégorie)
|
|
88
|
+
enableBetweenAds: settings.enableBetweenAds === 'on' || settings.enableBetweenAds === true,
|
|
89
|
+
showFirstTopicAd: settings.showFirstTopicAd === 'on' || settings.showFirstTopicAd === true,
|
|
90
|
+
placeholderIds: settings.placeholderIds || "",
|
|
91
|
+
intervalPosts: parseInt(settings.intervalPosts, 10) || 10,
|
|
92
|
+
|
|
93
|
+
// 3. Messages (Dans un post)
|
|
94
|
+
enableMessageAds: settings.enableMessageAds === 'on' || settings.enableMessageAds === true,
|
|
95
|
+
showFirstMessageAd: settings.showFirstMessageAd === 'on' || settings.showFirstMessageAd === true,
|
|
96
|
+
messagePlaceholderIds: settings.messagePlaceholderIds || "",
|
|
97
|
+
messageIntervalPosts: parseInt(settings.messageIntervalPosts, 10) || 10,
|
|
98
|
+
});
|
|
52
99
|
});
|
|
53
|
-
});
|
|
54
100
|
};
|
|
55
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Ajoute le lien dans le menu de navigation de l'administration
|
|
104
|
+
*/
|
|
56
105
|
plugin.addAdminNavigation = async (header) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
106
|
+
header.plugins.push({
|
|
107
|
+
route: '/plugins/ezoic-infinite',
|
|
108
|
+
icon: 'fa-ad',
|
|
109
|
+
name: 'Ezoic Infinite'
|
|
110
|
+
});
|
|
111
|
+
return header;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Gère la sauvegarde des paramètres
|
|
116
|
+
*/
|
|
117
|
+
plugin.onSettingsSet = async (data) => {
|
|
118
|
+
if (data.plugin === 'ezoic-infinite') {
|
|
119
|
+
// Logique optionnelle à l'enregistrement
|
|
120
|
+
}
|
|
63
121
|
};
|
|
64
122
|
|
|
65
123
|
module.exports = plugin;
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
let config = null;
|
|
11
11
|
let isInternalChange = false;
|
|
12
|
-
let definedIds = new Set();
|
|
13
12
|
let pendingIds = new Set();
|
|
14
13
|
let refreshTimer = null;
|
|
15
14
|
|
|
@@ -24,7 +23,6 @@
|
|
|
24
23
|
return p;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
// Cette fonction centralise les appels pour éviter de spammer Refresh()
|
|
28
26
|
function triggerEzoic() {
|
|
29
27
|
if (typeof window.ezstandalone === 'undefined' || pendingIds.size === 0) return;
|
|
30
28
|
|
|
@@ -32,21 +30,15 @@
|
|
|
32
30
|
const idsToProcess = Array.from(pendingIds);
|
|
33
31
|
pendingIds.clear();
|
|
34
32
|
|
|
35
|
-
//
|
|
33
|
+
// On enregistre les IDs
|
|
36
34
|
window.ezstandalone.define(idsToProcess);
|
|
37
35
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// dans la même micro-seconde que l'initialisation
|
|
45
|
-
try {
|
|
46
|
-
window.ezstandalone.refresh();
|
|
47
|
-
} catch (e) {
|
|
48
|
-
console.warn('[Ezoic] Refresh skipped to prevent conflict');
|
|
49
|
-
}
|
|
36
|
+
// On appelle UNIQUEMENT refresh.
|
|
37
|
+
// Si Ezoic n'est pas "enabled", il ignorera l'appel sans erreur bloquante.
|
|
38
|
+
try {
|
|
39
|
+
window.ezstandalone.refresh();
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.debug('[Ezoic] Refresh deferred');
|
|
50
42
|
}
|
|
51
43
|
});
|
|
52
44
|
}
|
|
@@ -54,39 +46,38 @@
|
|
|
54
46
|
function callEzoic(id) {
|
|
55
47
|
const pid = parseInt(id, 10);
|
|
56
48
|
if (isNaN(pid)) return;
|
|
57
|
-
|
|
58
|
-
// On ajoute l'ID à la liste des IDs à traiter
|
|
59
49
|
pendingIds.add(pid);
|
|
60
|
-
definedIds.add(pid);
|
|
61
50
|
|
|
62
|
-
// On attend 200ms que les autres pubs du même scroll arrivent
|
|
63
|
-
// avant de lancer l'appel Ezoic unique. C'est le "Debounce".
|
|
64
51
|
clearTimeout(refreshTimer);
|
|
65
|
-
refreshTimer = setTimeout(triggerEzoic,
|
|
52
|
+
refreshTimer = setTimeout(triggerEzoic, 300);
|
|
66
53
|
}
|
|
67
54
|
|
|
68
55
|
function redistribute() {
|
|
69
56
|
if (!config || config.excluded) return;
|
|
70
57
|
|
|
71
|
-
//
|
|
58
|
+
// 1. Accueil (Catégories)
|
|
59
|
+
const categoryItems = document.querySelectorAll('.category-item, [component="categories/category"]');
|
|
60
|
+
// 2. Liste Topics
|
|
72
61
|
const topicItems = document.querySelectorAll('li[component="category/topic"]');
|
|
62
|
+
// 3. Messages
|
|
73
63
|
const postItems = document.querySelectorAll('[component="post"]');
|
|
74
64
|
|
|
65
|
+
if (categoryItems.length > 0 && config.enableCategoryAds) {
|
|
66
|
+
process(Array.from(categoryItems), 'home', config.intervalCategories, config.showFirstCategoryAd);
|
|
67
|
+
}
|
|
75
68
|
if (topicItems.length > 0 && config.enableBetweenAds) {
|
|
76
|
-
|
|
69
|
+
process(Array.from(topicItems), 'topic-list', config.intervalPosts, config.showFirstTopicAd);
|
|
77
70
|
}
|
|
78
|
-
|
|
79
71
|
if (postItems.length > 0 && config.enableMessageAds) {
|
|
80
|
-
|
|
72
|
+
process(Array.from(postItems), 'message', config.messageIntervalPosts, config.showFirstMessageAd);
|
|
81
73
|
}
|
|
82
74
|
}
|
|
83
75
|
|
|
84
|
-
function
|
|
76
|
+
function process(items, kind, interval, showFirst) {
|
|
85
77
|
const int = parseInt(interval, 10) || 10;
|
|
86
78
|
items.forEach((item, index) => {
|
|
87
79
|
const pos = index + 1;
|
|
88
80
|
const shouldHaveAd = (pos === 1 && showFirst) || (pos % int === 0);
|
|
89
|
-
|
|
90
81
|
const next = item.nextElementSibling;
|
|
91
82
|
if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
|
|
92
83
|
const pool = getPool();
|
|
@@ -110,19 +101,17 @@
|
|
|
110
101
|
const setup = (raw, kind) => {
|
|
111
102
|
if (!raw) return;
|
|
112
103
|
raw.split(/[\s,]+/).filter(Boolean).forEach(id => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
pool.appendChild(d);
|
|
120
|
-
}
|
|
104
|
+
const d = document.createElement('div');
|
|
105
|
+
d.className = WRAP_CLASS;
|
|
106
|
+
d.setAttribute('data-kind', kind);
|
|
107
|
+
d.setAttribute('data-placeholder-id', id);
|
|
108
|
+
d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}"></div>`;
|
|
109
|
+
pool.appendChild(d);
|
|
121
110
|
});
|
|
122
111
|
};
|
|
123
|
-
setup(config.
|
|
112
|
+
setup(config.categoryPlaceholderIds, 'home');
|
|
113
|
+
setup(config.placeholderIds, 'topic-list');
|
|
124
114
|
setup(config.messagePlaceholderIds, 'message');
|
|
125
|
-
|
|
126
115
|
redistribute();
|
|
127
116
|
|
|
128
117
|
const observer = new MutationObserver(() => {
|
|
@@ -132,19 +121,7 @@
|
|
|
132
121
|
});
|
|
133
122
|
}
|
|
134
123
|
|
|
135
|
-
|
|
136
|
-
window.addEventListener('action:ajaxify.start', function() {
|
|
137
|
-
if (typeof window.ezstandalone !== 'undefined' && window.ezstandalone.enabled) {
|
|
138
|
-
// Optionnel : window.ezstandalone.destroy();
|
|
139
|
-
// Mais souvent il vaut mieux juste laisser Ezoic gérer
|
|
140
|
-
}
|
|
141
|
-
definedIds.clear();
|
|
142
|
-
pendingIds.clear();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
window.addEventListener('action:ajaxify.end', function() {
|
|
146
|
-
setTimeout(redistribute, 600);
|
|
147
|
-
});
|
|
124
|
+
window.addEventListener('action:ajaxify.end', () => setTimeout(redistribute, 500));
|
|
148
125
|
|
|
149
126
|
if (document.readyState === 'loading') {
|
|
150
127
|
document.addEventListener('DOMContentLoaded', init);
|
package/public/style.css
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
|
-
/* Container global */
|
|
1
|
+
/* Container global de pub */
|
|
2
2
|
.nodebb-ezoic-wrap {
|
|
3
3
|
display: block !important;
|
|
4
4
|
width: 100% !important;
|
|
5
|
-
min-width: 100% !important;
|
|
6
5
|
margin: 20px 0 !important;
|
|
7
6
|
min-height: 250px;
|
|
8
7
|
clear: both;
|
|
9
8
|
text-align: center;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
/*
|
|
11
|
+
/* Accueil (Catégories) */
|
|
12
|
+
[component="categories/category"] + .nodebb-ezoic-wrap,
|
|
13
|
+
.category-item + .nodebb-ezoic-wrap {
|
|
14
|
+
margin: 40px 0 !important;
|
|
15
|
+
border-top: 1px solid rgba(0,0,0,0.05);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Liste des Topics (Page catégorie) */
|
|
13
19
|
li[component="category/topic"] + .nodebb-ezoic-wrap {
|
|
14
20
|
list-style: none !important;
|
|
15
|
-
margin-left: 0 !important;
|
|
16
21
|
padding: 15px 0;
|
|
17
22
|
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
/*
|
|
21
|
-
.nodebb-ezoic-wrap
|
|
22
|
-
|
|
25
|
+
/* Messages (Dans un post) */
|
|
26
|
+
[component="post"] + .nodebb-ezoic-wrap {
|
|
27
|
+
margin: 40px 0 !important;
|
|
28
|
+
padding: 20px;
|
|
29
|
+
background: rgba(0,0,0,0.01);
|
|
23
30
|
}
|
|
24
31
|
|
|
25
|
-
/*
|
|
26
|
-
.ezoic-
|
|
27
|
-
|
|
28
|
-
display: inline-block !important;
|
|
29
|
-
}
|
|
32
|
+
/* Masquage des blocs vides */
|
|
33
|
+
.nodebb-ezoic-wrap:empty { min-height: 1px; }
|
|
34
|
+
.ezoic-ad { margin: 0 auto !important; }
|