Add file skeleton for app settings

Signed-off-by: Julius Härtl <jus@bitgrid.net>
pull/9565/head
Julius Härtl 2018-05-10 13:26:29 +07:00
parent 8594fdc493
commit 125d1d3d4e
No known key found for this signature in database
GPG Key ID: 4C614C6ED2CDE6DF
7 changed files with 584 additions and 0 deletions

@ -150,7 +150,9 @@ class AppSettingsController extends Controller {
$policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com');
$templateResponse->setContentSecurityPolicy($policy);
return new TemplateResponse('settings', 'settings', ['serverData' => $params]);
return $templateResponse;
}
private function getAllCategories() {

@ -0,0 +1,187 @@
<!--
- @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
-
- @author Julius Härtl <jus@bitgrid.net>
-
- @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/>.
-
-->
<template>
<div id="app-sidebar">
<h2>{{ app.name }}</h2>
</div>
</template>
<script>
import userRow from './userList/userRow';
import Multiselect from 'vue-multiselect';
import InfiniteLoading from 'vue-infinite-loading';
import Vue from 'vue';
export default {
name: 'userList',
props: ['users', 'showConfig', 'selectedGroup'],
components: {
userRow,
Multiselect,
InfiniteLoading
},
data() {
let unlimitedQuota = {id:'none', label:t('settings', 'Unlimited')},
defaultQuota = {id:'default', label:t('settings', 'Default quota')};
return {
unlimitedQuota: unlimitedQuota,
defaultQuota: defaultQuota,
loading: false,
scrolled: false,
newUser: {
id:'',
displayName:'',
password:'',
mailAddress:'',
groups: [],
subAdminsGroups: [],
quota: defaultQuota,
language: {code: 'en', name: t('settings', 'Default language')}
}
};
},
mounted() {
if (!this.settings.canChangePassword) {
OC.Notification.showTemporary(t('settings', 'Password change is disabled because the master key is disabled'));
}
/**
* Init default language from server data. The use of this.settings
* requires a computed variable,vwhich break the v-model binding of the form,
* this is a much easier solution than getter and setter
*/
Vue.set(this.newUser.language, 'code', this.settings.defaultLanguage);
},
computed: {
settings() {
return this.$store.getters.getServerData;
},
filteredUsers() {
if (this.selectedGroup === 'disabled') {
let disabledUsers = this.users.filter(user => user.enabled !== true);
if (disabledUsers.length===0 && this.$refs.infiniteLoading && this.$refs.infiniteLoading.isComplete) {
// disabled group is empty, redirection to all users
this.$router.push('users');
this.$refs.infiniteLoading.$emit('$InfiniteLoading:reset');
}
return disabledUsers;
}
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');
},
subAdminsGroups() {
// data provided php side
return this.$store.getters.getServerData.subadmingroups;
},
quotaOptions() {
// convert the preset array into objects
let quotaPreset = this.settings.quotaPreset.reduce((acc, cur) => acc.concat({id:cur, label:cur}), []);
// add default presets
quotaPreset.unshift(this.unlimitedQuota);
quotaPreset.unshift(this.defaultQuota);
return quotaPreset;
},
minPasswordLength() {
return this.$store.getters.getPasswordPolicyMinLength;
},
usersOffset() {
return this.$store.getters.getUsersOffset;
},
usersLimit() {
return this.$store.getters.getUsersLimit;
},
/* LANGUAGES */
languages() {
return Array(
{
label: t('settings', 'Common languages'),
languages: this.settings.languages.commonlanguages
},
{
label: t('settings', 'All languages'),
languages: this.settings.languages.languages
}
);
}
},
watch: {
// watch url change and group select
selectedGroup: function (val, old) {
this.$store.commit('resetUsers');
this.$refs.infiniteLoading.$emit('$InfiniteLoading:reset');
}
},
methods: {
onScroll(event) {
this.scrolled = event.target.scrollTop>0;
},
/**
* Validate quota string to make sure it's a valid human file size
*
* @param {string} quota Quota in readable format '5 GB'
* @returns {Object}
*/
validateQuota(quota) {
// only used for new presets sent through @Tag
let validQuota = OC.Util.computerFileSize(quota);
if (validQuota !== null && validQuota > 0) {
// unify format output
quota = OC.Util.humanFileSize(OC.Util.computerFileSize(quota));
return this.newUser.quota = {id: quota, label: quota};
}
// Default is unlimited
return this.newUser.quota = this.quotaOptions[0];
},
infiniteHandler($state) {
this.$store.dispatch('getUsers', {
offset: this.usersOffset,
limit: this.usersLimit,
group: this.selectedGroup !== 'disabled' ? this.selectedGroup : ''})
.then((response) => {response?$state.loaded():$state.complete()});
},
resetForm() {
// revert form to original state
Object.assign(this.newUser, this.$options.data.call(this).newUser);
this.loading = false;
},
createUser() {
this.loading = true;
this.$store.dispatch('addUser', {
userid: this.newUser.id,
password: this.newUser.password,
email: this.newUser.mailAddress,
groups: this.newUser.groups.map(group => group.id),
subadmin: this.newUser.subAdminsGroups.map(group => group.id),
quota: this.newUser.quota.id,
language: this.newUser.language.code,
}).then(() => this.resetForm());
}
}
}
</script>

@ -0,0 +1,116 @@
<!--
- @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
-
- @author Julius Härtl <jus@bitgrid.net>
-
- @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/>.
-
-->
<template>
<div id="app-content">
<div id="apps-list" class="installed">
<div class="apps-header" v-if="category === 'app-bundles'">
<div class="app-image"></div>
<h2>Firmen-Paket <input class="enable" type="submit" data-bundleid="EnterpriseBundle" data-active="true" value="Alle aktivieren"></h2>
<div class="app-version"></div>
<div class="app-level"></div>
<div class="app-groups"></div>
<div class="actions">&nbsp;</div>
</div>
<div class="section" v-for="app in apps">
<div class="app-image app-image-icon">
<svg width="32" height="32" viewBox="0 0 32 32">
<defs><filter id="invertIconApps-606"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix></filter></defs>
<image x="0" y="0" width="32" height="32" preserveAspectRatio="xMinYMin meet" filter="url(#invertIconApps-606)" xlink:href="/core/img/places/default-app-icon.svg?v=13.0.2.1" class="app-icon"></image>
</svg>
</div>
<div class="app-name">
{{ app.name }}
</div>
<div class="app-version">{{ app.version }}</div>
<div class="app-level">
<a href="https://apps.nextcloud.com/apps/apporder">Im Store anzeigen </a>
</div>
<div class="app-groups">
<div class="groups-enable">
<input type="checkbox" class="groups-enable__checkbox checkbox" id="groups_enable-apporder">
<label for="groups_enable-apporder">Auf Gruppen beschränken</label>
<input type="hidden" class="group_select" title="Alle" value="">
</div>
</div>
<div class="actions">
<div class="warning hidden"></div>
<input class="update hidden" type="submit" value="Aktualisierung auf false" data-appid="apporder">
<input class="enable" type="submit" data-appid="apporder" data-active="true" value="Deaktivieren">
</div>
</div>
</div>
</div>
</template>
<script>
import userRow from './userList/userRow';
import Multiselect from 'vue-multiselect';
import InfiniteLoading from 'vue-infinite-loading';
import Vue from 'vue';
export default {
name: 'appList',
props: ['category'],
components: {
Multiselect,
},
data() {
return {
loading: false,
scrolled: false,
};
},
watch: {
// watch url change and group select
category: function (val, old) {
this.$store.commit('resetApps');
this.$store.dispatch('getApps', { category: this.category });
}
},
mounted() {
this.$store.dispatch('getApps', { category: this.category });
},
computed: {
apps() {
return this.$store.getters.getApps;
},
},
methods: {
createUser() {
this.loading = true;
this.$store.dispatch('addUser', {
userid: this.newUser.id,
password: this.newUser.password,
email: this.newUser.mailAddress,
groups: this.newUser.groups.map(group => group.id),
subadmin: this.newUser.subAdminsGroups.map(group => group.id),
quota: this.newUser.quota.id,
language: this.newUser.language.code,
}).then(() => this.resetForm());
}
}
}
</script>

@ -1,6 +1,7 @@
import Vue from 'vue';
import Router from 'vue-router';
import Users from './views/Users';
import Apps from './views/Apps';
Vue.use(Router);
@ -32,6 +33,26 @@ export default new Router({
component: Users
}
]
},
{
path: '/:index(index.php/)?settings/apps',
component: Apps,
props: true,
name: 'apps',
children: [
{
path: ':category',
name: 'apps-category',
component: Apps,
children: [
{
path: ':id',
name: 'apps-details',
component: Apps
}
]
}
]
}
]
});

