Add missing download and token parameter in File interface (#14871)

* add missing download and token parameter

* add download name

* update all file download links (:download and :href), update icon, add single file drawer download

* add filename_download to adjustFieldsForDisplays

* add new computed use asset url logic

* switch composable to utility function

* switch to new URL and add tests

* add getAssetUrl to file-image

* Update app/src/interfaces/files/files.vue

Co-authored-by: Nitwel <mail@nitwel.de>

* update code style error

* add download to drawer of file-image, fix wrong css selector and update preview download icon

* Remove debugging database

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
Co-authored-by: Brainslug <br41nslug@users.noreply.github.com>
Co-authored-by: ian <licitdev@gmail.com>
Co-authored-by: Nitwel <mail@nitwel.de>
This commit is contained in:
Jonathan Schneider
2022-09-12 22:30:43 +02:00
committed by GitHub
parent b1106e183d
commit 212481ff07
6 changed files with 161 additions and 98 deletions

View File

@@ -35,8 +35,14 @@
<v-button v-tooltip="t('zoom')" icon rounded @click="lightboxActive = true">
<v-icon name="zoom_in" />
</v-button>
<v-button v-tooltip="t('download')" icon rounded :href="downloadSrc" :download="image.filename_download">
<v-icon name="file_download" />
<v-button
v-tooltip="t('download')"
icon
rounded
:href="getAssetUrl(image.id, true)"
:download="image.filename_download"
>
<v-icon name="download" />
</v-button>
<v-button v-tooltip="t('edit')" icon rounded @click="editImageDetails = true">
<v-icon name="open_in_new" />
@@ -61,7 +67,13 @@
:primary-key="image.id"
:edits="edits"
@input="update"
/>
>
<template #actions>
<v-button secondary rounded icon :download="image.filename_download" :href="getAssetUrl(image.id, true)">
<v-icon name="download" />
</v-button>
</template>
</drawer-item>
<image-editor v-if="!disabled && image" :id="image.id" v-model="editImageEditor" @refresh="refresh" />
@@ -76,7 +88,7 @@ import api, { addTokenToURL } from '@/api';
import { useRelationM2O } from '@/composables/use-relation-m2o';
import { RelationQuerySingle, useRelationSingle } from '@/composables/use-relation-single';
import { formatFilesize } from '@/utils/format-filesize';
import { getRootPath } from '@/utils/get-root-path';
import { getAssetUrl } from '@/utils/get-asset-url';
import { readableMimeType } from '@/utils/readable-mime-type';
import DrawerItem from '@/views/private/components/drawer-item.vue';
import FileLightbox from '@/views/private/components/file-lightbox.vue';
@@ -142,11 +154,6 @@ const src = computed(() => {
const ext = computed(() => (image.value ? readableMimeType(image.value.type, true) : 'unknown'));
const downloadSrc = computed(() => {
if (!image.value) return null;
return addTokenToURL(getRootPath() + 'assets/' + image.value.id);
});
const meta = computed(() => {
if (!image.value) return null;
const { filesize, width, height, type } = image.value;
@@ -238,74 +245,76 @@ img {
}
}
.shadow {
position: absolute;
bottom: 0;
left: 0;
z-index: 2;
width: 100%;
height: 40px;
overflow: hidden;
line-height: 1;
white-space: nowrap;
text-overflow: ellipsis;
background: linear-gradient(180deg, rgb(38 50 56 / 0) 0%, rgb(38 50 56 / 0.25) 100%);
transition: height var(--fast) var(--transition);
}
.image-preview {
.shadow {
position: absolute;
bottom: 0;
left: 0;
z-index: 2;
width: 100%;
height: 40px;
overflow: hidden;
line-height: 1;
white-space: nowrap;
text-overflow: ellipsis;
background: linear-gradient(180deg, rgb(38 50 56 / 0) 0%, rgb(38 50 56 / 0.25) 100%);
transition: height var(--fast) var(--transition);
}
.actions {
--v-button-color: var(--foreground-subdued);
--v-button-background-color: var(--white);
--v-button-color-hover: var(--foreground-normal);
--v-button-background-color-hover: var(--white);
.actions {
--v-button-color: var(--foreground-subdued);
--v-button-background-color: var(--white);
--v-button-color-hover: var(--foreground-normal);
--v-button-background-color-hover: var(--white);
position: absolute;
top: calc(50% - 32px);
left: 0;
z-index: 3;
display: flex;
justify-content: center;
width: 100%;
position: absolute;
top: calc(50% - 32px);
left: 0;
z-index: 3;
display: flex;
justify-content: center;
width: 100%;
.v-button {
margin-right: 12px;
transform: translateY(10px);
opacity: 0;
transition: var(--medium) var(--transition);
transition-property: opacity transform;
.v-button {
margin-right: 12px;
transform: translateY(10px);
opacity: 0;
transition: var(--medium) var(--transition);
transition-property: opacity transform;
@for $i from 0 through 4 {
&:nth-of-type(#{$i + 1}) {
transition-delay: $i * 25ms;
@for $i from 0 through 4 {
&:nth-of-type(#{$i + 1}) {
transition-delay: $i * 25ms;
}
}
}
.v-button:last-child {
margin-right: 0px;
}
}
.v-button:last-child {
margin-right: 0px;
.info {
position: absolute;
bottom: 0;
left: 0;
z-index: 3;
width: 100%;
padding: 8px 12px;
line-height: 1.2;
}
}
.info {
position: absolute;
bottom: 0;
left: 0;
z-index: 3;
width: 100%;
padding: 8px 12px;
line-height: 1.2;
}
.title {
color: var(--white);
}
.title {
color: var(--white);
}
.meta {
height: 17px;
max-height: 0;
overflow: hidden;
color: rgb(255 255 255 / 0.75);
transition: max-height var(--fast) var(--transition);
.meta {
height: 17px;
max-height: 0;
overflow: hidden;
color: rgb(255 255 255 / 0.75);
transition: max-height var(--fast) var(--transition);
}
}
.image-preview:focus-within,

View File

@@ -46,7 +46,7 @@
<v-list>
<template v-if="file">
<v-list-item :download="file.filename_download" :href="downloadURL">
<v-list-item clickable :download="file.filename_download" :href="getAssetUrl(file.id, true)">
<v-list-item-icon><v-icon name="get_app" /></v-list-item-icon>
<v-list-item-content>{{ t('download_file') }}</v-list-item-content>
</v-list-item>
@@ -86,7 +86,13 @@
:edits="edits"
:disabled="disabled"
@input="update"
/>
>
<template #actions>
<v-button secondary rounded icon :download="file.filename_download" :href="getAssetUrl(file.id, true)">
<v-icon name="download" />
</v-button>
</template>
</drawer-item>
<v-dialog
:model-value="activeDialog === 'upload'"
@@ -141,7 +147,8 @@
import { useI18n } from 'vue-i18n';
import { ref, computed, toRefs } from 'vue';
import DrawerCollection from '@/views/private/components/drawer-collection.vue';
import api, { addTokenToURL } from '@/api';
import api from '@/api';
import { getAssetUrl } from '@/utils/get-asset-url';
import { readableMimeType } from '@/utils/readable-mime-type';
import { unexpectedError } from '@/utils/unexpected-error';
import DrawerItem from '@/views/private/components/drawer-item.vue';
@@ -149,7 +156,6 @@ import { addQueryToPath } from '@/utils/add-query-to-path';
import { useRelationM2O } from '@/composables/use-relation-m2o';
import { useRelationSingle, RelationQuerySingle } from '@/composables/use-relation-single';
import { Filter } from '@directus/shared/types';
import { getRootPath } from '@/utils/get-root-path';
type FileInfo = {
id: string;
@@ -208,10 +214,6 @@ const assetURL = computed(() => {
return '/assets/' + id;
});
const downloadURL = computed(() => {
return addTokenToURL(getRootPath() + assetURL.value.slice(1));
});
const imageThumbnail = computed(() => {
if (file.value === null || props.value === null) return null;
if (file.value.type.includes('svg')) return assetURL.value;

View File

@@ -47,11 +47,15 @@
</template>
<v-list>
<v-list-item clickable :href="getUrl(element)">
<v-list-item clickable :href="getAssetUrl(getFilename(element))">
<v-list-item-icon><v-icon name="launch" /></v-list-item-icon>
<v-list-item-content>{{ t('open_file_in_tab') }}</v-list-item-content>
</v-list-item>
<v-list-item clickable :href="getUrl(element, true)">
<v-list-item
clickable
:download="element.directus_files_id.filename_download"
:href="getAssetUrl(getFilename(element), true)"
>
<v-list-item-icon><v-icon name="download" /></v-list-item-icon>
<v-list-item-content>{{ t('download_file') }}</v-list-item-content>
</v-list-item>
@@ -89,7 +93,7 @@
secondary
rounded
icon
download
:download="downloadName"
:href="downloadUrl"
>
<v-icon name="download" />
@@ -129,12 +133,11 @@ import { useI18n } from 'vue-i18n';
import DrawerItem from '@/views/private/components/drawer-item.vue';
import DrawerCollection from '@/views/private/components/drawer-collection.vue';
import Draggable from 'vuedraggable';
import { getAssetUrl } from '@/utils/get-asset-url';
import { adjustFieldsForDisplays } from '@/utils/adjust-fields-for-displays';
import { get, clamp, isEmpty } from 'lodash';
import { usePermissionsStore } from '@/stores/permissions';
import { useUserStore } from '@/stores/user';
import { addTokenToURL } from '@/api';
import { getRootPath } from '@/utils/get-root-path';
import { getFieldsFromTemplate } from '@directus/shared/utils';
import { Filter } from '@directus/shared/types';
@@ -202,7 +205,7 @@ const templateWithDefaults = computed(() => {
const fields = computed(() =>
adjustFieldsForDisplays(
getFieldsFromTemplate(templateWithDefaults.value),
[...getFieldsFromTemplate(templateWithDefaults.value), `${relationInfo.value?.relation.field}.filename_download`],
relationInfo.value?.junctionCollection.collection ?? ''
)
);
@@ -317,23 +320,28 @@ function onSelect(selected: string[]) {
select(selected.filter((id) => selectedPrimaryKeys.value.includes(id) === false));
}
const downloadUrl = computed(() => {
const downloadName = computed(() => {
if (relatedPrimaryKey.value === null || relationInfo.value?.relatedCollection.collection !== 'directus_files') return;
return addTokenToURL(getRootPath() + `assets/${relatedPrimaryKey.value}?download`);
const junctionField = relationInfo.value.junctionField.field;
const relationPkField = relationInfo.value.relatedPrimaryKeyField.field;
return displayItems.value.find((item) => get(item, [junctionField, relationPkField]))?.directus_files_id
?.filename_download;
});
function getUrl(junctionRow: Record<string, any>, addDownload?: boolean) {
const downloadUrl = computed(() => {
if (relatedPrimaryKey.value === null || relationInfo.value?.relatedCollection.collection !== 'directus_files') return;
return getAssetUrl(String(relatedPrimaryKey.value), true);
});
function getFilename(junctionRow: Record<string, any>) {
const junctionField = relationInfo.value?.junctionField.field;
if (!junctionField) return;
const key = junctionRow[junctionField]?.id ?? junctionRow[junctionField] ?? null;
if (!key) return null;
if (addDownload) {
return addTokenToURL(getRootPath() + `assets/${key}?download`);
}
return addTokenToURL(getRootPath() + `assets/${key}`);
return key;
}
const customFilter = computed(() => {