Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions js/editor.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/editor.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-editors.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-editors.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-files.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-files.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-public.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-public.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-text.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-text.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-viewer.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-viewer.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/vendors.js

Large diffs are not rendered by default.

89 changes: 89 additions & 0 deletions js/vendors.js.LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,51 @@

/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */

/**
* @copyright 2019 Christoph Wurst <[email protected]>
*
* @author Christoph Wurst <[email protected]>
*
* @license AGPL-3.0-or-later
*
* 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/>.
*
*/

/**
* @copyright 2019 Christoph Wurst <[email protected]>
*
* @author Christoph Wurst <[email protected]>
* @author John Molakvoæ <[email protected]>
*
* @license AGPL-3.0-or-later
*
* 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/>.
*
*/

/**
* @copyright Copyright (c) 2019 Georg Ehrke
*
Expand Down Expand Up @@ -132,6 +177,50 @@
*
*/

/**
* @copyright Copyright (c) 2021 John Molakvoæ <[email protected]>
*
* @author John Molakvoæ <[email protected]>
*
* @license AGPL-3.0-or-later
*
* 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/>.
*
*/

/**
* @copyright Copyright (c) 2022 John Molakvoæ <[email protected]>
*
* @author John Molakvoæ <[email protected]>
*
* @license AGPL-3.0-or-later
*
* 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/>.
*
*/

//! authors : Tim Wood, Iskren Chernev, Moment.js contributors