@ -0,0 +1,99 @@
/*
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @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/>.
*
*/
import api from './api';
const state = {
apps: [],
categories: [],
updateCount: 0
};
const mutations = {
initCategories(state, {categories, updateCount}) {
state.categories = categories;
state.updateCount = updateCount;
},
setUpdateCount(state, updateCount) {
state.updateCount = updateCount;
},
addCategory(state, category) {
state.categories.push(category);
},
appendCategories(state, categoriesArray) {
// convert obj to array
state.categories = categoriesArray;
},
setApps(state, apps) {
state.apps = apps;
},
reset(state) {
state.apps = [];
state.categories = [];
state.updateCount = 0;
}
};
const getters = {
getCategories(state) {
return state.categories;
},
getApps(state) {
return state.apps;
},
getUpdateCount(state) {
return state.updateCount;
}
};
const actions = {
getApps(context, { category }) {
return api.get(OC.generateUrl(`settings/apps/list?category=${category}`))
.then((response) => {
context.commit('setApps', response.data.apps);
return true;
})
.catch((error) => context.commit('API_FAILURE', error))
},
getCategories(context) {
return api.get(OC.generateUrl('settings/apps/categories'))
.then((response) => {
if (response.data.length > 0) {
context.commit('appendCategories', response.data);
return true;
}
return false;
})
.catch((error) => context.commit('API_FAILURE', error));
},
};
export default { state, mutations, getters, actions };

