Browse Source

Group filtering, disable state, ocs api final update

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
tags/v14.0.0beta1
John Molakvoæ (skjnldsv) 6 years ago
parent
commit
2ae82137cd
No account linked to committer's email address

+ 4
- 3
core/css/inputs.scss View File

@@ -636,8 +636,9 @@ input {
opacity: 1 !important;
}
}
&.multiselect--disabled {
background-color: nc-darken($color-main-background, 8%);
&.multiselect--disabled,
&.multiselect--disabled .multiselect__single {
background-color: nc-darken($color-main-background, 8%) !important;
}
.multiselect__tags {
display: flex;
@@ -683,7 +684,7 @@ input {
.multiselect__single {
padding: 8px 10px;
flex: 0 0 100%;
z-index: 5;
z-index: 1; /* above input */
background-color: $color-main-background;
cursor: pointer;
}

+ 8
- 0
settings/css/settings.scss View File

@@ -1289,6 +1289,9 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
grid-template-columns: 44px;
grid-auto-columns: min-content;
border-top: $color-border 1px solid;
&.disabled {
opacity: .5;
}
.name,
.displayName,
.password {
@@ -1363,6 +1366,7 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
left: 2px;
bottom: 2px;
height: 3px;
z-index: 5; /* above multiselect */
}
}
.icon-confirm {
@@ -1381,6 +1385,9 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
img {
display: block;
}
&.icon-loading > img {
display: none;
}
}
.toggleUserActions {
position: relative;
@@ -1419,6 +1426,7 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
display: flex;
align-items: center;
justify-content: center;
grid-row-start: span 4;
}
.users-list-end {
opacity: .5;

+ 10
- 10
settings/js/main.js
File diff suppressed because it is too large
View File


+ 3
- 8
settings/src/components/popoverMenu/popoverItem.vue View File

@@ -1,10 +1,10 @@
<template>
<li>
<a @click="dispatchToStore" v-if="item.href" :href="(item.href) ? item.href : '#' ">
<a @click="item.action" v-if="item.href" :href="(item.href) ? item.href : '#' ">
<span :class="item.icon"></span>
<span>{{item.text}}</span>
</a>
<button @click="dispatchToStore(item.action)" v-else>
<button @click="item.action" v-else>
<span :class="item.icon"></span>
<span>{{item.text}}</span>
</button>
@@ -13,11 +13,6 @@

<script>
export default {
props: ['item'],
methods: {
dispatchToStore () {
this.$store.dispatch(this.item.action, this.item.data);
}
}
props: ['item']
}
</script>

+ 45
- 8
settings/src/components/userList.vue View File

@@ -82,11 +82,17 @@
</div>
</form>

<user-row v-for="(user, key) in users" :user="user" :key="key" :settings="settings" :showConfig="showConfig"
<user-row v-for="(user, key) in filteredUsers" :user="user" :key="key" :settings="settings" :showConfig="showConfig"
:groups="groups" :subAdminsGroups="subAdminsGroups" :quotaOptions="quotaOptions" />
<infinite-loading @infinite="infiniteHandler">
<span slot="spinner"><div class="users-icon-loading"></div></span>
<span slot="no-more"><div class="users-list-end">— {{t('settings', 'no more results')}} —</div></span>
<infinite-loading @infinite="infiniteHandler" ref="infiniteLoading">
<div slot="spinner"><div class="users-icon-loading icon-loading"></div></div>
<div slot="no-more"><div class="users-list-end">— {{t('settings', 'no more results')}} —</div></div>
<div slot="no-results">
<div id="emptycontent">
<div class="icon-contacts-dark"></div>
<h2>{{t('settings', 'No users in here')}}</h2>
</div>
</div>
</infinite-loading>
</div>
</template>
@@ -132,6 +138,12 @@ export default {
settings() {
return this.$store.getters.getServerData;
},
filteredUsers() {
if (this.route.hash === '#group_disabled') {
return this.users.filter(user => user.enabled !== true);
}
return this.users.filter(user => user.enabled === true);
},
groups() {
// data provided php side + remove the disabled group
return this.$store.getters.getGroups.filter(group => group.id !== '_disabled');
@@ -156,7 +168,30 @@ export default {
},
usersLimit() {
return this.$store.getters.getUsersLimit;
},
},
route() {
return this.$store.getters.getRoute;
},
// get selected hash
selectedGroup() {
let hash = this.route.hash;
if (typeof hash === 'string' && hash.length > 0) {
// we have a valid hash: groupXXXX
// group_XXXX are reserved groups
let split = hash.split('group');
if (split.length === 2 && split[1].charAt(0) !== '_') {
return hash.split('group')[1];
}
}
return '';
}
},
watch: {
// watch url change and group select
selectedGroup: function (val, old) {
this.$store.commit('resetUsers');
this.$refs.infiniteLoading.$emit('$InfiniteLoading:reset');
}
},
methods: {
onScroll(event) {
@@ -182,7 +217,7 @@ export default {
},

infiniteHandler($state) {
this.$store.dispatch('getUsers', {offset:this.usersOffset, limit:this.usersLimit})
this.$store.dispatch('getUsers', {offset:this.usersOffset, limit:this.usersLimit, group:this.selectedGroup})
.then((response) => {response?$state.loaded():$state.complete()});
},

@@ -197,8 +232,10 @@ export default {
userid: this.newUser.id,
password: this.newUser.password,
email: this.newUser.mailAddress,
groups: this.newUser.groups.map(group => group.id)
}).then(() =>this.resetForm());
groups: this.newUser.groups.map(group => group.id),
subadmin: this.newUser.subAdminsGroups.map(group => group.id),
quota: this.newUser.quota.id
}).then(() => this.resetForm());
}
}
}

