Start with vue admin panel
Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Родитель
48c627ef0b
Коммит
b848201e58
|
@ -49,6 +49,7 @@ return [
|
|||
['name' => 'settings#setPersonalSettings', 'url' => 'ajax/personal.php', 'verb' => 'POST'],
|
||||
['name' => 'settings#setSettings', 'url' => 'ajax/admin.php', 'verb' => 'POST'],
|
||||
['name' => 'settings#getSettings', 'url' => 'ajax/settings.php', 'verb' => 'GET'],
|
||||
['name' => 'settings#updateWatermarkSettings', 'url' => 'settings/watermark', 'verb' => 'POST'],
|
||||
['name' => 'settings#checkSettings', 'url' => 'settings/check', 'verb' => 'GET'],
|
||||
|
||||
//Mobile access
|
||||
|
|
|
@ -16,9 +16,20 @@ use \OCP\IConfig;
|
|||
|
||||
class AppConfig{
|
||||
private $defaults = [
|
||||
'wopi_url' => 'https://localhost:9980'
|
||||
'wopi_url' => 'https://localhost:9980',
|
||||
'watermark_text' => '{userId}',
|
||||
'watermark_allGroupsList' => [],
|
||||
'watermark_allTagsList' => [],
|
||||
'watermark_linkTagsList' => [],
|
||||
|
||||
];
|
||||
|
||||
const APP_SETTING_TYPES = [
|
||||
'watermark_allGroupsList' => 'array',
|
||||
'watermark_allTagsList' => 'array',
|
||||
'watermark_linkTagsList' => 'array'
|
||||
];
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
|
@ -39,6 +50,18 @@ class AppConfig{
|
|||
return $this->config->getAppValue(Application::APPNAME, $key, $defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return array
|
||||
*/
|
||||
public function getAppValueArray($key) {
|
||||
$value = $this->config->getAppValue(Application::APPNAME, $key, []);
|
||||
if (self::APP_SETTING_TYPES[$key] === 'array') {
|
||||
$value = $value !== '' ? explode(',', $value) : [];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value by key
|
||||
* @param string $key
|
||||
|
@ -48,4 +71,20 @@ class AppConfig{
|
|||
public function setAppValue($key, $value) {
|
||||
$this->config->setAppValue(Application::APPNAME, $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all app settings
|
||||
* @return array
|
||||
*/
|
||||
public function getAppSettings() {
|
||||
$result = [];
|
||||
$keys = $this->config->getAppKeys('richdocuments');
|
||||
foreach ($keys as $key) {
|
||||
$value = $this->getAppValueArray($key);
|
||||
$value = $value === 'yes' ? true : $value;
|
||||
$result[$key] = $value === 'no' ? false : $value;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -182,6 +182,49 @@ class SettingsController extends Controller{
|
|||
return new JSONResponse($response);
|
||||
}
|
||||
|
||||
public function updateWatermarkSettings($settings = []) {
|
||||
$supportedOptions = [
|
||||
'watermark_text',
|
||||
'watermark_enabled',
|
||||
'watermark_shareAll',
|
||||
'watermark_shareRead',
|
||||
'watermark_linkSecure',
|
||||
'watermark_linkRead',
|
||||
'watermark_linkAll',
|
||||
'watermark_linkTags',
|
||||
'watermark_linkTagsList',
|
||||
'watermark_allGroups',
|
||||
'watermark_allGroupsList',
|
||||
'watermark_allTags',
|
||||
'watermark_allTagsList',
|
||||
];
|
||||
$message = $this->l10n->t('Saved');
|
||||
|
||||
$watermarkSettings = $settings['watermark'];
|
||||
foreach ($watermarkSettings as $key => $value) {
|
||||
$fullKey = 'watermark_' . $key;
|
||||
if (in_array($fullKey, $supportedOptions) !== true) {
|
||||
return new JSONResponse([
|
||||
'status' => 'error',
|
||||
'data' => ['message' => $this->l10n->t('Invalid config key') . ' ' . $fullKey]
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
$value = $value === true ? 'yes' : $value;
|
||||
$value = $value === false ? 'no' : $value;
|
||||
if (AppConfig::APP_SETTING_TYPES[$fullKey] === 'array') {
|
||||
$value = implode(',', $value);
|
||||
}
|
||||
$this->appConfig->setAppValue($fullKey, $value);
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => ['message' => $message]
|
||||
];
|
||||
|
||||
return new JSONResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace OCA\Richdocuments\Controller;
|
|||
|
||||
use OC\Files\View;
|
||||
use OCA\Richdocuments\Db\Wopi;
|
||||
use OCA\Richdocuments\AppConfig;
|
||||
use OCA\Richdocuments\Db\WopiMapper;
|
||||
use OCA\Richdocuments\TemplateManager;
|
||||
use OCA\Richdocuments\TokenManager;
|
||||
|
@ -55,6 +56,8 @@ class WopiController extends Controller {
|
|||
private $urlGenerator;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var AppConfig */
|
||||
private $appConfig;
|
||||
/** @var TokenManager */
|
||||
private $tokenManager;
|
||||
/** @var IUserManager */
|
||||
|
@ -92,6 +95,7 @@ class WopiController extends Controller {
|
|||
IRootFolder $rootFolder,
|
||||
IURLGenerator $urlGenerator,
|
||||
IConfig $config,
|
||||
AppConfig $appConfig,
|
||||
TokenManager $tokenManager,
|
||||
IUserManager $userManager,
|
||||
WopiMapper $wopiMapper,
|
||||
|
@ -104,6 +108,7 @@ class WopiController extends Controller {
|
|||
$this->rootFolder = $rootFolder;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->config = $config;
|
||||
$this->appConfig = $appConfig;
|
||||
$this->tokenManager = $tokenManager;
|
||||
$this->userManager = $userManager;
|
||||
$this->wopiMapper = $wopiMapper;
|
||||
|
@ -184,6 +189,20 @@ class WopiController extends Controller {
|
|||
$response['TemplateSaveAs'] = $file->getName();
|
||||
}
|
||||
|
||||
if ($this->shouldWatermark($isPublic, $wopi->getEditorUid(), $fileId, $wopi)) {
|
||||
$replacements = [
|
||||
'userId' => $wopi->getEditorUid(),
|
||||
'date' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
'themingName' => \OC::$server->getThemingDefaults()->getName(),
|
||||
'themingName' => \OC::$server->getThemingDefaults()->getName(),
|
||||
];
|
||||
$watermarkTemplate = $this->config->getAppValue('richdocuments', 'watermark_text', '{userId}');
|
||||
$response['WatermarkText'] = preg_replace_callback('/{(.+?)}/', function($matches) use ($replacements)
|
||||
{
|
||||
return $replacements[$matches[1]];
|
||||
}, $watermarkTemplate);
|
||||
}
|
||||
|
||||
$user = $this->userManager->get($wopi->getEditorUid());
|
||||
if($user !== null && $user->getAvatarImage(32) !== null) {
|
||||
$response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $wopi->getEditorUid(), 'size' => 32]);
|
||||
|
@ -216,6 +235,59 @@ class WopiController extends Controller {
|
|||
return $response;
|
||||
}
|
||||
|
||||
private function shouldWatermark($isPublic, $userId, $fileId, Wopi $wopi) {
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_enabled', 'no') === 'no') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($isPublic) {
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_linkAll', 'no') === 'yes') {
|
||||
return true;
|
||||
}
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_linkRead', 'no') === 'yes' && !$wopi->getCanwrite()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_linkSecure', 'no') === 'yes' && $wopi->getHideDownload()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_linkTags', 'no') === 'yes') {
|
||||
$tags = $this->appConfig->getAppValueArray('watermark_linkTagsList');
|
||||
$fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], 'files')[$fileId];
|
||||
foreach ($fileTags as $tagId) {
|
||||
if (in_array($tagId, $tags, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_shareAll', 'no') === 'yes') {
|
||||
return true;
|
||||
}
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_shareRead', 'no') === 'yes' && !$wopi->getCanwrite()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_allGroups', 'no') === 'yes') {
|
||||
$groups = $this->appConfig->getAppValueArray('watermark_allGroupsList');
|
||||
foreach ($groups as $group) {
|
||||
if (\OC::$server->getGroupManager()->isInGroup($userId, $group)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->config->getAppValue('richdocuments', 'watermark_allTags', 'no') === 'yes') {
|
||||
$tags = $this->appConfig->getAppValueArray('watermark_allTagsList');
|
||||
$fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], 'files');
|
||||
foreach ($fileTags as $tagId) {
|
||||
if (in_array($tagId, $tags)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an access token and a fileId, returns the contents of the file.
|
||||
* Expects a valid token in access_token parameter.
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
namespace OCA\Richdocuments\Settings;
|
||||
|
||||
use OCA\Richdocuments\AppConfig;
|
||||
use OCA\Richdocuments\Capabilities;
|
||||
use OCA\Richdocuments\TemplateManager;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
|
@ -34,6 +35,9 @@ class Admin implements ISettings {
|
|||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var AppConfig */
|
||||
private $appConfig;
|
||||
|
||||
/** @var TemplateManager */
|
||||
private $manager;
|
||||
|
||||
|
@ -48,9 +52,11 @@ class Admin implements ISettings {
|
|||
* @param Capabilities $capabilities
|
||||
*/
|
||||
public function __construct(IConfig $config,
|
||||
AppConfig $appConfig,
|
||||
TemplateManager $manager,
|
||||
Capabilities $capabilities) {
|
||||
$this->config = $config;
|
||||
$this->appConfig = $appConfig;
|
||||
$this->manager = $manager;
|
||||
$this->capabilities = $capabilities->getCapabilities()['richdocuments'];
|
||||
}
|
||||
|
@ -70,7 +76,8 @@ class Admin implements ISettings {
|
|||
'canonical_webroot' => $this->config->getAppValue('richdocuments', 'canonical_webroot'),
|
||||
'disable_certificate_verification' => $this->config->getAppValue('richdocuments', 'disable_certificate_verification'),
|
||||
'templates' => $this->manager->getSystemFormatted(),
|
||||
'templatesAvailable' => array_key_exists('templates', $this->capabilities) && $this->capabilities['templates']
|
||||
'templatesAvailable' => array_key_exists('templates', $this->capabilities) && $this->capabilities['templates'],
|
||||
'settings' => $this->appConfig->getAppSettings(),
|
||||
],
|
||||
'blank'
|
||||
);
|
||||
|
@ -91,4 +98,5 @@ class Admin implements ISettings {
|
|||
public function getPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
25
src/admin.js
25
src/admin.js
|
@ -1,6 +1,29 @@
|
|||
/* global OC, $ */
|
||||
import Vue from 'vue'
|
||||
import AdminSettings from './components/AdminSettings'
|
||||
|
||||
var documentsSettings = {
|
||||
// CSP config for webpack dynamic chunk loading
|
||||
// eslint-disable-next-line
|
||||
__webpack_nonce__ = btoa(OC.requestToken)
|
||||
|
||||
// Correct the root of the app for chunk loading
|
||||
// OC.linkTo matches the apps folders
|
||||
// eslint-disable-next-line
|
||||
__webpack_public_path__ = OC.linkTo('richdocuments', 'js/')
|
||||
|
||||
Vue.prototype.t = t
|
||||
Vue.prototype.n = n
|
||||
Vue.prototype.OC = OC
|
||||
Vue.prototype.OCA = OCA
|
||||
|
||||
const element = document.getElementById('admin-vue')
|
||||
|
||||
/* eslint-disable-next-line no-new */
|
||||
new Vue({
|
||||
render: h => h(AdminSettings, { props: { initial: JSON.parse(element.dataset.initial) } })
|
||||
}).$mount('#admin-vue')
|
||||
|
||||
const documentsSettings = {
|
||||
_createExtApp: function() {
|
||||
var app1 = document.createElement('div')
|
||||
app1.setAttribute('class', 'external-app')
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2019 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 class="section">
|
||||
<h2>Secure view settings</h2>
|
||||
|
||||
<p>{{ t('richdocuments', 'Secure view enables you to secure documents by embedding a watermark') }}</p>
|
||||
<settings-checkbox v-model="settings.watermark.enabled" label="Enable watermarking" hint=""
|
||||
:disabled="updating" @input="update" />
|
||||
<settings-input-text v-if="settings.watermark.enabled" v-model="settings.watermark.text" label="Watermark text"
|
||||
:hint="t('richdocuments', 'Supported placeholders: {userId}, {date}')"
|
||||
:disabled="updating" @update="update" />
|
||||
<div v-if="settings.watermark.enabled">
|
||||
<settings-checkbox v-model="settings.watermark.allTags" label="Show watermark on tagged files" :disabled="updating"
|
||||
@input="update" />
|
||||
<p v-if="settings.watermark.allTags" class="checkbox-details">
|
||||
<settings-select-tag v-model="settings.watermark.allTagsList" label="Select tags to enforce watermarking" @input="update" />
|
||||
</p>
|
||||
<settings-checkbox v-model="settings.watermark.allGroups" label="Show watermark for users of groups" :disabled="updating"
|
||||
@input="update" />
|
||||
<p v-if="settings.watermark.allGroups" class="checkbox-details">
|
||||
<settings-select-group v-model="settings.watermark.allGroupsList" label="Select tags to enforce watermarking" @input="update" />
|
||||
</p>
|
||||
<settings-checkbox v-model="settings.watermark.shareAll" label="Show watermark for all shares" hint=""
|
||||
:disabled="updating" @input="update" />
|
||||
<settings-checkbox v-if="!settings.watermark.shareAll" v-model="settings.watermark.shareRead" label="Show watermark for read only shares"
|
||||
hint=""
|
||||
:disabled="updating" @input="update" />
|
||||
|
||||
<h3>Link shares</h3>
|
||||
<settings-checkbox v-model="settings.watermark.linkAll" label="Show watermark for all link shares" hint=""
|
||||
:disabled="updating" @input="update" />
|
||||
<settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkSecure" label="Show watermark for download hidden shares"
|
||||
hint=""
|
||||
:disabled="updating" @input="update" />
|
||||
<settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkRead" label="Show watermark for read only link shares"
|
||||
hint=""
|
||||
:disabled="updating" @input="update" />
|
||||
<settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkTags" label="Show watermark on link shares with specific system tags"
|
||||
:disabled="updating" @input="update" />
|
||||
<p v-if="!settings.watermark.linkAll && settings.watermark.linkTags" class="checkbox-details">
|
||||
<settings-select-tag v-model="settings.watermark.linkTagsList" label="Select tags to enforce watermarking" @input="update" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import axios from 'nextcloud-axios'
|
||||
import SettingsCheckbox from './SettingsCheckbox'
|
||||
import SettingsInputText from './SettingsInputText'
|
||||
import SettingsSelectTag from './SettingsSelectTag'
|
||||
import SettingsSelectGroup from './SettingsSelectGroup'
|
||||
import { generateUrl } from 'nextcloud-router'
|
||||
|
||||
export default {
|
||||
name: 'AdminSettings',
|
||||
components: {
|
||||
SettingsCheckbox,
|
||||
SettingsInputText,
|
||||
SettingsSelectTag,
|
||||
SettingsSelectGroup
|
||||
},
|
||||
props: {
|
||||
initial: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
updating: false,
|
||||
groups: [],
|
||||
tags: [],
|
||||
settings: {
|
||||
watermark: {
|
||||
enabled: false,
|
||||
shareAll: false,
|
||||
shareRead: false,
|
||||
linkSecure: false,
|
||||
linkRead: false,
|
||||
linkAll: false,
|
||||
linkTags: false,
|
||||
linkTagsList: [],
|
||||
allGroups: false,
|
||||
allGroupsList: [],
|
||||
allTags: false,
|
||||
allTagsList: [],
|
||||
text: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
for (let key in this.initial) {
|
||||
if (!this.initial.hasOwnProperty(key)) {
|
||||
continue
|
||||
}
|
||||
|
||||
let [ parent, setting ] = key.split('_')
|
||||
if (parent === 'watermark') {
|
||||
Vue.set(this.settings[parent], setting, this.initial[key])
|
||||
} else {
|
||||
Vue.set(this.settings, key, this.initial[key])
|
||||
}
|
||||
|
||||
}
|
||||
Vue.set(this.settings, 'data', this.initial)
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.updating = true
|
||||
let settings = this.settings
|
||||
axios.post(generateUrl('/apps/richdocuments/settings/watermark'), { settings }).then((response) => {
|
||||
this.updating = false
|
||||
}).catch((error) => {
|
||||
this.updating = false
|
||||
OC.Notification.showTemporary('Failed to save settings')
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
p.checkbox-details {
|
||||
margin-left: 25px;
|
||||
margin-top: -10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
input,
|
||||
.multiselect {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,78 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2019 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>
|
||||
<p>
|
||||
<input :id="id" type="checkbox" class="checkbox"
|
||||
:checked="inputVal" :disabled="disabled" @change="$emit('input', $event.target.checked)">
|
||||
<label :for="id">{{ label }}</label><br>
|
||||
<em v-if="hint !== ''">{{ hint }}</em>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let uuid = 0
|
||||
export default {
|
||||
name: 'SettingsCheckbox',
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputVal: this.value
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
id() {
|
||||
return 'settings-checkbox-' + this.uuid
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.inputVal = this.value
|
||||
}
|
||||
},
|
||||
beforeCreate: function() {
|
||||
this.uuid = uuid.toString()
|
||||
uuid += 1
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,98 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2019 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>
|
||||
<form @submit.prevent="">
|
||||
<div class="input-wrapper">
|
||||
<label :for="id">{{ label }}</label>
|
||||
<input :id="id" type="text" :value="inputVal"
|
||||
:disabled="disabled" @input="$emit('input', $event.target.value)">
|
||||
<input type="submit" class="icon-confirm" value=""
|
||||
@click="$emit('update', inputVal)">
|
||||
</div>
|
||||
<p v-if="hint !== ''" class="hint">
|
||||
{{ hint }}
|
||||
</p>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let uuid = 0
|
||||
export default {
|
||||
name: 'SettingsInputText',
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputVal: this.value
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
id() {
|
||||
return 'settings-input-text-' + this.uuid
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.inputVal = this.value
|
||||
}
|
||||
},
|
||||
beforeCreate: function() {
|
||||
this.uuid = uuid.toString()
|
||||
uuid += 1
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
label {
|
||||
width: 100%;
|
||||
}
|
||||
input[type=text] {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.hint {
|
||||
color: var(--color-text-lighter);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,125 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2019 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>
|
||||
<multiselect v-model="inputValObjects"
|
||||
:options="Object.values(groups)" :options-limit="5"
|
||||
:placeholder="label"
|
||||
track-by="id"
|
||||
label="displayname"
|
||||
class="multiselect-vue" :multiple="true"
|
||||
:close-on-select="false" :tag-width="60"
|
||||
:disabled="disabled" @input="update"
|
||||
@search-change="asyncFindGroup">
|
||||
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
||||
</multiselect>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'nextcloud-axios'
|
||||
import Multiselect from 'nextcloud-vue/dist/Components/Multiselect'
|
||||
|
||||
let uuid = 0
|
||||
export default {
|
||||
name: 'SettingsSelectGroup',
|
||||
components: {
|
||||
Multiselect
|
||||
},
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValObjects: [],
|
||||
groups: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
id() {
|
||||
return 'settings-select-group-' + this.uuid
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.inputValObjects = this.getValueObject()
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.uuid = uuid.toString()
|
||||
uuid += 1
|
||||
this.asyncFindGroup('').then((result) => {
|
||||
this.inputValObjects = this.getValueObject()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getValueObject() {
|
||||
return this.value.filter((group) => group !== '' && typeof group !== 'undefined').map(
|
||||
(id) => {
|
||||
if (typeof this.groups[id] === 'undefined') {
|
||||
return {
|
||||
id: id,
|
||||
displayname: id
|
||||
}
|
||||
}
|
||||
return this.groups[id]
|
||||
}
|
||||
)
|
||||
},
|
||||
update() {
|
||||
this.$emit('input', this.inputValObjects.map((element) => element.id))
|
||||
},
|
||||
asyncFindGroup(query) {
|
||||
query = typeof query === 'string' ? encodeURI(query) : ''
|
||||
return axios.get(OC.linkToOCS(`cloud/groups/details?search=${query}&limit=1`, 2))
|
||||
.then((response) => {
|
||||
if (Object.keys(response.data.ocs.data.groups).length > 0) {
|
||||
response.data.ocs.data.groups.forEach((element) => {
|
||||
if (typeof this.groups[element.id] === 'undefined') {
|
||||
this.groups[element.id] = element
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}).catch((error) => {
|
||||
this.$emit('error', error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,200 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2019 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>
|
||||
<multiselect v-model="inputValObjects"
|
||||
:options="tags" :options-limit="5"
|
||||
:placeholder="label"
|
||||
track-by="id"
|
||||
:custom-label="tagLabel"
|
||||
class="multiselect-vue" :multiple="true"
|
||||
:close-on-select="false" :tag-width="60"
|
||||
:disabled="disabled" @input="update"
|
||||
@search-change="asyncFind">
|
||||
<span slot="noResult">{{ t('settings', 'No results') }}</span>
|
||||
<template #option="scope">
|
||||
{{ tagLabel(scope.option) }}
|
||||
</template>
|
||||
</multiselect>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'nextcloud-axios'
|
||||
|
||||
import { generateRemoteUrl } from 'nextcloud-router'
|
||||
import Multiselect from 'nextcloud-vue/dist/Components/Multiselect'
|
||||
|
||||
const xmlToJson = (xml) => {
|
||||
let obj = {}
|
||||
|
||||
if (xml.nodeType === 1) {
|
||||
if (xml.attributes.length > 0) {
|
||||
obj['@attributes'] = {}
|
||||
for (let j = 0; j < xml.attributes.length; j++) {
|
||||
const attribute = xml.attributes.item(j)
|
||||
obj['@attributes'][attribute.nodeName] = attribute.nodeValue
|
||||
}
|
||||
}
|
||||
} else if (xml.nodeType === 3) {
|
||||
obj = xml.nodeValue
|
||||
}
|
||||
|
||||
if (xml.hasChildNodes()) {
|
||||
for (let i = 0; i < xml.childNodes.length; i++) {
|
||||
const item = xml.childNodes.item(i)
|
||||
const nodeName = item.nodeName
|
||||
if (typeof (obj[nodeName]) === 'undefined') {
|
||||
obj[nodeName] = xmlToJson(item)
|
||||
} else {
|
||||
if (typeof obj[nodeName].push === 'undefined') {
|
||||
var old = obj[nodeName]
|
||||
obj[nodeName] = []
|
||||
obj[nodeName].push(old)
|
||||
}
|
||||
obj[nodeName].push(xmlToJson(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
const parseXml = (xml) => {
|
||||
let dom = null
|
||||
try {
|
||||
dom = (new DOMParser()).parseFromString(xml, 'text/xml')
|
||||
} catch (e) {
|
||||
console.error('Failed to parse xml document', e)
|
||||
}
|
||||
return dom
|
||||
}
|
||||
|
||||
const xmlToTagList = (xml) => {
|
||||
let json = xmlToJson(parseXml(xml))
|
||||
let list = json['d:multistatus']['d:response']
|
||||
let result = []
|
||||
for (let index in list) {
|
||||
let tag = list[index]['d:propstat']
|
||||
|
||||
if (tag['d:status']['#text'] !== 'HTTP/1.1 200 OK') {
|
||||
continue
|
||||
}
|
||||
result.push({
|
||||
id: tag['d:prop']['oc:id']['#text'],
|
||||
displayName: tag['d:prop']['oc:display-name']['#text'],
|
||||
canAssign: tag['d:prop']['oc:can-assign']['#text'] === 'true',
|
||||
userAssignable: tag['d:prop']['oc:user-assignable']['#text'] === 'true',
|
||||
userVisible: tag['d:prop']['oc:user-visible']['#text'] === 'true'
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const searchTags = function() {
|
||||
return axios({
|
||||
method: 'PROPFIND',
|
||||
url: generateRemoteUrl('dav') + '/systemtags/',
|
||||
data: `<?xml version="1.0"?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
|
||||
<d:prop>
|
||||
<oc:id />
|
||||
<oc:display-name />
|
||||
<oc:user-visible />
|
||||
<oc:user-assignable />
|
||||
<oc:can-assign />
|
||||
</d:prop>
|
||||
</d:propfind>`
|
||||
}).then((response) => {
|
||||
return xmlToTagList(response.data)
|
||||
})
|
||||
}
|
||||
|
||||
let uuid = 0
|
||||
export default {
|
||||
name: 'SettingsSelectTag',
|
||||
components: {
|
||||
Multiselect
|
||||
},
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValObjects: [],
|
||||
tags: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
id() {
|
||||
return 'settings-input-text-' + this.uuid
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.inputValObjects = this.getValueObject()
|
||||
}
|
||||
},
|
||||
beforeCreate: function() {
|
||||
this.uuid = uuid.toString()
|
||||
uuid += 1
|
||||
searchTags().then((result) => {
|
||||
this.tags = result
|
||||
this.inputValObjects = this.getValueObject()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
asyncFind(query) {
|
||||
},
|
||||
getValueObject() {
|
||||
return this.value.filter((tag) => tag !== '').map(
|
||||
(id) => this.tags.find((tag) => tag.id === id)
|
||||
)
|
||||
},
|
||||
update() {
|
||||
this.$emit('input', this.inputValObjects.map((element) => element.id))
|
||||
},
|
||||
tagLabel({ displayName, userVisible, userAssignable }) {
|
||||
if (userVisible === false) {
|
||||
return `${displayName} (invisible)`
|
||||
}
|
||||
if (userAssignable === false) {
|
||||
return `${displayName} (restricted)`
|
||||
}
|
||||
return displayName
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -110,3 +110,5 @@ script('files', 'jquery.fileupload');
|
|||
</ul>
|
||||
</form>
|
||||
<?php } ?>
|
||||
|
||||
<div id="admin-vue" data-initial="<?php p(json_encode($_['settings'], true)); ?>"></div>
|
||||
|
|
Загрузка…
Ссылка в новой задаче