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
3 changes: 3 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,6 @@ title: Capabilities
* `force-mute` - "forceMute" signaling messages can be sent to mute other participants.
* `conversation-v2` - The conversations API v2 is less load heavy and should be used by clients when available. Check the difference in the [Conversation API documentation](conversation.md).
* `chat-reference-id` - an optional referenceId can be sent with a chat message to be able to identify it in parallel get requests to earlier fade out a temporary message

## 11.0
* `config => previews => max-gif-size` - Maximum size in bytes below which a GIF can be embedded directly in the page at render time. Bigger files will be rendered statically using the preview endpoint instead. Can be set with `occ config:app:set spreed max-gif-size --value=X` where X is the new value in bytes. Defaults to 3 MB.
3 changes: 3 additions & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ public function getCapabilities(): array {
'max-length' => ChatManager::MAX_CHAT_LENGTH,
],
'conversations' => [],
'previews' => [
'max-gif-size' => (int)$this->serverConfig->getAppValue('spreed', 'max-gif-size', '3145728')
],
],
];

Expand Down
3 changes: 3 additions & 0 deletions lib/Chat/Parser/SystemMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ protected function getFileFromShare(Participant $participant, string $shareId):
$share = $this->shareProvider->getShareById($shareId);
$node = $share->getNode();
$name = $node->getName();
$size = $node->getSize();
$path = $name;