+ 31
- 8
settings/src/components/userList/userRow.vue View File

@@ -1,6 +1,6 @@
<template>
<div class="row">
<div class="avatar"><img alt="" width="32" height="32" :src="generateAvatar(user.id, 32)" :srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'"></div>
<div class="row" :class="{'disabled': loading.delete || loading.disable}">
<div class="avatar" :class="{'icon-loading': loading.delete || loading.disable}"><img alt="" width="32" height="32" :src="generateAvatar(user.id, 32)" :srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'"></div>
<div class="name">{{user.id}}</div>
<form class="displayName" :class="{'icon-loading-small': loading.displayName}" v-on:submit.prevent="updateDisplayName">
<input :id="'displayName'+user.id+rand" type="text"
@@ -59,7 +59,7 @@
{{user.lastLogin>0 ? OC.Util.relativeModifiedDate(user.lastLogin) : t('settings','Never')}}
</div>
<div class="userActions">
<div class="toggleUserActions" v-if="OC.currentUser !== user.id && user.id !== 'admin'">
<div class="toggleUserActions" v-if="OC.currentUser !== user.id && user.id !== 'admin' && !loading.all">
<div class="icon-more" v-click-outside="hideMenu" @click="showMenu"></div>
<div class="popovermenu" :class="{ 'open': openedMenu }">
<popover-menu :menu="userActions" />
@@ -100,7 +100,9 @@ export default {
mailAddress: false,
groups: false,
subadmins: false,
quota: false
quota: false,
delete: false,
disable: false
}
}
},
@@ -110,13 +112,11 @@ export default {
return [{
icon: 'icon-delete',
text: t('settings','Delete user'),
action: 'deleteUser',
data: this.user.id
action: this.deleteUser
},{
'icon': this.user.enabled ? 'icon-close' : 'icon-add',
'text': this.user.enabled ? t('settings','Disable user') : t('settings','Enable user'),
'action': 'enableDisableUser',
data: {userid: this.user.id, enabled: !this.user.enabled}
'action': this.enableDisableUser
}]
},

@@ -199,6 +199,29 @@ export default {
return '+'+count;
},

deleteUser() {
this.loading.delete = true;
this.loading.all = true;
let userid = this.user.id;
return this.$store.dispatch('deleteUser', {userid})
.then(() => {
this.loading.delete = false
this.loading.all = false
});
},

enableDisableUser() {
this.loading.delete = true;
this.loading.all = true;
let userid = this.user.id;
let enabled = !this.user.enabled;
return this.$store.dispatch('enableDisableUser', {userid, enabled})
.then(() => {
this.loading.delete = false
this.loading.all = false
});
},

