forked from vergnet/site-accueil-insa
553 lines
16 KiB
Vue
553 lines
16 KiB
Vue
<!--
|
|
Matomo - free/libre analytics platform
|
|
@link https://matomo.org
|
|
@license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
-->
|
|
|
|
<template>
|
|
<ContentBlock
|
|
class="userEditForm"
|
|
:class="{ loading: isSavingUserInfo }"
|
|
:content-title="`${formTitle} ${!isAdd ? `${theUser.login}` : ''}`"
|
|
>
|
|
<div
|
|
class="row"
|
|
v-form=""
|
|
>
|
|
<div v-if="isAdd" class="col s12 m6 invite-notes">
|
|
<div class="form-help">
|
|
<span v-html="$sanitize(
|
|
translate('UsersManager_InviteSuccessNotification',
|
|
[inviteTokenExpiryDays]))">
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="col m2 entityList"
|
|
v-if="!isAdd"
|
|
>
|
|
<ul class="listCircle">
|
|
<li
|
|
:class="{active: activeTab === 'basic'}"
|
|
class="menuBasicInfo"
|
|
>
|
|
<a
|
|
href=""
|
|
@click.prevent="activeTab = 'basic'"
|
|
>{{ translate('UsersManager_BasicInformation') }}</a>
|
|
</li>
|
|
<li
|
|
:class="{active: activeTab === 'permissions'}"
|
|
class="menuPermissions"
|
|
>
|
|
<a
|
|
href=""
|
|
@click.prevent="activeTab = 'permissions'"
|
|
style="margin-right:3.5px"
|
|
>
|
|
{{ translate('UsersManager_Permissions') }}
|
|
</a>
|
|
<span
|
|
class="icon-warning"
|
|
v-if="!userHasAccess && !theUser.superuser_access"
|
|
/>
|
|
</li>
|
|
<li
|
|
:class="{active: activeTab === 'superuser'}"
|
|
class="menuSuperuser"
|
|
v-if="currentUserRole === 'superuser'"
|
|
>
|
|
<a
|
|
href=""
|
|
@click.prevent="activeTab = 'superuser'"
|
|
>{{ translate('UsersManager_SuperUserAccess') }}</a>
|
|
</li>
|
|
<li
|
|
:class="{active: activeTab === '2fa'}"
|
|
class="menuUserTwoFa"
|
|
v-if="currentUserRole === 'superuser' && theUser.uses_2fa && !isAdd"
|
|
>
|
|
<a
|
|
href=""
|
|
@click.prevent="activeTab = '2fa'"
|
|
>{{ translate('UsersManager_TwoFactorAuthentication') }}</a>
|
|
</li>
|
|
</ul>
|
|
<div class="save-button-spacer hide-on-small-only">
|
|
</div>
|
|
<div
|
|
class="entityCancel"
|
|
@click.prevent="onDoneEditing()"
|
|
>
|
|
<a
|
|
href=""
|
|
class="entityCancelLink"
|
|
>
|
|
<span class="icon-arrow-left-2"></span>
|
|
{{ translate('UsersManager_BackToUser') }}</a>
|
|
</div>
|
|
</div>
|
|
<div class="visibleTab col m10">
|
|
<div
|
|
v-if="activeTab === 'basic'"
|
|
class="basic-info-tab"
|
|
>
|
|
<div>
|
|
<Field
|
|
v-model="theUser.login"
|
|
:disabled="isSavingUserInfo || !isAdd || isShowingPasswordConfirm"
|
|
uicontrol="text"
|
|
name="user_login"
|
|
:maxlength="100"
|
|
:title="translate('General_Username')"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Field
|
|
v-if="!isPending"
|
|
:model-value="theUser.password"
|
|
:disabled="isSavingUserInfo || (currentUserRole !== 'superuser' && !isAdd)
|
|
|| isShowingPasswordConfirm"
|
|
@update:model-value="theUser.password = $event; isPasswordModified = true"
|
|
uicontrol="password"
|
|
name="user_password"
|
|
:title="translate('General_Password')"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Field
|
|
v-model="theUser.email"
|
|
:disabled="isSavingUserInfo || (currentUserRole !== 'superuser' && !isAdd)
|
|
|| isShowingPasswordConfirm"
|
|
v-if="currentUserRole === 'superuser' || isAdd"
|
|
uicontrol="text"
|
|
name="user_email"
|
|
:maxlength="100"
|
|
:title="translate('UsersManager_Email')"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Field
|
|
v-model="firstSiteAccess"
|
|
:disabled="isSavingUserInfo"
|
|
v-if="isAdd"
|
|
uicontrol="site"
|
|
name="user_site"
|
|
:ui-control-attributes="{ onlySitesWithAdminAccess: true }"
|
|
:title="translate('UsersManager_FirstWebsitePermission')"
|
|
:inline-help="translate('UsersManager_FirstSiteInlineHelp')"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<div class="form-group row" style="position: relative">
|
|
<div class="col s12 m6 save-button">
|
|
<SaveButton
|
|
v-if="currentUserRole === 'superuser' || isAdd"
|
|
:value="saveButtonLabel"
|
|
:disabled="isAdd && (!firstSiteAccess || !firstSiteAccess.id)"
|
|
:saving="isSavingUserInfo"
|
|
@confirm="saveUserInfo"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<p class="resend-notes" v-if="user && isPending"
|
|
>
|
|
{{ translate('UsersManager_InvitationSent') }}
|
|
<span class="resend-link" @click="resendRequestedUser"
|
|
v-html="$sanitize(translate('UsersManager_ResendInvite') +
|
|
'/'+ translate('UsersManager_CopyLink'))"></span>
|
|
</p>
|
|
<PasswordConfirmation
|
|
v-model="showPasswordConfirmationForInviteUser"
|
|
@confirmed="inviteUser"
|
|
>
|
|
<p>{{ translate('UsersManager_ConfirmWithPassword') }}</p>
|
|
</PasswordConfirmation>
|
|
</div>
|
|
<div
|
|
class="entityCancel"
|
|
v-if="isAdd"
|
|
>
|
|
<a
|
|
href=""
|
|
class="entityCancelLink"
|
|
@click.prevent="onDoneEditing()"
|
|
>
|
|
<span class="icon icon-arrow-left-2"></span>
|
|
{{ translate('UsersManager_BackToUser') }}</a>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="!isAdd"
|
|
v-show="activeTab === 'permissions'"
|
|
class="user-permissions"
|
|
>
|
|
<div
|
|
v-if="!theUser.superuser_access"
|
|
>
|
|
<UserPermissionsEdit
|
|
:user-login="theUser.login"
|
|
@user-has-access-detected="userHasAccess = $event.hasAccess"
|
|
@access-changed="isUserModified = true"
|
|
:access-levels="accessLevels"
|
|
:filter-access-levels="filterAccessLevels"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-if="theUser.superuser_access"
|
|
class="alert alert-info"
|
|
>
|
|
{{ translate('UsersManager_SuperUsersPermissionsNotice') }}
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="activeTab === 'superuser' && currentUserRole === 'superuser' && !isAdd"
|
|
class="superuser-access form-group"
|
|
>
|
|
<p>{{ translate('UsersManager_SuperUserIntro1') }}</p>
|
|
<p><strong>{{ translate('UsersManager_SuperUserIntro2') }}</strong></p>
|
|
<div>
|
|
<Field
|
|
v-model="superUserAccessChecked"
|
|
@click="confirmSuperUserChange()"
|
|
:disabled="isSavingUserInfo"
|
|
uicontrol="checkbox"
|
|
name="superuser_access"
|
|
:title="translate('UsersManager_HasSuperUserAccess')"
|
|
/>
|
|
</div>
|
|
<PasswordConfirmation
|
|
v-model="showPasswordConfirmationForSuperUser"
|
|
@confirmed="toggleSuperuserAccess"
|
|
@aborted="setSuperUserAccessChecked()"
|
|
>
|
|
<h2>{{ translate('UsersManager_AreYouSure') }}</h2>
|
|
<p v-if="theUser.superuser_access">
|
|
{{ translate('UsersManager_RemoveSuperuserAccessConfirm') }}
|
|
</p>
|
|
<p v-if="!theUser.superuser_access">
|
|
{{ translate('UsersManager_AddSuperuserAccessConfirm') }}
|
|
</p>
|
|
</PasswordConfirmation>
|
|
</div>
|
|
<div
|
|
v-show="activeTab === '2fa'"
|
|
v-if="currentUserRole === 'superuser' && !isAdd"
|
|
class="twofa-reset form-group"
|
|
>
|
|
<p>{{ translate('UsersManager_ResetTwoFactorAuthenticationInfo') }}</p>
|
|
<div
|
|
class="resetTwoFa"
|
|
>
|
|
<SaveButton
|
|
:saving="isResetting2FA"
|
|
@confirm="confirmReset2FA()"
|
|
:value="translate('UsersManager_ResetTwoFactorAuthentication')"
|
|
/>
|
|
</div>
|
|
<PasswordConfirmation
|
|
v-model="showPasswordConfirmationFor2FA"
|
|
@confirmed="reset2FA"
|
|
>
|
|
<h2>{{ translate('UsersManager_AreYouSure') }}</h2>
|
|
<p>{{ translate('UsersManager_ConfirmWithPassword') }}</p>
|
|
</PasswordConfirmation>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<PasswordConfirmation
|
|
v-model="isShowingPasswordConfirm"
|
|
@confirmed="updateUser"
|
|
>
|
|
<h2 v-html="$sanitize(changePasswordTitle)"></h2>
|
|
<p>{{ translate('UsersManager_ConfirmWithPassword') }}</p>
|
|
</PasswordConfirmation>
|
|
</ContentBlock>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, readonly } from 'vue';
|
|
import {
|
|
ContentBlock,
|
|
SiteRef,
|
|
translate,
|
|
AjaxHelper,
|
|
NotificationsStore,
|
|
} from 'CoreHome';
|
|
import {
|
|
PasswordConfirmation,
|
|
Form,
|
|
Field,
|
|
SaveButton,
|
|
} from 'CorePluginsAdmin';
|
|
import UserPermissionsEdit from '../UserPermissionsEdit/UserPermissionsEdit.vue';
|
|
import User from '../User';
|
|
|
|
const DEFAULT_USER: User = {
|
|
login: '',
|
|
superuser_access: false,
|
|
uses_2fa: false,
|
|
password: '',
|
|
email: '',
|
|
invite_status: '',
|
|
};
|
|
|
|
interface UserEditFormState {
|
|
theUser: User;
|
|
activeTab: string;
|
|
permissionsForIdSite: string | number;
|
|
isSavingUserInfo: boolean;
|
|
userHasAccess: boolean;
|
|
firstSiteAccess: SiteRef | null;
|
|
isUserModified: boolean;
|
|
isPasswordModified: boolean;
|
|
superUserAccessChecked: boolean | null;
|
|
showPasswordConfirmationForSuperUser: boolean;
|
|
showPasswordConfirmationFor2FA: boolean;
|
|
showPasswordConfirmationForInviteUser: boolean;
|
|
isResetting2FA: boolean;
|
|
isShowingPasswordConfirm: boolean;
|
|
}
|
|
|
|
export default defineComponent({
|
|
props: {
|
|
user: Object,
|
|
currentUserRole: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
accessLevels: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
filterAccessLevels: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
initialSiteId: {
|
|
type: [String, Number],
|
|
required: true,
|
|
},
|
|
initialSiteName: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
inviteTokenExpiryDays: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
},
|
|
components: {
|
|
ContentBlock,
|
|
Field,
|
|
SaveButton,
|
|
UserPermissionsEdit,
|
|
PasswordConfirmation,
|
|
},
|
|
directives: {
|
|
Form,
|
|
},
|
|
data(): UserEditFormState {
|
|
return {
|
|
theUser: (this.user as User) || { ...DEFAULT_USER },
|
|
activeTab: 'basic',
|
|
permissionsForIdSite: 1,
|
|
isSavingUserInfo: false,
|
|
userHasAccess: true,
|
|
firstSiteAccess: {
|
|
id: this.initialSiteId,
|
|
name: this.initialSiteName,
|
|
},
|
|
isUserModified: false,
|
|
isPasswordModified: false,
|
|
superUserAccessChecked: null,
|
|
showPasswordConfirmationForSuperUser: false,
|
|
showPasswordConfirmationFor2FA: false,
|
|
showPasswordConfirmationForInviteUser: false,
|
|
isResetting2FA: false,
|
|
isShowingPasswordConfirm: false,
|
|
};
|
|
},
|
|
emits: ['done', 'updated', 'resendInvite'],
|
|
watch: {
|
|
user(newVal) {
|
|
this.onUserChange(newVal);
|
|
},
|
|
},
|
|
created() {
|
|
this.onUserChange(this.user as User);
|
|
},
|
|
methods: {
|
|
onUserChange(newVal: User) {
|
|
this.theUser = newVal || { ...DEFAULT_USER };
|
|
|
|
if (!this.theUser.password) {
|
|
this.resetPasswordVar();
|
|
}
|
|
|
|
this.setSuperUserAccessChecked();
|
|
},
|
|
confirmSuperUserChange() {
|
|
this.showPasswordConfirmationForSuperUser = true;
|
|
},
|
|
confirmReset2FA() {
|
|
this.showPasswordConfirmationFor2FA = true;
|
|
},
|
|
toggleSuperuserAccess(password: string) {
|
|
this.isSavingUserInfo = true;
|
|
AjaxHelper.post(
|
|
{
|
|
method: 'UsersManager.setSuperUserAccess',
|
|
},
|
|
{
|
|
userLogin: this.theUser.login,
|
|
hasSuperUserAccess: this.theUser.superuser_access ? '0' : '1',
|
|
passwordConfirmation: password,
|
|
},
|
|
).then(() => {
|
|
this.theUser.superuser_access = !this.theUser.superuser_access;
|
|
}).catch(() => {
|
|
// ignore error (still displayed to user)
|
|
}).then(() => { // eslint-disable-line
|
|
this.isSavingUserInfo = false;
|
|
this.setSuperUserAccessChecked();
|
|
});
|
|
},
|
|
saveUserInfo() {
|
|
if (this.isAdd) {
|
|
this.showPasswordConfirmationForInviteUser = true;
|
|
} else {
|
|
this.isShowingPasswordConfirm = true;
|
|
}
|
|
},
|
|
resendRequestedUser() {
|
|
this.$emit('resendInvite', {
|
|
user: this.user,
|
|
});
|
|
},
|
|
inviteUser(password: string) {
|
|
this.isSavingUserInfo = true;
|
|
return AjaxHelper.post(
|
|
{
|
|
method: 'UsersManager.inviteUser',
|
|
},
|
|
{
|
|
userLogin: this.theUser.login,
|
|
email: this.theUser.email,
|
|
initialIdSite: this.firstSiteAccess ? this.firstSiteAccess.id : undefined,
|
|
passwordConfirmation: password,
|
|
},
|
|
).catch((e) => {
|
|
this.isSavingUserInfo = false;
|
|
throw e;
|
|
}).then(() => {
|
|
this.firstSiteAccess = null;
|
|
this.isSavingUserInfo = false;
|
|
this.isUserModified = true;
|
|
this.theUser.invite_status = 'pending';
|
|
|
|
this.resetPasswordVar();
|
|
this.showUserCreatedNotification();
|
|
this.$emit('updated', { user: readonly(this.theUser) });
|
|
});
|
|
},
|
|
resetPasswordVar() {
|
|
if (!this.isAdd) {
|
|
// make sure password is not stored in the client after update/save
|
|
this.theUser.password = 'XXXXXXXX';
|
|
}
|
|
},
|
|
showUserSavedNotification() {
|
|
NotificationsStore.show({
|
|
message: translate('General_YourChangesHaveBeenSaved'),
|
|
context: 'success',
|
|
type: 'toast',
|
|
});
|
|
},
|
|
showUserCreatedNotification() {
|
|
NotificationsStore.show({
|
|
message: translate('UsersManager_InviteSuccess'),
|
|
context: 'success',
|
|
type: 'toast',
|
|
});
|
|
},
|
|
reset2FA(password: string) {
|
|
this.isResetting2FA = true;
|
|
return AjaxHelper.post({
|
|
method: 'TwoFactorAuth.resetTwoFactorAuth',
|
|
userLogin: this.theUser.login,
|
|
passwordConfirmation: password,
|
|
}).catch((e) => {
|
|
this.isResetting2FA = false;
|
|
throw e;
|
|
}).then(() => {
|
|
this.isResetting2FA = false;
|
|
this.theUser.uses_2fa = false;
|
|
this.activeTab = 'basic';
|
|
|
|
this.showUserSavedNotification();
|
|
});
|
|
},
|
|
updateUser(password: string) {
|
|
this.isSavingUserInfo = true;
|
|
return AjaxHelper.post(
|
|
{
|
|
method: 'UsersManager.updateUser',
|
|
},
|
|
{
|
|
userLogin: this.theUser.login,
|
|
password: (this.isPasswordModified && this.theUser.password)
|
|
? this.theUser.password
|
|
: undefined,
|
|
passwordConfirmation: password,
|
|
email: this.theUser.email,
|
|
},
|
|
).then(() => {
|
|
this.isSavingUserInfo = false;
|
|
this.isUserModified = true;
|
|
this.isPasswordModified = false;
|
|
|
|
this.resetPasswordVar();
|
|
this.showUserSavedNotification();
|
|
this.$emit('updated', { user: readonly(this.theUser) });
|
|
}).catch(() => {
|
|
this.isSavingUserInfo = false;
|
|
});
|
|
},
|
|
setSuperUserAccessChecked() {
|
|
this.superUserAccessChecked = !!this.theUser.superuser_access;
|
|
},
|
|
onDoneEditing() {
|
|
this.$emit('done', { isUserModified: this.isUserModified });
|
|
},
|
|
},
|
|
computed: {
|
|
formTitle() {
|
|
return this.isAdd ? translate('UsersManager_AddNewUser') : '';
|
|
},
|
|
saveButtonLabel() {
|
|
return this.isAdd
|
|
? translate('UsersManager_InviteUser')
|
|
: translate('UsersManager_SaveBasicInfo');
|
|
},
|
|
isPending() {
|
|
if (!this.user) {
|
|
return true;
|
|
}
|
|
if (this.user.invite_status === 'pending' || Number.isInteger(this.user.invite_status)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
isAdd() {
|
|
return !this.user;
|
|
},
|
|
changePasswordTitle() {
|
|
return translate(
|
|
'UsersManager_AreYouSureChangeDetails',
|
|
`<strong>${this.theUser.login}</strong>`,
|
|
);
|
|
},
|
|
},
|
|
});
|
|
</script>
|