if (!$participant->isGuest()) {
Expand All @@ -349,6 +350,7 @@ protected function getFileFromShare(Participant $participant, string $shareId):
$fullPath = $userNode->getPath();
$pathSegments = explode('/', $fullPath, 4);
$name = $userNode->getName();
$size = $userNode->getSize();
$path = $pathSegments[3] ?? $path;
}
} else {
Expand All @@ -370,6 +372,7 @@ protected function getFileFromShare(Participant $participant, string $shareId):
'type' => 'file',
'id' => (string) $node->getId(),
'name' => $name,
'size' => $size,
'path' => $path,
'link' => $url,
'mimetype' => $node->getMimeType(),
Expand Down
11 changes: 9 additions & 2 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 @@ -20,6 +20,7 @@
"@nextcloud/auth": "^1.3.0",
"@nextcloud/axios": "^1.4.0",
"@nextcloud/browser-storage": "^0.1.1",
"@nextcloud/capabilities": "^1.0.2",
"@nextcloud/dialogs": "^3.0.0",
"@nextcloud/event-bus": "^1.2.0",
"@nextcloud/initial-state": "^1.2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
:class="{ 'file-preview--viewer-available': isViewerAvailable, 'file-preview--upload-editor': isUploadEditor }"
@click="handleClick"
@keydown.enter="handleClick">
<img v-if="(!isLoading && !failed) || hasTemporaryImageUrl"
<img v-if="(!isLoading && !failed)"
:class="previewSizeClass"
class="file-preview__image"
alt=""
Expand All @@ -51,9 +51,10 @@
</template>

<script>
import { generateUrl, imagePath } from '@nextcloud/router'
import { generateUrl, imagePath, generateRemoteUrl } from '@nextcloud/router'
import ProgressBar from '@nextcloud/vue/dist/Components/ProgressBar'
import Close from 'vue-material-design-icons/Close'
import { getCapabilities } from '@nextcloud/capabilities'

export default {
name: 'FilePreview',
Expand All @@ -80,6 +81,10 @@ export default {
type: String,
default: '',
},
size: {
type: Number,
default: -1,
},
link: {
type: String,
default: '',
Expand All @@ -94,7 +99,7 @@ export default {
},
previewSize: {
type: Number,
default: 128,
default: 384,
},
// In case this component is used to display a file that is being uploaded
// this parameter is used to access the file upload status in the store
Expand All @@ -109,6 +114,7 @@ export default {
default: '',
},
// True if this component is used in the upload editor
// FIXME: file-preview should be encapsulated and not be aware of its surroundings
isUploadEditor: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -153,20 +159,43 @@ export default {
return 'preview'
},
previewUrl() {
const userId = this.$store.getters.getUserId()
if (this.hasTemporaryImageUrl) {
return this.localUrl
}

if (this.previewAvailable !== 'yes' || this.$store.getters.getUserId() === null) {
if (this.previewAvailable !== 'yes') {
return OC.MimeType.getIconUrl(this.mimetype)
}

// max size of a gif for which we allow direct embedding
const maxGifSize = getCapabilities()?.caps?.spreed?.config?.previews?.['max-gif-size'] || 3145728
if (this.mimetype === 'image/gif' && this.size <= maxGifSize) {
// return direct image so it can be animated
if (userId === null) {
// guest mode, use public link download URL
return this.link + '/download/' + this.name
} else {
// use direct DAV URL
return generateRemoteUrl(`dav/files/${userId}`) + this.internalAbsolutePath
}
}

const previewSize = Math.ceil(this.previewSize * window.devicePixelRatio)
return generateUrl('/core/preview?fileId={fileId}&x={width}&y={height}', {
fileId: this.id,
width: previewSize,
height: previewSize,
})
if (userId === null) {
// guest mode: grab token from the link URL
// FIXME: use a cleaner way...
const token = this.link.substr(this.link.lastIndexOf('/') + 1)
return generateUrl('/apps/files_sharing/publicpreview/{token}?x=-1&y={height}&a=1', {
token: token,
height: previewSize,
})
} else {
return generateUrl('/core/preview?fileId={fileId}&x=-1&y={height}&a=1', {
fileId: this.id,
height: previewSize,
})
}
},
isViewerAvailable() {
if (!OCA.Viewer) {
Expand Down Expand Up @@ -290,15 +319,22 @@ export default {
object-fit: cover;
}

.loading {
display: inline-block;
width: 100%;
}

.preview {
display: block;
width: 128px;
height: 128px;
display: inline-block;
border-radius: var(--border-radius);
max-width: 100%;
max-height: 384px;
}
.preview-64 {
display: block;
width: 64px;
height: 64px;
display: inline-block;
border-radius: var(--border-radius);
max-width: 100%;
max-height: 64px;
}

strong {
Expand All @@ -308,7 +344,6 @@ export default {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin-top: 4px;
}

&:not(.file-preview--viewer-available) {
Expand All @@ -323,6 +358,11 @@ export default {
padding: 12px;
.preview {
margin: auto;
width: 128px;
height: 128px;
}
.loading {
width: 100%;
}
}
}
Expand Down
22 changes: 16 additions & 6 deletions tests/php/CapabilitiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ public function testGetCapabilitiesGuest(): void {
$this->talkConfig->expects($this->never())
->method('isDisabledForUser');

$this->serverConfig->expects($this->once())
$this->serverConfig->expects($this->any())
->method('getAppValue')
->with('spreed', 'has_reference_id', 'no')
->willReturn('no');
->willReturnMap([
['spreed', 'has_reference_id', 'no', 'no'],
['spreed', 'max-gif-size', '3145728', '200000'],
]);

$this->assertInstanceOf(IPublicCapability::class, $capabilities);
$this->assertSame([
Expand Down Expand Up @@ -108,6 +110,9 @@ public function testGetCapabilitiesGuest(): void {
'conversations' => [
'can-create' => false,
],
'previews' => [
'max-gif-size' => 200000,
],
],
],
], $capabilities->getCapabilities());
Expand Down Expand Up @@ -155,10 +160,12 @@ public function testGetCapabilitiesUserAllowed(bool $isNotAllowed, bool $canCrea
->with($user)
->willReturn($isNotAllowed);

$this->serverConfig->expects($this->once())
$this->serverConfig->expects($this->any())
->method('getAppValue')
->with('spreed', 'has_reference_id', 'no')
->willReturn('yes');
->willReturnMap([
['spreed', 'has_reference_id', 'no', 'yes'],
['spreed', 'max-gif-size', '3145728', '200000'],
]);

$this->assertInstanceOf(IPublicCapability::class, $capabilities);
$this->assertSame([
Expand Down Expand Up @@ -201,6 +208,9 @@ public function testGetCapabilitiesUserAllowed(bool $isNotAllowed, bool $canCrea
'conversations' => [
'can-create' => $canCreate,
],
'previews' => [
'max-gif-size' => 200000,
],
],
],
], $capabilities->getCapabilities());
Expand Down
15 changes: 15 additions & 0 deletions tests/php/Chat/Parser/SystemMessageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,9 @@ public function testGetFileFromShareForGuest() {
$node->expects($this->once())
->method('getMimeType')
->willReturn('text/plain');
$node->expects($this->once())
->method('getSize')
->willReturn(65530);

$share = $this->createMock(IShare::class);
$share->expects($this->once())
Expand Down Expand Up @@ -469,6 +472,7 @@ public function testGetFileFromShareForGuest() {
'type' => 'file',
'id' => '54',
'name' => 'name',
'size' => 65530,
'path' => 'name',
'link' => 'absolute-link',
'mimetype' => 'text/plain',
Expand All @@ -490,6 +494,9 @@ public function testGetFileFromShareForOwner() {
$node->expects($this->once())
->method('getMimeType')
->willReturn('httpd/unix-directory');
$node->expects($this->once())
->method('getSize')
->willReturn(65520);

$share = $this->createMock(IShare::class);
$share->expects($this->once())
Expand Down Expand Up @@ -529,6 +536,7 @@ public function testGetFileFromShareForOwner() {
'type' => 'file',
'id' => '54',
'name' => 'name',
'size' => 65520,
'path' => 'path/to/file/name',
'link' => 'absolute-link-owner',
'mimetype' => 'httpd/unix-directory',
Expand All @@ -547,6 +555,9 @@ public function testGetFileFromShareForRecipient() {
$node->expects($this->once())
->method('getMimeType')
->willReturn('application/octet-stream');
$node->expects($this->once())
->method('getSize')
->willReturn(65510);

$share = $this->createMock(IShare::class);
$share->expects($this->once())
Expand All @@ -573,6 +584,9 @@ public function testGetFileFromShareForRecipient() {
$file->expects($this->once())
->method('getPath')
->willReturn('/user/files/Shared/different');
$file->expects($this->once())
->method('getSize')
->willReturn(65515);

$userFolder = $this->createMock(Folder::class);
$userFolder->expects($this->once())
Expand Down Expand Up @@ -602,6 +616,7 @@ public function testGetFileFromShareForRecipient() {
'type' => 'file',
'id' => '54',
'name' => 'different',
'size' => 65515,
'path' => 'Shared/different',
'link' => 'absolute-link-owner',
'mimetype' => 'application/octet-stream',
Expand Down