/**
* Set user displayName
*

+ 1
- 1
settings/src/store/api.js View File

@@ -33,7 +33,7 @@ export default {
.catch((error) => Promise.reject(error));
},
patch(url, data) {
return axios.patch(sanitize(url), { data: data, headers: tokenHeaders.headers })
return axios.patch(sanitize(url), data, tokenHeaders)
.then((response) => Promise.resolve(response))
.catch((error) => Promise.reject(error));
},

+ 15
- 8
settings/src/store/index.js View File

@@ -1,17 +1,23 @@
import Vue from 'vue'
import Vuex from 'vuex'
import users from './users'
import settings from './settings'
import Vue from 'vue';
import Vuex from 'vuex';
import users from './users';
import settings from './settings';

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production'
const debug = process.env.NODE_ENV !== 'production';

const mutations = {
API_FAILURE(state, error) {
console.log(state, error);
}
}
};

const getters = {
getRoute(state) {
return state.route;
}
};

export default new Vuex.Store({
modules: {
@@ -20,5 +26,6 @@ export default new Vuex.Store({
},
strict: debug,

mutations
})
mutations,
getters
});

+ 46
- 7
settings/src/store/users.js View File

@@ -19,6 +19,7 @@ const state = {
minPasswordLength: 0,
usersOffset: 0,
usersLimit: 25,
userCount: 0
};

const mutations = {
@@ -31,9 +32,10 @@ const mutations = {
setPasswordPolicyMinLength(state, length) {
state.minPasswordLength = length!=='' ? length : 0;
},
initGroups(state, {groups, orderBy}) {
initGroups(state, {groups, orderBy, userCount}) {
state.groups = groups;
state.orderBy = orderBy;
state.userCount = userCount;
state.groups = orderGroups(state.groups, state.orderBy);
},
addGroup(state, groupid) {
@@ -87,7 +89,10 @@ const mutations = {
},
enableDisableUser(state, { userid, enabled }) {
state.users.find(user => user.id == userid).enabled = enabled;
// increment or not
state.groups.find(group => group.id == '_disabled').usercount += enabled ? -1 : 1;
state.userCount += enabled ? 1 : -1;
console.log(enabled);
},
setUserData(state, { userid, key, value }) {
if (key === 'quota') {
@@ -97,6 +102,14 @@ const mutations = {
state.users.find(user => user.id == userid)[key] = value;
}
},

/**
* Reset users list
*/
resetUsers(state) {
state.users = [];
state.usersOffset = 0;
}
};

const getters = {
@@ -114,10 +127,14 @@ const getters = {
},
getUsersLimit(state) {
return state.usersLimit;
},
getUserCount(state) {
return state.userCount;
}
};