@ -1,6 +1,7 @@
import Vue from 'vue';
import Vuex from 'vuex';
import users from './users';
import apps from './apps';
import settings from './settings';
import oc from './oc';
@ -23,6 +24,7 @@ const mutations = {
export default new Vuex.Store({
modules: {
users,
apps,
settings,
oc
},

@ -0,0 +1,157 @@
<!--
- @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
-
- @author Julius Härtl <jus@bitgrid.net>
-
- @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/>.
-
-->
<template>
<div id="app">
<app-navigation :menu="menu" />
<app-list :category="category"></app-list>
</div>
</template>
<script>
import appNavigation from '../components/appNavigation';
import appList from '../components/appList';
import Vue from 'vue';
import VueLocalStorage from 'vue-localstorage'
import Multiselect from 'vue-multiselect';
import api from '../store/api';
Vue.use(VueLocalStorage)
Vue.use(VueLocalStorage)
export default {
name: 'Apps',
props: ['category'],
components: {
appNavigation,
appList,
},
beforeMount() {
this.$store.dispatch('getCategories');
this.$store.commit('setUpdateCount', this.$store.getters.getServerData.updateCount)
},
data() {
return {
}
},
computed: {
categories() {
return this.$store.getters.getCategories;
},
apps() {
return this.$store.getters.getApps;
},
loading() {
return Object.keys(this.apps).length === 0;
},
updateCount() {
return this.$store.getters.getUpdateCount;
},
settings() {
return this.$store.getters.getServerData;
},
// BUILD APP NAVIGATION MENU OBJECT
menu() {
// Data provided php side
let categories = this.$store.getters.getCategories;
categories = Array.isArray(categories) ? categories : [];
// Map groups
categories = categories.map(category => {
let item = {};
item.id = category.ident;
item.icon = 'icon-category-' + category.ident;
item.classes = []; // empty classes, active will be set later
item.router = { // router link to
name: 'apps-category',
params: {category: category.ident}
};
item.text = category.displayName;
return item;
});
// Add everyone group
let defaultCategories = [
{
id: 'app-category-your-apps',
classes: [],
router: {name: 'apps'},
icon: 'icon-category-installed',
text: t('settings', 'Your apps'),
},
{
id: 'app-category-enabled',
classes: [],
icon: 'icon-category-enabled',
router: {name: 'apps-category', params: {category: 'enabled'}},
text: t('settings', 'Active apps'),
}, {
id: 'app-category-disabled',
classes: [],
icon: 'icon-category-disabled',
router: {name: 'apps-category', params: {category: 'disabled'}},
text: t('settings', 'Disabled apps'),
}
];
if (this.$store.getters.getUpdateCount > 0) {
defaultCategories.push({
id: 'app-category-updates',
classes: [],
icon: 'icon-download',
router: {name: 'apps-category', params: {category: 'updates'}},
text: t('settings', 'Updates'),
utils: {counter: this.$store.getters.getUpdateCount}
});
}
defaultCategories.push({
id: 'app-category-app-bundles',
classes: [],
icon: 'icon-category-app-bundles',
router: {name: 'apps-category', params: {category: 'app-bundles'}},
text: t('settings', 'App bundles'),
});
categories = defaultCategories.concat(categories);
// Set current group as active
let activeGroup = categories.findIndex(group => group.id === this.category);
if (activeGroup >= 0) {
categories[activeGroup].classes.push('active');
} else {
categories[0].classes.push('active');
}
// Return
return {
id: 'appscategories',
items: categories
}
},
}
}
</script>