mirror of
https://github.com/directus/directus.git
synced 2026-02-15 00:34:56 -05:00
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:
committed by
GitHub
parent
b1106e183d
commit
212481ff07
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
Reference in New Issue
Block a user