const actions = {

/**
* Get all users with full details
*
@@ -125,10 +142,25 @@ const actions = {
* @param {Object} options
* @param {int} options.offset List offset to request
* @param {int} options.limit List number to return from offset
* @param {string} options.search Search amongst users
* @param {string} options.group Get users from group
* @returns {Promise}
*/
getUsers(context, { offset, limit, search }) {
getUsers(context, { offset, limit, search, group }) {
search = typeof search === 'string' ? search : '';
group = typeof group === 'string' ? group : '';
if (group !== '') {
return api.get(OC.linkToOCS(`cloud/groups/${group}/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2))
.then((response) => {
if (Object.keys(response.data.ocs.data.users).length > 0) {
context.commit('appendUsers', response.data.ocs.data.users);
return true;
}
return false;
})
.catch((error) => context.commit('API_FAILURE', error));
}

return api.get(OC.linkToOCS(`cloud/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2))
.then((response) => {
if (Object.keys(response.data.ocs.data.users).length > 0) {
@@ -200,7 +232,7 @@ const actions = {
},

/**
* Add group
* Remove group
*
* @param {Object} context
* @param {string} gid Group id
@@ -289,7 +321,7 @@ const actions = {
* @param {string} userid User id
* @returns {Promise}
*/
deleteUser(context, userid) {
deleteUser(context, { userid }) {
return api.requireAdmin().then((response) => {
return api.delete(OC.linkToOCS(`cloud/users/${userid}`, 2))
.then((response) => context.commit('deleteUser', userid))
@@ -305,12 +337,19 @@ const actions = {
* @param {string} options.userid User id
* @param {string} options.password User password
* @param {string} options.email User email
* @param {string} options.groups User groups
* @param {string} options.subadmin User subadmin groups
* @param {string} options.quota User email
* @returns {Promise}
*/
addUser({context, dispatch}, {userid, password, email, groups}) {
addUser({context, dispatch}, { userid, password, email, groups, subadmin, quota }) {
console.log(subadmin, quota);
return api.requireAdmin().then((response) => {
return api.post(OC.linkToOCS(`cloud/users`, 2), {userid, password, email, groups})
.then((response) => dispatch('addUserData', userid))
return api.post(OC.linkToOCS(`cloud/users`, 2), { userid, password, email, groups, subadmin, quota })
.then((response) => {
//let quotaDis = dispatch('setUserData', { userid, key: 'quota', value:quota });
//let subadminDis = dispatch('addUserSubAdmin', userid);
})
.catch((error) => context.commit('API_FAILURE', { userid, error }));
});
},

+ 26
- 9
settings/src/views/Users.vue View File

@@ -39,7 +39,8 @@ export default {
beforeMount() {
this.$store.commit('initGroups', {
groups: this.$store.getters.getServerData.groups,
orderBy: this.$store.getters.getServerData.sortGroups
orderBy: this.$store.getters.getServerData.sortGroups,
userCount: this.$store.getters.getServerData.userCount
});
this.$store.dispatch('getPasswordPolicyMinLength');
},
@@ -66,6 +67,9 @@ export default {
}
},
computed: {
route() {
return this.$store.getters.getRoute;
},
users() {
return this.$store.getters.getUsers;
},
@@ -96,6 +100,9 @@ export default {
this.setLocalStorage('showStoragePath', status);
}
},
userCount() {
return this.$store.getters.getUserCount;
},
menu() {
let self = this;
// Data provided php side
@@ -114,24 +121,34 @@ export default {
});

// Adjust data
if (groups[0].id === 'admin') {
groups[0].text = t('settings', 'Admins');} // rename admin group
if (groups[1].id === '_disabled') {
groups[1].text = t('settings', 'Disabled users'); // rename disabled group
if (groups[1].utils.counter === 0) {
groups.splice(1, 1); // remove disabled if empty
let adminGroup = groups.find(group => group.id == 'admin');
let disabledGroup = groups.find(group => group.id == '_disabled');
if (adminGroup.text) {
adminGroup.text = t('settings', 'Admins');} // rename admin group
if (disabledGroup.text) {
disabledGroup.text = t('settings', 'Disabled users'); // rename disabled group
if (disabledGroup.utils.counter === 0) {
groups.splice(groups.findIndex(group => group.id == '_disabled'), 1); // remove disabled if empty
}
}

// Add everyone group
groups.unshift({
id: '_everyone',
classes: ['active'],
classes: [],
href:'#group_everyone',
text: t('settings', 'Everyone'),
utils: {counter: this.users.length}
utils: {counter: this.userCount}
});

// Set current group as active
let activeGroup = groups.findIndex(group => group.href === this.route.hash);
if (activeGroup >= 0) {
groups[activeGroup].classes.push('active');
} else {
groups[0].classes.push('active');
}

// Return
return {
id: 'usergrouplist',

+ 13
- 7
settings/users.php View File

@@ -113,16 +113,22 @@ $disabledUsersGroup = [
$allGroups = array_merge_recursive($adminGroup, $groups);

/* QUOTAS PRESETS */
$quotaPreset=$config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB');
$quotaPreset=explode(',', $quotaPreset);
foreach($quotaPreset as &$preset) {
$preset=trim($preset);
$quotaPreset = $config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB');
$quotaPreset = explode(',', $quotaPreset);
foreach ($quotaPreset as &$preset) {
$preset = trim($preset);
}
$quotaPreset=array_diff($quotaPreset, array('default', 'none'));
$defaultQuota=$config->getAppValue('files', 'default_quota', 'none');
$quotaPreset = array_diff($quotaPreset, array('default', 'none'));
$defaultQuota = $config->getAppValue('files', 'default_quota', 'none');

\OC::$server->getEventDispatcher()->dispatch('OC\Settings\Users::loadAdditionalScripts');

/* TOTAL USERS COUNT */
function addition($v, $w) {
return $v+$w;
}
$userCount = array_reduce($userManager->countUsers(), 'addition', 0);

/* FINAL DATA */
$serverData = array();
// groups
@@ -132,7 +138,7 @@ $serverData['subadmingroups'] = $groups;
$serverData['subadmins'] = $subAdmins;
$serverData['sortGroups'] = $sortGroupsBy;
$serverData['quotaPreset'] = $quotaPreset;
$serverData['userCount'] = $userManager->countUsers();
$serverData['userCount'] = $userCount-$disabledUsers;
// Settings
$serverData['defaultQuota'] = $defaultQuota;
$serverData['canChangePassword'] = $canChangePassword;

Loading…
Cancel
Save