fix: Move to new dialogs APIs
Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Родитель
5945876f8c
Коммит
c6306bb4a3
|
@ -21,5 +21,8 @@ module.exports = {
|
|||
'jsdoc/check-values': 'off',
|
||||
'jsdoc/valid-types': 'off',
|
||||
'jsdoc/no-undefined-types': 'off',
|
||||
'jsdoc/require-param-description': 'off',
|
||||
'jsdoc/require-param-type': 'off',
|
||||
'jsdoc/require-property-description': 'off',
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,12 +83,12 @@ describe('Nextcloud integration', function() {
|
|||
cy.get('#w2ui-overlay-download-as-menu .menu-text').eq(1).click()
|
||||
})
|
||||
|
||||
cy.get('.oc-dialog').should('be.visible')
|
||||
cy.get('.oc-dialog input[type=text]')
|
||||
cy.get('.saveas-dialog').should('be.visible')
|
||||
cy.get('.saveas-dialog input[type=text]')
|
||||
.should('be.visible')
|
||||
.should('have.value', 'document.rtf')
|
||||
.should('have.value', '/document.rtf')
|
||||
|
||||
cy.get('.oc-dialog button.primary').click()
|
||||
cy.get('.saveas-dialog button.button-vue--vue-primary').click()
|
||||
|
||||
cy.get('@loleafletframe').within(() => {
|
||||
cy.get('#closebutton').click()
|
||||
|
|
13
src/admin.js
13
src/admin.js
|
@ -1,4 +1,6 @@
|
|||
import './init-shared.js'
|
||||
import Vue from 'vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import AdminSettings from './components/AdminSettings.vue'
|
||||
import '../css/admin.scss'
|
||||
|
@ -59,11 +61,8 @@ function deleteTemplate(event) {
|
|||
elmt.classList.remove('icon-delete')
|
||||
|
||||
// send request
|
||||
$.ajax({
|
||||
url: remote,
|
||||
type: 'DELETE',
|
||||
})
|
||||
.done(function() {
|
||||
axios.delete(remote)
|
||||
.then(function() {
|
||||
// remove template
|
||||
elmt.parentElement.remove()
|
||||
// is list empty? Only the default template is left
|
||||
|
@ -72,7 +71,7 @@ function deleteTemplate(event) {
|
|||
emptyElmt.classList.remove('hidden')
|
||||
}
|
||||
})
|
||||
.fail(function(e) {
|
||||
.catch(function(e) {
|
||||
// failure, show warning
|
||||
elmt.textContent = t('richdocuments', 'Error')
|
||||
elmt.classList.remove('icon-loading')
|
||||
|
@ -139,6 +138,6 @@ function initTemplateManager() {
|
|||
})
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initTemplateManager()
|
||||
})
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<NcModal>
|
||||
<div class="confirmation-dialog">
|
||||
<h1>{{ name }}</h1>
|
||||
<p>{{ description }}</p>
|
||||
<div class="confirmation-dialog--buttons">
|
||||
<NcButton type="secondary"
|
||||
@click="() => close(false)">
|
||||
{{ cancelButtonText }}
|
||||
</NcButton>
|
||||
<NcButton type="primary"
|
||||
@click="() => close(true)">
|
||||
{{ confirmButtonText }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</div>
|
||||
</NcModal>
|
||||
</template>
|
||||
<script>
|
||||
import { NcButton, NcModal } from '@nextcloud/vue'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
|
||||
export default {
|
||||
name: 'Confirmation',
|
||||
components: {
|
||||
NcButton,
|
||||
NcModal,
|
||||
},
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
confirmButtonText: {
|
||||
type: String,
|
||||
default: t('richdocuments', 'Confirm'),
|
||||
},
|
||||
cancelButtonText: {
|
||||
type: String,
|
||||
default: t('richdocuments', 'Cancel'),
|
||||
},
|
||||
},
|
||||
emits: ['close'],
|
||||
methods: {
|
||||
t,
|
||||
close(result) {
|
||||
this.$emit('close', result)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.confirmation-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin: 24px;
|
||||
h1 {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
&--buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,149 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2023 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>
|
||||
<NcModal>
|
||||
<div class="saveas-dialog">
|
||||
<h1>{{ name }}</h1>
|
||||
<p>{{ description }}</p>
|
||||
<NcTextField ref="nameInput"
|
||||
v-model="newFileName"
|
||||
:label-visible="true"
|
||||
:label="t('richdocuments', 'Path to save')"
|
||||
:placeholder="'/path/to/save'" />
|
||||
<div class="saveas-dialog--buttons">
|
||||
<NcButton type="secondary"
|
||||
@click="cancel">
|
||||
{{ t('richdocuments', 'Cancel') }}
|
||||
</NcButton>
|
||||
<NcButton type="primary"
|
||||
@click="close">
|
||||
{{ t('richdocuments', 'Save') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</div>
|
||||
</NcModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
|
||||
import { NcModal, NcButton, NcTextField } from '@nextcloud/vue'
|
||||
export default {
|
||||
name: 'SaveAs',
|
||||
components: {
|
||||
NcModal,
|
||||
NcButton,
|
||||
NcTextField,
|
||||
},
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
default: t('richdocuments', 'Save As'),
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
buttonText: {
|
||||
type: String,
|
||||
default: t('richdocuments', 'Save'),
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
emits: ['close'],
|
||||
data() {
|
||||
return {
|
||||
selectedPath: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
newFileName: {
|
||||
get() {
|
||||
if (this.selectedPath !== '') {
|
||||
return this.selectedPath
|
||||
}
|
||||
const filename = this.path
|
||||
const extension = filename.split('.').pop()
|
||||
const filenameWithoutExtension = filename.substring(0, filename.length - extension.length - 1)
|
||||
return filenameWithoutExtension + '.' + (this.format !== '' ? this.format : extension)
|
||||
},
|
||||
set(event) {
|
||||
this.selectedPath = event.target.value
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// prepare filename for having a separate picker for the path (.split('/').pop())
|
||||
const filename = this.path
|
||||
const extension = filename.split('.').pop()
|
||||
const filenameWithoutExtension = filename.substring(0, filename.length - extension.length - 1)
|
||||
this.$nextTick(() => {
|
||||
const input = this.$refs.nameInput.$refs.inputField.$el.querySelector('input')
|
||||
input.setSelectionRange(0, filenameWithoutExtension.length)
|
||||
input.focus()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
t,
|
||||
close() {
|
||||
this.$emit('close', this.newFileName)
|
||||
},
|
||||
cancel() {
|
||||
this.$emit('close', null)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.saveas-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin: 24px;
|
||||
|
||||
h1 {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
&--buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -66,6 +66,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
uuid: '',
|
||||
inputValObjects: [],
|
||||
groups: {},
|
||||
}
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
<!--
|
||||
- @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>
|
||||
<NcMultiselect 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>
|
||||
</NcMultiselect>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
import { generateRemoteUrl } from '@nextcloud/router'
|
||||
import { NcMultiselect } from '@nextcloud/vue'
|
||||
|
||||
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') {
|
||||
const 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) => {
|
||||
const json = xmlToJson(parseXml(xml))
|
||||
const list = json['d:multistatus']['d:response']
|
||||
const result = []
|
||||
for (const index in list) {
|
||||
const tag = list[index]['d:propstat']
|
||||
|
||||
if (!tag) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ((tag['d:status']['#text'] ?? null) !== '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: {
|
||||
NcMultiselect,
|
||||
},
|
||||
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() {
|
||||
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>
|
|
@ -1,5 +1,5 @@
|
|||
import './init-shared.js'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { encodePath } from '@nextcloud/paths'
|
||||
import { generateOcsUrl, getRootUrl, imagePath } from '@nextcloud/router'
|
||||
import { getRequestToken } from '@nextcloud/auth'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
|
@ -18,6 +18,8 @@ import { getWopiUrl, getSearchParam, getNextcloudUrl } from './helpers/url.js'
|
|||
|
||||
import '../css/document.scss'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { spawnDialog } from '@nextcloud/dialogs'
|
||||
import SaveAs from './components/Modal/SaveAs.vue'
|
||||
|
||||
const PostMessages = new PostMessageService({
|
||||
parent: window.parent,
|
||||
|
@ -472,13 +474,6 @@ const documentsMain = {
|
|||
case 'File_Rename':
|
||||
documentsMain.fileName = args.NewName
|
||||
break
|
||||
case 'Clicked_Button':
|
||||
if (parsed.args.Id === 'Open_Local_Editor') {
|
||||
documentsMain.UI.initiateOpenLocally()
|
||||
} else {
|
||||
console.debug('[document] Unhandled `Clicked_Button` post message', parsed)
|
||||
}
|
||||
break
|
||||
case 'Views_List':
|
||||
documentsMain.users = []
|
||||
parsed.args.forEach((view) => {
|
||||
|
@ -505,30 +500,14 @@ const documentsMain = {
|
|||
}
|
||||
|
||||
if (msgId === 'UI_SaveAs') {
|
||||
// TODO Move to file picker dialog with input field
|
||||
OC.dialogs.prompt(
|
||||
t('richdocuments', 'Please enter the filename to store the document as.'),
|
||||
t('richdocuments', 'Save As'),
|
||||
function(result, value) {
|
||||
if (result === true && value) {
|
||||
PostMessages.sendWOPIPostMessage('loolframe', 'Action_SaveAs', { Filename: value, Notify: true })
|
||||
}
|
||||
spawnDialog(
|
||||
SaveAs,
|
||||
{
|
||||
path: documentsMain.filename,
|
||||
format: args.Format,
|
||||
},
|
||||
true,
|
||||
t('richdocuments', 'New filename'),
|
||||
false,
|
||||
).then(function() {
|
||||
const $dialog = $('.oc-dialog:visible')
|
||||
const $buttons = $dialog.find('.oc-dialog-buttonrow button')
|
||||
$buttons.eq(0).text(t('richdocuments', 'Cancel'))
|
||||
$buttons.eq(1).text(t('richdocuments', 'Save'))
|
||||
const nameInput = $dialog.find('input')[0]
|
||||
nameInput.style.minWidth = '250px'
|
||||
nameInput.style.maxWidth = '400px'
|
||||
nameInput.value = args.format ? documentsMain.fileName.substring(0, documentsMain.fileName.lastIndexOf('.') + 1) + args.format : documentsMain.fileName
|
||||
nameInput.selectionStart = 0
|
||||
nameInput.selectionEnd = documentsMain.fileName.lastIndexOf('.')
|
||||
})
|
||||
(value) => value && this.sendPostMessage('Action_SaveAs', { Filename: value, Notify: true }),
|
||||
)
|
||||
} else if (msgId === 'Action_Save_Resp') {
|
||||
if (args.success && args.fileName) {
|
||||
documentsMain.fileName = args.fileName
|
||||
|
@ -559,26 +538,6 @@ const documentsMain = {
|
|||
})
|
||||
},
|
||||
|
||||
initiateOpenLocally() {
|
||||
OC.dialogs.confirmDestructive(
|
||||
t('richdocuments', 'When opening a file locally, the document will close for all users currently viewing the document.'),
|
||||
t('richdocuments', 'Open file locally'),
|
||||
{
|
||||
type: OC.dialogs.YES_NO_BUTTONS,
|
||||
confirm: t('richdocuments', 'Open locally'),
|
||||
confirmClasses: 'error',
|
||||
cancel: t('richdocuments', 'Continue editing online'),
|
||||
},
|
||||
(decision) => {
|
||||
if (!decision) {
|
||||
return
|
||||
}
|
||||
documentsMain.openingLocally = true
|
||||
PostMessages.sendWOPIPostMessage('loolframe', 'Get_Views')
|
||||
},
|
||||
)
|
||||
},
|
||||
|
||||
removeViews(views) {
|
||||
PostMessages.sendWOPIPostMessage('loolframe', 'Action_Save', {
|
||||
DontTerminateEdit: false,
|
||||
|
@ -600,43 +559,6 @@ const documentsMain = {
|
|||
return axios.post(unlockUrl, { access_token: documentsMain.token }, unlockConfig)
|
||||
},
|
||||
|
||||
openLocally() {
|
||||
if (documentsMain.openingLocally) {
|
||||
documentsMain.openingLocally = false
|
||||
|
||||
axios.post(
|
||||
OC.linkToOCS('apps/files/api/v1', 2) + 'openlocaleditor?format=json',
|
||||
{ path: documentsMain.fullPath },
|
||||
).then((result) => {
|
||||
const url = 'nc://open/'
|
||||
+ Config.get('userId') + '@' + getNextcloudUrl()
|
||||
+ encodePath(documentsMain.fullPath)
|
||||
+ '?token=' + result.data.ocs.data.token
|
||||
|
||||
this.showOpenLocalConfirmation(url, window.top)
|
||||
window.location.href = url
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
showOpenLocalConfirmation(url, _window) {
|
||||
_window.OC.dialogs.confirmDestructive(
|
||||
t('richdocuments', 'If the file does not open in your local editor, make sure the Nextcloud desktop app is installed and open and try again.'),
|
||||
t('richdocuments', 'Opening file locally …'),
|
||||
{
|
||||
type: OC.dialogs.YES_NO_BUTTONS,
|
||||
confirm: t('richdocuments', 'Try again'),
|
||||
cancel: t('richdocuments', 'Close'),
|
||||
},
|
||||
(decision) => {
|
||||
if (decision) {
|
||||
_window.location = url
|
||||
this.showOpenLocalConfirmation(url, _window)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
|
||||
async sendUserList(search) {
|
||||
let users = documentsMain.users
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import './init-shared.js'
|
||||
|
||||
import '../css/filetypes.scss'
|
||||
import '../css/files.scss'
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
// eslint-disable-next-line
|
||||
__webpack_nonce__ = btoa(OC.requestToken)
|
||||
// eslint-disable-next-line
|
||||
__webpack_public_path__ = OC.linkTo('richdocuments', 'js/')
|
|
@ -21,11 +21,12 @@
|
|||
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { spawnDialog } from '@nextcloud/dialogs'
|
||||
import { encodePath } from '@nextcloud/paths'
|
||||
import { getRootUrl, generateOcsUrl } from '@nextcloud/router'
|
||||
import { getNextcloudUrl } from '../helpers/url.js'
|
||||
import Confirmation from '../components/Modal/Confirmation.vue'
|
||||
|
||||
// FIXME: Migrate to vue component
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
@ -52,14 +53,13 @@ export default {
|
|||
},
|
||||
|
||||
showOpenLocalConfirmation() {
|
||||
window.OC.dialogs.confirmDestructive(
|
||||
t('richdocuments', 'When opening a file locally, the document will close for all users currently viewing the document.'),
|
||||
t('richdocuments', 'Open file locally'),
|
||||
spawnDialog(
|
||||
Confirmation,
|
||||
{
|
||||
type: OC.dialogs.YES_NO_BUTTONS,
|
||||
confirm: t('richdocuments', 'Open locally'),
|
||||
confirmClasses: 'error',
|
||||
cancel: t('richdocuments', 'Continue editing online'),
|
||||
name: t('richdocuments', 'Open file locally'),
|
||||
description: t('richdocuments', 'When opening a file locally, the document will close for all users currently viewing the document.'),
|
||||
confirmButtonText: t('richdocuments', 'Open locally'),
|
||||
cancelButtonText: t('richdocuments', 'Continue editing online'),
|
||||
},
|
||||
(decision) => {
|
||||
if (!decision) {
|
||||
|
@ -67,19 +67,19 @@ export default {
|
|||
}
|
||||
this.openingLocally = true
|
||||
this.sendPostMessage('Get_Views')
|
||||
})
|
||||
},
|
||||
)
|
||||
},
|
||||
|
||||
showOpenLocalFinished() {
|
||||
const fileName = this.filename
|
||||
window.OC.dialogs.confirmDestructive(
|
||||
t('richdocuments', 'The file should now open locally. If you don\'t see this happening, make sure that the desktop client is installed on your system.'),
|
||||
t('richdocuments', 'Open file locally'),
|
||||
spawnDialog(
|
||||
Confirmation,
|
||||
{
|
||||
type: OC.dialogs.YES_NO_BUTTONS,
|
||||
confirm: t('richdocuments', 'Retry to open locally'),
|
||||
confirmClasses: 'primary',
|
||||
cancel: t('richdocuments', 'Continue editing online'),
|
||||
name: t('richdocuments', 'Open file locally'),
|
||||
description: t('richdocuments', 'The file should now open locally. If you don\'t see this happening, make sure that the desktop client is installed on your system.'),
|
||||
confirmButtonText: t('richdocuments', 'Retry to open locally'),
|
||||
cancelButtonText: t('richdocuments', 'Continue editing online'),
|
||||
},
|
||||
(decision) => {
|
||||
if (!decision) {
|
||||
|
@ -88,7 +88,8 @@ export default {
|
|||
}
|
||||
this.openingLocally = true
|
||||
this.sendPostMessage('Get_Views')
|
||||
})
|
||||
},
|
||||
)
|
||||
},
|
||||
|
||||
unlockFile() {
|
||||
|
|
|
@ -19,33 +19,21 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FIXME: Migrate to vue component
|
||||
import { spawnDialog } from '@nextcloud/dialogs'
|
||||
import SaveAs from '../components/Modal/SaveAs.vue'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
async saveAs(format) {
|
||||
window.OC.dialogs.prompt(
|
||||
t('richdocuments', 'Please enter the filename to store the document as.'),
|
||||
t('richdocuments', 'Save As'),
|
||||
(result, value) => {
|
||||
if (result === true && value) {
|
||||
this.sendPostMessage('Action_SaveAs', { Filename: value, Notify: true })
|
||||
}
|
||||
spawnDialog(
|
||||
SaveAs,
|
||||
{
|
||||
path: this.filename,
|
||||
format,
|
||||
description: t('richdocuments', 'Save a copy of the file as a new name and continue editing the new file'),
|
||||
},
|
||||
true,
|
||||
t('richdocuments', 'New filename'),
|
||||
false,
|
||||
).then(() => {
|
||||
const $dialog = $('.oc-dialog:visible')
|
||||
const $buttons = $dialog.find('.oc-dialog-buttonrow button')
|
||||
$buttons.eq(0).text(t('richdocuments', 'Cancel'))
|
||||
$buttons.eq(1).text(t('richdocuments', 'Save'))
|
||||
const nameInput = $dialog.find('input')[0]
|
||||
nameInput.style.minWidth = '250px'
|
||||
nameInput.style.maxWidth = '400px'
|
||||
nameInput.value = format ? this.basename.substring(0, this.basename.lastIndexOf('.') + 1) + format : this.basename
|
||||
nameInput.selectionStart = 0
|
||||
nameInput.selectionEnd = this.basename.lastIndexOf('.')
|
||||
})
|
||||
(value) => value && this.sendPostMessage('Action_SaveAs', { Filename: value, Notify: true }),
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import './init-shared.js'
|
||||
import '../css/admin.scss'
|
||||
import { generateFilePath } from '@nextcloud/router'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
|
||||
$(function() {
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
|
||||
(function() {
|
||||
const PersonalSettings = function() {
|
||||
this.templateInput = document.getElementById('templateInputField')
|
||||
this.templateSelectButton = document.getElementById('templateSelectButton')
|
||||
|
@ -91,4 +89,4 @@ $(function() {
|
|||
}
|
||||
|
||||
return new PersonalSettings()
|
||||
})
|
||||
})()
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import './init-shared.js'
|
||||
|
||||
import Vue from 'vue'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
</div>
|
||||
<div v-else class="office-target-picker">
|
||||
<h2>{{ t('richdocuments', 'Link to office document section') }}</h2>
|
||||
<NcLoadingIcon v-if="!sections || !fileId" :size="44" />
|
||||
<NcLoadingIcon v-if="sections === null || !fileId" :size="44" />
|
||||
<div v-else>
|
||||
<NcEmptyContent v-if="sections.length === 0" :title="t('richdocuments', 'Could not find any section in the document')">
|
||||
<NcEmptyContent v-if="sections.length === 0" :name="t('richdocuments', 'Could not find any section in the document')">
|
||||
<template #icon>
|
||||
<TableOfContentsIcon />
|
||||
</template>
|
||||
|
@ -38,7 +38,7 @@
|
|||
<ul>
|
||||
<NcListItem v-for="entry in section.entries"
|
||||
:key="entry.id"
|
||||
:title="entry.name"
|
||||
:name="entry.name"
|
||||
:class="{ 'list-item__wrapper--active': entry.id === target }"
|
||||
@click="setTarget(entry)">
|
||||
<template v-if="entry.preview" #icon>
|
||||
|
@ -58,7 +58,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { FilePickerType } from '@nextcloud/dialogs'
|
||||
import { getFilePickerBuilder } from '@nextcloud/dialogs'
|
||||
import { generateUrl, generateOcsUrl } from '@nextcloud/router'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { NcButton, NcEmptyContent, NcListItem, NcLoadingIcon } from '@nextcloud/vue'
|
||||
|
@ -106,25 +106,20 @@ export default {
|
|||
},
|
||||
async openFilePicker() {
|
||||
const self = this
|
||||
OC.dialogs.filepicker(
|
||||
t('files', 'Select file or folder to link to'),
|
||||
function(file) {
|
||||
const client = OC.Files.getClient()
|
||||
client.getFileInfo(file).then((_status, fileInfo) => {
|
||||
self.fileId = fileInfo.id
|
||||
})
|
||||
self.filePath = file
|
||||
self.fetchReferences()
|
||||
},
|
||||
false, // multiselect
|
||||
OC.getCapabilities().richdocuments.mimetypes, // mime filter
|
||||
false, // modal
|
||||
FilePickerType.Choose, // type
|
||||
'',
|
||||
{
|
||||
target: this.$refs.picker,
|
||||
},
|
||||
)
|
||||
await getFilePickerBuilder(t('files', 'Select file or folder to link to'))
|
||||
.setMimeTypeFilter(OC.getCapabilities().richdocuments.mimetypes)
|
||||
.addButton({
|
||||
label: t('richdocuments', 'Insert image'),
|
||||
callback: (files) => {
|
||||
const file = files[0]
|
||||
this.fileId = file.fileid
|
||||
self.filePath = file.path
|
||||
self.fetchReferences()
|
||||
},
|
||||
})
|
||||
.setContainer(this.$refs.picker)
|
||||
.build()
|
||||
.pick()
|
||||
},
|
||||
setTarget(entry) {
|
||||
this.target = entry.id
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { getFilePickerBuilder } from '@nextcloud/dialogs'
|
||||
import { getFilePickerBuilder, spawnDialog } from '@nextcloud/dialogs'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { getCurrentDirectory } from '../helpers/filesApp.js'
|
||||
|
@ -32,6 +32,7 @@ import {
|
|||
davResultToNode,
|
||||
davRootPath,
|
||||
} from '@nextcloud/files'
|
||||
import SaveAs from '../components/Modal/SaveAs.vue'
|
||||
|
||||
const isPublic = document.getElementById('isPublic') && document.getElementById('isPublic').value === '1'
|
||||
|
||||
|
@ -256,31 +257,6 @@ export default {
|
|||
this.renderAvatars()
|
||||
},
|
||||
|
||||
_addAvatarList() {
|
||||
// Add the avatar toolbar if possible
|
||||
const avatarList = $('<div id="richdocuments-avatars">')
|
||||
avatarList.on('click', function(e) {
|
||||
e.stopPropagation()
|
||||
$('#editors-menu').toggle()
|
||||
})
|
||||
$('#richdocuments-header').append(avatarList)
|
||||
},
|
||||
|
||||
_addHeaderShareButton() {
|
||||
if ($('header').length) {
|
||||
const isInverted = Boolean(window?.OCA?.Theming?.inverted)
|
||||
const $button = $('<div id="richdocuments-sharing"><a class="icon-collabora icon-shared ' + (isInverted ? 'icon-black' : 'icon-white') + '"></a></div>')
|
||||
$('#richdocuments-header').append($button)
|
||||
$button.on('click', () => {
|
||||
if (!$('#app-sidebar').is(':visible')) {
|
||||
return this.share()
|
||||
}
|
||||
OC.Apps.hideAppSidebar()
|
||||
})
|
||||
$('.searchbox').hide()
|
||||
}
|
||||
},
|
||||
|
||||
_addHeaderFileActions() {
|
||||
console.debug('[FilesAppIntegration] Adding header file actions')
|
||||
OC.unregisterMenu($('#richdocuments-actions .icon-more'), $('#richdocuments-actions-menu'))
|
||||
|
@ -468,11 +444,15 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
OC.dialogs.prompt(
|
||||
t('richdocuments', 'Please enter the filename for the new document'),
|
||||
t('richdocuments', 'Save As'),
|
||||
function(result, value) {
|
||||
if (result === true && value) {
|
||||
spawnDialog(
|
||||
SaveAs,
|
||||
{
|
||||
name: t('richdocuments', 'New file'),
|
||||
description: t('richdocuments', 'Please enter the filename for the new file'),
|
||||
buttonText: t('richdocuments', 'Create'),
|
||||
},
|
||||
(value) => {
|
||||
if (value) {
|
||||
if (type === 'text') {
|
||||
type = 'document'
|
||||
}
|
||||
|
@ -480,15 +460,7 @@ export default {
|
|||
window.open(url, '_blank')
|
||||
}
|
||||
},
|
||||
true,
|
||||
t('richdocuments', 'New filename'),
|
||||
false,
|
||||
).then(function() {
|
||||
const $dialog = parent.$('.oc-dialog:visible')
|
||||
const $buttons = $dialog.find('button')
|
||||
$buttons.eq(0).text(t('richdocuments', 'Cancel'))
|
||||
$buttons.eq(1).text(t('richdocuments', 'Create a new document'))
|
||||
})
|
||||
)
|
||||
},
|
||||
|
||||
loggingContext() {
|
||||
|
|
|
@ -25,6 +25,7 @@ import { getCurrentDirectory } from '../helpers/filesApp.js'
|
|||
import Types from '../helpers/types.js'
|
||||
import { createEmptyFile } from '../services/api.js'
|
||||
import { generateUrl, generateFilePath, generateOcsUrl } from '@nextcloud/router'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
|
||||
/** @type OC.Plugin */
|
||||
const NewFileMenu = {
|
||||
|
@ -113,7 +114,7 @@ const NewFileMenu = {
|
|||
fileActions: FileList.fileActions,
|
||||
})
|
||||
} else {
|
||||
OC.dialogs.alert(response.data.message, t('core', 'Could not create file'))
|
||||
showError(t('core', 'Could not create file') + ': ' + response.data.message)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -21,16 +21,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import './init-shared.js'
|
||||
import '../css/filetypes.scss'
|
||||
|
||||
import Office from './view/Office.vue'
|
||||
import { getCapabilities } from '@nextcloud/capabilities'
|
||||
|
||||
// eslint-disable-next-line
|
||||
__webpack_nonce__ = btoa(OC.requestToken)
|
||||
// eslint-disable-next-line
|
||||
__webpack_public_path__ = OC.linkTo('richdocuments', 'js/')
|
||||
|
||||
const supportedMimes = getCapabilities().richdocuments.mimetypes
|
||||
|
||||
if (OCA.Viewer) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче