feat(writeapi): user settings API

v1.18.x
Julian Lam 4 years ago
parent aab5f018b9
commit c26f2b6599

@ -0,0 +1,143 @@
Settings:
type: object
properties:
showemail:
type: boolean
description: Show user email in profile page
usePagination:
type: boolean
description: Toggles between pagination (when enabled), or infinite scrolling (when disabled)
topicsPerPage:
type: number
description: Number of topics displayed on a category page
postsPerPage:
type: number
description: Number of posts displayed on a topic page
topicPostSort:
type: string
description: Default sorting strategy of the posts in of a topic
openOutgoingLinksInNewTab:
type: boolean
description: Whether to automatically open all external links in a new tab
dailyDigestFreq:
type: string
description: How often to receive the scheduled digest from this forum
showfullname:
type: boolean
description: Show user full name in profile page
followTopicsOnCreate:
type: boolean
description: Automatically be notified of new posts in a topic, when you create a topic
followTopicsOnReply:
type: boolean
description:
restrictChat:
type: boolean
description:
topicSearchEnabled:
type: boolean
description:
categoryTopicSort:
type: string
description:
userLang:
type: string
description:
bootswatchSkin:
type: string
description:
homePageRoute:
type: string
description:
scrollToMyPost:
type: boolean
description:
notificationType_new-chat:
type: string
description:
notificationType_new-reply:
type: string
description:
notificationType_post-edit:
type: string
description:
sendChatNotifications:
nullable: true
sendPostNotifications:
nullable: true
notificationType_upvote:
type: string
description:
notificationType_new-topic:
type: string
description:
notificationType_follow:
type: string
description:
notificationType_group-invite:
type: string
description:
upvoteNotifFreq:
type: string
description:
notificationType_mention:
type: string
description:
acpLang:
type: string
description:
notificationType_new-register:
type: string
description:
notificationType_post-queue:
type: string
description:
notificationType_new-post-flag:
type: string
description:
notificationType_new-user-flag:
type: string
description:
categoryWatchState:
type: string
description:
notificationType_group-request-membership:
type: string
description:
uid:
type: number
description:
description: A user identifier
required:
- showemail
- usePagination
- topicsPerPage
- postsPerPage
- topicPostSort
- openOutgoingLinksInNewTab
- dailyDigestFreq
- showfullname
- followTopicsOnCreate
- followTopicsOnReply
- restrictChat
- topicSearchEnabled
- categoryTopicSort
- userLang
- bootswatchSkin
- homePageRoute
- scrollToMyPost
- notificationType_new-chat
- notificationType_new-reply
- notificationType_upvote
- notificationType_new-topic
- notificationType_follow
- notificationType_group-invite
- upvoteNotifFreq
- acpLang
- notificationType_new-register
- notificationType_post-queue
- notificationType_new-post-flag
- notificationType_new-user-flag
- categoryWatchState
- notificationType_group-request-membership
- uid

@ -20,114 +20,7 @@ get:
- type: object
properties:
settings:
type: object
properties:
showemail:
type: boolean
usePagination:
type: boolean
topicsPerPage:
type: number
postsPerPage:
type: number
topicPostSort:
type: string
openOutgoingLinksInNewTab:
type: boolean
dailyDigestFreq:
type: string
showfullname:
type: boolean
followTopicsOnCreate:
type: boolean
followTopicsOnReply:
type: boolean
restrictChat:
type: boolean
topicSearchEnabled:
type: boolean
categoryTopicSort:
type: string
userLang:
type: string
bootswatchSkin:
type: string
homePageRoute:
type: string
scrollToMyPost:
type: boolean
notificationType_new-chat:
type: string
notificationType_new-reply:
type: string
notificationType_post-edit:
type: string
sendChatNotifications:
nullable: true
sendPostNotifications:
nullable: true
notificationType_upvote:
type: string
notificationType_new-topic:
type: string
notificationType_follow:
type: string
notificationType_group-invite:
type: string
upvoteNotifFreq:
type: string
notificationType_mention:
type: string
acpLang:
type: string
notificationType_new-register:
type: string
notificationType_post-queue:
type: string
notificationType_new-post-flag:
type: string
notificationType_new-user-flag:
type: string
categoryWatchState:
type: string
notificationType_group-request-membership:
type: string
uid:
type: number
description: A user identifier
required:
- showemail
- usePagination
- topicsPerPage
- postsPerPage
- topicPostSort
- openOutgoingLinksInNewTab
- dailyDigestFreq
- showfullname
- followTopicsOnCreate
- followTopicsOnReply
- restrictChat
- topicSearchEnabled
- categoryTopicSort
- userLang
- bootswatchSkin
- homePageRoute
- scrollToMyPost
- notificationType_new-chat
- notificationType_new-reply
- notificationType_upvote
- notificationType_new-topic
- notificationType_follow
- notificationType_group-invite
- upvoteNotifFreq
- acpLang
- notificationType_new-register
- notificationType_post-queue
- notificationType_new-post-flag
- notificationType_new-user-flag
- categoryWatchState
- notificationType_group-request-membership
- uid
$ref: ../../../components/schemas/SettingsObj.yaml#/Settings
languages:
type: array
items:

@ -30,6 +30,10 @@ tags:
paths:
/users/{uid}:
$ref: 'write/users/uid.yaml'
/users/{uid}/settings:
$ref: 'write/users/uid/settings.yaml'
/users/{uid}/settings/{setting}:
$ref: 'write/users/uid/settings/setting.yaml'
/users/{uid}/password:
$ref: 'write/users/uid/password.yaml'
/users/{uid}/follow:

@ -0,0 +1,35 @@
put:
tags:
- users
summary: update user settings
parameters:
- in: path
name: uid
schema:
type: integer
required: true
description: uid of the user
requestBody:
content:
application/json:
schema:
type: object
properties:
settings:
type: object
description: An object containing key-value pairs of user settings to update
example:
showemail: '0'
showfullname: '1'
responses:
'200':
description: successfully updated user settings
content:
application/json:
schema:
type: object
properties:
status:
$ref: ../../../components/schemas/Status.yaml#/Status
response:
$ref: ../../../components/schemas/SettingsObj.yaml#/Settings

@ -0,0 +1,40 @@
put:
tags:
- users
summary: update one user setting
parameters:
- in: path
name: uid
schema:
type: integer
required: true
description: uid of the user
example: '1'
- in: path
name: setting
schema:
type: string
required: true
description: name of the setting you wish to update
example: 'showemail'
requestBody:
content:
application/json:
schema:
type: object
properties:
value:
type: string
example: '1'
responses:
'200':
description: successfully updated user settings
content:
application/json:
schema:
type: object
properties:
status:
$ref: ../../../../components/schemas/Status.yaml#/Status
response:
type: object

