Skip to content
This repository has been archived by the owner on Jan 27, 2021. It is now read-only.

Add enable and disable batch actions #109

Merged
merged 8 commits into from
Sep 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog/unreleased/add-enable-disable-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Add enable/disable capabilities to the WebUI

We've added batch actions into the accounts listing to provide options to enable and disable accounts.

https://github.com/owncloud/product/issues/118
https://github.com/owncloud/ocis-accounts/pull/109
23 changes: 4 additions & 19 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,25 @@ go 1.13
require (
github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415
github.com/blevesearch/bleve v1.0.9
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
github.com/go-chi/chi v4.1.2+incompatible
github.com/go-chi/render v1.0.1
github.com/go-test/deep v1.0.6 // indirect
github.com/gofrs/uuid v3.3.0+incompatible
github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/protobuf v1.4.2
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/mennanov/fieldmask-utils v0.3.2
github.com/micro/cli/v2 v2.1.2
github.com/micro/go-micro/v2 v2.9.1
github.com/oklog/run v1.1.0
github.com/olekukonko/tablewriter v0.0.4
github.com/onsi/ginkgo v1.10.1 // indirect
github.com/onsi/gomega v1.7.0 // indirect
github.com/owncloud/ocis v1.0.0-rc1 // indirect
github.com/owncloud/ocis-pkg/v2 v2.4.1-0.20200902134813-1e87c6173ada
github.com/owncloud/ocis-settings v0.3.2-0.20200828130413-0cc0f5bf26fe
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/owncloud/ocis-settings v0.3.2-0.20200903035407-ad5de8264f91
github.com/restic/calens v0.2.0
github.com/rs/zerolog v1.19.0
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.6.1
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
github.com/tredoe/osutil v1.0.5
golang.org/x/net v0.0.0-20200625001655-4c5254603344
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece
golang.org/x/net v0.0.0-20200822124328-c89045814202
google.golang.org/genproto v0.0.0-20200624020401-64a14ca9d1ad
google.golang.org/protobuf v1.25.0
)

Expand Down
626 changes: 623 additions & 3 deletions go.sum

Large diffs are not rendered by default.

35 changes: 32 additions & 3 deletions pkg/assets/embed.go

Large diffs are not rendered by default.

92 changes: 87 additions & 5 deletions ui/components/App.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
<template>
<div>
<div class="uk-container uk-padding">
<h1>
Accounts
</h1>
<h1 v-text="$gettext('Accounts')" />
<oc-grid v-if="numberOfSelectedAccounts > 0" key="selected-accounts-info" gutter="small" class="uk-flex-middle">
<span v-text="selectionInfoText" />
<span>|</span>
<div>
<oc-button v-text="$gettext('Clear selection')" variation="raw" @click="RESET_ACCOUNTS_SELECTION" />
</div>
<div>
<oc-action-drop class="accounts-actions-dropdown">
<template v-slot:button>
<span class="uk-margin-xsmall-right" v-text="$gettext('Actions')" />
<oc-icon name="expand_more" />
LukasHirt marked this conversation as resolved.
Show resolved Hide resolved
</template>
<template v-slot:actions>
<oc-button
LukasHirt marked this conversation as resolved.
Show resolved Hide resolved
v-for="(action, index) in actions"
:key="action.label"
:id="action.id"
variation="raw"
role="menuitem"
:class="{ 'uk-margin-small-bottom': index + 1 !== actions.length }"
class="uk-width-1-1 uk-flex-left"
@click="action.handler"
>
{{ action.label }}
</oc-button>
</template>
</oc-action-drop>
</div>
</oc-grid>
<template v-if="isInitialized">
<accounts-list :accounts="accounts" />
</template>
Expand All @@ -13,22 +40,77 @@
</template>

<script>
import { mapGetters, mapActions } from 'vuex'
import { mapGetters, mapActions, mapState, mapMutations } from 'vuex'
import AccountsList from './accounts/AccountsList.vue'
export default {
name: 'App',
components: { AccountsList },
computed: {
...mapGetters('Accounts', ['isInitialized', 'getAccountsSorted']),
...mapState('Accounts', ['selectedAccounts']),

accounts () {
return this.getAccountsSorted
},

numberOfSelectedAccounts () {
return this.selectedAccounts.length
},

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-actions-dropdown-action-enable',
label: this.$gettext('Enable'),
handler: () => this.toggleAccountStatus(true)
})
}

