mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-02-25 16:11:19 +01:00
Merge branch 'master' into develop
This commit is contained in:
@@ -108,7 +108,7 @@
|
||||
"nodebb-plugin-spam-be-gone": "2.3.1",
|
||||
"nodebb-plugin-web-push": "0.7.3",
|
||||
"nodebb-rewards-essentials": "1.0.1",
|
||||
"nodebb-theme-harmony": "2.0.29",
|
||||
"nodebb-theme-harmony": "2.0.30",
|
||||
"nodebb-theme-lavender": "7.1.17",
|
||||
"nodebb-theme-peace": "2.2.39",
|
||||
"nodebb-theme-persona": "14.0.15",
|
||||
|
||||
@@ -268,6 +268,7 @@
|
||||
|
||||
"invalid-plugin-id": "Invalid plugin ID",
|
||||
"plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP",
|
||||
"plugin-installation-via-acp-disabled": "Plugin installation via ACP is disabled",
|
||||
"plugins-set-in-configuration": "You are not allowed to change plugin state as they are defined at runtime (config.json, environmental variables or terminal arguments), please modify the configuration instead.",
|
||||
"theme-not-set-in-configuration": "When defining active plugins in configuration, changing themes requires adding the new theme to the list of active plugins before updating it in the ACP",
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ get:
|
||||
type: boolean
|
||||
jobsDisabled:
|
||||
type: boolean
|
||||
acpPluginInstallDisabled:
|
||||
type: boolean
|
||||
git:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -85,12 +85,24 @@ define('forum/topic/threadTools', [
|
||||
|
||||
topicContainer.on('click', '[component="topic/event/delete"]', function () {
|
||||
const eventId = $(this).attr('data-topic-event-id');
|
||||
const eventEl = $(this).parents('[component="topic/event"]');
|
||||
const eventEl = $(this).parents('[data-topic-event-id]');
|
||||
bootbox.confirm('[[topic:delete-event-confirm]]', (ok) => {
|
||||
if (ok) {
|
||||
api.del(`/topics/${tid}/events/${eventId}`, {})
|
||||
.then(function () {
|
||||
const itemsParent = eventEl.parents('[component="topic/event/items"]');
|
||||
eventEl.remove();
|
||||
if (itemsParent.length) {
|
||||
const childrenCount = itemsParent.children().length;
|
||||
const eventParent = itemsParent.parents('[component="topic/event"]');
|
||||
if (!childrenCount) {
|
||||
eventParent.remove();
|
||||
} else {
|
||||
eventParent
|
||||
.find('[data-bs-toggle]')
|
||||
.translateText(`[[topic:announcers-x, ${childrenCount}]]`);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(alerts.error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
define('logout', ['hooks'], function (hooks) {
|
||||
define('logout', ['hooks', 'alerts'], function (hooks, alerts) {
|
||||
return function logout(redirect) {
|
||||
redirect = redirect === undefined ? true : redirect;
|
||||
hooks.fire('action:app.logout');
|
||||
@@ -23,6 +23,9 @@ define('logout', ['hooks'], function (hooks) {
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function (jqXHR) {
|
||||
alerts.error(String(jqXHR.responseText || '[[error:logout-error]]'));
|
||||
},
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
@@ -88,6 +88,7 @@ async function getNodeInfo() {
|
||||
isPrimary: nconf.get('isPrimary'),
|
||||
runJobs: nconf.get('runJobs'),
|
||||
jobsDisabled: nconf.get('jobsDisabled'),
|
||||
acpPluginInstallDisabled: nconf.get('acpPluginInstallDisabled'),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ async function loadUserInfo(callerUid, uids) {
|
||||
const confirmObj = confirmObjs[index];
|
||||
user['email:expired'] = !confirmObj.expires || Date.now() >= confirmObj.expires;
|
||||
user['email:pending'] = confirmObj.expires && Date.now() < confirmObj.expires;
|
||||
user.emailToConfirm = confirmObj.email;
|
||||
user.emailToConfirm = validator.escape(String(confirmObj.email));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -459,7 +459,7 @@ authenticationController.localLogin = async function (req, username, password, n
|
||||
}
|
||||
};
|
||||
|
||||
authenticationController.logout = async function (req, res, next) {
|
||||
authenticationController.logout = async function (req, res) {
|
||||
if (!req.loggedIn || !req.sessionID) {
|
||||
res.clearCookie(nconf.get('sessionKey'), meta.configs.cookie.get());
|
||||
return res.status(200).send('not-logged-in');
|
||||
@@ -475,21 +475,22 @@ authenticationController.logout = async function (req, res, next) {
|
||||
|
||||
await user.setUserField(uid, 'lastonline', Date.now() - (meta.config.onlineCutoff * 60000));
|
||||
await db.sortedSetAdd('users:online', Date.now() - (meta.config.onlineCutoff * 60000), uid);
|
||||
await plugins.hooks.fire('static:user.loggedOut', { req: req, res: res, uid: uid, sessionID: sessionID });
|
||||
await plugins.hooks.fire('static:user.loggedOut', { req, res, uid, sessionID });
|
||||
|
||||
// Force session check for all connected socket.io clients with the same session id
|
||||
sockets.in(`sess_${sessionID}`).emit('checkSession', 0);
|
||||
const payload = {
|
||||
next: `${nconf.get('relative_path')}/`,
|
||||
};
|
||||
plugins.hooks.fire('filter:user.logout', payload);
|
||||
await plugins.hooks.fire('filter:user.logout', payload);
|
||||
|
||||
if (req.body.noscript === 'true') {
|
||||
return res.redirect(payload.next);
|
||||
}
|
||||
res.status(200).send(payload);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
winston.error(`${req.method} ${req.originalUrl}\n${err.stack}`);
|
||||
res.status(500).send(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ function loadConfig(configFile) {
|
||||
isCluster: false,
|
||||
isPrimary: true,
|
||||
jobsDisabled: false,
|
||||
acpPluginInstallDisabled: false,
|
||||
fontawesome: {
|
||||
pro: false,
|
||||
styles: '*',
|
||||
@@ -65,7 +66,7 @@ function loadConfig(configFile) {
|
||||
});
|
||||
|
||||
// Explicitly cast as Bool, loader.js passes in isCluster as string 'true'/'false'
|
||||
const castAsBool = ['isCluster', 'isPrimary', 'jobsDisabled'];
|
||||
const castAsBool = ['isCluster', 'isPrimary', 'jobsDisabled', 'acpPluginInstallDisabled'];
|
||||
nconf.stores.env.readOnly = false;
|
||||
castAsBool.forEach((prop) => {
|
||||
const value = nconf.get(prop);
|
||||
|
||||
@@ -22,6 +22,10 @@ Plugins.toggleActive = async function (socket, plugin_id) {
|
||||
};
|
||||
|
||||
Plugins.toggleInstall = async function (socket, data) {
|
||||
const isInstalled = await plugins.isInstalled(data.id);
|
||||
if (nconf.get('acpPluginInstallDisabled') && !isInstalled) {
|
||||
throw new Error('[[error:plugin-installation-via-acp-disabled]]');
|
||||
}
|
||||
postsCache.reset();
|
||||
await plugins.checkWhitelist(data.id, data.version);
|
||||
const pluginData = await plugins.toggleInstall(data.id, data.version);
|
||||
|
||||
@@ -198,6 +198,7 @@ Topics.getTopicWithPosts = async function (topicData, set, uid, start, stop, rev
|
||||
);
|
||||
p.eventStart = undefined;
|
||||
p.eventEnd = undefined;
|
||||
p.events = mergeConsecutiveShareEvents(p.events);
|
||||
});
|
||||
|
||||
topicData.category = category;
|
||||
@@ -230,6 +231,23 @@ Topics.getTopicWithPosts = async function (topicData, set, uid, start, stop, rev
|
||||
return result.topic;
|
||||
};
|
||||
|
||||
function mergeConsecutiveShareEvents(arr) {
|
||||
return arr.reduce((acc, curr) => {
|
||||
const last = acc[acc.length - 1];
|
||||
if (last && last.type === curr.type && last.type === 'share') {
|
||||
if (!last.items) {
|
||||
last.items = [{ ...last }];
|
||||
['user', 'text', 'timestamp', 'timestampISO'].forEach(field => delete last[field]);
|
||||
}
|
||||
last.items.push(curr);
|
||||
} else {
|
||||
acc.push(curr);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
async function getDeleter(topicData) {
|
||||
if (!parseInt(topicData.deleterUid, 10)) {
|
||||
return null;
|
||||
|
||||
@@ -144,6 +144,7 @@ before(async function () {
|
||||
nconf.set('version', packageInfo.version);
|
||||
nconf.set('runJobs', false);
|
||||
nconf.set('jobsDisabled', false);
|
||||
nconf.set('acpPluginInstallDisabled', false);
|
||||
|
||||
|
||||
await db.init();
|
||||
|
||||
Reference in New Issue
Block a user