{{_ "You_are_not_authorized_to_view_this_page"}}
+ {{else}} + +diff --git a/app/api/server/v1/emoji-custom.js b/app/api/server/v1/emoji-custom.js index f638f025b693..243386792058 100644 --- a/app/api/server/v1/emoji-custom.js +++ b/app/api/server/v1/emoji-custom.js @@ -122,7 +122,7 @@ API.v1.addRoute('emoji-custom.update', { authRequired: true }, { if (!fields._id) { return callback(new Meteor.Error('The required "_id" query param is missing.')); } - const emojiToUpdate = EmojiCustom.findOneByID(fields._id); + const emojiToUpdate = EmojiCustom.findOneById(fields._id); if (!emojiToUpdate) { return callback(new Meteor.Error('Emoji not found.')); } diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index 1cf3926f035d..82596a4e46f6 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -18,6 +18,7 @@ import { } from '../../../lib'; import { getFullUserData } from '../../../lib/server/functions/getFullUserData'; import { API } from '../api'; +import { setStatusMessage } from '../../../lib/server'; API.v1.addRoute('users.create', { authRequired: true }, { post() { @@ -325,6 +326,73 @@ API.v1.addRoute('users.setAvatar', { authRequired: true }, { }, }); +API.v1.addRoute('users.getStatus', { authRequired: true }, { + get() { + if (this.isUserFromParams()) { + const user = Users.findOneById(this.userId); + return API.v1.success({ + message: user.statusText, + connectionStatus: user.statusConnection, + status: user.status, + }); + } + + const user = this.getUserFromParams(); + + return API.v1.success({ + message: user.statusText, + status: user.status, + }); + }, +}); + +API.v1.addRoute('users.setStatus', { authRequired: true }, { + post() { + check(this.bodyParams, Match.ObjectIncluding({ + status: Match.Maybe(String), + message: Match.Maybe(String), + })); + + if (!settings.get('Accounts_AllowUserStatusMessageChange')) { + throw new Meteor.Error('error-not-allowed', 'Change status is not allowed', { + method: 'users.setStatus', + }); + } + + let user; + if (this.isUserFromParams()) { + user = Meteor.users.findOne(this.userId); + } else if (hasPermission(this.userId, 'edit-other-user-info')) { + user = this.getUserFromParams(); + } else { + return API.v1.unauthorized(); + } + + Meteor.runAsUser(user._id, () => { + if (this.bodyParams.message) { + setStatusMessage(user._id, this.bodyParams.message); + } + if (this.bodyParams.status) { + const validStatus = ['online', 'away', 'offline', 'busy']; + if (validStatus.includes(this.bodyParams.status)) { + Meteor.users.update(this.userId, { + $set: { + status: this.bodyParams.status, + statusDefault: this.bodyParams.status, + }, + }); + } else { + throw new Meteor.Error('error-invalid-status', 'Valid status types include online, away, offline, and busy.', { + method: 'users.setStatus', + }); + } + } + }); + + return API.v1.success(); + }, +}); + API.v1.addRoute('users.update', { authRequired: true }, { post() { check(this.bodyParams, { @@ -334,6 +402,7 @@ API.v1.addRoute('users.update', { authRequired: true }, { name: Match.Maybe(String), password: Match.Maybe(String), username: Match.Maybe(String), + statusText: Match.Maybe(String), active: Match.Maybe(Boolean), roles: Match.Maybe(Array), joinDefaultChannels: Match.Maybe(Boolean), @@ -369,6 +438,7 @@ API.v1.addRoute('users.updateOwnBasicInfo', { authRequired: true }, { email: Match.Maybe(String), name: Match.Maybe(String), username: Match.Maybe(String), + statusText: Match.Maybe(String), currentPassword: Match.Maybe(String), newPassword: Match.Maybe(String), }), @@ -379,6 +449,7 @@ API.v1.addRoute('users.updateOwnBasicInfo', { authRequired: true }, { email: this.bodyParams.data.email, realname: this.bodyParams.data.name, username: this.bodyParams.data.username, + statusText: this.bodyParams.data.statusText, newPassword: this.bodyParams.data.newPassword, typedPassword: this.bodyParams.data.currentPassword, }; @@ -581,6 +652,7 @@ API.v1.addRoute('users.presence', { authRequired: true }, { name: 1, status: 1, utcOffset: 1, + statusText: 1, }, }; diff --git a/app/authorization/server/startup.js b/app/authorization/server/startup.js index 8d57052b66db..72f155c8a013 100644 --- a/app/authorization/server/startup.js +++ b/app/authorization/server/startup.js @@ -46,6 +46,7 @@ Meteor.startup(function() { { _id: 'leave-p', roles: ['admin', 'user', 'bot', 'anonymous'] }, { _id: 'manage-assets', roles: ['admin'] }, { _id: 'manage-emoji', roles: ['admin'] }, + { _id: 'manage-user-status', roles: ['admin'] }, { _id: 'manage-integrations', roles: ['admin'] }, { _id: 'manage-own-integrations', roles: ['admin'] }, { _id: 'manage-oauth-apps', roles: ['admin'] }, diff --git a/app/custom-sounds/server/methods/deleteCustomSound.js b/app/custom-sounds/server/methods/deleteCustomSound.js index ba26d32673b2..b72c852bacfc 100644 --- a/app/custom-sounds/server/methods/deleteCustomSound.js +++ b/app/custom-sounds/server/methods/deleteCustomSound.js @@ -10,7 +10,7 @@ Meteor.methods({ let sound = null; if (hasPermission(this.userId, 'manage-sounds')) { - sound = CustomSounds.findOneByID(_id); + sound = CustomSounds.findOneById(_id); } else { throw new Meteor.Error('not_authorized'); } @@ -20,7 +20,7 @@ Meteor.methods({ } RocketChatFileCustomSoundsInstance.deleteFile(`${ sound._id }.${ sound.extension }`); - CustomSounds.removeByID(_id); + CustomSounds.removeById(_id); Notifications.notifyAll('deleteCustomSound', { soundData: sound }); return true; diff --git a/app/custom-sounds/server/methods/insertOrUpdateSound.js b/app/custom-sounds/server/methods/insertOrUpdateSound.js index 36ea5839736b..03a2ff69a4d8 100644 --- a/app/custom-sounds/server/methods/insertOrUpdateSound.js +++ b/app/custom-sounds/server/methods/insertOrUpdateSound.js @@ -32,7 +32,7 @@ Meteor.methods({ let matchingResults = []; if (soundData._id) { - matchingResults = CustomSounds.findByNameExceptID(soundData.name, soundData._id).fetch(); + matchingResults = CustomSounds.findByNameExceptId(soundData.name, soundData._id).fetch(); } else { matchingResults = CustomSounds.findByName(soundData.name).fetch(); } diff --git a/app/emoji-custom/server/methods/deleteEmojiCustom.js b/app/emoji-custom/server/methods/deleteEmojiCustom.js index b9623960ea2b..0e5b383f7188 100644 --- a/app/emoji-custom/server/methods/deleteEmojiCustom.js +++ b/app/emoji-custom/server/methods/deleteEmojiCustom.js @@ -10,7 +10,7 @@ Meteor.methods({ let emoji = null; if (hasPermission(this.userId, 'manage-emoji')) { - emoji = EmojiCustom.findOneByID(emojiID); + emoji = EmojiCustom.findOneById(emojiID); } else { throw new Meteor.Error('not_authorized'); } @@ -20,7 +20,7 @@ Meteor.methods({ } RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${ emoji.name }.${ emoji.extension }`)); - EmojiCustom.removeByID(emojiID); + EmojiCustom.removeById(emojiID); Notifications.notifyLogged('deleteEmojiCustom', { emojiData: emoji }); return true; diff --git a/app/lib/lib/roomTypes/direct.js b/app/lib/lib/roomTypes/direct.js index a8a000b7dfd8..0f55c1388ee3 100644 --- a/app/lib/lib/roomTypes/direct.js +++ b/app/lib/lib/roomTypes/direct.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Session } from 'meteor/session'; -import { ChatRoom, Subscriptions } from '../../../models'; +import { ChatRoom, Subscriptions, Users } from '../../../models'; import { openRoom } from '../../../ui-utils'; import { getUserPreference, RoomTypeConfig, RoomTypeRouteConfig, RoomSettingsEnum, UiTextContext } from '../../../utils'; import { hasPermission, hasAtLeastOnePermission } from '../../../authorization'; @@ -92,6 +92,14 @@ export class DirectMessageRoomType extends RoomTypeConfig { return Session.get(`user_${ subscription.name }_status`); } + getUserStatusText(roomId) { + const userId = roomId.replace(Meteor.userId(), ''); + const userData = Users.findOne({ _id: userId }); + if (userData && userData.statusText) { + return userData.statusText; + } + } + getDisplayName(room) { return room.usernames.join(' x '); } diff --git a/app/lib/server/functions/getFullUserData.js b/app/lib/server/functions/getFullUserData.js index 04a667bac1c3..9e41f6c03831 100644 --- a/app/lib/server/functions/getFullUserData.js +++ b/app/lib/server/functions/getFullUserData.js @@ -15,6 +15,7 @@ const defaultFields = { type: 1, active: 1, reason: 1, + statusText: 1, }; const fullFields = { diff --git a/app/lib/server/functions/index.js b/app/lib/server/functions/index.js index 118ca8f784e0..b5a307a4e148 100644 --- a/app/lib/server/functions/index.js +++ b/app/lib/server/functions/index.js @@ -23,6 +23,7 @@ export { saveUser } from './saveUser'; export { sendMessage } from './sendMessage'; export { setEmail } from './setEmail'; export { setRealName, _setRealName } from './setRealName'; +export { setStatusMessage, _setStatusMessage } from './setStatusMessage'; export { setUserAvatar } from './setUserAvatar'; export { _setUsername, setUsername } from './setUsername'; export { unarchiveRoom } from './unarchiveRoom'; diff --git a/app/lib/server/functions/saveUser.js b/app/lib/server/functions/saveUser.js index 320ad9cc6c8e..ea9c8772b6bc 100644 --- a/app/lib/server/functions/saveUser.js +++ b/app/lib/server/functions/saveUser.js @@ -10,7 +10,7 @@ import { settings } from '../../../settings'; import PasswordPolicy from '../lib/PasswordPolicyClass'; import { validateEmailDomain } from '../lib'; -import { checkEmailAvailability, checkUsernameAvailability, setUserAvatar, setEmail, setRealName, setUsername } from '.'; +import { checkEmailAvailability, checkUsernameAvailability, setUserAvatar, setEmail, setRealName, setUsername, setStatusMessage } from '.'; const passwordPolicy = new PasswordPolicy(); @@ -133,6 +133,13 @@ function validateUserEditing(userId, userData) { }); } + if (userData.statusText && !settings.get('Accounts_AllowUserStatusMessageChange') && (!canEditOtherUserInfo || editingMyself)) { + throw new Meteor.Error('error-action-not-allowed', 'Edit user status is not allowed', { + method: 'insertOrUpdateUser', + action: 'Update_user', + }); + } + if (userData.name && !settings.get('Accounts_AllowRealNameChange') && (!canEditOtherUserInfo || editingMyself)) { throw new Meteor.Error('error-action-not-allowed', 'Edit user real name is not allowed', { method: 'insertOrUpdateUser', @@ -248,6 +255,10 @@ export const saveUser = function(userId, userData) { setRealName(userData._id, userData.name); } + if (typeof userData.statusText === 'string') { + setStatusMessage(userData._id, userData.statusText); + } + if (userData.email) { const shouldSendVerificationEmailToUser = userData.verified !== true; setEmail(userData._id, userData.email, shouldSendVerificationEmailToUser); diff --git a/app/lib/server/functions/setStatusMessage.js b/app/lib/server/functions/setStatusMessage.js new file mode 100644 index 000000000000..ceae68be66f8 --- /dev/null +++ b/app/lib/server/functions/setStatusMessage.js @@ -0,0 +1,45 @@ +import { Meteor } from 'meteor/meteor'; +import s from 'underscore.string'; + +import { Users } from '../../../models'; +import { Notifications } from '../../../notifications'; +import { hasPermission } from '../../../authorization'; +import { RateLimiter } from '../lib'; + +export const _setStatusMessage = function(userId, statusMessage) { + statusMessage = s.trim(statusMessage); + if (statusMessage.length > 120) { + statusMessage = statusMessage.substr(0, 120); + } + + if (!userId) { + return false; + } + + const user = Users.findOneById(userId); + + // User already has desired statusMessage, return + if (user.statusText === statusMessage) { + return user; + } + + // Set new statusMessage + Users.updateStatusText(user._id, statusMessage); + user.statusText = statusMessage; + + Notifications.notifyLogged('Users:StatusMessageChanged', { + _id: user._id, + name: user.name, + username: user.username, + statusText: user.statusText, + }); + + return true; +}; + +export const setStatusMessage = RateLimiter.limitFunction(_setStatusMessage, 1, 60000, { + 0() { + // Administrators have permission to change others status, so don't limit those + return !Meteor.userId() || !hasPermission(Meteor.userId(), 'edit-other-user-info'); + }, +}); diff --git a/app/lib/server/startup/settings.js b/app/lib/server/startup/settings.js index ae78b61fce32..9d83ead14338 100644 --- a/app/lib/server/startup/settings.js +++ b/app/lib/server/startup/settings.js @@ -44,6 +44,10 @@ settings.addGroup('Accounts', function() { type: 'boolean', public: true, }); + this.add('Accounts_AllowUserStatusMessageChange', true, { + type: 'boolean', + public: true, + }); this.add('Accounts_AllowUsernameChange', true, { type: 'boolean', public: true, diff --git a/app/models/client/index.js b/app/models/client/index.js index fbcbee481f5c..0c10bf534d30 100644 --- a/app/models/client/index.js +++ b/app/models/client/index.js @@ -21,6 +21,7 @@ import { UserRoles } from './models/UserRoles'; import { AuthzCachedCollection, ChatPermissions } from './models/ChatPermissions'; import { WebdavAccounts } from './models/WebdavAccounts'; import CustomSounds from './models/CustomSounds'; +import CustomUserStatus from './models/CustomUserStatus'; import EmojiCustom from './models/EmojiCustom'; const Users = _.extend({}, users, Meteor.users); @@ -51,6 +52,7 @@ export { ChatSubscription, Rooms, CustomSounds, + CustomUserStatus, EmojiCustom, WebdavAccounts, }; diff --git a/app/models/client/models/CustomUserStatus.js b/app/models/client/models/CustomUserStatus.js new file mode 100644 index 000000000000..2cdfa29e3627 --- /dev/null +++ b/app/models/client/models/CustomUserStatus.js @@ -0,0 +1,10 @@ +import { Base } from './_Base'; + +class CustomUserStatus extends Base { + constructor() { + super(); + this._initModel('custom_user_status'); + } +} + +export default new CustomUserStatus(); diff --git a/app/models/server/index.js b/app/models/server/index.js index 74b882789e00..9aa14dea719e 100644 --- a/app/models/server/index.js +++ b/app/models/server/index.js @@ -15,6 +15,7 @@ import Statistics from './models/Statistics'; import Permissions from './models/Permissions'; import Roles from './models/Roles'; import CustomSounds from './models/CustomSounds'; +import CustomUserStatus from './models/CustomUserStatus'; import Integrations from './models/Integrations'; import IntegrationHistory from './models/IntegrationHistory'; import CredentialTokens from './models/CredentialTokens'; @@ -58,6 +59,7 @@ export { Permissions, Roles, CustomSounds, + CustomUserStatus, Integrations, IntegrationHistory, CredentialTokens, diff --git a/app/models/server/models/CustomSounds.js b/app/models/server/models/CustomSounds.js index 40b25d5dc80a..b9971b954229 100644 --- a/app/models/server/models/CustomSounds.js +++ b/app/models/server/models/CustomSounds.js @@ -8,7 +8,7 @@ class CustomSounds extends Base { } // find one - findOneByID(_id, options) { + findOneById(_id, options) { return this.findOne(_id, options); } @@ -21,7 +21,7 @@ class CustomSounds extends Base { return this.find(query, options); } - findByNameExceptID(name, except, options) { + findByNameExceptId(name, except, options) { const query = { _id: { $nin: [except] }, name, @@ -48,7 +48,7 @@ class CustomSounds extends Base { // REMOVE - removeByID(_id) { + removeById(_id) { return this.remove(_id); } } diff --git a/app/models/server/models/CustomUserStatus.js b/app/models/server/models/CustomUserStatus.js new file mode 100644 index 000000000000..9e0818dc6354 --- /dev/null +++ b/app/models/server/models/CustomUserStatus.js @@ -0,0 +1,66 @@ +import { Base } from './_Base'; + +class CustomUserStatus extends Base { + constructor() { + super('custom_user_status'); + + this.tryEnsureIndex({ name: 1 }); + } + + // find one + findOneById(_id, options) { + return this.findOne(_id, options); + } + + // find + findByName(name, options) { + const query = { + name, + }; + + return this.find(query, options); + } + + findByNameExceptId(name, except, options) { + const query = { + _id: { $nin: [except] }, + name, + }; + + return this.find(query, options); + } + + // update + setName(_id, name) { + const update = { + $set: { + name, + }, + }; + + return this.update({ _id }, update); + } + + setStatusType(_id, statusType) { + const update = { + $set: { + statusType, + }, + }; + + return this.update({ _id }, update); + } + + // INSERT + create(data) { + return this.insert(data); + } + + + // REMOVE + removeById(_id) { + return this.remove(_id); + } +} + +export default new CustomUserStatus(); diff --git a/app/models/server/models/EmojiCustom.js b/app/models/server/models/EmojiCustom.js index 8f9f676072f5..d0cd7d7bc4cb 100644 --- a/app/models/server/models/EmojiCustom.js +++ b/app/models/server/models/EmojiCustom.js @@ -10,7 +10,7 @@ class EmojiCustom extends Base { } // find one - findOneByID(_id, options) { + findOneById(_id, options) { return this.findOne(_id, options); } @@ -83,7 +83,7 @@ class EmojiCustom extends Base { // REMOVE - removeByID(_id) { + removeById(_id) { return this.remove(_id); } } diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index 0daea240a2ab..734370cb2da5 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -15,6 +15,7 @@ export class Users extends Base { this.tryEnsureIndex({ name: 1 }); this.tryEnsureIndex({ lastLogin: 1 }); this.tryEnsureIndex({ status: 1 }); + this.tryEnsureIndex({ statusText: 1 }); this.tryEnsureIndex({ active: 1 }, { sparse: 1 }); this.tryEnsureIndex({ statusConnection: 1 }, { sparse: 1 }); this.tryEnsureIndex({ type: 1 }); @@ -682,6 +683,16 @@ export class Users extends Base { return this.update(query, update); } + updateStatusText(_id, statusText) { + const update = { + $set: { + statusText, + }, + }; + + return this.update(_id, update); + } + updateLastLoginById(_id) { const update = { $set: { diff --git a/app/slashcommands-join/server/server.js b/app/slashcommands-join/server/server.js index ef6bb96377cd..5d8cc8ac2e04 100644 --- a/app/slashcommands-join/server/server.js +++ b/app/slashcommands-join/server/server.js @@ -1,8 +1,3 @@ - -/* -* Join is a named function that will replace /join commands -* @param {Object} message - The message object -*/ import { Meteor } from 'meteor/meteor'; import { Match } from 'meteor/check'; import { Random } from 'meteor/random'; @@ -12,7 +7,7 @@ import { Rooms, Subscriptions } from '../../models'; import { Notifications } from '../../notifications'; import { slashCommands } from '../../utils'; -slashCommands.add('join', function Join(command, params, item) { +function Join(command, params, item) { if (command !== 'join' || !Match.test(params, String)) { return; } @@ -42,7 +37,9 @@ slashCommands.add('join', function Join(command, params, item) { }); } Meteor.call('joinRoom', room._id); -}, { +} + +slashCommands.add('join', Join, { description: 'Join_the_given_channel', params: '#channel', }); diff --git a/app/slashcommands-status/client/index.js b/app/slashcommands-status/client/index.js new file mode 100644 index 000000000000..11e5ad1b8640 --- /dev/null +++ b/app/slashcommands-status/client/index.js @@ -0,0 +1 @@ +import '../lib/status'; diff --git a/app/slashcommands-status/index.js b/app/slashcommands-status/index.js new file mode 100644 index 000000000000..a67eca871efb --- /dev/null +++ b/app/slashcommands-status/index.js @@ -0,0 +1,8 @@ +import { Meteor } from 'meteor/meteor'; + +if (Meteor.isClient) { + module.exports = require('./client/index.js'); +} +if (Meteor.isServer) { + module.exports = require('./server/index.js'); +} diff --git a/app/slashcommands-status/lib/status.js b/app/slashcommands-status/lib/status.js new file mode 100644 index 000000000000..8188ed41a598 --- /dev/null +++ b/app/slashcommands-status/lib/status.js @@ -0,0 +1,46 @@ +import { Meteor } from 'meteor/meteor'; +import { TAPi18n } from 'meteor/tap:i18n'; +import { Random } from 'meteor/random'; + +import { handleError, slashCommands } from '../../utils'; +import { hasPermission } from '../../authorization'; +import { Notifications } from '../../notifications'; + +function Status(command, params, item) { + if (command === 'status') { + if ((Meteor.isClient && hasPermission('edit-other-user-info')) || (Meteor.isServer && hasPermission(Meteor.userId(), 'edit-other-user-info'))) { + const user = Meteor.users.findOne(Meteor.userId()); + + Meteor.call('setUserStatus', null, params, (err) => { + if (err) { + if (Meteor.isClient) { + return handleError(err); + } + + if (err.error === 'error-not-allowed') { + Notifications.notifyUser(Meteor.userId(), 'message', { + _id: Random.id(), + rid: item.rid, + ts: new Date(), + msg: TAPi18n.__('StatusMessage_Change_Disabled', null, user.language), + }); + } + + throw err; + } else { + Notifications.notifyUser(Meteor.userId(), 'message', { + _id: Random.id(), + rid: item.rid, + ts: new Date(), + msg: TAPi18n.__('StatusMessage_Changed_Successfully', null, user.language), + }); + } + }); + } + } +} + +slashCommands.add('status', Status, { + description: 'Slash_Status_Description', + params: 'Slash_Status_Params', +}); diff --git a/app/slashcommands-status/server/index.js b/app/slashcommands-status/server/index.js new file mode 100644 index 000000000000..11e5ad1b8640 --- /dev/null +++ b/app/slashcommands-status/server/index.js @@ -0,0 +1 @@ +import '../lib/status'; diff --git a/app/slashcommands-topic/lib/topic.js b/app/slashcommands-topic/lib/topic.js index 1bc495e8f277..1e490046336a 100644 --- a/app/slashcommands-topic/lib/topic.js +++ b/app/slashcommands-topic/lib/topic.js @@ -4,10 +4,6 @@ import { handleError, slashCommands } from '../../utils'; import { ChatRoom } from '../../models'; import { callbacks } from '../../callbacks'; import { hasPermission } from '../../authorization'; -/* - * Join is a named function that will replace /topic commands - * @param {Object} message - The message object - */ function Topic(command, params, item) { if (command === 'topic') { diff --git a/app/theme/client/imports/components/header.css b/app/theme/client/imports/components/header.css index 21b33b1ea3c2..063647aca0bb 100644 --- a/app/theme/client/imports/components/header.css +++ b/app/theme/client/imports/components/header.css @@ -201,8 +201,13 @@ text-overflow: ellipsis; } - &-visual-status { - text-transform: capitalize; + &__visual-status { + overflow: hidden; + + width: 100%; + max-width: fit-content; + + text-overflow: ellipsis; } &__status { diff --git a/app/theme/client/imports/components/popover.css b/app/theme/client/imports/components/popover.css index ef22fd3a1efd..15c2b539286d 100644 --- a/app/theme/client/imports/components/popover.css +++ b/app/theme/client/imports/components/popover.css @@ -118,6 +118,30 @@ &--star-filled .rc-icon { fill: currentColor; } + + &--online { + & .rc-popover__icon { + color: var(--status-online); + } + } + + &--away { + & .rc-popover__icon { + color: var(--status-away); + } + } + + &--busy { + & .rc-popover__icon { + color: var(--status-busy); + } + } + + &--offline { + & .rc-popover__icon { + color: var(--status-invisible); + } + } } &__label { diff --git a/app/theme/client/imports/general/base_old.css b/app/theme/client/imports/general/base_old.css index 9b4218c41c2b..bb298b0566c5 100644 --- a/app/theme/client/imports/general/base_old.css +++ b/app/theme/client/imports/general/base_old.css @@ -3742,58 +3742,58 @@ rc-old select, } } - & .edit-form { - padding: 20px 20px 0; + & .room-info-content > div { + margin: 0 0 20px; + } +} - white-space: normal; +.rc-old .edit-form { + padding: 20px 20px 0; - & h3 { - margin-bottom: 8px; + white-space: normal; - font-size: 24px; - line-height: 22px; - } + & h3 { + margin-bottom: 8px; - & p { - font-size: 12px; - font-weight: 300; - line-height: 18px; - } + font-size: 24px; + line-height: 22px; + } - & > .input-line { - margin-top: 20px; + & p { + font-size: 12px; + font-weight: 300; + line-height: 18px; + } - & #password { - width: 70%; - } + & > .input-line { + margin-top: 20px; - & #roleSelect { - width: 70%; - } + & #password { + width: 70%; } - & nav { - padding: 0; - - &.buttons { - margin-top: 2em; - } + & #roleSelect { + width: 70%; } + } - & .form-divisor { - height: 9px; - margin: 2em 0; - - text-align: center; + & nav { + padding: 0; - & > span { - padding: 0 1em; - } + &.buttons { + margin-top: 2em; } } - & .room-info-content > div { - margin: 0 0 20px; + & .form-divisor { + height: 9px; + margin: 2em 0; + + text-align: center; + + & > span { + padding: 0 1em; + } } } @@ -5370,9 +5370,8 @@ rc-old select, position: absolute; right: 25px; - width: 80px; height: 30px; - padding-top: 4px; + padding: 4px; cursor: pointer; text-align: center; diff --git a/app/ui-account/client/accountProfile.html b/app/ui-account/client/accountProfile.html index c3e3e5a40a57..59435c30f2ff 100644 --- a/app/ui-account/client/accountProfile.html +++ b/app/ui-account/client/accountProfile.html @@ -70,6 +70,26 @@ {{/if}} +
{{_ "You_are_not_authorized_to_view_this_page"}}
+ {{else}} + +{{_ "You_are_not_authorized_to_view_this_page"}}
+ {{else}} +{\n \"en\": {\n \"Channels\": \"Rooms\"\n },\n \"pt\": {\n \"Channels\": \"Salas\"\n }\n}
",
+ "Custom_User_Status": "Custom User Status",
+ "Custom_User_Status_Add": "Add Custom User Status",
+ "Custom_User_Status_Added_Successfully" : "Custom User Status Added Successfully",
+ "Custom_User_Status_Delete_Warning": "Deleting a Custom User Status cannot be undone.",
+ "Custom_User_Status_Error_Invalid_User_Status": "Invalid User Status",
+ "Custom_User_Status_Error_Name_Already_In_Use": "The Custom User Status Name is already in use.",
+ "Custom_User_Status_Has_Been_Deleted": "Custom User Status Has Been Deleted",
+ "Custom_User_Status_Info": "Custom User Status Info",
+ "Custom_User_Status_Updated_Successfully": "Custom User Status Updated Successfully",
"Customize": "Customize",
"CustomSoundsFilesystem": "Custom Sounds Filesystem",
"Dashboard": "Dashboard",
@@ -1112,6 +1122,7 @@
"E2E_password_reveal_text": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.