if (isAnyAccountEnabled) {
actions.push({
id: 'accounts-actions-dropdown-action-disable',
label: this.$gettext('Disable'),
handler: () => this.toggleAccountStatus(false)
})
}

return actions
}
},
methods: {
...mapActions('Accounts', ['initialize'])
...mapActions('Accounts', ['initialize', 'toggleAccountStatus']),
...mapMutations('Accounts', ['RESET_ACCOUNTS_SELECTION'])
},
created () {
this.initialize()
},
beforeDestroy () {
this.RESET_ACCOUNTS_SELECTION()
}
}
</script>

<style>
/* TODO: After https://github.com/owncloud/owncloud-design-system/pull/418 gets merged
there won't be an extra span and this won't be needed anymore */
.accounts-selection-actions-btn > span {
display: flex;
align-items: center;
}

/* TODO: Adjust in ODS */
.oc-dropdown-menu {
width: 150px;
}
</style>
15 changes: 15 additions & 0 deletions ui/components/accounts/AccountsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
<oc-table middle divider>
<oc-table-group>
<oc-table-row>
<oc-table-cell shrink type="head">
<oc-checkbox
:value="areAllAccountsSelected"
:label="$gettext('Select all users')"
hide-label
@change="toggleSelectionAll"
/>
</oc-table-cell>
<oc-table-cell shrink type="head" />
<oc-table-cell type="head" v-text="$gettext('Username')" />
<oc-table-cell type="head" v-text="$gettext('Display name')" />
Expand All @@ -25,6 +33,7 @@
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import AccountsListRow from './AccountsListRow.vue'

export default {
Expand All @@ -37,6 +46,12 @@ export default {
type: Array,
required: true
}
},
computed: {
...mapGetters('Accounts', ['areAllAccountsSelected'])
},
methods: {
...mapActions('Accounts', ['toggleSelectionAll'])
}
}
</script>
36 changes: 32 additions & 4 deletions ui/components/accounts/AccountsListRow.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<template>
<oc-table-row>
<oc-table-cell>
<oc-checkbox :value="isAccountSelected" @change="TOGGLE_SELECTION_ACCOUNT(account)" :label="selectAccountLabel" hide-label />
</oc-table-cell>
<oc-table-cell>
<avatar :user-name="account.displayName || account.onPremisesSamAccountName" :userid="account.id" :width="35" />
</oc-table-cell>
Expand Down Expand Up @@ -39,14 +42,28 @@
<oc-table-cell v-text="account.uidNumber || '-'" />
<oc-table-cell v-text="account.gidNumber || '-'" />
<oc-table-cell class="uk-text-center">
<oc-icon v-if="account.accountEnabled" name="ready" variation="success" :aria-label="$gettext('Account is enabled')" />
<oc-icon v-else name="deprecated" variation="danger" :aria-label="$gettext('Account is disabled')" />
<oc-icon
v-if="account.accountEnabled"
key="account-icon-enabled"
name="ready"
variation="success"
:aria-label="$gettext('Account is enabled')"
class="accounts-status-indicator-enabled"
/>
<oc-icon
v-else
name="deprecated"
key="account-icon-disabled"
variation="danger"
:aria-label="$gettext('Account is disabled')"
class="accounts-status-indicator-disabled"
/>
</oc-table-cell>
</oc-table-row>
</template>

