refactor accounts
Signed-off-by: Christian Richter <crichter@owncloud.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div>
|
||||
<main class="oc-flex oc-flex-column oc-height-1-1 oc-p-m" id="accounts-app">
|
||||
<template v-if="isInitialized">
|
||||
<h1 class="oc-invisible-sr">
|
||||
<translate>Accounts</translate>
|
||||
</h1>
|
||||
<div class="oc-app-bar">
|
||||
<accounts-batch-actions
|
||||
v-if="isAnyAccountSelected"
|
||||
:number-of-selected-accounts="numberOfSelectedAccounts"
|
||||
:selected-accounts="selectedAccounts"
|
||||
/>
|
||||
<accounts-create v-else />
|
||||
</div>
|
||||
<oc-grid class="oc-flex-1 oc-overflow-auto">
|
||||
<div class="oc-width-expand">
|
||||
<accounts-list :accounts="accounts" />
|
||||
</div>
|
||||
</oc-grid>
|
||||
</template>
|
||||
<template v-else-if="hasFailed">
|
||||
<oc-alert
|
||||
variation="warning"
|
||||
no-close
|
||||
class="oc-m"
|
||||
id="accounts-list-loading-failed"
|
||||
>
|
||||
<oc-icon
|
||||
name="error-warning"
|
||||
variation="warning"
|
||||
class="oc-float-left oc-mr-s"
|
||||
/>
|
||||
<translate>You don't have permissions to manage accounts.</translate>
|
||||
</oc-alert>
|
||||
</template>
|
||||
<oc-loader id="accounts-list-loader" v-else />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions, mapState } from 'vuex'
|
||||
import AccountsList from './accounts/AccountsList.vue'
|
||||
import AccountsCreate from './accounts/AccountsCreate.vue'
|
||||
import AccountsBatchActions from './accounts/AccountsBatchActions.vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: { AccountsBatchActions, AccountsList, AccountsCreate },
|
||||
computed: {
|
||||
...mapGetters('Accounts', [
|
||||
'isInitialized',
|
||||
'hasFailed',
|
||||
'getAccountsSorted',
|
||||
'isAnyAccountSelected'
|
||||
]),
|
||||
...mapState('Accounts', ['selectedAccounts']),
|
||||
|
||||
accounts () {
|
||||
return this.getAccountsSorted
|
||||
},
|
||||
numberOfSelectedAccounts () {
|
||||
return this.selectedAccounts.length
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('Accounts', ['initialize'])
|
||||
},
|
||||
created () {
|
||||
this.initialize()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<oc-grid key="selected-accounts-info" gutter="small" class="oc-flex-middle">
|
||||
<span v-text="selectionInfoText" />
|
||||
<span>|</span>
|
||||
<div>
|
||||
<oc-button
|
||||
v-text="$gettext('Clear selection')"
|
||||
appearance="raw"
|
||||
@click="RESET_ACCOUNTS_SELECTION"
|
||||
/>
|
||||
</div>
|
||||
<oc-grid gutter="small" id="accounts-batch-actions">
|
||||
<div v-for="action in actions" :key="action.label">
|
||||
<div
|
||||
v-if="isConfirmationInProgress[action.id]"
|
||||
:variation="action.confirmation.variation || 'primary'"
|
||||
noClose
|
||||
class="oc-flex oc-flex-middle tmp-alert-fixes"
|
||||
>
|
||||
<span>{{ action.confirmation.message }}</span>
|
||||
<oc-button
|
||||
:id="action.confirmation.cancel.id"
|
||||
@click="action.confirmation.cancel.handler"
|
||||
:variation="action.confirmation.cancel.variation || 'passive'"
|
||||
>
|
||||
{{ action.confirmation.cancel.label }}
|
||||
</oc-button>
|
||||
<oc-button
|
||||
:id="action.confirmation.confirm.id"
|
||||
@click="action.confirmation.confirm.handler"
|
||||
:variation="action.confirmation.confirm.variation || 'primary'"
|
||||
>
|
||||
{{ action.confirmation.confirm.label }}
|
||||
</oc-button>
|
||||
</div>
|
||||
<oc-button
|
||||
v-else
|
||||
:id="action.id"
|
||||
@click="action.handler"
|
||||
:variation="action.variation || 'primary'"
|
||||
:icon="action.icon"
|
||||
>
|
||||
{{ action.label }}
|
||||
</oc-button>
|
||||
</div>
|
||||
</oc-grid>
|
||||
</oc-grid>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapMutations } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'AccountsBatchActions',
|
||||
props: {
|
||||
numberOfSelectedAccounts: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
selectedAccounts: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
isConfirmationInProgress: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectionInfoText () {
|
||||
const translated = this.$ngettext('%{ amount } selected user', '%{ amount } selected users', this.numberOfSelectedAccounts)
|
||||
return this.$gettextInterpolate(translated, { amount: this.numberOfSelectedAccounts })
|
||||
},
|
||||
actions () {
|
||||
const actions = []
|
||||
const numberOfDisabledAccounts = this.selectedAccounts.filter(account => !account.accountEnabled).length
|
||||
const isAnyAccountDisabled = numberOfDisabledAccounts > 0
|
||||
const isAnyAccountEnabled = numberOfDisabledAccounts < this.numberOfSelectedAccounts
|
||||
|
||||
if (isAnyAccountDisabled) {
|
||||
actions.push({
|
||||
id: 'accounts-batch-action-enable',
|
||||
label: this.$gettext('Activate'),
|
||||
icon: 'ready',
|
||||
handler: () => this.setAccountActivated(true)
|
||||
})
|
||||
}
|
||||
|
||||
if (isAnyAccountEnabled) {
|
||||
actions.push({
|
||||
id: 'accounts-batch-action-disable',
|
||||
label: this.$gettext('Block'),
|
||||
icon: 'deprecated',
|
||||
handler: () => this.setAccountActivated(false)
|
||||
})
|
||||
}
|
||||
|
||||
const idDeleteAction = 'accounts-batch-action-delete'
|
||||
actions.push({
|
||||
id: idDeleteAction,
|
||||
label: this.$gettext('Delete'),
|
||||
icon: 'delete',
|
||||
variation: 'danger',
|
||||
handler: () => this.showConfirmationRequest(idDeleteAction),
|
||||
confirmation: {
|
||||
variation: 'danger',
|
||||
message: this.$ngettext(
|
||||
'Delete the selected account?',
|
||||
'Delete the selected accounts?',
|
||||
this.numberOfSelectedAccounts
|
||||
),
|
||||
cancel: {
|
||||
id: 'accounts-batch-action-delete-cancel',
|
||||
label: this.$gettext('Cancel'),
|
||||
handler: () => this.hideConfirmationRequest(idDeleteAction)
|
||||
},
|
||||
confirm: {
|
||||
id: 'accounts-batch-action-delete-confirm',
|
||||
label: this.$gettext('Confirm'),
|
||||
variation: 'danger',
|
||||
handler: this.deleteAccounts
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return actions
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('Accounts', ['setAccountActivated', 'deleteAccounts']),
|
||||
...mapMutations('Accounts', ['RESET_ACCOUNTS_SELECTION']),
|
||||
showConfirmationRequest (actionId) {
|
||||
this.isConfirmationInProgress = { ...this.isConfirmationInProgress, [actionId]: true }
|
||||
},
|
||||
hideConfirmationRequest (actionId) {
|
||||
this.isConfirmationInProgress = { ...this.isConfirmationInProgress, [actionId]: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tmp-alert-fixes {
|
||||
color: rgb(224, 0, 0) !important;
|
||||
|
||||
font-size: 1.125rem !important;
|
||||
font-weight: 600 !important;
|
||||
line-height: 1.4 !important;
|
||||
}
|
||||
.tmp-alert-fixes > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.tmp-alert-fixes > button {
|
||||
padding: 0.2rem 0.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div>
|
||||
<oc-grid v-if="isFormInProgress" gutter="small">
|
||||
<oc-text-input
|
||||
id="accounts-new-account-input-username"
|
||||
type="text"
|
||||
v-model="formData.username"
|
||||
:error-message="formValidation.usernameError"
|
||||
:label="$gettext('Username')"
|
||||
:disabled="isRequestInProgress"
|
||||
@keydown.enter="createAccount"
|
||||
/>
|
||||
<oc-text-input
|
||||
id="accounts-new-account-input-email"
|
||||
type="email"
|
||||
v-model="formData.email"
|
||||
:error-message="formValidation.emailError"
|
||||
:label="$gettext('Email')"
|
||||
:disabled="isRequestInProgress"
|
||||
@keydown.enter="createAccount"
|
||||
/>
|
||||
<oc-text-input
|
||||
id="accounts-new-account-input-password"
|
||||
type="password"
|
||||
v-model="formData.password"
|
||||
:error-message="formValidation.passwordError"
|
||||
:label="$gettext('Password')"
|
||||
:disabled="isRequestInProgress"
|
||||
@keydown.enter="createAccount"
|
||||
/>
|
||||
<div class="oc-flex">
|
||||
<oc-button
|
||||
class="oc-mr-s oc-mb-s"
|
||||
v-text="$gettext('Cancel')"
|
||||
@click="cancelForm"
|
||||
:disabled="isRequestInProgress"
|
||||
/>
|
||||
<oc-button
|
||||
id="accounts-new-account-button-confirm"
|
||||
class="oc-mr-s oc-mb-s"
|
||||
variation="primary"
|
||||
appearance="filled"
|
||||
:disabled="isRequestInProgress"
|
||||
@click="createAccount"
|
||||
gap-size="small"
|
||||
:class="{ 'border-ods-tmp-fix': !isRequestInProgress }"
|
||||
>
|
||||
<oc-spinner
|
||||
v-if="isRequestInProgress"
|
||||
key="account-creation-in-progress"
|
||||
size="small"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span
|
||||
v-text="
|
||||
isRequestInProgress ? $gettext('Creating') : $gettext('Create')
|
||||
"
|
||||
/>
|
||||
</oc-button>
|
||||
</div>
|
||||
</oc-grid>
|
||||
<oc-grid v-else gutter="small">
|
||||
<div>
|
||||
<oc-button
|
||||
id="accounts-new-account-trigger"
|
||||
key="create-accounts-button"
|
||||
variation="primary"
|
||||
appearance="filled"
|
||||
gap-size="small"
|
||||
@click="setFormInProgress(true)"
|
||||
>
|
||||
<oc-icon name="user-add" />
|
||||
<translate>Create new account</translate>
|
||||
</oc-button>
|
||||
</div>
|
||||
</oc-grid>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import isEmail from 'validator/es/lib/isEmail'
|
||||
import isEmpty from 'validator/es/lib/isEmpty'
|
||||
import debounce from 'debounce'
|
||||
import { mapActions } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'AccountsCreate',
|
||||
|
||||
data: () => ({
|
||||
isFormInProgress: false,
|
||||
isRequestInProgress: false,
|
||||
formData: {
|
||||
username: '',
|
||||
email: '',
|
||||
password: ''
|
||||
},
|
||||
formValidation: {
|
||||
usernameError: '',
|
||||
emailError: '',
|
||||
passwordError: ''
|
||||
}
|
||||
}),
|
||||
|
||||
methods: {
|
||||
...mapActions('Accounts', ['createNewAccount']),
|
||||
|
||||
setFormInProgress (inProgress) {
|
||||
this.isFormInProgress = inProgress
|
||||
},
|
||||
|
||||
cancelForm () {
|
||||
this.isRequestInProgress = false
|
||||
this.setFormInProgress(false)
|
||||
this.formData = {
|
||||
username: '',
|
||||
email: '',
|
||||
password: ''
|
||||
}
|
||||
this.formValidation = {
|
||||
usernameError: '',
|
||||
emailError: '',
|
||||
passwordError: ''
|
||||
}
|
||||
},
|
||||
|
||||
createAccount () {
|
||||
// note: use bitwise AND because we want all checks to be performed
|
||||
if (!(this.checkUsername() & this.checkEmail() & this.checkPassword())) {
|
||||
return
|
||||
}
|
||||
|
||||
this.isRequestInProgress = true
|
||||
this.createNewAccount(this.formData)
|
||||
.then((success) => {
|
||||
if (success) {
|
||||
this.cancelForm()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.isRequestInProgress = false
|
||||
})
|
||||
},
|
||||
|
||||
checkUsername () {
|
||||
if (isEmpty(this.formData.username)) {
|
||||
debounce(this.formValidation.usernameError = this.$gettext('Username cannot be empty'), 500)
|
||||
return false
|
||||
}
|
||||
// hacky check: we want to allow emails and the username part of emails as username
|
||||
if (!isEmail(this.formData.username) && !isEmail(this.formData.username + '@validate.it')) {
|
||||
debounce(this.formValidation.usernameError = this.$gettext('Invalid username'), 500)
|
||||
return false
|
||||
}
|
||||
|
||||
this.formValidation.usernameError = ''
|
||||
return true
|
||||
},
|
||||
|
||||
checkEmail () {
|
||||
if (isEmpty(this.formData.email)) {
|
||||
debounce(this.formValidation.emailError = this.$gettext('Email cannot be empty'), 500)
|
||||
return false
|
||||
}
|
||||
|
||||
if (!isEmail(this.formData.email)) {
|
||||
debounce(this.formValidation.emailError = this.$gettext('Invalid email address'), 500)
|
||||
return false
|
||||
}
|
||||
|
||||
this.formValidation.emailError = ''
|
||||
return true
|
||||
},
|
||||
|
||||
checkPassword () {
|
||||
// Later on some restrictions might be applied here
|
||||
if (isEmpty(this.formData.password)) {
|
||||
debounce(this.formValidation.passwordError = this.$gettext('Password cannot be empty'), 500)
|
||||
return false
|
||||
}
|
||||
|
||||
this.formValidation.passwordError = ''
|
||||
return true
|
||||
}
|
||||
},
|
||||
onDestroy () {
|
||||
this.cancelForm()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#accounts-new-account-button-confirm > span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div>
|
||||
<oc-table-simple id="accounts-user-list" class="oc-mt-l">
|
||||
<oc-thead>
|
||||
<oc-tr>
|
||||
<oc-th shrink type="head" align-h="center">
|
||||
<oc-checkbox
|
||||
class="oc-ml-s"
|
||||
:value="areAllAccountsSelected"
|
||||
@input="toggleSelectionAll"
|
||||
:label="$gettext('Select all users')"
|
||||
hide-label
|
||||
/>
|
||||
</oc-th>
|
||||
<oc-th shrink type="head" />
|
||||
<oc-th type="head" v-text="$gettext('Username')" />
|
||||
<oc-th type="head" v-text="$gettext('Display name')" />
|
||||
<oc-th type="head" v-text="$gettext('Email')" />
|
||||
<oc-th type="head" v-text="$gettext('Role')" />
|
||||
<oc-th
|
||||
shrink
|
||||
type="head"
|
||||
v-text="$gettext('Activated')"
|
||||
align-h="center"
|
||||
/>
|
||||
</oc-tr>
|
||||
</oc-thead>
|
||||
<oc-tbody>
|
||||
<accounts-list-row
|
||||
v-for="account in accounts"
|
||||
:key="`account-list-row-${account.id}`"
|
||||
:account="account"
|
||||
/>
|
||||
</oc-tbody>
|
||||
</oc-table-simple>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters, mapMutations } from 'vuex'
|
||||
import AccountsListRow from './AccountsListRow.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountsList',
|
||||
components: {
|
||||
AccountsListRow
|
||||
},
|
||||
props: {
|
||||
accounts: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('Accounts', ['areAllAccountsSelected'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions('Accounts', ['toggleSelectionAll']),
|
||||
...mapMutations('Accounts', ['RESET_ACCOUNTS_SELECTION'])
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.RESET_ACCOUNTS_SELECTION()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<oc-tr>
|
||||
<oc-td align-h="center">
|
||||
<oc-checkbox
|
||||
class="oc-ml-s"
|
||||
size="large"
|
||||
:value="selectedAccounts"
|
||||
:option="account"
|
||||
@input="TOGGLE_SELECTION_ACCOUNT(account)"
|
||||
:label="selectAccountLabel"
|
||||
hide-label
|
||||
/>
|
||||
</oc-td>
|
||||
<oc-td>
|
||||
<avatar
|
||||
:user-name="account.displayName || account.onPremisesSamAccountName"
|
||||
:userid="account.id"
|
||||
:width="35"
|
||||
/>
|
||||
</oc-td>
|
||||
<oc-td v-text="account.onPremisesSamAccountName" />
|
||||
<oc-td v-text="account.displayName || '-'" />
|
||||
<oc-td v-text="account.mail" />
|
||||
<oc-td>
|
||||
<oc-button
|
||||
:id="`accounts-roles-select-trigger-${account.id}`"
|
||||
class="accounts-roles-select-trigger"
|
||||
appearance="outline"
|
||||
>
|
||||
<span class="oc-flex oc-flex-middle accounts-roles-current-role">
|
||||
{{ currentRole ? currentRole.displayName : $gettext("Select role") }}
|
||||
<oc-icon name="arrow-down-s" aria-hidden="true" />
|
||||
</span>
|
||||
</oc-button>
|
||||
<oc-drop
|
||||
:drop-id="`accounts-roles-select-dropdown-${account.id}`"
|
||||
:toggle="`#accounts-roles-select-trigger-${account.id}`"
|
||||
mode="click"
|
||||
close-on-click
|
||||
:options="{ delayHide: 0 }"
|
||||
>
|
||||
<ul class="oc-list">
|
||||
<li v-for="role in roles" :key="role.id">
|
||||
<oc-radio
|
||||
class="accounts-roles-dropdown-role"
|
||||
v-model="currentRole"
|
||||
:option="role"
|
||||
@input="changeRole(role.id)"
|
||||
:label="role.displayName"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</oc-drop>
|
||||
</oc-td>
|
||||
<oc-td align-h="center">
|
||||
<oc-icon
|
||||
v-if="account.accountEnabled"
|
||||
key="account-icon-enabled"
|
||||
name="user-follow"
|
||||
variation="success"
|
||||
:aria-label="$gettext('Account is activated')"
|
||||
class="accounts-status-indicator-enabled"
|
||||
/>
|
||||
<oc-icon
|
||||
v-else
|
||||
key="account-icon-disabled"
|
||||
name="user-unfollow"
|
||||
variation="danger"
|
||||
:aria-label="$gettext('Account is blocked')"
|
||||
class="accounts-status-indicator-disabled"
|
||||
/>
|
||||
</oc-td>
|
||||
</oc-tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapState, mapActions, mapMutations } from 'vuex'
|
||||
import { isObjectEmpty } from '../../helpers/utils'
|
||||
import { injectAuthToken } from '../../helpers/auth'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { RoleService_AssignRoleToUser, RoleService_ListRoleAssignments } from '../../client/settings'
|
||||
import Avatar from './Avatar.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountsListRow',
|
||||
|
||||
components: { Avatar },
|
||||
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
currentRole: null
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['user', 'getServerForJsClient']),
|
||||
...mapState('Accounts', ['roles', 'selectedAccounts']),
|
||||
|
||||
selectAccountLabel () {
|
||||
const translated = this.$gettext('Select %{ account }')
|
||||
|
||||
return this.$gettextInterpolate(translated, { account: this.account.displayName }, true)
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
this.getUsersCurrentRole()
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions(['showMessage']),
|
||||
...mapMutations('Accounts', ['TOGGLE_SELECTION_ACCOUNT']),
|
||||
|
||||
async changeRole (roleId) {
|
||||
injectAuthToken(this.user.token)
|
||||
|
||||
const response = await RoleService_AssignRoleToUser({
|
||||
$domain: this.getServerForJsClient,
|
||||
body: {
|
||||
account_uuid: this.account.id,
|
||||
role_id: roleId
|
||||
}
|
||||
})
|
||||
|
||||
if (response.status === 201) {
|
||||
const roleId = response.data.assignment.roleId
|
||||
this.currentRole = this.roles.find(role => {
|
||||
return role.id === roleId
|
||||
})
|
||||
} else {
|
||||
this.showMessage({
|
||||
title: this.$gettext('Failed to change role.'),
|
||||
desc: response.statusText,
|
||||
status: 'danger'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async getUsersCurrentRole () {
|
||||
injectAuthToken(this.user.token)
|
||||
|
||||
const response = await RoleService_ListRoleAssignments({
|
||||
$domain: this.getServerForJsClient,
|
||||
body: {
|
||||
account_uuid: this.account.id
|
||||
}
|
||||
})
|
||||
|
||||
if (response.status === 201) {
|
||||
const assignedRole = response.data
|
||||
|
||||
if (isObjectEmpty(assignedRole)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.currentRole = this.roles.find(role => {
|
||||
return role.id === assignedRole.assignments[0].roleId
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<component :is="type" v-if="enabled">
|
||||
<oc-spinner
|
||||
v-if="loading"
|
||||
key="avatar-loading"
|
||||
size="small"
|
||||
:aria-label="$gettext('Loading')"
|
||||
:style="`width: ${width}px; height: ${width}px;`"
|
||||
/>
|
||||
<oc-avatar
|
||||
v-else
|
||||
key="avatar-loaded"
|
||||
:width="width"
|
||||
:src="avatarSource"
|
||||
:user-name="userName"
|
||||
/>
|
||||
</component>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* FIXME: this component has been copied over from ownCloud Web. It should be moved over to ODS, then we can reuse it in
|
||||
* this extension.
|
||||
*/
|
||||
name: 'Avatar',
|
||||
props: {
|
||||
/**
|
||||
* The html element used for the avatar container.
|
||||
* `div, span`
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
default: 'div',
|
||||
validator: value => {
|
||||
return value.match(/(div|span)/)
|
||||
}
|
||||
},
|
||||
userName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
userid: {
|
||||
/**
|
||||
* Allow empty string to show placeholder
|
||||
*/
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 42
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
/**
|
||||
* Set to object URL when loaded, or on failure, icon placeholder is shown
|
||||
*/
|
||||
avatarSource: '',
|
||||
/**
|
||||
* Shows spinner in place whilst loading avatar from server
|
||||
*/
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getToken', 'configuration']),
|
||||
enabled: function () {
|
||||
return this.configuration.enableAvatars || true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
userid: function (userid, old) {
|
||||
this.setUser(userid)
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
// Handled mounted situation. Userid might not be set yet so try placeholder
|
||||
if (this.userid !== '') {
|
||||
this.setUser(this.userid)
|
||||
} else {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Load a new avatar from this userid
|
||||
*/
|
||||
setUser (userid) {
|
||||
this.loading = true
|
||||
this.avatarSource = ''
|
||||
if (!this.enabled || userid === '') {
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
const headers = new Headers()
|
||||
const instance = this.configuration.server || window.location.origin
|
||||
const url = instance + '/remote.php/dav/avatars/' + this.userid + '/128.png'
|
||||
headers.append('Authorization', 'Bearer ' + this.getToken)
|
||||
headers.append('X-Requested-With', 'XMLHttpRequest')
|
||||
fetch(url, { headers })
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.blob()
|
||||
}
|
||||
if (response.status !== 404) {
|
||||
throw new Error(`Unexpected status code ${response.status}`)
|
||||
}
|
||||
})
|
||||
.then(blob => {
|
||||
this.loading = false
|
||||
if (blob) {
|
||||
this.avatarSource = window.URL.createObjectURL(blob)
|
||||
} else {
|
||||
// 404, none found
|
||||
this.avatarSource = ''
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.avatarSource = ''
|
||||
this.loading = false
|
||||
console.error(`Error loading avatar image for user "${this.userid}": `, error.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user