Files
NodeBB/src/user/settings.js

221 lines
8.2 KiB
JavaScript
Raw Normal View History

'use strict';
const validator = require('validator');
2019-07-16 20:44:00 -04:00
const meta = require('../meta');
const db = require('../database');
const activitypub = require('../activitypub');
2019-07-16 20:44:00 -04:00
const plugins = require('../plugins');
const notifications = require('../notifications');
const languages = require('../languages');
module.exports = function (User) {
2023-08-16 19:49:21 -04:00
const spiderDefaultSettings = {
usePagination: 1,
topicPostSort: 'oldest_to_newest',
postsPerPage: 20,
topicsPerPage: 20,
};
const remoteDefaultSettings = Object.freeze({
categoryWatchState: 'notwatching',
});
2019-07-16 20:44:00 -04:00
User.getSettings = async function (uid) {
2018-11-12 00:20:44 -05:00
if (parseInt(uid, 10) <= 0) {
2023-08-16 19:49:21 -04:00
const isSpider = parseInt(uid, 10) === -1;
return await onSettingsLoaded(uid, isSpider ? spiderDefaultSettings : {});
}
2021-02-03 23:59:08 -07:00
let settings = await db.getObject(`user:${uid}:settings`);
2019-07-16 20:44:00 -04:00
settings = settings || {};
settings.uid = uid;
return await onSettingsLoaded(uid, settings);
};
2019-07-16 20:44:00 -04:00
User.getMultipleUserSettings = async function (uids) {
2014-04-16 17:38:34 -04:00
if (!Array.isArray(uids) || !uids.length) {
2019-07-16 20:44:00 -04:00
return [];
2014-04-16 17:38:34 -04:00
}
2021-02-03 23:59:08 -07:00
const keys = uids.map(uid => `user:${uid}:settings`);
2019-07-16 20:44:00 -04:00
let settings = await db.getObjects(keys);
2021-02-04 00:01:39 -07:00
settings = settings.map((userSettings, index) => {
2019-07-16 20:44:00 -04:00
userSettings = userSettings || {};
userSettings.uid = uids[index];
return userSettings;
});
return await Promise.all(settings.map(s => onSettingsLoaded(s.uid, s)));
2014-04-16 17:38:34 -04:00
};
2019-07-16 20:44:00 -04:00
async function onSettingsLoaded(uid, settings) {
const data = await plugins.hooks.fire('filter:user.getSettings', { uid, settings });
2019-07-16 20:44:00 -04:00
settings = data.settings;
const defaultTopicsPerPage = meta.config.topicsPerPage;
const defaultPostsPerPage = meta.config.postsPerPage;
settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1;
settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1;
settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1;
settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off');
settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1;
settings.unreadCutoff = Math.max(1, Math.min(
Math.max(meta.config.unreadCutoff, 14),
settings.unreadCutoff ?
parseInt(settings.unreadCutoff, 10) :
meta.config.unreadCutoff,
));
settings.topicsPerPage = Math.max(1, Math.min(
2021-02-04 02:07:29 -07:00
meta.config.maxTopicsPerPage,
settings.topicsPerPage ?
parseInt(settings.topicsPerPage, 10) :
defaultTopicsPerPage,
));
settings.postsPerPage = Math.max(1, Math.min(
2021-02-04 02:07:29 -07:00
meta.config.maxPostsPerPage,
settings.postsPerPage ?
parseInt(settings.postsPerPage, 10) :
defaultPostsPerPage,
));
2019-07-16 20:44:00 -04:00
settings.userLang = settings.userLang || meta.config.defaultLang || 'en-GB';
settings.acpLang = settings.acpLang || settings.userLang;
settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest');
settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'recently_replied');
2019-07-16 20:44:00 -04:00
settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1;
settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1;
settings.upvoteNotifFreq = getSetting(settings, 'upvoteNotifFreq', 'all');
settings.disableIncomingChats = parseInt(getSetting(settings, 'disableIncomingChats', 0), 10) === 1;
2019-07-16 20:44:00 -04:00
settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1;
settings.updateUrlWithPostIndex = parseInt(getSetting(settings, 'updateUrlWithPostIndex', 1), 10) === 1;
settings.bootswatchSkin = validator.escape(String(settings.bootswatchSkin || ''));
settings.homePageRoute = validator.escape(String(settings.homePageRoute || '')).replace(/&#x2F;/g, '/');
2019-07-16 20:44:00 -04:00
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching');
const notificationTypes = await notifications.getAllNotificationTypes();
2021-02-04 00:01:39 -07:00
notificationTypes.forEach((notificationType) => {
2019-07-16 20:44:00 -04:00
settings[notificationType] = getSetting(settings, notificationType, 'notification');
});
settings.chatAllowList = parseJSONSetting(settings.chatAllowList || '[]', []).map(String);
settings.chatDenyList = parseJSONSetting(settings.chatDenyList || '[]', []).map(String);
2019-07-16 20:44:00 -04:00
return settings;
2014-12-20 17:26:39 -05:00
}
function parseJSONSetting(value, defaultValue) {
try {
return JSON.parse(value);
} catch (err) {
return defaultValue;
}
}
function getSetting(settings, key, defaultValue) {
2015-09-11 12:51:10 -04:00
if (settings[key] || settings[key] === 0) {
return settings[key];
} else if (activitypub.helpers.isUri(settings.uid) && remoteDefaultSettings[key]) {
return remoteDefaultSettings[key];
2015-09-11 12:51:10 -04:00
} else if (meta.config[key] || meta.config[key] === 0) {
return meta.config[key];
}
return defaultValue;
2015-09-11 12:51:10 -04:00
}
2019-07-16 20:44:00 -04:00
User.saveSettings = async function (uid, data) {
await validateSettings(data);
data.userLang = data.userLang || meta.config.defaultLang;
2014-03-18 14:11:26 -04:00
plugins.hooks.fire('action:user.saveSettings', { uid: uid, settings: data });
2014-12-19 17:19:29 -05:00
2019-07-16 20:44:00 -04:00
const settings = {
2015-09-14 16:20:56 -04:00
showemail: data.showemail,
showfullname: data.showfullname,
unreadCutoff: data.unreadCutoff,
2015-09-14 16:20:56 -04:00
openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab,
dailyDigestFreq: data.dailyDigestFreq || 'off',
usePagination: data.usePagination,
topicsPerPage: data.topicsPerPage,
postsPerPage: data.postsPerPage,
userLang: data.userLang,
2018-02-12 17:12:18 -05:00
acpLang: data.acpLang || meta.config.defaultLang,
2015-09-14 16:20:56 -04:00
followTopicsOnCreate: data.followTopicsOnCreate,
followTopicsOnReply: data.followTopicsOnReply,
disableIncomingChats: data.disableIncomingChats,
2015-09-14 16:20:56 -04:00
topicSearchEnabled: data.topicSearchEnabled,
updateUrlWithPostIndex: data.updateUrlWithPostIndex,
2017-02-18 01:27:46 -07:00
homePageRoute: ((data.homePageRoute === 'custom' ? data.homePageCustom : data.homePageRoute) || '').replace(/^\//, ''),
2016-08-29 12:43:32 +03:00
scrollToMyPost: data.scrollToMyPost,
upvoteNotifFreq: data.upvoteNotifFreq,
bootswatchSkin: data.bootswatchSkin,
categoryWatchState: data.categoryWatchState,
categoryTopicSort: data.categoryTopicSort,
topicPostSort: data.topicPostSort,
chatAllowList: data.chatAllowList,
chatDenyList: data.chatDenyList,
2015-09-14 16:20:56 -04:00
};
2019-07-16 20:44:00 -04:00
const notificationTypes = await notifications.getAllNotificationTypes();
2021-02-04 00:01:39 -07:00
notificationTypes.forEach((notificationType) => {
2019-07-16 20:44:00 -04:00
if (data[notificationType]) {
settings[notificationType] = data[notificationType];
}
});
const result = await plugins.hooks.fire('filter:user.saveSettings', { uid, settings, data });
2021-02-03 23:59:08 -07:00
await db.setObject(`user:${uid}:settings`, result.settings);
2019-07-16 20:44:00 -04:00
await User.updateDigestSetting(uid, data.dailyDigestFreq);
return await User.getSettings(uid);
};
async function validateSettings(data) {
const maxUnreadCutoff = Math.max(meta.config.unreadCutoff, 14);
if (
!data.unreadCutoff ||
parseInt(data.unreadCutoff, 10) <= 0 ||
parseInt(data.unreadCutoff, 10) > maxUnreadCutoff
) {
throw new Error(`[[error:invalid-unread-cutoff, ${maxUnreadCutoff}]]`);
}
const maxPostsPerPage = meta.config.maxPostsPerPage || 20;
if (
!data.postsPerPage ||
parseInt(data.postsPerPage, 10) <= 1 ||
parseInt(data.postsPerPage, 10) > maxPostsPerPage
) {
throw new Error(`[[error:invalid-pagination-value, 2, ${maxPostsPerPage}]]`);
}
const maxTopicsPerPage = meta.config.maxTopicsPerPage || 20;
if (
!data.topicsPerPage ||
parseInt(data.topicsPerPage, 10) <= 1 ||
parseInt(data.topicsPerPage, 10) > maxTopicsPerPage
) {
throw new Error(`[[error:invalid-pagination-value, 2, ${maxTopicsPerPage}]]`);
}
const languageCodes = await languages.listCodes();
if (data.userLang && !languageCodes.includes(data.userLang)) {
throw new Error('[[error:invalid-language]]');
}
if (data.acpLang && !languageCodes.includes(data.acpLang)) {
throw new Error('[[error:invalid-language]]');
}
}
2019-07-16 20:44:00 -04:00
User.updateDigestSetting = async function (uid, dailyDigestFreq) {
await db.sortedSetsRemove(['digest:day:uids', 'digest:week:uids', 'digest:month:uids'], uid);
2021-10-18 20:10:14 -04:00
if (['day', 'week', 'biweek', 'month'].includes(dailyDigestFreq)) {
2021-02-03 23:59:08 -07:00
await db.sortedSetAdd(`digest:${dailyDigestFreq}:uids`, Date.now(), uid);
2019-07-16 20:44:00 -04:00
}
2016-09-20 17:46:32 +03:00
};
2014-12-20 17:26:39 -05:00
2019-07-16 20:44:00 -04:00
User.setSetting = async function (uid, key, value) {
2018-11-12 00:20:44 -05:00
if (parseInt(uid, 10) <= 0) {
2019-07-16 20:44:00 -04:00
return;
2016-12-02 17:05:46 +03:00
}
2021-02-03 23:59:08 -07:00
await db.setObjectField(`user:${uid}:settings`, key, value);
};
2014-04-10 20:31:57 +01:00
};