Move users management to multi line
Signed-off-by: Greta Doci <gretadoci@gmail.com>
This commit is contained in:
parent
c6e51924c8
commit
c864bc8321
|
@ -524,7 +524,6 @@ td, th {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
&.password,
|
&.password,
|
||||||
&.displayName,
|
|
||||||
&.mailAddress {
|
&.mailAddress {
|
||||||
min-width: 5em;
|
min-width: 5em;
|
||||||
max-width: 12em;
|
max-width: 12em;
|
||||||
|
@ -705,6 +704,7 @@ span.version {
|
||||||
#searchresults {
|
#searchresults {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#apps-list.store {
|
#apps-list.store {
|
||||||
.section {
|
.section {
|
||||||
|
@ -1351,8 +1351,8 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
|
|
||||||
/* USERS LIST -------------------------------------------------------------- */
|
/* USERS LIST -------------------------------------------------------------- */
|
||||||
#body-settings {
|
#body-settings {
|
||||||
$grid-row-height: 46px;
|
$grid-row-height: 60px;
|
||||||
$grid-col-min-width: 120px;
|
$grid-col-min-width: 150px;
|
||||||
#app-content.user-list-grid {
|
#app-content.user-list-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-columns: 1fr;
|
grid-auto-columns: 1fr;
|
||||||
|
@ -1376,7 +1376,6 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
|
|
||||||
/* grid col width */
|
/* grid col width */
|
||||||
.name,
|
.name,
|
||||||
.displayName,
|
|
||||||
.password,
|
.password,
|
||||||
.mailAddress,
|
.mailAddress,
|
||||||
.languages,
|
.languages,
|
||||||
|
@ -1384,12 +1383,17 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
.userBackend,
|
.userBackend,
|
||||||
.lastLogin {
|
.lastLogin {
|
||||||
min-width: $grid-col-min-width;
|
min-width: $grid-col-min-width;
|
||||||
|
display: flex;
|
||||||
|
color: var(--color-text-dark);
|
||||||
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
.groups,
|
.groups,
|
||||||
.subadmins,
|
.subadmins,
|
||||||
.quota {
|
.quota {
|
||||||
.multiselect {
|
.multiselect {
|
||||||
min-width: $grid-col-min-width;
|
min-width: $grid-col-min-width;
|
||||||
|
color: var(--color-text-dark);
|
||||||
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.obfuscated {
|
.obfuscated {
|
||||||
|
@ -1399,6 +1403,10 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
.userActions {
|
.userActions {
|
||||||
min-width: 44px;
|
min-width: 44px;
|
||||||
}
|
}
|
||||||
|
.subtitle {
|
||||||
|
color: var(--color-text-maxcontrast);
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
/* various */
|
/* various */
|
||||||
&#grid-header,
|
&#grid-header,
|
||||||
|
@ -1427,16 +1435,23 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
&#grid-header {
|
&#grid-header {
|
||||||
color: var(--color-text-maxcontrast);
|
color: var(--color-text-maxcontrast);
|
||||||
z-index: 60; /* above new-user */
|
z-index: 60; /* above new-user */
|
||||||
|
border-bottom-width: thin;
|
||||||
|
|
||||||
#headerDisplayName,
|
#headerDisplayName,
|
||||||
#headerPassword,
|
#headerPassword,
|
||||||
#headerAddress,
|
#headerAddress,
|
||||||
#headerGroups,
|
#headerGroups,
|
||||||
#headerSubAdmins,
|
#headerSubAdmins,
|
||||||
|
#theHeaderUserBackend,
|
||||||
|
#theHeaderLastLogin,
|
||||||
#headerQuota,
|
#headerQuota,
|
||||||
|
#theHeaderStorageLocation,
|
||||||
#headerLanguages {
|
#headerLanguages {
|
||||||
/* Line up header text with column content for when there’s inputs */
|
/* Line up header text with column content for when there’s inputs */
|
||||||
padding-left: 7px;
|
padding-left: 7px;
|
||||||
|
text-transform: none;
|
||||||
|
color: var(--color-text-maxcontrast);
|
||||||
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -1451,8 +1466,7 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
> form {
|
> form {
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
color: var(--color-text-lighter);
|
||||||
color: var(--color-text);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
> input:not(:focus):not(:active) {
|
> input:not(:focus):not(:active) {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
@ -1478,7 +1492,7 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.name,
|
&.name,
|
||||||
&.storageLocation {
|
&.userBackend {
|
||||||
/* better multi-line visual */
|
/* better multi-line visual */
|
||||||
line-height: 1.3em;
|
line-height: 1.3em;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
@ -1492,16 +1506,14 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
&.quota {
|
&.quota {
|
||||||
.multiselect--active + progress {
|
height: 44px;
|
||||||
display: none;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
progress {
|
progress {
|
||||||
position: absolute;
|
width: 100%;
|
||||||
width: calc(100% - 4px); /* minus left and right */
|
margin: 0 10px;
|
||||||
left: 2px;
|
|
||||||
bottom: 2px;
|
|
||||||
height: 3px;
|
height: 3px;
|
||||||
z-index: 5; /* above multiselect */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.icon-confirm {
|
.icon-confirm {
|
||||||
|
@ -1520,16 +1532,22 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.userActions {
|
&.userActions {
|
||||||
|
.action-item {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
#newsubmit {
|
#newsubmit {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.toggleUserActions {
|
.toggleUserActions {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
align-items: center;
|
||||||
.icon-more {
|
.icon-more {
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
margin-left: 40px;
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: .7;
|
opacity: .7;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -29,7 +29,9 @@
|
||||||
<button v-if="showUpdateAll"
|
<button v-if="showUpdateAll"
|
||||||
id="app-list-update-all"
|
id="app-list-update-all"
|
||||||
class="primary"
|
class="primary"
|
||||||
@click="updateAll">{{t('settings', 'Update all')}}</button>
|
@click="updateAll">
|
||||||
|
{{ t('settings', 'Update all') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<transition-group name="app-list" tag="div" class="apps-list-container">
|
<transition-group name="app-list" tag="div" class="apps-list-container">
|
||||||
<AppItem v-for="app in apps"
|
<AppItem v-for="app in apps"
|
||||||
|
|
|
@ -22,14 +22,17 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="app-content" class="user-list-grid" @scroll.passive="onScroll">
|
<div id="app-content" class="user-list-grid" @scroll.passive="onScroll">
|
||||||
<div id="grid-header" class="row" :class="{'sticky': scrolled && !showConfig.showNewUserForm}">
|
<div id="grid-header"
|
||||||
|
:class="{'sticky': scrolled && !showConfig.showNewUserForm}"
|
||||||
|
class="row">
|
||||||
<div id="headerAvatar" class="avatar" />
|
<div id="headerAvatar" class="avatar" />
|
||||||
<div id="headerName" class="name">
|
<div id="headerName" class="name">
|
||||||
{{ t('settings', 'Username') }}
|
{{ t('settings', 'Username') }}
|
||||||
</div>
|
|
||||||
<div id="headerDisplayName" class="displayName">
|
<div class="subtitle">
|
||||||
{{ t('settings', 'Display name') }}
|
{{ t('settings', 'Display name') }}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div id="headerPassword" class="password">
|
<div id="headerPassword" class="password">
|
||||||
{{ t('settings', 'Password') }}
|
{{ t('settings', 'Password') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,99 +55,103 @@
|
||||||
class="languages">
|
class="languages">
|
||||||
{{ t('settings', 'Language') }}
|
{{ t('settings', 'Language') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showConfig.showUserBackend || showConfig.showStoragePath"
|
||||||
|
class="headerUserBackend userBackend">
|
||||||
|
<div v-if="showConfig.showUserBackend" class="userBackend">
|
||||||
|
{{ t('settings', 'User backend') }}
|
||||||
|
</div>
|
||||||
<div v-if="showConfig.showStoragePath"
|
<div v-if="showConfig.showStoragePath"
|
||||||
class="headerStorageLocation storageLocation">
|
class="subtitle storageLocation">
|
||||||
{{ t('settings', 'Storage location') }}
|
{{ t('settings', 'Storage location') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showConfig.showUserBackend"
|
|
||||||
class="headerUserBackend userBackend">
|
|
||||||
{{ t('settings', 'User backend') }}
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showConfig.showLastLogin"
|
<div v-if="showConfig.showLastLogin"
|
||||||
class="headerLastLogin lastLogin">
|
class="headerLastLogin lastLogin">
|
||||||
{{ t('settings', 'Last login') }}
|
{{ t('settings', 'Last login') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="userActions" />
|
<div class="userActions" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form v-show="showConfig.showNewUserForm"
|
<form v-show="showConfig.showNewUserForm"
|
||||||
id="new-user"
|
id="new-user"
|
||||||
class="row"
|
|
||||||
:disabled="loading.all"
|
|
||||||
:class="{'sticky': scrolled && showConfig.showNewUserForm}"
|
:class="{'sticky': scrolled && showConfig.showNewUserForm}"
|
||||||
|
:disabled="loading.all"
|
||||||
|
class="row"
|
||||||
@submit.prevent="createUser">
|
@submit.prevent="createUser">
|
||||||
<div :class="loading.all?'icon-loading-small':'icon-add'" />
|
<div :class="loading.all?'icon-loading-small':'icon-add'" />
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<input id="newusername"
|
<input id="newusername"
|
||||||
ref="newusername"
|
ref="newusername"
|
||||||
v-model="newUser.id"
|
v-model="newUser.id"
|
||||||
type="text"
|
:disabled="settings.newUserGenerateUserID"
|
||||||
required
|
|
||||||
:placeholder="settings.newUserGenerateUserID
|
:placeholder="settings.newUserGenerateUserID
|
||||||
? t('settings', 'Will be autogenerated')
|
? t('settings', 'Will be autogenerated')
|
||||||
: t('settings', 'Username')"
|
: t('settings', 'Username')"
|
||||||
name="username"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
|
name="username"
|
||||||
pattern="[a-zA-Z0-9 _\.@\-']+"
|
pattern="[a-zA-Z0-9 _\.@\-']+"
|
||||||
:disabled="settings.newUserGenerateUserID">
|
required
|
||||||
|
type="text">
|
||||||
</div>
|
</div>
|
||||||
<div class="displayName">
|
<div class="displayName">
|
||||||
<input id="newdisplayname"
|
<input id="newdisplayname"
|
||||||
v-model="newUser.displayName"
|
v-model="newUser.displayName"
|
||||||
type="text"
|
|
||||||
:placeholder="t('settings', 'Display name')"
|
:placeholder="t('settings', 'Display name')"
|
||||||
name="displayname"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
autocorrect="off">
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
name="displayname"
|
||||||
|
type="text">
|
||||||
</div>
|
</div>
|
||||||
<div class="password">
|
<div class="password">
|
||||||
<input id="newuserpassword"
|
<input id="newuserpassword"
|
||||||
ref="newuserpassword"
|
ref="newuserpassword"
|
||||||
v-model="newUser.password"
|
v-model="newUser.password"
|
||||||
type="password"
|
:minlength="minPasswordLength"
|
||||||
:required="newUser.mailAddress===''"
|
|
||||||
:placeholder="t('settings', 'Password')"
|
:placeholder="t('settings', 'Password')"
|
||||||
name="password"
|
:required="newUser.mailAddress===''"
|
||||||
autocomplete="new-password"
|
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
autocomplete="new-password"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
:minlength="minPasswordLength">
|
name="password"
|
||||||
|
type="password">
|
||||||
</div>
|
</div>
|
||||||
<div class="mailAddress">
|
<div class="mailAddress">
|
||||||
<input id="newemail"
|
<input id="newemail"
|
||||||
v-model="newUser.mailAddress"
|
v-model="newUser.mailAddress"
|
||||||
type="email"
|
|
||||||
:required="newUser.password==='' || settings.newUserRequireEmail"
|
|
||||||
:placeholder="t('settings', 'Email')"
|
:placeholder="t('settings', 'Email')"
|
||||||
name="email"
|
:required="newUser.password==='' || settings.newUserRequireEmail"
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
autocorrect="off">
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
name="email"
|
||||||
|
type="email">
|
||||||
</div>
|
</div>
|
||||||
<div class="groups">
|
<div class="groups">
|
||||||
<!-- hidden input trick for vanilla html5 form validation -->
|
<!-- hidden input trick for vanilla html5 form validation -->
|
||||||
<input v-if="!settings.isAdmin"
|
<input v-if="!settings.isAdmin"
|
||||||
id="newgroups"
|
id="newgroups"
|
||||||
type="text"
|
:class="{'icon-loading-small': loading.groups}"
|
||||||
|
:required="!settings.isAdmin"
|
||||||
:value="newUser.groups"
|
:value="newUser.groups"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
:required="!settings.isAdmin"
|
type="text">
|
||||||
:class="{'icon-loading-small': loading.groups}">
|
|
||||||
<Multiselect v-model="newUser.groups"
|
<Multiselect v-model="newUser.groups"
|
||||||
:options="canAddGroups"
|
|
||||||
:disabled="loading.groups||loading.all"
|
|
||||||
tag-placeholder="create"
|
|
||||||
:placeholder="t('settings', 'Add user in group')"
|
|
||||||
label="name"
|
|
||||||
track-by="id"
|
|
||||||
class="multiselect-vue"
|
|
||||||
:multiple="true"
|
|
||||||
:taggable="true"
|
|
||||||
:close-on-select="false"
|
:close-on-select="false"
|
||||||
|
:disabled="loading.groups||loading.all"
|
||||||
|
:multiple="true"
|
||||||
|
:options="canAddGroups"
|
||||||
|
:placeholder="t('settings', 'Add user in group')"
|
||||||
:tag-width="60"
|
:tag-width="60"
|
||||||
|
:taggable="true"
|
||||||
|
class="multiselect-vue"
|
||||||
|
label="name"
|
||||||
|
tag-placeholder="create"
|
||||||
|
track-by="id"
|
||||||
@tag="createGroup">
|
@tag="createGroup">
|
||||||
<!-- If user is not admin, he is a subadmin.
|
<!-- If user is not admin, he is a subadmin.
|
||||||
Subadmins can't create users outside their groups
|
Subadmins can't create users outside their groups
|
||||||
|
@ -152,63 +159,64 @@
|
||||||
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
||||||
</Multiselect>
|
</Multiselect>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subAdminsGroups.length>0 && settings.isAdmin" class="subadmins">
|
<div v-if="subAdminsGroups.length>0 && settings.isAdmin"
|
||||||
|
class="subadmins">
|
||||||
<Multiselect v-model="newUser.subAdminsGroups"
|
<Multiselect v-model="newUser.subAdminsGroups"
|
||||||
|
:close-on-select="false"
|
||||||
|
:multiple="true"
|
||||||
:options="subAdminsGroups"
|
:options="subAdminsGroups"
|
||||||
:placeholder="t('settings', 'Set user as admin for')"
|
:placeholder="t('settings', 'Set user as admin for')"
|
||||||
label="name"
|
:tag-width="60"
|
||||||
track-by="id"
|
|
||||||
class="multiselect-vue"
|
class="multiselect-vue"
|
||||||
:multiple="true"
|
label="name"
|
||||||
:close-on-select="false"
|
track-by="id">
|
||||||
:tag-width="60">
|
|
||||||
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
||||||
</Multiselect>
|
</Multiselect>
|
||||||
</div>
|
</div>
|
||||||
<div class="quota">
|
<div class="quota">
|
||||||
<Multiselect v-model="newUser.quota"
|
<Multiselect v-model="newUser.quota"
|
||||||
|
:allow-empty="false"
|
||||||
:options="quotaOptions"
|
:options="quotaOptions"
|
||||||
:placeholder="t('settings', 'Select user quota')"
|
:placeholder="t('settings', 'Select user quota')"
|
||||||
|
:taggable="true"
|
||||||
|
class="multiselect-vue"
|
||||||
label="label"
|
label="label"
|
||||||
track-by="id"
|
track-by="id"
|
||||||
class="multiselect-vue"
|
|
||||||
:allow-empty="false"
|
|
||||||
:taggable="true"
|
|
||||||
@tag="validateQuota" />
|
@tag="validateQuota" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showConfig.showLanguages" class="languages">
|
<div v-if="showConfig.showLanguages" class="languages">
|
||||||
<Multiselect v-model="newUser.language"
|
<Multiselect v-model="newUser.language"
|
||||||
|
:allow-empty="false"
|
||||||
:options="languages"
|
:options="languages"
|
||||||
:placeholder="t('settings', 'Default language')"
|
:placeholder="t('settings', 'Default language')"
|
||||||
label="name"
|
|
||||||
track-by="code"
|
|
||||||
class="multiselect-vue"
|
class="multiselect-vue"
|
||||||
:allow-empty="false"
|
group-label="label"
|
||||||
group-values="languages"
|
group-values="languages"
|
||||||
group-label="label" />
|
label="name"
|
||||||
|
track-by="code" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showConfig.showStoragePath" class="storageLocation" />
|
<div v-if="showConfig.showStoragePath" class="storageLocation" />
|
||||||
<div v-if="showConfig.showUserBackend" class="userBackend" />
|
<div v-if="showConfig.showUserBackend" class="userBackend" />
|
||||||
<div v-if="showConfig.showLastLogin" class="lastLogin" />
|
<div v-if="showConfig.showLastLogin" class="lastLogin" />
|
||||||
<div class="userActions">
|
<div class="userActions">
|
||||||
<input id="newsubmit"
|
<input id="newsubmit"
|
||||||
type="submit"
|
:title="t('settings', 'Add a new user')"
|
||||||
class="button primary icon-checkmark-white has-tooltip"
|
class="button primary icon-checkmark-white has-tooltip"
|
||||||
value=""
|
type="submit"
|
||||||
:title="t('settings', 'Add a new user')">
|
value="">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<user-row v-for="(user, key) in filteredUsers"
|
<user-row v-for="(user, key) in filteredUsers"
|
||||||
:key="key"
|
:key="key"
|
||||||
:user="user"
|
:external-actions="externalActions"
|
||||||
|
:groups="groups"
|
||||||
|
:languages="languages"
|
||||||
|
:quota-options="quotaOptions"
|
||||||
:settings="settings"
|
:settings="settings"
|
||||||
:show-config="showConfig"
|
:show-config="showConfig"
|
||||||
:groups="groups"
|
|
||||||
:sub-admins-groups="subAdminsGroups"
|
:sub-admins-groups="subAdminsGroups"
|
||||||
:quota-options="quotaOptions"
|
:user="user" />
|
||||||
:languages="languages"
|
|
||||||
:external-actions="externalActions" />
|
|
||||||
<InfiniteLoading ref="infiniteLoading" @infinite="infiniteHandler">
|
<InfiniteLoading ref="infiniteLoading" @infinite="infiniteHandler">
|
||||||
<div slot="spinner">
|
<div slot="spinner">
|
||||||
<div class="users-icon-loading icon-loading" />
|
<div class="users-icon-loading icon-loading" />
|
||||||
|
@ -328,7 +336,10 @@ export default {
|
||||||
},
|
},
|
||||||
quotaOptions() {
|
quotaOptions() {
|
||||||
// convert the preset array into objects
|
// convert the preset array into objects
|
||||||
let quotaPreset = this.settings.quotaPreset.reduce((acc, cur) => acc.concat({ id: cur, label: cur }), [])
|
let quotaPreset = this.settings.quotaPreset.reduce((acc, cur) => acc.concat({
|
||||||
|
id: cur,
|
||||||
|
label: cur
|
||||||
|
}), [])
|
||||||
// add default presets
|
// add default presets
|
||||||
quotaPreset.unshift(this.unlimitedQuota)
|
quotaPreset.unshift(this.unlimitedQuota)
|
||||||
quotaPreset.unshift(this.defaultQuota)
|
quotaPreset.unshift(this.defaultQuota)
|
||||||
|
@ -437,7 +448,9 @@ export default {
|
||||||
group: this.selectedGroup !== 'disabled' ? this.selectedGroup : '',
|
group: this.selectedGroup !== 'disabled' ? this.selectedGroup : '',
|
||||||
search: this.searchQuery
|
search: this.searchQuery
|
||||||
})
|
})
|
||||||
.then((response) => { response ? $state.loaded() : $state.complete() })
|
.then((response) => {
|
||||||
|
response ? $state.loaded() : $state.complete()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/* SEARCH */
|
/* SEARCH */
|
||||||
|
|
|
@ -24,14 +24,15 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- Obfuscated user: Logged in user does not have permissions to see all of the data -->
|
<!-- Obfuscated user: Logged in user does not have permissions to see all of the data -->
|
||||||
<div v-if="Object.keys(user).length ===1" class="row" :data-id="user.id">
|
<div v-if="Object.keys(user).length ===1" :data-id="user.id" class="row">
|
||||||
<div class="avatar" :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}">
|
<div :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}"
|
||||||
|
class="avatar">
|
||||||
<img v-if="!loading.delete && !loading.disable && !loading.wipe"
|
<img v-if="!loading.delete && !loading.disable && !loading.wipe"
|
||||||
alt=""
|
|
||||||
width="32"
|
|
||||||
height="32"
|
|
||||||
:src="generateAvatar(user.id, 32)"
|
:src="generateAvatar(user.id, 32)"
|
||||||
:srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'">
|
:srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'"
|
||||||
|
alt=""
|
||||||
|
height="32"
|
||||||
|
width="32">
|
||||||
</div>
|
</div>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
{{ user.id }}
|
{{ user.id }}
|
||||||
|
@ -42,163 +43,189 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- User full data -->
|
<!-- User full data -->
|
||||||
|
<UserRowSimple
|
||||||
|
v-else-if="!editing"
|
||||||
|
:editing.sync="editing"
|
||||||
|
:feedback-message="feedbackMessage"
|
||||||
|
:groups="groups"
|
||||||
|
:languages="languages"
|
||||||
|
:loading="loading"
|
||||||
|
:opened-menu="openedMenu"
|
||||||
|
:settings="settings"
|
||||||
|
:show-config="showConfig"
|
||||||
|
:sub-admins-groups="subAdminsGroups"
|
||||||
|
:user-actions="userActions"
|
||||||
|
:user="user"
|
||||||
|
@hideMenu="hideMenu"
|
||||||
|
@toggleMenu="toggleMenu" />
|
||||||
<div v-else
|
<div v-else
|
||||||
class="row"
|
|
||||||
:class="{'disabled': loading.delete || loading.disable}"
|
:class="{'disabled': loading.delete || loading.disable}"
|
||||||
:data-id="user.id">
|
:data-id="user.id"
|
||||||
<div class="avatar" :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}">
|
class="row row--editable">
|
||||||
|
<div :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}"
|
||||||
|
class="avatar">
|
||||||
<img v-if="!loading.delete && !loading.disable && !loading.wipe"
|
<img v-if="!loading.delete && !loading.disable && !loading.wipe"
|
||||||
alt=""
|
|
||||||
width="32"
|
|
||||||
height="32"
|
|
||||||
:src="generateAvatar(user.id, 32)"
|
:src="generateAvatar(user.id, 32)"
|
||||||
:srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'">
|
:srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'"
|
||||||
|
alt=""
|
||||||
|
height="32"
|
||||||
|
width="32">
|
||||||
</div>
|
</div>
|
||||||
<!-- dirty hack to ellipsis on two lines -->
|
<!-- dirty hack to ellipsis on two lines -->
|
||||||
<div class="name">
|
<div class="displayName">
|
||||||
{{ user.id }}
|
<form
|
||||||
</div>
|
:class="{'icon-loading-small': loading.displayName}"
|
||||||
<form class="displayName" :class="{'icon-loading-small': loading.displayName}" @submit.prevent="updateDisplayName">
|
class="displayName"
|
||||||
|
@submit.prevent="updateDisplayName">
|
||||||
<template v-if="user.backendCapabilities.setDisplayName">
|
<template v-if="user.backendCapabilities.setDisplayName">
|
||||||
<input v-if="user.backendCapabilities.setDisplayName"
|
<input v-if="user.backendCapabilities.setDisplayName"
|
||||||
:id="'displayName'+user.id+rand"
|
:id="'displayName'+user.id+rand"
|
||||||
ref="displayName"
|
ref="displayName"
|
||||||
type="text"
|
|
||||||
:disabled="loading.displayName||loading.all"
|
:disabled="loading.displayName||loading.all"
|
||||||
:value="user.displayname"
|
:value="user.displayname"
|
||||||
|
autocapitalize="off"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
autocapitalize="off"
|
spellcheck="false"
|
||||||
spellcheck="false">
|
type="text">
|
||||||
<input v-if="user.backendCapabilities.setDisplayName"
|
<input v-if="user.backendCapabilities.setDisplayName"
|
||||||
type="submit"
|
|
||||||
class="icon-confirm"
|
class="icon-confirm"
|
||||||
|
type="submit"
|
||||||
value="">
|
value="">
|
||||||
</template>
|
</template>
|
||||||
<div v-else v-tooltip.auto="t('settings', 'The backend does not support changing the display name')" class="name">
|
<div v-else
|
||||||
{{ user.displayname }}
|
v-tooltip.auto="t('settings', 'The backend does not support changing the display name')"
|
||||||
</div>
|
class="name" />
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
<form v-if="settings.canChangePassword && user.backendCapabilities.setPassword"
|
<form v-if="settings.canChangePassword && user.backendCapabilities.setPassword"
|
||||||
class="password"
|
|
||||||
:class="{'icon-loading-small': loading.password}"
|
:class="{'icon-loading-small': loading.password}"
|
||||||
|
class="password"
|
||||||
@submit.prevent="updatePassword">
|
@submit.prevent="updatePassword">
|
||||||
<input :id="'password'+user.id+rand"
|
<input :id="'password'+user.id+rand"
|
||||||
ref="password"
|
ref="password"
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
:disabled="loading.password || loading.all"
|
:disabled="loading.password || loading.all"
|
||||||
:minlength="minPasswordLength"
|
:minlength="minPasswordLength"
|
||||||
value=""
|
:placeholder="t('settings', 'Add new password')"
|
||||||
:placeholder="t('settings', 'New password')"
|
autocapitalize="off"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
autocapitalize="off"
|
required
|
||||||
spellcheck="false">
|
spellcheck="false"
|
||||||
<input type="submit" class="icon-confirm" value="">
|
type="password"
|
||||||
|
value="">
|
||||||
|
<input class="icon-confirm" type="submit" value="">
|
||||||
</form>
|
</form>
|
||||||
<div v-else />
|
<div v-else />
|
||||||
<form class="mailAddress" :class="{'icon-loading-small': loading.mailAddress}" @submit.prevent="updateEmail">
|
<form :class="{'icon-loading-small': loading.mailAddress}"
|
||||||
|
class="mailAddress"
|
||||||
|
@submit.prevent="updateEmail">
|
||||||
<input :id="'mailAddress'+user.id+rand"
|
<input :id="'mailAddress'+user.id+rand"
|
||||||
ref="mailAddress"
|
ref="mailAddress"
|
||||||
type="email"
|
|
||||||
:disabled="loading.mailAddress||loading.all"
|
:disabled="loading.mailAddress||loading.all"
|
||||||
|
:placeholder="t('settings', 'Add new email address')"
|
||||||
:value="user.email"
|
:value="user.email"
|
||||||
|
autocapitalize="off"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
autocapitalize="off"
|
spellcheck="false"
|
||||||
spellcheck="false">
|
type="email">
|
||||||
<input type="submit" class="icon-confirm" value="">
|
<input class="icon-confirm" type="submit" value="">
|
||||||
</form>
|
</form>
|
||||||
<div class="groups" :class="{'icon-loading-small': loading.groups}">
|
<div :class="{'icon-loading-small': loading.groups}" class="groups">
|
||||||
<Multiselect :value="userGroups"
|
<Multiselect :close-on-select="false"
|
||||||
:options="availableGroups"
|
|
||||||
:disabled="loading.groups||loading.all"
|
:disabled="loading.groups||loading.all"
|
||||||
tag-placeholder="create"
|
:limit="2"
|
||||||
|
:multiple="true"
|
||||||
|
:options="availableGroups"
|
||||||
:placeholder="t('settings', 'Add user in group')"
|
:placeholder="t('settings', 'Add user in group')"
|
||||||
label="name"
|
:tag-width="60"
|
||||||
track-by="id"
|
|
||||||
class="multiselect-vue"
|
|
||||||
:limit="2"
|
|
||||||
:multiple="true"
|
|
||||||
:taggable="settings.isAdmin"
|
:taggable="settings.isAdmin"
|
||||||
:close-on-select="false"
|
:value="userGroups"
|
||||||
:tag-width="60"
|
class="multiselect-vue"
|
||||||
@tag="createGroup"
|
label="name"
|
||||||
|
tag-placeholder="create"
|
||||||
|
track-by="id"
|
||||||
|
@remove="removeUserGroup"
|
||||||
@select="addUserGroup"
|
@select="addUserGroup"
|
||||||
@remove="removeUserGroup">
|
@tag="createGroup">
|
||||||
<span slot="limit" v-tooltip.auto="formatGroupsTitle(userGroups)" class="multiselect__limit">+{{ userGroups.length-2 }}</span>
|
|
||||||
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
||||||
</Multiselect>
|
</Multiselect>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subAdminsGroups.length>0 && settings.isAdmin" class="subadmins" :class="{'icon-loading-small': loading.subadmins}">
|
<div v-if="subAdminsGroups.length>0 && settings.isAdmin"
|
||||||
<Multiselect :value="userSubAdminsGroups"
|
:class="{'icon-loading-small': loading.subadmins}"
|
||||||
:options="subAdminsGroups"
|
class="subadmins">
|
||||||
|
<Multiselect :close-on-select="false"
|
||||||
:disabled="loading.subadmins||loading.all"
|
:disabled="loading.subadmins||loading.all"
|
||||||
:placeholder="t('settings', 'Set user as admin for')"
|
|
||||||
label="name"
|
|
||||||
track-by="id"
|
|
||||||
class="multiselect-vue"
|
|
||||||
:limit="2"
|
:limit="2"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
:close-on-select="false"
|
:options="subAdminsGroups"
|
||||||
|
:placeholder="t('settings', 'Set user as admin for')"
|
||||||
:tag-width="60"
|
:tag-width="60"
|
||||||
@select="addUserSubAdmin"
|
:value="userSubAdminsGroups"
|
||||||
@remove="removeUserSubAdmin">
|
class="multiselect-vue"
|
||||||
<span slot="limit" v-tooltip.auto="formatGroupsTitle(userSubAdminsGroups)" class="multiselect__limit">+{{ userSubAdminsGroups.length-2 }}</span>
|
label="name"
|
||||||
|
track-by="id"
|
||||||
|
@remove="removeUserSubAdmin"
|
||||||
|
@select="addUserSubAdmin">
|
||||||
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
||||||
</Multiselect>
|
</Multiselect>
|
||||||
</div>
|
</div>
|
||||||
<div v-tooltip.auto="usedSpace" class="quota" :class="{'icon-loading-small': loading.quota}">
|
<div v-tooltip.auto="usedSpace"
|
||||||
<Multiselect :value="userQuota"
|
:class="{'icon-loading-small': loading.quota}"
|
||||||
:options="quotaOptions"
|
class="quota">
|
||||||
|
<Multiselect :allow-empty="false"
|
||||||
:disabled="loading.quota||loading.all"
|
:disabled="loading.quota||loading.all"
|
||||||
tag-placeholder="create"
|
:options="quotaOptions"
|
||||||
:placeholder="t('settings', 'Select user quota')"
|
:placeholder="t('settings', 'Select user quota')"
|
||||||
label="label"
|
|
||||||
track-by="id"
|
|
||||||
class="multiselect-vue"
|
|
||||||
:allow-empty="false"
|
|
||||||
:taggable="true"
|
:taggable="true"
|
||||||
@tag="validateQuota"
|
:value="userQuota"
|
||||||
@input="setUserQuota" />
|
class="multiselect-vue"
|
||||||
<progress class="quota-user-progress"
|
label="label"
|
||||||
:class="{'warn':usedQuota>80}"
|
tag-placeholder="create"
|
||||||
:value="usedQuota"
|
track-by="id"
|
||||||
max="100" />
|
@input="setUserQuota"
|
||||||
|
@tag="validateQuota" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showConfig.showLanguages"
|
<div v-if="showConfig.showLanguages"
|
||||||
class="languages"
|
:class="{'icon-loading-small': loading.languages}"
|
||||||
:class="{'icon-loading-small': loading.languages}">
|
class="languages">
|
||||||
<Multiselect :value="userLanguage"
|
<Multiselect :allow-empty="false"
|
||||||
:options="languages"
|
|
||||||
:disabled="loading.languages||loading.all"
|
:disabled="loading.languages||loading.all"
|
||||||
|
:options="languages"
|
||||||
:placeholder="t('settings', 'No language set')"
|
:placeholder="t('settings', 'No language set')"
|
||||||
|
:value="userLanguage"
|
||||||
|
class="multiselect-vue"
|
||||||
|
group-label="label"
|
||||||
|
group-values="languages"
|
||||||
label="name"
|
label="name"
|
||||||
track-by="code"
|
track-by="code"
|
||||||
class="multiselect-vue"
|
|
||||||
:allow-empty="false"
|
|
||||||
group-values="languages"
|
|
||||||
group-label="label"
|
|
||||||
@input="setUserLanguage" />
|
@input="setUserLanguage" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showConfig.showStoragePath" class="storageLocation">
|
|
||||||
{{ user.storageLocation }}
|
<!-- don't show this on edit mode -->
|
||||||
</div>
|
<div v-if="showConfig.showStoragePath || showConfig.showUserBackend"
|
||||||
<div v-if="showConfig.showUserBackend" class="userBackend">
|
class="storageLocation" />
|
||||||
{{ user.backend }}
|
<div v-if="showConfig.showLastLogin" />
|
||||||
</div>
|
|
||||||
<div v-if="showConfig.showLastLogin" v-tooltip.auto="user.lastLogin>0 ? OC.Util.formatDate(user.lastLogin) : ''" class="lastLogin">
|
|
||||||
{{ user.lastLogin>0 ? OC.Util.relativeModifiedDate(user.lastLogin) : t('settings','Never') }}
|
|
||||||
</div>
|
|
||||||
<div class="userActions">
|
<div class="userActions">
|
||||||
<div v-if="OC.currentUser !== user.id && user.id !== 'admin' && !loading.all" class="toggleUserActions">
|
<div v-if="OC.currentUser !== user.id && user.id !== 'admin' && !loading.all"
|
||||||
<div v-click-outside="hideMenu" class="icon-more" @click="toggleMenu" />
|
class="toggleUserActions">
|
||||||
<div class="popovermenu" :class="{ 'open': openedMenu }">
|
<Actions>
|
||||||
|
<ActionButton icon="icon-checkmark"
|
||||||
|
@click="editing = false">
|
||||||
|
{{ t('settings', 'Done') }}
|
||||||
|
</ActionButton>
|
||||||
|
</Actions>
|
||||||
|
<div v-click-outside="hideMenu"
|
||||||
|
class="icon-more"
|
||||||
|
@click="toggleMenu" />
|
||||||
|
<div :class="{ 'open': openedMenu }" class="popovermenu">
|
||||||
<PopoverMenu :menu="userActions" />
|
<PopoverMenu :menu="userActions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="feedback" :style="{opacity: feedbackMessage !== '' ? 1 : 0}">
|
<div :style="{opacity: feedbackMessage !== '' ? 1 : 0}"
|
||||||
|
class="feedback">
|
||||||
<div class="icon-checkmark" />
|
<div class="icon-checkmark" />
|
||||||
{{ feedbackMessage }}
|
{{ feedbackMessage }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -210,19 +237,30 @@
|
||||||
import ClickOutside from 'vue-click-outside'
|
import ClickOutside from 'vue-click-outside'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VTooltip from 'v-tooltip'
|
import VTooltip from 'v-tooltip'
|
||||||
import { PopoverMenu, Multiselect } from 'nextcloud-vue'
|
import {
|
||||||
|
PopoverMenu,
|
||||||
|
Multiselect,
|
||||||
|
Actions,
|
||||||
|
ActionButton
|
||||||
|
} from 'nextcloud-vue'
|
||||||
|
import UserRowSimple from './UserRowSimple'
|
||||||
|
import UserRowMixin from '../../mixins/UserRowMixin'
|
||||||
|
|
||||||
Vue.use(VTooltip)
|
Vue.use(VTooltip)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UserRow',
|
name: 'UserRow',
|
||||||
components: {
|
components: {
|
||||||
|
UserRowSimple,
|
||||||
PopoverMenu,
|
PopoverMenu,
|
||||||
|
Actions,
|
||||||
|
ActionButton,
|
||||||
Multiselect
|
Multiselect
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
ClickOutside
|
ClickOutside
|
||||||
},
|
},
|
||||||
|
mixins: [UserRowMixin],
|
||||||
props: {
|
props: {
|
||||||
user: {
|
user: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -262,6 +300,7 @@ export default {
|
||||||
rand: parseInt(Math.random() * 1000),
|
rand: parseInt(Math.random() * 1000),
|
||||||
openedMenu: false,
|
openedMenu: false,
|
||||||
feedbackMessage: '',
|
feedbackMessage: '',
|
||||||
|
editing: false,
|
||||||
loading: {
|
loading: {
|
||||||
all: false,
|
all: false,
|
||||||
displayName: false,
|
displayName: false,
|
||||||
|
@ -305,92 +344,9 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return actions.concat(this.externalActions)
|
return actions.concat(this.externalActions)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/* GROUPS MANAGEMENT */
|
|
||||||
userGroups() {
|
|
||||||
let userGroups = this.groups.filter(group => this.user.groups.includes(group.id))
|
|
||||||
return userGroups
|
|
||||||
},
|
|
||||||
userSubAdminsGroups() {
|
|
||||||
let userSubAdminsGroups = this.subAdminsGroups.filter(group => this.user.subadmin.includes(group.id))
|
|
||||||
return userSubAdminsGroups
|
|
||||||
},
|
|
||||||
availableGroups() {
|
|
||||||
return this.groups.map((group) => {
|
|
||||||
// clone object because we don't want
|
|
||||||
// to edit the original groups
|
|
||||||
let groupClone = Object.assign({}, group)
|
|
||||||
|
|
||||||
// two settings here:
|
|
||||||
// 1. user NOT in group but no permission to add
|
|
||||||
// 2. user is in group but no permission to remove
|
|
||||||
groupClone.$isDisabled
|
|
||||||
= (group.canAdd === false
|
|
||||||
&& !this.user.groups.includes(group.id))
|
|
||||||
|| (group.canRemove === false
|
|
||||||
&& this.user.groups.includes(group.id))
|
|
||||||
return groupClone
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
/* QUOTA MANAGEMENT */
|
|
||||||
usedSpace() {
|
|
||||||
if (this.user.quota.used) {
|
|
||||||
return t('settings', '{size} used', { size: OC.Util.humanFileSize(this.user.quota.used) })
|
|
||||||
}
|
|
||||||
return t('settings', '{size} used', { size: OC.Util.humanFileSize(0) })
|
|
||||||
},
|
|
||||||
usedQuota() {
|
|
||||||
let quota = this.user.quota.quota
|
|
||||||
if (quota > 0) {
|
|
||||||
quota = Math.min(100, Math.round(this.user.quota.used / quota * 100))
|
|
||||||
} else {
|
|
||||||
var usedInGB = this.user.quota.used / (10 * Math.pow(2, 30))
|
|
||||||
// asymptotic curve approaching 50% at 10GB to visualize used stace with infinite quota
|
|
||||||
quota = 95 * (1 - (1 / (usedInGB + 1)))
|
|
||||||
}
|
|
||||||
return isNaN(quota) ? 0 : quota
|
|
||||||
},
|
|
||||||
// Mapping saved values to objects
|
|
||||||
userQuota() {
|
|
||||||
if (this.user.quota.quota >= 0) {
|
|
||||||
// if value is valid, let's map the quotaOptions or return custom quota
|
|
||||||
let humanQuota = OC.Util.humanFileSize(this.user.quota.quota)
|
|
||||||
let userQuota = this.quotaOptions.find(quota => quota.id === humanQuota)
|
|
||||||
return userQuota || { id: humanQuota, label: humanQuota }
|
|
||||||
} else if (this.user.quota.quota === 'default') {
|
|
||||||
// default quota is replaced by the proper value on load
|
|
||||||
return this.quotaOptions[0]
|
|
||||||
}
|
|
||||||
return this.quotaOptions[1] // unlimited
|
|
||||||
},
|
|
||||||
|
|
||||||
/* PASSWORD POLICY? */
|
|
||||||
minPasswordLength() {
|
|
||||||
return this.$store.getters.getPasswordPolicyMinLength
|
|
||||||
},
|
|
||||||
|
|
||||||
/* LANGUAGE */
|
|
||||||
userLanguage() {
|
|
||||||
let availableLanguages = this.languages[0].languages.concat(this.languages[1].languages)
|
|
||||||
let userLang = availableLanguages.find(lang => lang.code === this.user.language)
|
|
||||||
if (typeof userLang !== 'object' && this.user.language !== '') {
|
|
||||||
return {
|
|
||||||
code: this.user.language,
|
|
||||||
name: this.user.language
|
|
||||||
}
|
|
||||||
} else if (this.user.language === '') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return userLang
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
// required if popup needs to stay opened after menu click
|
|
||||||
// since we only have disable/delete actions, let's close it directly
|
|
||||||
// this.popupItem = this.$el;
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
/* MENU HANDLING */
|
/* MENU HANDLING */
|
||||||
toggleMenu() {
|
toggleMenu() {
|
||||||
|
@ -400,35 +356,6 @@ export default {
|
||||||
this.openedMenu = false
|
this.openedMenu = false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate avatar url
|
|
||||||
*
|
|
||||||
* @param {string} user The user name
|
|
||||||
* @param {int} size Size integer, default 32
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
generateAvatar(user, size = 32) {
|
|
||||||
return OC.generateUrl(
|
|
||||||
'/avatar/{user}/{size}?v={version}',
|
|
||||||
{
|
|
||||||
user: user,
|
|
||||||
size: size,
|
|
||||||
version: oc_userconfig.avatar.version
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format array of groups objects to a string for the popup
|
|
||||||
*
|
|
||||||
* @param {array} groups The groups
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
formatGroupsTitle(groups) {
|
|
||||||
let names = groups.map(group => group.name)
|
|
||||||
return names.slice(2).join(', ')
|
|
||||||
},
|
|
||||||
|
|
||||||
wipeUserDevices() {
|
wipeUserDevices() {
|
||||||
let userid = this.user.id
|
let userid = this.user.id
|
||||||
OC.dialogs.confirmDestructive(
|
OC.dialogs.confirmDestructive(
|
||||||
|
@ -486,7 +413,10 @@ export default {
|
||||||
this.loading.all = true
|
this.loading.all = true
|
||||||
let userid = this.user.id
|
let userid = this.user.id
|
||||||
let enabled = !this.user.enabled
|
let enabled = !this.user.enabled
|
||||||
return this.$store.dispatch('enableDisableUser', { userid, enabled })
|
return this.$store.dispatch('enableDisableUser', {
|
||||||
|
userid,
|
||||||
|
enabled
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.loading.delete = false
|
this.loading.delete = false
|
||||||
this.loading.all = false
|
this.loading.all = false
|
||||||
|
@ -602,7 +532,10 @@ export default {
|
||||||
let gid = group.id
|
let gid = group.id
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('removeUserGroup', { userid, gid })
|
await this.$store.dispatch('removeUserGroup', {
|
||||||
|
userid,
|
||||||
|
gid
|
||||||
|
})
|
||||||
this.loading.groups = false
|
this.loading.groups = false
|
||||||
// remove user from current list if current list is the removed group
|
// remove user from current list if current list is the removed group
|
||||||
if (this.$route.params.selectedGroup === gid) {
|
if (this.$route.params.selectedGroup === gid) {
|
||||||
|
@ -624,7 +557,10 @@ export default {
|
||||||
let gid = group.id
|
let gid = group.id
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('addUserSubAdmin', { userid, gid })
|
await this.$store.dispatch('addUserSubAdmin', {
|
||||||
|
userid,
|
||||||
|
gid
|
||||||
|
})
|
||||||
this.loading.subadmins = false
|
this.loading.subadmins = false
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
@ -642,7 +578,10 @@ export default {
|
||||||
let gid = group.id
|
let gid = group.id
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('removeUserSubAdmin', { userid, gid })
|
await this.$store.dispatch('removeUserSubAdmin', {
|
||||||
|
userid,
|
||||||
|
gid
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="row"
|
||||||
|
:class="{'disabled': loading.delete || loading.disable}"
|
||||||
|
:data-id="user.id">
|
||||||
|
<div class="avatar" :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}">
|
||||||
|
<img v-if="!loading.delete && !loading.disable && !loading.wipe"
|
||||||
|
alt=""
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
:src="generateAvatar(user.id, 32)"
|
||||||
|
:srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'">
|
||||||
|
</div>
|
||||||
|
<!-- dirty hack to ellipsis on two lines -->
|
||||||
|
<div class="name">
|
||||||
|
{{ user.id }}
|
||||||
|
<div class="displayName subtitle">
|
||||||
|
{{ user.displayname }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div />
|
||||||
|
<div class="mailAddress">
|
||||||
|
{{ user.email }}
|
||||||
|
</div>
|
||||||
|
<div class="groups">
|
||||||
|
{{ userGroupsLabels }}
|
||||||
|
</div>
|
||||||
|
<div v-if="subAdminsGroups.length > 0 && settings.isAdmin" class="subAdminsGroups">
|
||||||
|
{{ userSubAdminsGroupsLabels }}
|
||||||
|
</div>
|
||||||
|
<div v-tooltip.auto="usedSpace" class="quota">
|
||||||
|
<progress
|
||||||
|
class="quota-user-progress"
|
||||||
|
:class="{'warn': usedQuota > 80}"
|
||||||
|
:value="usedQuota"
|
||||||
|
max="100" />
|
||||||
|
</div>
|
||||||
|
<div v-if="showConfig.showLanguages" class="languages">
|
||||||
|
{{ userLanguage.name }}
|
||||||
|
</div>
|
||||||
|
<div v-if="showConfig.showUserBackend || showConfig.showStoragePath" class="userBackend">
|
||||||
|
<div v-if="showConfig.showUserBackend" class="userBackend">
|
||||||
|
{{ user.backend }}
|
||||||
|
</div>
|
||||||
|
<div v-if="showConfig.showStoragePath" class="storageLocation subtitle">
|
||||||
|
{{ user.storageLocation }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="showConfig.showLastLogin" v-tooltip.auto="userLastLoginTooltip" class="lastLogin">
|
||||||
|
{{ userLastLogin }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="userActions">
|
||||||
|
<div v-if="canEdit && !loading.all" class="toggleUserActions">
|
||||||
|
<Actions>
|
||||||
|
<ActionButton icon="icon-rename" @click="toggleEdit">
|
||||||
|
{{ t('settings', 'Edit User') }}
|
||||||
|
</ActionButton>
|
||||||
|
</Actions>
|
||||||
|
<div v-click-outside="hideMenu" class="icon-more" @click="$emit('toggleMenu')" />
|
||||||
|
<div class="popovermenu" :class="{ 'open': openedMenu }">
|
||||||
|
<PopoverMenu :menu="userActions" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="feedback" :style="{opacity: feedbackMessage !== '' ? 1 : 0}">
|
||||||
|
<div class="icon-checkmark" />
|
||||||
|
{{ feedbackMessage }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { PopoverMenu, Actions, ActionButton } from 'nextcloud-vue'
|
||||||
|
import ClickOutside from 'vue-click-outside'
|
||||||
|
import { getCurrentUser } from '@nextcloud/auth'
|
||||||
|
|
||||||
|
import UserRowMixin from '../../mixins/UserRowMixin'
|
||||||
|
export default {
|
||||||
|
name: 'UserRowSimple',
|
||||||
|
components: {
|
||||||
|
PopoverMenu,
|
||||||
|
ActionButton,
|
||||||
|
Actions
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
ClickOutside
|
||||||
|
},
|
||||||
|
mixins: [UserRowMixin],
|
||||||
|
props: {
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
showConfig: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
userActions: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
openedMenu: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
feedbackMessage: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
subAdminsGroups: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
userGroupsLabels() {
|
||||||
|
return this.userGroups
|
||||||
|
.map(group => group.name)
|
||||||
|
.join(', ')
|
||||||
|
},
|
||||||
|
userSubAdminsGroupsLabels() {
|
||||||
|
return this.userSubAdminsGroups
|
||||||
|
.map(group => group.name)
|
||||||
|
.join(', ')
|
||||||
|
},
|
||||||
|
usedSpace() {
|
||||||
|
if (this.user.quota.used) {
|
||||||
|
return t('settings', '{size} used', { size: OC.Util.humanFileSize(this.user.quota.used) })
|
||||||
|
}
|
||||||
|
return t('settings', '{size} used', { size: OC.Util.humanFileSize(0) })
|
||||||
|
},
|
||||||
|
canEdit() {
|
||||||
|
return getCurrentUser().uid !== this.user.id && this.user.id !== 'admin'
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
hideMenu() {
|
||||||
|
this.$emit('hideMenu')
|
||||||
|
},
|
||||||
|
toggleEdit() {
|
||||||
|
this.$emit('update:editing', true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,171 @@
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
|
||||||
|
*
|
||||||
|
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
groups: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
subAdminsGroups: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
quotaOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
showConfig: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
languages: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
externalActions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/* GROUPS MANAGEMENT */
|
||||||
|
userGroups() {
|
||||||
|
const userGroups = this.groups.filter(group => this.user.groups.includes(group.id))
|
||||||
|
return userGroups
|
||||||
|
},
|
||||||
|
userSubAdminsGroups() {
|
||||||
|
const userSubAdminsGroups = this.subAdminsGroups.filter(group => this.user.subadmin.includes(group.id))
|
||||||
|
return userSubAdminsGroups
|
||||||
|
},
|
||||||
|
availableGroups() {
|
||||||
|
return this.groups.map((group) => {
|
||||||
|
// clone object because we don't want
|
||||||
|
// to edit the original groups
|
||||||
|
let groupClone = Object.assign({}, group)
|
||||||
|
|
||||||
|
// two settings here:
|
||||||
|
// 1. user NOT in group but no permission to add
|
||||||
|
// 2. user is in group but no permission to remove
|
||||||
|
groupClone.$isDisabled
|
||||||
|
= (group.canAdd === false
|
||||||
|
&& !this.user.groups.includes(group.id))
|
||||||
|
|| (group.canRemove === false
|
||||||
|
&& this.user.groups.includes(group.id))
|
||||||
|
return groupClone
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/* QUOTA MANAGEMENT */
|
||||||
|
usedSpace() {
|
||||||
|
if (this.user.quota.used) {
|
||||||
|
return t('settings', '{size} used', { size: OC.Util.humanFileSize(this.user.quota.used) })
|
||||||
|
}
|
||||||
|
return t('settings', '{size} used', { size: OC.Util.humanFileSize(0) })
|
||||||
|
},
|
||||||
|
usedQuota() {
|
||||||
|
let quota = this.user.quota.quota
|
||||||
|
if (quota > 0) {
|
||||||
|
quota = Math.min(100, Math.round(this.user.quota.used / quota * 100))
|
||||||
|
} else {
|
||||||
|
var usedInGB = this.user.quota.used / (10 * Math.pow(2, 30))
|
||||||
|
// asymptotic curve approaching 50% at 10GB to visualize used stace with infinite quota
|
||||||
|
quota = 95 * (1 - (1 / (usedInGB + 1)))
|
||||||
|
}
|
||||||
|
return isNaN(quota) ? 0 : quota
|
||||||
|
},
|
||||||
|
// Mapping saved values to objects
|
||||||
|
userQuota() {
|
||||||
|
if (this.user.quota.quota >= 0) {
|
||||||
|
// if value is valid, let's map the quotaOptions or return custom quota
|
||||||
|
let humanQuota = OC.Util.humanFileSize(this.user.quota.quota)
|
||||||
|
let userQuota = this.quotaOptions.find(quota => quota.id === humanQuota)
|
||||||
|
return userQuota || { id: humanQuota, label: humanQuota }
|
||||||
|
} else if (this.user.quota.quota === 'default') {
|
||||||
|
// default quota is replaced by the proper value on load
|
||||||
|
return this.quotaOptions[0]
|
||||||
|
}
|
||||||
|
return this.quotaOptions[1] // unlimited
|
||||||
|
},
|
||||||
|
|
||||||
|
/* PASSWORD POLICY? */
|
||||||
|
minPasswordLength() {
|
||||||
|
return this.$store.getters.getPasswordPolicyMinLength
|
||||||
|
},
|
||||||
|
|
||||||
|
/* LANGUAGE */
|
||||||
|
userLanguage() {
|
||||||
|
let availableLanguages = this.languages[0].languages.concat(this.languages[1].languages)
|
||||||
|
let userLang = availableLanguages.find(lang => lang.code === this.user.language)
|
||||||
|
if (typeof userLang !== 'object' && this.user.language !== '') {
|
||||||
|
return {
|
||||||
|
code: this.user.language,
|
||||||
|
name: this.user.language
|
||||||
|
}
|
||||||
|
} else if (this.user.language === '') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return userLang
|
||||||
|
},
|
||||||
|
|
||||||
|
/* LAST LOGIN */
|
||||||
|
userLastLoginTooltip() {
|
||||||
|
if (this.user.lastLogin > 0) {
|
||||||
|
return OC.Util.formatDate(this.user.lastLogin)
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
userLastLogin() {
|
||||||
|
if (this.user.lastLogin > 0) {
|
||||||
|
return OC.Util.relativeModifiedDate(this.user.lastLogin)
|
||||||
|
}
|
||||||
|
return t('settings', 'Never')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Generate avatar url
|
||||||
|
*
|
||||||
|
* @param {string} user The user name
|
||||||
|
* @param {int} size Size integer, default 32
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
generateAvatar(user, size = 32) {
|
||||||
|
return OC.generateUrl(
|
||||||
|
'/avatar/{user}/{size}?v={version}',
|
||||||
|
{
|
||||||
|
user: user,
|
||||||
|
size: size,
|
||||||
|
version: oc_userconfig.avatar.version
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
*
|
*
|
||||||
* @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com)
|
* @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com)
|
||||||
* @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
|
* @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
|
||||||
|
* @copyright Copyright (c) 2019, Greta Doci <gretadoci@gmail.com>
|
||||||
*
|
*
|
||||||
* @license GNU AGPL version 3 or any later version
|
* @license GNU AGPL version 3 or any later version
|
||||||
*
|
*
|
||||||
|
@ -80,7 +81,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
|
||||||
* @return Locator
|
* @return Locator
|
||||||
*/
|
*/
|
||||||
public static function rowForUser($user) {
|
public static function rowForUser($user) {
|
||||||
return Locator::forThe()->xpath("//div[@id='app-content']/div/div[normalize-space() = '$user']/..")->
|
return Locator::forThe()->css("div.user-list-grid div.row[data-id=$user]")->
|
||||||
describedAs("Row for user $user in Users Settings");
|
describedAs("Row for user $user in Users Settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +156,23 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
|
||||||
describedAs("The selected option of the $cell select for the user $user in Users Settings");
|
describedAs("The selected option of the $cell select for the user $user in Users Settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Locator
|
||||||
|
*/
|
||||||
|
public static function editModeToggle($user) {
|
||||||
|
return Locator::forThe()->css(".toggleUserActions button.icon-rename")->
|
||||||
|
descendantOf(self::rowForUser($user))->
|
||||||
|
describedAs("The edit toggle button for the user $user in Users Settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Locator
|
||||||
|
*/
|
||||||
|
public static function editModeOn($user) {
|
||||||
|
return Locator::forThe()->css("div.user-list-grid div.row.row--editable[data-id=$user]")->
|
||||||
|
describedAs("I see the edit mode is on for the user $user in Users Settings");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When I click the New user button
|
* @When I click the New user button
|
||||||
*/
|
*/
|
||||||
|
@ -204,6 +222,13 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
|
||||||
$this->actor->find(self::createNewUserButton(), 10)->click();
|
$this->actor->find(self::createNewUserButton(), 10)->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @When I toggle the edit mode for the user :user
|
||||||
|
*/
|
||||||
|
public function iToggleTheEditModeForUser($user) {
|
||||||
|
$this->actor->find(self::editModeToggle($user), 10)->click();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When I create user :user with password :password
|
* @When I create user :user with password :password
|
||||||
*/
|
*/
|
||||||
|
@ -289,7 +314,8 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
|
||||||
* @Then I see that the display name for the user :user is :displayName
|
* @Then I see that the display name for the user :user is :displayName
|
||||||
*/
|
*/
|
||||||
public function iSeeThatTheDisplayNameForTheUserIs($user, $displayName) {
|
public function iSeeThatTheDisplayNameForTheUserIs($user, $displayName) {
|
||||||
PHPUnit_Framework_Assert::assertEquals($displayName, $this->actor->find(self::displayNameCellForUser($user), 10)->getValue());
|
PHPUnit_Framework_Assert::assertEquals(
|
||||||
|
$displayName, $this->actor->find(self::displayNameCellForUser($user), 10)->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -308,5 +334,10 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
|
||||||
$this->actor->find(self::selectedSelectOption('quota', $user), 2)->getText(), $quota);
|
$this->actor->find(self::selectedSelectOption('quota', $user), 2)->getText(), $quota);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Then I see that the edit mode is on for user :user
|
||||||
|
*/
|
||||||
|
public function iSeeThatTheEditModeIsOn($user) {
|
||||||
|
WaitFor::elementToBeEventuallyShown($this->actor, self::editModeOn($user));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ Feature: users
|
||||||
And I am logged in as the admin
|
And I am logged in as the admin
|
||||||
And I open the User settings
|
And I open the User settings
|
||||||
And I see that the list of users contains the user user0
|
And I see that the list of users contains the user user0
|
||||||
|
When I toggle the edit mode for the user user0
|
||||||
|
Then I see that the edit mode is on for user user0
|
||||||
# disabled because we need the TAB patch:
|
# disabled because we need the TAB patch:
|
||||||
# https://github.com/minkphp/MinkSelenium2Driver/pull/244
|
# https://github.com/minkphp/MinkSelenium2Driver/pull/244
|
||||||
# When I assign the user user0 to the group admin
|
# When I assign the user user0 to the group admin
|
||||||
|
@ -128,6 +130,8 @@ Feature: users
|
||||||
And I am logged in as the admin
|
And I am logged in as the admin
|
||||||
And I open the User settings
|
And I open the User settings
|
||||||
And I see that the list of users contains the user user0
|
And I see that the list of users contains the user user0
|
||||||
|
When I toggle the edit mode for the user user0
|
||||||
|
Then I see that the edit mode is on for user user0
|
||||||
And I see that the password of user0 is ""
|
And I see that the password of user0 is ""
|
||||||
When I set the password for user0 to 123456
|
When I set the password for user0 to 123456
|
||||||
And I see that the password cell for user user0 is done loading
|
And I see that the password cell for user user0 is done loading
|
||||||
|
@ -149,6 +153,8 @@ Feature: users
|
||||||
And I am logged in as the admin
|
And I am logged in as the admin
|
||||||
And I open the User settings
|
And I open the User settings
|
||||||
And I see that the list of users contains the user user0
|
And I see that the list of users contains the user user0
|
||||||
|
When I toggle the edit mode for the user user0
|
||||||
|
Then I see that the edit mode is on for user user0
|
||||||
And I see that the user quota of user0 is Unlimited
|
And I see that the user quota of user0 is Unlimited
|
||||||
# disabled because we need the TAB patch:
|
# disabled because we need the TAB patch:
|
||||||
# https://github.com/minkphp/MinkSelenium2Driver/pull/244
|
# https://github.com/minkphp/MinkSelenium2Driver/pull/244
|
||||||
|
|
Loading…
Reference in New Issue