//! license : MIT
Expand Down
2 changes: 1 addition & 1 deletion js/vendors.js.map

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@nextcloud/browser-storage": "^0.2.0",
"@nextcloud/dialogs": "^4.0.1",
"@nextcloud/event-bus": "^3.0.2",
"@nextcloud/files": "^3.0.0-beta.7",
"@nextcloud/initial-state": "^2.0.0",
"@nextcloud/l10n": "^2.1.0",
"@nextcloud/logger": "^2.5.0",
Expand Down
4 changes: 2 additions & 2 deletions src/components/ImageView/ShowImageModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
:has-next="true"
:has-previous="true"
:close-button-contained="false"
dark="true"
:dark="true"
@next="showNextImage"
@previous="showPreviousImage"
@close="$emit('close')">
Expand All @@ -33,7 +33,7 @@ export default {
validator(imagesList) {
return (imagesList.length === 0)
? true
: imagesList.findIndex(({ basename, source }) => !(basename && source)) !== -1
: imagesList.every(image => image.basename && image.source)
},
},
startIndex: {
Expand Down
19 changes: 11 additions & 8 deletions src/nodes/ImageView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -280,28 +280,30 @@ export default {
},
methods: {
async init() {
const candidates = this.$attachmentResolver.resolve(this.src)
const candidates = await this.$attachmentResolver.resolve(this.src)
return this.load(candidates)
},
async load(candidates) {
const [candidate, ...fallbacks] = candidates
return this.loadImage(candidate.url, candidate.type, candidate.name).catch((e) => {
return this.loadImage(candidate.url, candidate.type, candidate.name, candidate.metadata).catch((e) => {
if (fallbacks.length > 0) {
return this.load(fallbacks)
// TODO if fallback works, rewrite the url with correct document ID
}
return Promise.reject(e)
})
},
async loadImage(imageUrl, attachmentType, name = null) {
async loadImage(imageUrl, attachmentType, name = null, metadata = null) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = async () => {
this.imageUrl = imageUrl
this.imageLoaded = true
this.loaded = true
this.attachmentType = attachmentType
if (attachmentType === this.$attachmentResolver.ATTACHMENT_TYPE_MEDIA) {
if (attachmentType === this.$attachmentResolver.ATTACHMENT_TYPE_MEDIA && metadata) {
this.attachmentMetadata = metadata
} else if (attachmentType === this.$attachmentResolver.ATTACHMENT_TYPE_MEDIA) {
await this.loadMediaMetadata(name)
}
resolve(imageUrl)
Expand Down Expand Up @@ -340,20 +342,21 @@ export default {
onLoaded() {
this.loaded = true
},
handleImageClick(src) {
async handleImageClick(src) {
const imageViews = Array.from(document.querySelectorAll('figure[data-component="image-view"].image-view'))
let basename, relativePath

imageViews.forEach(imgv => {
for (const imgv of imageViews) {
relativePath = imgv.getAttribute('data-src')
basename = relativePath.split('/').slice(-1).join()
const { url: source } = this.$attachmentResolver.resolve(relativePath, true).shift()
const response = await this.$attachmentResolver.resolve(relativePath, true)
const { url: source } = response.shift()
this.embeddedImagesList.push({
source,
basename,
relativePath,
})
})
}
this.imageIndex = this.embeddedImagesList.findIndex(image => image.relativePath === src)
this.showImageModal = true
},
Expand Down
42 changes: 38 additions & 4 deletions src/services/AttachmentResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

import { generateUrl, generateRemoteUrl } from '@nextcloud/router'
import pathNormalize from 'path-normalize'
import axios from '@nextcloud/axios'
import { formatFileSize } from '@nextcloud/files'

import { logger } from '../helpers/logger.js'

Expand Down Expand Up @@ -53,7 +55,7 @@ export default class AttachmentResolver {
*
* Currently returns either one or two urls.
*/
resolve(src, preferRawImage = false) {
async resolve(src, preferRawImage = false) {
if (this.#session && src.startsWith('text://')) {
const imageFileName = getQueryVariable(src, 'imageFileName')
return [{
Expand All @@ -62,6 +64,7 @@ export default class AttachmentResolver {
}]
}

// Has session and URL points to attachment from current document
if (this.#session && src.startsWith(`.attachments.${this.#session?.documentId}/`)) {
const imageFileName = decodeURIComponent(src.replace(`.attachments.${this.#session?.documentId}/`, '').split('?')[0])
return [
Expand Down Expand Up @@ -91,11 +94,11 @@ export default class AttachmentResolver {
}]
}

// if it starts with '.attachments.1234/'
if (src.match(/^\.attachments\.\d+\//)) {
// Has session and URL points to attachment from a (different) text document
if (this.#session && src.match(/^\.attachments\.\d+\//)) {
const imageFileName = this.#relativePath(src)
.replace(/\.attachments\.\d+\//, '')
// try the webdav url and attachment API if it fails
// Try webdav URL, use attachment API as fallback
return [
{
type: this.ATTACHMENT_TYPE_IMAGE,
Expand All @@ -113,6 +116,26 @@ export default class AttachmentResolver {
]
}

// Doesn't have session and URL points to attachment from (current or different) text document
if (!this.#session && src.match(/^\.attachments\.\d+\//)) {
const imageFileName = this.#relativePath(src)
.replace(/\.attachments\.\d+\//, '')
const { mimeType, size } = await this.getMetadata(this.#davUrl(src))
// Without session, use webdav URL for images and mimetype icon for media attachments
return [
{
type: this.ATTACHMENT_TYPE_IMAGE,
url: this.#davUrl(src),
},
{
type: this.ATTACHMENT_TYPE_MEDIA,
url: this.getMimeUrl(mimeType),
metadata: { size },
name: imageFileName,
},
]
}

return [{
type: this.ATTACHMENT_TYPE_IMAGE,
url: this.#davUrl(src),
Expand Down Expand Up @@ -249,6 +272,17 @@ export default class AttachmentResolver {
return pathNormalize(f)
}

async getMetadata(src) {
const headResponse = await axios.head(src)
const mimeType = headResponse.headers['content-type']
const size = formatFileSize(headResponse.headers['content-length'])
return { mimeType, size }
}

getMimeUrl(mimeType) {
return mimeType ? OC.MimeType.getIconUrl(mimeType) : null
}

}

/**
Expand Down
Loading