@ -1,7 +1,7 @@
'use strict';
define('forum/account/settings', ['forum/account/header', 'components', 'translator'], function (header, components, translator) {
define('forum/account/settings', ['forum/account/header', 'components', 'translator', 'api'], function (header, components, translator, api) {
var AccountSettings = {};
// If page skin is changed but not saved, switch the skin back
@ -67,11 +67,7 @@ define('forum/account/settings', ['forum/account/header', 'components', 'transla
}
function saveSettings(settings) {
socket.emit('user.saveSettings', { uid: ajaxify.data.theirid, settings: settings }, function (err, newSettings) {
if (err) {
return app.alertError(err.message);
}
api.put(`/users/${ajaxify.data.uid}/settings`, { settings }).then((newSettings) => {
app.alertSuccess('[[success:settings-saved]]');
var languageChanged = false;
for (var key in newSettings) {

@ -24,7 +24,7 @@ define('forum/category', [
topicList.init('category', loadTopicsAfter);
sort.handleSort('categoryTopicSort', 'user.setCategorySort', 'category/' + ajaxify.data.slug);
sort.handleSort('categoryTopicSort', 'setCategorySort', 'category/' + ajaxify.data.slug);
if (!config.usePagination) {
navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback);

@ -49,7 +49,7 @@ define('forum/topic', [
threadTools.init(tid, $('.topic'));
events.init();
sort.handleSort('topicPostSort', 'user.setTopicSort', 'topic/' + ajaxify.data.slug);
sort.handleSort('topicPostSort', 'setTopicSort', 'topic/' + ajaxify.data.slug);
if (!config.usePagination) {
infinitescroll.init($('[component="topic"]'), posts.loadMorePosts);

@ -1,10 +1,10 @@
'use strict';
define('sort', ['components'], function (components) {
define('sort', ['components', 'api'], function (components, api) {
var module = {};
module.handleSort = function (field, method, gotoOnSave) {
module.handleSort = function (field, key, gotoOnSave) {
var threadSort = components.get('thread/sort');
threadSort.find('i').removeClass('fa-check');
var currentSetting = threadSort.find('a[data-sort="' + config[field] + '"]');
@ -20,10 +20,9 @@ define('sort', ['components'], function (components) {
}
var newSetting = $(this).attr('data-sort');
if (app.user.uid) {
socket.emit(method, newSetting, function (err) {
if (err) {
return app.alertError(err.message);
}
api.put(`/users/${app.user.uid}/settings/${key}`, {
value: newSetting,
}).then(() => {
refresh(newSetting, utils.params());
});
} else {

@ -89,6 +89,23 @@ usersAPI.deleteMany = async function (caller, data) {
}
};
usersAPI.updateSettings = async function (caller, data) {
if (!caller.uid || !data || !data.settings) {
throw new Error('[[error:invalid-data]]');
}
const canEdit = await privileges.users.canEdit(caller.uid, data.uid);
if (!canEdit) {
throw new Error('[[error:no-privileges]]');
}
return await user.saveSettings(data.uid, data.settings);
};
usersAPI.updateSetting = async function (caller, data) {
await user.setSetting(data.uid, data.setting, data.value);
};
usersAPI.changePassword = async function (caller, data) {
await user.changePassword(caller.uid, Object.assign(data, { ip: caller.ip }));
await events.log({

@ -28,6 +28,16 @@ Users.deleteMany = async (req, res) => {
helpers.formatApiResponse(200, res);
};
Users.updateSettings = async (req, res) => {
const settings = await api.users.updateSettings(req, { ...req.body, uid: req.params.uid });
helpers.formatApiResponse(200, res, settings);
};
Users.updateSetting = async (req, res) => {
await api.users.updateSetting(req, { ...req.params, value: req.body.value });
helpers.formatApiResponse(200, res);
};
Users.changePassword = async (req, res) => {
await api.users.changePassword(req, { ...req.body, uid: req.params.uid });
helpers.formatApiResponse(200, res);

@ -21,6 +21,9 @@ function authenticatedRoutes() {
setupApiRoute(router, 'put', '/:uid', [...middlewares, middleware.assert.user], controllers.write.users.update);
setupApiRoute(router, 'delete', '/:uid', [...middlewares, middleware.assert.user, middleware.exposePrivileges], controllers.write.users.delete);
setupApiRoute(router, 'put', '/:uid/settings', [...middlewares, middleware.checkRequired.bind(null, ['settings'])], controllers.write.users.updateSettings);
setupApiRoute(router, 'put', '/:uid/settings/:setting', [...middlewares, middleware.checkRequired.bind(null, ['value'])], controllers.write.users.updateSetting);
setupApiRoute(router, 'put', '/:uid/password', [...middlewares, middleware.checkRequired.bind(null, ['newPassword']), middleware.assert.user], controllers.write.users.changePassword);
setupApiRoute(router, 'put', '/:uid/follow', [...middlewares, middleware.assert.user], controllers.write.users.follow);

@ -168,22 +168,27 @@ SocketUser.unfollow = async function (socket, data) {
};
SocketUser.saveSettings = async function (socket, data) {
if (!socket.uid || !data || !data.settings) {
throw new Error('[[error:invalid-data]]');
}
const canEdit = await privileges.users.canEdit(socket.uid, data.uid);
if (!canEdit) {
throw new Error('[[error:no-privileges]]');
}
return await user.saveSettings(data.uid, data.settings);
sockets.warnDeprecated(socket, 'PUT /api/v3/users/:uid/settings');
const settings = await api.users.updateSettings(socket, data);
return settings;
};
SocketUser.setTopicSort = async function (socket, sort) {
await user.setSetting(socket.uid, 'topicPostSort', sort);
sockets.warnDeprecated(socket, 'PUT /api/v3/users/:uid/setting/topicPostSort');
await api.users.updateSetting(socket, {
uid: socket.uid,
setting: 'topicPostSort',
value: sort,
});
};
SocketUser.setCategorySort = async function (socket, sort) {
await user.setSetting(socket.uid, 'categoryTopicSort', sort);
sockets.warnDeprecated(socket, 'PUT /api/v3/users/:uid/setting/categoryTopicSort');
await api.users.updateSetting(socket, {
uid: socket.uid,
setting: 'categoryTopicSort',
value: sort,
});
};
SocketUser.getUnreadCount = async function (socket) {

Loading…
Cancel
Save