Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl 2019-07-19 19:12:00 +02:00
Родитель 48c627ef0b
Коммит b848201e58
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4C614C6ED2CDE6DF
12 изменённых файлов: 853 добавлений и 3 удалений

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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>