diff --git a/app/api/server/lib/users.js b/app/api/server/lib/users.js
index df7c13bbaee6..378f922877f6 100644
--- a/app/api/server/lib/users.js
+++ b/app/api/server/lib/users.js
@@ -30,4 +30,7 @@ export async function findUsersToAutocomplete({ uid, selector }) {
};
}
-
+export async function findUser(_id) {
+ const cursor = await Users.findOne({ _id });
+ return cursor;
+}
diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js
index 5398a697c9e0..2d08e1982a38 100644
--- a/app/api/server/v1/users.js
+++ b/app/api/server/v1/users.js
@@ -19,7 +19,7 @@ import {
import { getFullUserDataByIdOrUsername } from '../../../lib/server/functions/getFullUserData';
import { API } from '../api';
import { setStatusText } from '../../../lib/server';
-import { findUsersToAutocomplete } from '../lib/users';
+import { findUsersToAutocomplete, findUser } from '../lib/users';
import { getUserForCheck, emailCheck } from '../../../2fa/server/code';
import { resetUserE2EEncriptionKey } from '../../../../server/lib/resetUserE2EKey';
import { setUserStatus } from '../../../../imports/users-presence/server/activeUsers';
@@ -29,9 +29,14 @@ API.v1.addRoute('users.create', { authRequired: true }, {
check(this.bodyParams, {
email: String,
name: String,
+ surname: String,
+ patronymic: String,
password: String,
username: String,
active: Match.Maybe(Boolean),
+ organization: Match.Maybe(String),
+ position: Match.Maybe(String),
+ phone: Match.Maybe(String),
bio: Match.Maybe(String),
nickname: Match.Maybe(String),
statusText: Match.Maybe(String),
@@ -464,8 +469,13 @@ API.v1.addRoute('users.update', { authRequired: true, twoFactorRequired: true },
data: Match.ObjectIncluding({
email: Match.Maybe(String),
name: Match.Maybe(String),
+ surname: Match.Maybe(String),
+ patronymic: Match.Maybe(String),
password: Match.Maybe(String),
username: Match.Maybe(String),
+ organization: Match.Maybe(String),
+ position: Match.Maybe(String),
+ phone: Match.Maybe(String),
bio: Match.Maybe(String),
nickname: Match.Maybe(String),
statusText: Match.Maybe(String),
@@ -869,3 +879,10 @@ API.v1.addRoute('users.resetE2EKey', { authRequired: true, twoFactorRequired: tr
return API.v1.success();
},
});
+
+API.v1.addRoute('users.getOne', { authRequired: true }, {
+ get() {
+ const { query } = this.parseJsonQuery();
+ return API.v1.success(Promise.await(findUser(query._id)));
+ },
+});
diff --git a/app/lib/server/functions/getFullUserData.js b/app/lib/server/functions/getFullUserData.js
index 7eb7b2e96431..e6aa71cff8c6 100644
--- a/app/lib/server/functions/getFullUserData.js
+++ b/app/lib/server/functions/getFullUserData.js
@@ -8,7 +8,9 @@ import { hasPermission } from '../../../authorization';
const logger = new Logger('getFullUserData');
const defaultFields = {
+ surname: 1,
name: 1,
+ patronymic: 1,
username: 1,
nickname: 1,
status: 1,
@@ -23,6 +25,8 @@ const defaultFields = {
const fullFields = {
emails: 1,
+ organization: 1,
+ position: 1,
phone: 1,
statusConnection: 1,
bio: 1,
diff --git a/app/lib/server/functions/saveUserIdentity.js b/app/lib/server/functions/saveUserIdentity.js
index 601eb124abf9..743313105e2f 100644
--- a/app/lib/server/functions/saveUserIdentity.js
+++ b/app/lib/server/functions/saveUserIdentity.js
@@ -1,5 +1,7 @@
import { setUsername } from './setUsername';
import { setRealName } from './setRealName';
+import { setSurname } from './setSurname';
+import { setPatronymic } from './setPatronymic';
import { Messages, Rooms, Subscriptions, LivechatDepartmentAgents, Users } from '../../../models/server';
import { FileUpload } from '../../../file-upload/server';
import { updateGroupDMsName } from './updateGroupDMsName';
@@ -9,20 +11,26 @@ import { updateGroupDMsName } from './updateGroupDMsName';
* @param {string} userId user performing the action
* @param {object} changes changes to the user
*/
-export function saveUserIdentity(userId, { _id, name: rawName, username: rawUsername }) {
+export function saveUserIdentity(userId, { _id, name: rawName, username: rawUsername, surname: rawSurname, patronymic: rawPatronymc }) {
if (!_id) {
return false;
}
const name = String(rawName).trim();
const username = String(rawUsername).trim();
+ const surname = String(rawSurname).trim();
+ const patronymic = String(rawPatronymc).trim();
const user = Users.findOneById(_id);
const previousUsername = user.username;
const previousName = user.name;
+ const previousSurname = user.surname;
+ const previousPatronymic = user.patronymic;
const nameChanged = previousName !== name;
const usernameChanged = previousUsername !== username;
+ const surnameChanged = previousSurname !== surname;
+ const patronymicChanged = previousPatronymic !== patronymic;
if (typeof rawUsername !== 'undefined' && usernameChanged) {
if (!setUsername(_id, username, user)) {
@@ -37,6 +45,18 @@ export function saveUserIdentity(userId, { _id, name: rawName, username: rawUser
}
}
+ if (typeof rawSurname !== 'undefined' && surnameChanged) {
+ if (!setSurname(_id, surname, user)) {
+ return false;
+ }
+ }
+
+ if (typeof rawPatronymc !== 'undefined' && patronymicChanged) {
+ if (!setPatronymic(_id, patronymic, user)) {
+ return false;
+ }
+ }
+
// if coming from old username, update all references
if (previousUsername && usernameChanged) {
if (typeof rawUsername !== 'undefined') {
diff --git a/app/lib/server/functions/setPatronymic.js b/app/lib/server/functions/setPatronymic.js
new file mode 100644
index 000000000000..f1aa21d32007
--- /dev/null
+++ b/app/lib/server/functions/setPatronymic.js
@@ -0,0 +1,35 @@
+import { Meteor } from 'meteor/meteor';
+import s from 'underscore.string';
+
+import { Users } from '../../../models/server';
+import { settings } from '../../../settings';
+import { hasPermission } from '../../../authorization';
+import { RateLimiter } from '../lib';
+
+export const _setPatronymic = function(userId, patronymic, fullUser) {
+ patronymic = s.trim(patronymic);
+
+ if (!userId || (settings.get('Accounts_RequireNameForSignUp') && !patronymic)) {
+ return false;
+ }
+
+ const user = fullUser || Users.findOneById(userId);
+
+ // User already has desired name, return
+ if (s.trim(user.patronymic) === patronymic) {
+ return user;
+ }
+
+ if (patronymic) {
+ Users.setPatronymic(user._id, patronymic);
+ } else {
+ Users.unsetPatronymic(user._id);
+ }
+ user.patronymic = patronymic;
+
+ return user;
+};
+
+export const setPatronymic = RateLimiter.limitFunction(_setPatronymic, 1, 60000, {
+ 0() { return !Meteor.userId() || !hasPermission(Meteor.userId(), 'edit-other-user-info'); }, // Administrators have permission to change others names, so don't limit those
+});
diff --git a/app/lib/server/functions/setSurname.js b/app/lib/server/functions/setSurname.js
new file mode 100644
index 000000000000..e10ef8bae2ba
--- /dev/null
+++ b/app/lib/server/functions/setSurname.js
@@ -0,0 +1,35 @@
+import { Meteor } from 'meteor/meteor';
+import s from 'underscore.string';
+
+import { Users } from '../../../models/server';
+import { settings } from '../../../settings';
+import { hasPermission } from '../../../authorization';
+import { RateLimiter } from '../lib';
+
+export const _setSurname = function(userId, surname, fullUser) {
+ surname = s.trim(surname);
+
+ if (!userId || (settings.get('Accounts_RequireNameForSignUp') && !surname)) {
+ return false;
+ }
+
+ const user = fullUser || Users.findOneById(userId);
+
+ // User already has desired name, return
+ if (s.trim(user.surname) === surname) {
+ return user;
+ }
+
+ if (surname) {
+ Users.setSurname(user._id, surname);
+ } else {
+ Users.unsetSurname(user._id);
+ }
+ user.surname = surname;
+
+ return user;
+};
+
+export const setSurname = RateLimiter.limitFunction(_setSurname, 1, 60000, {
+ 0() { return !Meteor.userId() || !hasPermission(Meteor.userId(), 'edit-other-user-info'); }, // Administrators have permission to change others names, so don't limit those
+});
diff --git a/app/lib/server/startup/settings.js b/app/lib/server/startup/settings.js
index 1e545fff282a..dcf630b47c20 100644
--- a/app/lib/server/startup/settings.js
+++ b/app/lib/server/startup/settings.js
@@ -45,6 +45,14 @@ settings.addGroup('Accounts', function() {
type: 'boolean',
public: true,
});
+ this.add('Accounts_AllowOrganizationChange', true, {
+ type: 'boolean',
+ public: true,
+ });
+ this.add('Accounts_AllowPositionChange', true, {
+ type: 'boolean',
+ public: true,
+ });
this.add('Accounts_AllowUserStatusMessageChange', true, {
type: 'boolean',
public: true,
diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js
index 3507585943b8..ff0e43d60700 100644
--- a/app/models/server/models/Users.js
+++ b/app/models/server/models/Users.js
@@ -36,6 +36,11 @@ export class Users extends Base {
this.tryEnsureIndex({ roles: 1 }, { sparse: 1 });
this.tryEnsureIndex({ name: 1 });
+ this.tryEnsureIndex({ surname: 1 });
+ this.tryEnsureIndex({ patronymic: 1 });
+ this.tryEnsureIndex({ organization: 1 });
+ this.tryEnsureIndex({ position: 1 });
+ this.tryEnsureIndex({ phone: 1 });
this.tryEnsureIndex({ bio: 1 }, { sparse: 1 });
this.tryEnsureIndex({ nickname: 1 }, { sparse: 1 });
this.tryEnsureIndex({ createdAt: 1 });
@@ -1048,6 +1053,34 @@ export class Users extends Base {
return this.update(query, update);
}
+ setSurname(_id, surname) {
+ const update = {
+ $set: {
+ surname,
+ },
+ };
+ return this.update(_id, update);
+ }
+
+ unsetSurname(_id) {
+ const update = {
+ $unset: {
+ surname: 1,
+ },
+ };
+ return this.update(_id, update);
+ }
+
+ addSurname(_id, surname) {
+ const update = {
+ $set: {
+ surname,
+ },
+ };
+
+ return this.update(_id, update);
+ }
+
setName(_id, name) {
const update = {
$set: {
@@ -1068,6 +1101,34 @@ export class Users extends Base {
return this.update(_id, update);
}
+ setPatronymic(_id, patronymic) {
+ const update = {
+ $set: {
+ patronymic,
+ },
+ };
+ return this.update(_id, update);
+ }
+
+ unsetPatronymic(_id) {
+ const update = {
+ $unset: {
+ patronymic: 1,
+ },
+ };
+ return this.update(_id, update);
+ }
+
+ addPatronymic(_id, patronymic) {
+ const update = {
+ $set: {
+ patronymic,
+ },
+ };
+
+ return this.update(_id, update);
+ }
+
setCustomFields(_id, fields) {
const values = {};
Object.keys(fields).forEach((key) => {
@@ -1196,6 +1257,81 @@ export class Users extends Base {
return this.update(_id, update);
}
+ setOrganization(_id, organization) {
+ const update = {
+ ...organization.trim() ? {
+ $set: {
+ organization,
+ },
+ } : {
+ $unset: {
+ organization: 1,
+ },
+ },
+ };
+ return this.update(_id, update);
+ }
+
+ addOrganization(_id, organization) {
+ const update = {
+ $set: {
+ organization,
+ },
+ };
+
+ return this.update(_id, update);
+ }
+
+ setPosition(_id, position) {
+ const update = {
+ ...position.trim() ? {
+ $set: {
+ position,
+ },
+ } : {
+ $unset: {
+ position: 1,
+ },
+ },
+ };
+ return this.update(_id, update);
+ }
+
+ addPosition(_id, position) {
+ const update = {
+ $set: {
+ position,
+ },
+ };
+
+ return this.update(_id, update);
+ }
+
+ setPhone(_id, phone) {
+ const update = {
+ ...phone.trim() ? {
+ $set: {
+ phone,
+ },
+ } : {
+ $unset: {
+ phone: 1,
+ },
+ },
+ };
+ return this.update(_id, update);
+ }
+
+ addPhone(_id, phone) {
+ const update = {
+ $set: {
+ phone,
+ },
+ };
+
+ return this.update(_id, update);
+ }
+
setBio(_id, bio = '') {
const update = {
...bio.trim() ? {
diff --git a/app/ui-login/client/login/form.html b/app/ui-login/client/login/form.html
index 3a20bd66344f..f1cd65dbab38 100644
--- a/app/ui-login/client/login/form.html
+++ b/app/ui-login/client/login/form.html
@@ -44,13 +44,81 @@
{{{_ "Registration_Succeeded"}}}
{{/if}}
{{/if}}
- {{#if state 'register'}}
+ {{#if state 'registerStepOne'}}
+
+
+
+
+
+ {{/if}}
+ {{#if state 'register'}}
+
diff --git a/app/ui-login/client/login/form.js b/app/ui-login/client/login/form.js
index 198d6c49d355..025cd1824e24 100644
--- a/app/ui-login/client/login/form.js
+++ b/app/ui-login/client/login/form.js
@@ -12,17 +12,74 @@ import { settings } from '../../../settings';
import { callbacks } from '../../../callbacks';
import { t, handleError } from '../../../utils';
+const getLoginExample = (surname, name, patronymic) => {
+ if (!surname || !name) {
+ return '';
+ }
+
+ const translit = (str) => {
+ const L = {
+ 'А':'A','а':'a','Б':'B','б':'b','В':'V','в':'v','Г':'G','г':'g',
+ 'Д':'D','д':'d','Е':'E','е':'e','Ё':'Yo','ё':'yo','Ж':'Zh','ж':'zh',
+ 'З':'Z','з':'z','И':'I','и':'i','Й':'Y','й':'y','К':'K','к':'k',
+ 'Л':'L','л':'l','М':'M','м':'m','Н':'N','н':'n','О':'O','о':'o',
+ 'П':'P','п':'p','Р':'R','р':'r','С':'S','с':'s','Т':'T','т':'t',
+ 'У':'U','у':'u','Ф':'F','ф':'f','Х':'Kh','х':'kh','Ц':'Ts','ц':'ts',
+ 'Ч':'Ch','ч':'ch','Ш':'Sh','ш':'sh','Щ':'Sch','щ':'sch','Ъ':'"','ъ':'"',
+ 'Ы':'Y','ы':'y','Ь':"'",'ь':"'",'Э':'E','э':'e','Ю':'Yu','ю':'yu',
+ 'Я':'Ya','я':'ya'
+ };
+ let reg = '';
+ for (const kr in L) {
+ reg += kr;
+ }
+ reg = new RegExp('[' + reg + ']', 'g');
+ const translate = function(a) {
+ return a in L ? L[a] : a;
+ };
+ return str.replace(reg, translate).toLowerCase();
+ };
+
+ const capitalizeFirstLetter = (translitString) => {
+ return translitString.charAt(0).toUpperCase() + translitString.slice(1);
+ };
+
+ const patron = patronymic ? patronymic[0] : '';
+ return capitalizeFirstLetter(translit(surname + name[0] + patron));
+};
+
Template.loginForm.helpers({
userName() {
const user = Meteor.user();
return user && user.username;
},
+ surname() {
+ return Template.instance().registerStepOneForm.get().surname ?? '';
+ },
+ name() {
+ return Template.instance().registerStepOneForm.get().name ?? '';
+ },
+ patronymic() {
+ return Template.instance().registerStepOneForm.get().patronymic ?? '';
+ },
+ organization() {
+ return Template.instance().registerStepOneForm.get().organization ?? '';
+ },
+ position() {
+ return Template.instance().registerStepOneForm.get().position ?? '';
+ },
+ phone() {
+ return Template.instance().registerStepOneForm.get().phone ?? '';
+ },
namePlaceholder() {
if (settings.get('Accounts_RequireNameForSignUp')) {
- return t('Name');
+ return t('Name_login');
}
return t('Name_optional');
},
+ loginPlaceholder() {
+ return t('Name_login') + ', ' + t('For_example') + ': ' + Template.instance().loginExample.get();
+ },
showFormLogin() {
return settings.get('Accounts_ShowFormLogin');
},
@@ -42,6 +99,8 @@ Template.loginForm.helpers({
return t('Send_confirmation_email');
case 'forgot-password':
return t('Reset_password');
+ case 'registerStepOne':
+ return t('Further');
}
},
loginTerms() {
@@ -107,7 +166,14 @@ Template.loginForm.events({
});
return;
}
+ if (state === 'registerStepOne') {
+ instance.loginExample.set(getLoginExample(formData.surname, formData.surname, formData.patronymic));
+ instance.registerStepOneForm.set(formData);
+ instance.loading.set(false);
+ return instance.state.set('register');
+ }
if (state === 'register') {
+ Object.assign(formData, instance.registerStepOneForm.get());
formData.secretURL = FlowRouter.getParam('hash');
return Meteor.call('registerUser', formData, function(error) {
instance.loading.set(false);
@@ -161,8 +227,19 @@ Template.loginForm.events({
});
}
},
+ 'click .registerStepBack'() {
+ Template.instance().state.set('registerStepOne');
+ Template.instance().loading.set(false);
+ return callbacks.run('loginPageStateChange', Template.instance().state.get());
+ },
+ 'keyup [name=phone]'(event) {
+ const text = $(event.target).val().replace(/[^\d]/g, '');
+ $(event.target).val(text);
+ $('#login-card input[name=phone], #login-card select[name=phone]').addClass('error');
+ $('#login-card input[name=phone]~.input-error, #login-card select[name=phone]~.input-error').text(t('Incorrect_phone_number_input'));
+ },
'click .register'() {
- Template.instance().state.set('register');
+ Template.instance().state.set('registerStepOne');
return callbacks.run('loginPageStateChange', Template.instance().state.get());
},
'click .back-to-login'() {
@@ -179,6 +256,8 @@ Template.loginForm.onCreated(function() {
const instance = this;
this.customFields = new ReactiveVar();
this.loading = new ReactiveVar(false);
+ this.loginExample = new ReactiveVar('');
+ this.registerStepOneForm = new ReactiveVar({});
Tracker.autorun(() => {
const Accounts_CustomFields = settings.get('Accounts_CustomFields');
if (typeof Accounts_CustomFields === 'string' && Accounts_CustomFields.trim() !== '') {
@@ -241,7 +320,7 @@ Template.loginForm.onCreated(function() {
formObj[field.name] = field.value;
});
const state = instance.state.get();
- if (state !== 'login') {
+ if (state !== 'login' && state !== 'registerStepOne') {
if (!(formObj.email && /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+\b/i.test(formObj.email))) {
validationObj.email = t('Invalid_email');
}
@@ -251,15 +330,29 @@ Template.loginForm.onCreated(function() {
validationObj.emailOrUsername = t('Invalid_email');
}
}
- if (state !== 'forgot-password' && state !== 'email-verification') {
+ if (state !== 'forgot-password' && state !== 'email-verification' && state !== 'registerStepOne') {
if (!formObj.pass) {
validationObj.pass = t('Invalid_pass');
}
}
- if (state === 'register') {
- if (settings.get('Accounts_RequireNameForSignUp') && !formObj.name) {
+ if (state === 'registerStepOne') {
+ if (!formObj.name) {
validationObj.name = t('Invalid_name');
}
+ if (!formObj.surname) {
+ validationObj.surname = t('Invalid_surname');
+ }
+ if (!formObj.organization) {
+ validationObj.organization = t('Invalid_organization');
+ }
+ if (!formObj.position) {
+ validationObj.position = t('Invalid_position');
+ }
+ }
+ if (state === 'register') {
+ if (settings.get('Accounts_RequireNameForSignUp') && !formObj.login) {
+ validationObj.login = t('Invalid_login');
+ }
if (settings.get('Accounts_RequirePasswordConfirmation') && formObj['confirm-pass'] !== formObj.pass) {
validationObj['confirm-pass'] = t('Invalid_confirm_pass');
}
@@ -282,6 +375,9 @@ Template.loginForm.onCreated(function() {
instance.loading.set(false);
return false;
}
+ // if (state === 'registerStepOne') {
+ // this.registerStepOneForm = formObj;
+ // }
return formObj;
};
if (FlowRouter.getParam('hash')) {
diff --git a/client/account/AccountProfileForm.js b/client/account/AccountProfileForm.js
index 368889d16ba1..12834031f521 100644
--- a/client/account/AccountProfileForm.js
+++ b/client/account/AccountProfileForm.js
@@ -13,7 +13,7 @@ import UserStatusMenu from '../components/basic/userStatus/UserStatusMenu';
const STATUS_TEXT_MAX_LENGTH = 120;
-export default function AccountProfileForm({ values, handlers, user, settings, onSaveStateChange, ...props }) {
+export default function AccountProfileForm({ values, handlers, user, data, settings, onSaveStateChange, ...props }) {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
@@ -26,6 +26,9 @@ export default function AccountProfileForm({ values, handlers, user, settings, o
const {
allowRealNameChange,
+ // allowOrganizationChange,
+ // allowPositionChange,
+ // allowPhoneChange,
allowUserStatusMessageChange,
allowEmailChange,
allowPasswordChange,
@@ -48,8 +51,21 @@ export default function AccountProfileForm({ values, handlers, user, settings, o
nickname,
} = values;
+ let phone = data.phone;
const {
+ surname,
+ patronymic,
+ organization,
+ position,
+ } = data;
+
+ const {
+ handleSurname,
handleRealname,
+ handlePatronymic,
+ handleOrganization,
+ handlePosition,
+ handlePhone,
handleEmail,
handleUsername,
handlePassword,
@@ -111,6 +127,24 @@ export default function AccountProfileForm({ values, handlers, user, settings, o
const statusTextError = useMemo(() => (!statusText || statusText.length <= STATUS_TEXT_MAX_LENGTH || statusText.length === 0 ? undefined : t('Max_length_is', STATUS_TEXT_MAX_LENGTH)), [statusText, t]);
const { emails: [{ verified = false }] } = user;
+ const phoneNumberValidation = (text) => {
+ // console.log(text);
+ // console.log(text.currentTarget);
+ // console.log(text.replace(/[^\d]/g, ''));
+ // this.setState({ phone: text.replace(/[^\d]/g, '') });
+ //return text.replace(/[^\d]/g, '');
+ };
+
+ const isNumberKey = (event) => {
+ const charCode = event.which ? event.which : event.keyCode;
+ // console.log(charCode);
+ if (charCode > 31 && (charCode < 48 || charCode > 57)) {
+ return false;
+ }
+ // console.log(phone);
+ // console.log(charCode.toString());
+ return true;
+ };
const canSave = !![
!!passwordError,
@@ -132,32 +166,81 @@ export default function AccountProfileForm({ values, handlers, user, settings, o
{useMemo(() =>
, [username, handleAvatar, allowUserAvatarChange, avatarSuggestions, user.avatarETag])}
-
- {useMemo(() =>
- {t('Name')}
-
-
-
- {!allowRealNameChange &&
- {t('RealName_Change_Disabled')}
- }
-
- {nameError}
-
- , [t, realname, handleRealname, allowRealNameChange, nameError])}
- {useMemo(() =>
- {t('Username')}
-
- }/>
-
- {!canChangeUsername &&
- {t('Username_Change_Disabled')}
- }
-
- {usernameError}
-
- , [t, username, handleUsername, canChangeUsername, usernameError])}
-
+ {useMemo(() =>
+ {t('Username')}
+
+ }/>
+
+ {!canChangeUsername &&
+ {t('Username_Change_Disabled')}
+ }
+
+ {usernameError}
+
+ , [t, username, handleUsername, canChangeUsername, usernameError])}
+ {useMemo(() =>
+ {t('Surname')}
+
+ }/>
+
+ {!allowRealNameChange &&
+ {t('RealName_Change_Disabled')}
+ }
+
+ {nameError}
+
+ , [t, surname, handleSurname, allowRealNameChange, nameError])}
+ {useMemo(() =>
+ {t('Name')}
+
+ isNumberKey(event)} flexGrow={1} value={realname} onChange={handleRealname} addon={}/>
+
+ {!allowRealNameChange &&
+ {t('RealName_Change_Disabled')}
+ }
+
+ {nameError}
+
+ , [t, realname, handleRealname, allowRealNameChange, nameError])}
+ {useMemo(() =>
+ {t('Patronymic')}
+
+ }/>
+
+ {!allowRealNameChange &&
+ {t('RealName_Change_Disabled')}
+ }
+
+ {nameError}
+
+ , [t, patronymic, handlePatronymic, allowRealNameChange, nameError])}
+ {useMemo(() =>
+ {t('Organization')}
+
+ }/>
+
+
+ {nameError}
+
+ , [t, organization, handleOrganization, nameError])}
+ {useMemo(() =>
+ {t('Position')}
+
+ }/>
+
+
+ {nameError}
+
+ , [t, position, handlePosition, nameError])}
+ {useMemo(() =>
+ {t('Phone_number')}
+
+ { return isNumberKey(event)}} onChange={handlePhone} addon={}/>
+
+
+ {nameError}
+
+ , [t, phone, handlePhone, nameError])}
{useMemo(() =>
{t('StatusMessage')}
diff --git a/client/account/AccountProfilePage.js b/client/account/AccountProfilePage.js
index 86989d48051f..40d45cd03780 100644
--- a/client/account/AccountProfilePage.js
+++ b/client/account/AccountProfilePage.js
@@ -14,6 +14,7 @@ import { useMethod } from '../contexts/ServerContext';
import { useSetModal } from '../contexts/ModalContext';
import { useUpdateAvatar } from '../hooks/useUpdateAvatar';
import { getUserEmailAddress } from '../helpers/getUserEmailAddress';
+import { useEndpointData } from '../hooks/useEndpointData';
const ActionConfirmModal = ({ onSave, onCancel, title, text, isPassword, ...props }) => {
const t = useTranslation();
@@ -43,7 +44,12 @@ const ActionConfirmModal = ({ onSave, onCancel, title, text, isPassword, ...prop
};
const getInitialValues = (user) => ({
+ surname: user.surname ?? '',
realname: user.name ?? '',
+ patronymic: user.patronymic ?? '',
+ organization: user.organization ?? '',
+ position: user.position ?? '',
+ phone: user.phone ?? '',
email: getUserEmailAddress(user) ?? '',
username: user.username ?? '',
password: '',
@@ -63,7 +69,12 @@ const AccountProfilePage = () => {
const user = useUser();
- const { values, handlers, hasUnsavedChanges, commit } = useForm(getInitialValues(user));
+ const userId = user._id;
+ const query = useMemo(() => ({
+ query: JSON.stringify({ _id: userId }),
+ }), [userId]);
+ const data = useEndpointData('users.getOne', query) || { result: [] };
+
const [canSave, setCanSave] = useState(true);
const setModal = useSetModal();
const [loggingOut, setLoggingOut] = useState(false);
@@ -74,11 +85,31 @@ const AccountProfilePage = () => {
const closeModal = useCallback(() => setModal(null), [setModal]);
+ const { values, handlers, hasUnsavedChanges, commit } = useForm(getInitialValues(user));
+ if (values.surname) {
+ data.surname = values.surname;
+ }
+ if (values.patronymic) {
+ data.patronymic = values.patronymic;
+ }
+ if (values.organization) {
+ data.organization = values.organization;
+ }
+ if (values.position) {
+ data.position = values.position;
+ }
+ if (values.phone) {
+ data.phone = values.phone;
+ }
+
const localPassword = Boolean(user?.services?.password?.bcrypt?.trim());
const requirePasswordConfirmation = (values.email !== getUserEmailAddress(user) || !!values.password) && localPassword;
const erasureType = useSetting('Message_ErasureType');
const allowRealNameChange = useSetting('Accounts_AllowRealNameChange');
+ // const allowOrganizationChange = useSetting('Accounts_AllowOrganizationChange');
+ // const allowPositionChange = useSetting('Accounts_AllowPositionChange');
+ //const allowPhoneChange = useSetting('Accounts_AllowPhoneChange');
const allowUserStatusMessageChange = useSetting('Accounts_AllowUserStatusMessageChange');
const allowUsernameChange = useSetting('Accounts_AllowUsernameChange');
const allowEmailChange = useSetting('Accounts_AllowEmailChange');
@@ -95,6 +126,9 @@ const AccountProfilePage = () => {
const settings = useMemo(() => ({
allowRealNameChange,
+ // allowOrganizationChange,
+ // allowPositionChange,
+ //allowPhoneChange,
allowUserStatusMessageChange,
allowEmailChange,
allowPasswordChange,
@@ -105,6 +139,9 @@ const AccountProfilePage = () => {
namesRegex,
}), [
allowDeleteOwnAccount,
+ // allowOrganizationChange,
+ // allowPositionChange,
+ //allowPhoneChange,
allowEmailChange,
allowPasswordChange,
allowRealNameChange,
@@ -116,7 +153,12 @@ const AccountProfilePage = () => {
]);
const {
+ surname,
realname,
+ patronymic,
+ organization,
+ position,
+ phone,
email,
avatar,
username,
@@ -138,12 +180,19 @@ const AccountProfilePage = () => {
const avatarResult = await updateAvatar();
if (avatarResult) { handleAvatar(''); }
await saveFn({
+ ...allowRealNameChange && { surname },
...allowRealNameChange && { realname },
+ ...allowRealNameChange && { patronymic },
+ // ...allowOrganizationChange && { organization },
+ // ...allowPositionChange && { position },
...allowEmailChange && getUserEmailAddress(user) !== email && { email },
...allowPasswordChange && { newPassword: password },
...canChangeUsername && { username },
...allowUserStatusMessageChange && { statusText },
...typedPassword && { typedPassword: SHA256(typedPassword) },
+ organization,
+ position,
+ phone,
statusType,
nickname,
bio: bio || '',
@@ -173,12 +222,20 @@ const AccountProfilePage = () => {
allowEmailChange,
allowPasswordChange,
allowRealNameChange,
+ // allowOrganizationChange,
+ // allowPositionChange,
+ //allowPhoneChange,
allowUserStatusMessageChange,
bio,
canChangeUsername,
email,
password,
+ surname,
realname,
+ patronymic,
+ organization,
+ position,
+ phone,
statusText,
username,
user,
@@ -259,7 +316,7 @@ const AccountProfilePage = () => {
-
+