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'}} +
+ @@ -139,6 +207,9 @@

{{{_ "Registration_Succeeded"}}}

{{else}}
+ {{#if state 'register'}} + + {{/if}}
{{/if}}
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 = () => { - +