diff options
author | Julius Härtl <jus@bitgrid.net> | 2018-05-10 13:26:29 +0200 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2018-06-06 11:40:09 +0200 |
commit | 125d1d3d4e7494a0982b1f2adec1e8de2b91b5f7 (patch) | |
tree | 88744d8b3061fc77145ce9dcd21a85f462001603 /settings/src | |
parent | 8594fdc493a0bb16bfe37a7cde6c5b8f37a9f4e2 (diff) | |
download | nextcloud-server-125d1d3d4e7494a0982b1f2adec1e8de2b91b5f7.tar.gz nextcloud-server-125d1d3d4e7494a0982b1f2adec1e8de2b91b5f7.zip |
Add file skeleton for app settings
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'settings/src')
-rw-r--r-- | settings/src/components/appDetails.vue | 187 | ||||
-rw-r--r-- | settings/src/components/appList.vue | 116 | ||||
-rw-r--r-- | settings/src/router.js | 21 | ||||
-rw-r--r-- | settings/src/store/apps.js | 99 | ||||
-rw-r--r-- | settings/src/store/index.js | 2 | ||||
-rw-r--r-- | settings/src/views/Apps.vue | 157 |
6 files changed, 582 insertions, 0 deletions
diff --git a/settings/src/components/appDetails.vue b/settings/src/components/appDetails.vue new file mode 100644 index 00000000000..d007555dd5b --- /dev/null +++ b/settings/src/components/appDetails.vue @@ -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> diff --git a/settings/src/components/appList.vue b/settings/src/components/appList.vue new file mode 100644 index 00000000000..8dc7fede01d --- /dev/null +++ b/settings/src/components/appList.vue @@ -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"> </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> diff --git a/settings/src/router.js b/settings/src/router.js index 270949d542c..bfcff91e992 100644 --- a/settings/src/router.js +++ b/settings/src/router.js @@ -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 + } + ] + } + ] } ] }); diff --git a/settings/src/store/apps.js b/settings/src/store/apps.js new file mode 100644 index 00000000000..d9888465dd6 --- /dev/null +++ b/settings/src/store/apps.js @@ -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 };
\ No newline at end of file diff --git a/settings/src/store/index.js b/settings/src/store/index.js index 2bd8d76e41b..00f3ad809ad 100644 --- a/settings/src/store/index.js +++ b/settings/src/store/index.js @@ -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 }, diff --git a/settings/src/views/Apps.vue b/settings/src/views/Apps.vue new file mode 100644 index 00000000000..eac9bec7527 --- /dev/null +++ b/settings/src/views/Apps.vue @@ -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> |