<script>
import { mapGetters, mapState, mapActions } from 'vuex'
import { mapGetters, mapState, mapActions, mapMutations } from 'vuex'
import { isObjectEmpty } from '../../helpers/utils'
import { injectAuthToken } from '../../helpers/auth'
// eslint-disable-next-line camelcase
Expand All @@ -73,7 +90,17 @@ export default {

computed: {
...mapGetters(['user', 'configuration']),
...mapState('Accounts', ['roles'])
...mapState('Accounts', ['roles', 'selectedAccounts']),

isAccountSelected () {
return this.selectedAccounts.indexOf(this.account) > -1
},

selectAccountLabel () {
const translated = this.$gettext('Select %{ account }')

return this.$gettextInterpolate(translated, { account: this.account.displayName }, true)
}
},

created () {
Expand All @@ -82,6 +109,7 @@ export default {

methods: {
...mapActions(['showMessage']),
...mapMutations('Accounts', ['TOGGLE_SELECTION_ACCOUNT']),

async changeRole (roleId) {
injectAuthToken(this.user.token)
Expand Down
83 changes: 80 additions & 3 deletions ui/store/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable camelcase */
import { AccountsService_ListAccounts } from '../client/accounts'
import { AccountsService_ListAccounts, AccountsService_UpdateAccount } from '../client/accounts'
import { RoleService_ListRoles } from '../client/settings'
/* eslint-enable camelcase */
import { injectAuthToken } from '../helpers/auth'
Expand All @@ -8,7 +8,8 @@ const state = {
config: null,
initialized: false,
accounts: {},
roles: null
roles: null,
selectedAccounts: []
}

const getters = {
Expand All @@ -21,7 +22,8 @@ const getters = {
}
return a1.onPremisesSamAccountName.localeCompare(a2.onPremisesSamAccountName)
})
}
},
areAllAccountsSelected: state => state.accounts.length === state.selectedAccounts.length
}

const mutations = {
Expand All @@ -36,6 +38,24 @@ const mutations = {
},
SET_ROLES (state, roles) {
state.roles = roles
},
TOGGLE_SELECTION_ACCOUNT (state, account) {
const accountIndex = state.selectedAccounts.indexOf(account)

accountIndex > -1 ? state.selectedAccounts.splice(accountIndex, 1) : state.selectedAccounts.push(account)
},
SET_SELECTED_ACCOUNTS (state, accounts) {
state.selectedAccounts = accounts
},

UPDATE_ACCOUNT (state, updatedAccount) {
const accountIndex = state.accounts.findIndex(account => account.id === updatedAccount.id)

state.accounts.splice(accountIndex, 1, updatedAccount)
},

RESET_ACCOUNTS_SELECTION (state) {
state.selectedAccounts = []
}
}

Expand Down Expand Up @@ -87,6 +107,63 @@ const actions = {
status: 'danger'
}, { root: true })
}
},

toggleSelectionAll ({ commit, getters, state }) {
getters.areAllAccountsSelected ? commit('RESET_ACCOUNTS_SELECTION') : commit('SET_SELECTED_ACCOUNTS', [...state.accounts])
},

async toggleAccountStatus ({ commit, dispatch, state, rootGetters }, status) {
const failedAccounts = []
injectAuthToken(rootGetters.user.token)

for (const account of state.selectedAccounts) {
if (account.accountEnabled === status) {
continue
}

const response = await AccountsService_UpdateAccount({
$domain: rootGetters.configuration.server,
body: {
account: {
id: account.id,
accountEnabled: status
},
update_mask: {
paths: ['AccountEnabled']
}
}
})

if (response.status === 201) {
commit('UPDATE_ACCOUNT', { ...account, accountEnabled: status })
} else {
failedAccounts.push({ account: account.diisplayName, statusText: response.statusText })
}
}

if (failedAccounts.length === 1) {
const failedMessageTitle = status ? 'Failed to enable account.' : 'Failed to disable account.'

dispatch('showMessage', {
title: failedMessageTitle,
desc: failedAccounts[0].statusText,
status: 'danger'
}, { root: true })
}

if (failedAccounts.length > 1) {
const failedMessageTitle = status ? 'Failed to enable accounts.' : 'Failed to disable accounts.'
const failedMessageDesc = status ? 'Could not enable multiple accounts.' : 'Could not disable multiple accounts.'

dispatch('showMessage', {
title: failedMessageTitle,
desc: failedMessageDesc,
status: 'danger'
}, { root: true })
}

commit('RESET_ACCOUNTS_SELECTION')
}
}

Expand Down
Loading