2014-03-12 20:53:42 -04:00
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2020-01-17 11:48:00 -05:00
|
|
|
const validator = require('validator');
|
|
|
|
|
|
2019-07-16 20:44:00 -04:00
|
|
|
const meta = require('../meta');
|
|
|
|
|
const db = require('../database');
|
2024-02-05 16:57:17 -05:00
|
|
|
const activitypub = require('../activitypub');
|
2019-07-16 20:44:00 -04:00
|
|
|
const plugins = require('../plugins');
|
|
|
|
|
const notifications = require('../notifications');
|
2020-01-13 12:27:50 -05:00
|
|
|
const languages = require('../languages');
|
2014-03-12 20:53:42 -04:00
|
|
|
|
2016-10-13 11:43:39 +02:00
|
|
|
module.exports = function (User) {
|
2023-08-16 19:49:21 -04:00
|
|
|
const spiderDefaultSettings = {
|
|
|
|
|
usePagination: 1,
|
|
|
|
|
topicPostSort: 'oldest_to_newest',
|
|
|
|
|
postsPerPage: 20,
|
|
|
|
|
topicsPerPage: 20,
|
|
|
|
|
};
|
2024-02-05 16:57:17 -05:00
|
|
|
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 : {});
|
2014-11-04 18:31:21 -05:00
|
|
|
}
|
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);
|
2014-03-12 20:53:42 -04:00
|
|
|
};
|
|
|
|
|
|
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) {
|
2026-02-16 22:57:00 -05:00
|
|
|
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;
|
2026-02-16 22:57:00 -05:00
|
|
|
settings.unreadCutoff = Math.max(1, Math.min(
|
|
|
|
|
Math.max(meta.config.unreadCutoff, 14),
|
|
|
|
|
settings.unreadCutoff ?
|
|
|
|
|
parseInt(settings.unreadCutoff, 10) :
|
|
|
|
|
meta.config.unreadCutoff,
|
|
|
|
|
));
|
2026-01-28 10:48:09 -05:00
|
|
|
settings.topicsPerPage = Math.max(1, Math.min(
|
2021-02-04 02:07:29 -07:00
|
|
|
meta.config.maxTopicsPerPage,
|
2026-01-28 10:48:09 -05:00
|
|
|
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,
|
2026-01-28 10:48:09 -05:00
|
|
|
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');
|
2024-03-04 16:06:04 -05:00
|
|
|
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');
|
2025-04-25 11:54:11 -04:00
|
|
|
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;
|
2020-11-16 21:31:55 -05:00
|
|
|
settings.updateUrlWithPostIndex = parseInt(getSetting(settings, 'updateUrlWithPostIndex', 1), 10) === 1;
|
2020-01-17 11:48:00 -05:00
|
|
|
settings.bootswatchSkin = validator.escape(String(settings.bootswatchSkin || ''));
|
2020-10-12 14:18:59 -04:00
|
|
|
settings.homePageRoute = validator.escape(String(settings.homePageRoute || '')).replace(///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');
|
|
|
|
|
});
|
|
|
|
|
|
2025-04-25 11:54:11 -04:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2025-04-25 11:54:11 -04:00
|
|
|
function parseJSONSetting(value, defaultValue) {
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(value);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
return defaultValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-23 11:19:49 +02:00
|
|
|
function getSetting(settings, key, defaultValue) {
|
2015-09-11 12:51:10 -04:00
|
|
|
if (settings[key] || settings[key] === 0) {
|
|
|
|
|
return settings[key];
|
2024-02-05 16:57:17 -05:00
|
|
|
} 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];
|
|
|
|
|
}
|
2016-01-23 11:19:49 +02:00
|
|
|
return defaultValue;
|
2015-09-11 12:51:10 -04:00
|
|
|
}
|
|
|
|
|
|
2019-07-16 20:44:00 -04:00
|
|
|
User.saveSettings = async function (uid, data) {
|
2026-02-16 22:57:00 -05:00
|
|
|
await validateSettings(data);
|
2014-03-12 20:53:42 -04:00
|
|
|
|
2015-02-27 13:24:45 -05:00
|
|
|
data.userLang = data.userLang || meta.config.defaultLang;
|
2014-03-18 14:11:26 -04:00
|
|
|
|
2020-11-20 16:06:26 -05: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,
|
2026-02-16 22:57:00 -05:00
|
|
|
unreadCutoff: data.unreadCutoff,
|
2015-09-14 16:20:56 -04:00
|
|
|
openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab,
|
|
|
|
|
dailyDigestFreq: data.dailyDigestFreq || 'off',
|
|
|
|
|
usePagination: data.usePagination,
|
2026-02-16 22:57:00 -05:00
|
|
|
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,
|
2025-04-25 11:54:11 -04:00
|
|
|
disableIncomingChats: data.disableIncomingChats,
|
2015-09-14 16:20:56 -04:00
|
|
|
topicSearchEnabled: data.topicSearchEnabled,
|
2020-11-16 21:31:55 -05:00
|
|
|
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,
|
2017-11-21 12:14:14 -07:00
|
|
|
upvoteNotifFreq: data.upvoteNotifFreq,
|
2018-11-07 13:44:35 -05:00
|
|
|
bootswatchSkin: data.bootswatchSkin,
|
2018-12-14 16:24:17 -05:00
|
|
|
categoryWatchState: data.categoryWatchState,
|
2020-10-28 17:13:58 -04:00
|
|
|
categoryTopicSort: data.categoryTopicSort,
|
|
|
|
|
topicPostSort: data.topicPostSort,
|
2025-04-25 11:54:11 -04:00
|
|
|
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];
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-02-16 22:57:00 -05:00
|
|
|
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);
|
2014-03-12 20:53:42 -04:00
|
|
|
};
|
2014-06-06 22:12:14 -04:00
|
|
|
|
2026-02-16 22:57:00 -05:00
|
|
|
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
|
|
|
}
|
2017-10-23 18:31:16 -04:00
|
|
|
|
2021-02-03 23:59:08 -07:00
|
|
|
await db.setObjectField(`user:${uid}:settings`, key, value);
|
2014-06-06 22:12:14 -04:00
|
|
|
};
|
2014-04-10 20:31:57 +01:00
|
|
|
};
|