feat: adds dynamic url field to upload-enabled collections

This commit is contained in:
James
2021-10-18 21:15:32 -04:00
parent 3b99deda45
commit cc4d1fd045
11 changed files with 182 additions and 144 deletions

View File

@@ -139,7 +139,7 @@ If you are using a plugin to send your files off to a third-party file storage h
This is a fairly advanced feature. If you do disable local file storage, by default, your admin panel's thumbnails will be broken as you will not have stored a file. It will be totally up to you to use either a plugin or your own hooks to store your files in a permanent manner, as well as provide your own admin thumbnail using <strong>upload.adminThumbnail</strong>.
</Banner>
### Admin thumbnails
### Admin Thumbnails
You can specify how Payload retrieves admin thumbnails for your upload-enabled Collections. This property accepts two different configurations:

View File

@@ -10,12 +10,12 @@ const baseClass = 'file-meta';
const Meta: React.FC<Props> = (props) => {
const {
filename, filesize, width, height, mimeType, staticURL,
filename, filesize, width, height, mimeType, staticURL, url,
} = props;
const { serverURL } = useConfig();
const fileURL = `${serverURL}${staticURL}/${filename}`;
const fileURL = url || `${serverURL}${staticURL}/${filename}`;
return (
<div className={baseClass}>

View File

@@ -6,4 +6,5 @@ export type Props = {
width?: number,
height?: number,
sizes?: unknown,
url?: string
}

View File

@@ -31,6 +31,7 @@ const FileDetails: React.FC<Props> = (props) => {
height,
mimeType,
sizes,
url,
} = doc;
const [moreInfoOpen, setMoreInfoOpen] = useState(false);
@@ -52,6 +53,7 @@ const FileDetails: React.FC<Props> = (props) => {
width={width as number}
height={height as number}
mimeType={mimeType as string}
url={url as string}
/>
{hasSizes && (
<Button
@@ -91,18 +93,24 @@ const FileDetails: React.FC<Props> = (props) => {
height={moreInfoOpen ? 'auto' : 0}
>
<ul className={`${baseClass}__sizes`}>
{Object.entries(sizes).map(([key, val]) => (
<li key={key}>
<div className={`${baseClass}__size-label`}>
{key}
</div>
<Meta
{...val}
mimeType={mimeType}
staticURL={staticURL}
/>
</li>
))}
{Object.entries(sizes).map(([key, val]) => {
if (val?.filename) {
return (
<li key={key}>
<div className={`${baseClass}__size-label`}>
{key}
</div>
<Meta
{...val}
mimeType={mimeType}
staticURL={staticURL}
/>
</li>
);
}
return null;
})}
</ul>
</AnimateHeight>
)}

View File

@@ -7,11 +7,10 @@ import baseAuthFields from '../../fields/baseFields/baseAuthFields';
import baseAPIKeyFields from '../../fields/baseFields/baseAPIKeyFields';
import baseVerificationFields from '../../fields/baseFields/baseVerificationFields';
import baseAccountLockFields from '../../fields/baseFields/baseAccountLockFields';
import baseUploadFields from '../../fields/baseFields/baseUploadFields';
import baseImageUploadFields from '../../fields/baseFields/baseImageUploadFields';
import getBaseUploadFields from '../../fields/baseFields/getBaseUploadFields';
import { formatLabels } from '../../utilities/formatLabels';
import { defaults, authDefaults } from './defaults';
import { mimeTypeValidator } from '../../fields/baseFields/mimeTypeValidator';
import { Config } from '../../config/types';
const mergeBaseFields = (fields, baseFields) => {
const mergedFields = [];
@@ -56,7 +55,7 @@ const mergeBaseFields = (fields, baseFields) => {
return baseFields;
};
const sanitizeCollection = (collections: CollectionConfig[], collection: CollectionConfig): SanitizedCollectionConfig => {
const sanitizeCollection = (config: Config, collection: CollectionConfig): SanitizedCollectionConfig => {
// /////////////////////////////////
// Make copy of collection config
// /////////////////////////////////
@@ -73,15 +72,10 @@ const sanitizeCollection = (collections: CollectionConfig[], collection: Collect
sanitized.upload.staticURL = sanitized.upload.staticURL || `/${sanitized.slug}`;
sanitized.admin.useAsTitle = (sanitized.admin.useAsTitle && sanitized.admin.useAsTitle !== 'id') ? sanitized.admin.useAsTitle : 'filename';
let uploadFields = baseUploadFields;
if (sanitized.upload.mimeTypes) {
uploadFields.find((field) => fieldIsNamed(field) && field.name === 'mimeType').validate = mimeTypeValidator(sanitized.upload.mimeTypes);
}
if (sanitized.upload.imageSizes && Array.isArray(sanitized.upload.imageSizes)) {
uploadFields = uploadFields.concat(baseImageUploadFields(sanitized.upload.imageSizes));
}
let uploadFields = getBaseUploadFields({
config,
collection: sanitized,
});
uploadFields = mergeBaseFields(sanitized.fields, uploadFields);
@@ -121,7 +115,7 @@ const sanitizeCollection = (collections: CollectionConfig[], collection: Collect
// Sanitize fields
// /////////////////////////////////
const validRelationships = collections.map((c) => c.slug);
const validRelationships = config.collections.map((c) => c.slug);
sanitized.fields = sanitizeFields(sanitized.fields, validRelationships);
return sanitized as SanitizedCollectionConfig;

View File

@@ -12,13 +12,13 @@ const sanitizeConfig = (config: Config): SanitizedConfig => {
if (!sanitizedConfig.admin.user) {
sanitizedConfig.admin.user = 'users';
const sanitizedDefaultUser = sanitizeCollection(sanitizedConfig.collections, defaultUser);
const sanitizedDefaultUser = sanitizeCollection(sanitizedConfig, defaultUser);
sanitizedConfig.collections.push(sanitizedDefaultUser);
} else if (!sanitizedConfig.collections.find((c) => c.slug === sanitizedConfig.admin.user)) {
throw new InvalidConfiguration(`${sanitizedConfig.admin.user} is not a valid admin user collection`);
}
sanitizedConfig.collections = sanitizedConfig.collections.map((collection) => sanitizeCollection(sanitizedConfig.collections, collection));
sanitizedConfig.collections = sanitizedConfig.collections.map((collection) => sanitizeCollection(sanitizedConfig, collection));
checkDuplicateCollections(sanitizedConfig.collections);
if (sanitizedConfig.globals.length > 0) {

View File

@@ -1,81 +0,0 @@
import { ImageSize } from '../../uploads/types';
import { Field } from '../config/types';
export default (imageSizes: ImageSize[]): Field[] => [
{
name: 'width',
label: 'Width',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
}, {
name: 'height',
label: 'Height',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
},
{
name: 'sizes',
label: 'Sizes',
type: 'group',
admin: {
disabled: true,
},
fields: imageSizes.map((size) => ({
label: size.name,
name: size.name,
type: 'group',
admin: {
disabled: true,
},
fields: [
{
name: 'width',
label: 'Width',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
}, {
name: 'height',
label: 'Height',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
}, {
name: 'mimeType',
label: 'MIME Type',
type: 'text',
admin: {
readOnly: true,
disabled: true,
},
}, {
name: 'filesize',
label: 'File Size',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
}, {
name: 'filename',
label: 'File Name',
type: 'text',
admin: {
readOnly: true,
disabled: true,
},
},
],
})),
},
];

View File

@@ -1,31 +0,0 @@
import { Field } from '../config/types';
export default [
{
name: 'filename',
label: 'Filename',
type: 'text',
required: true,
unique: true,
admin: {
disabled: true,
readOnly: true,
},
}, {
name: 'mimeType',
label: 'MIME Type',
type: 'text',
admin: {
readOnly: true,
disabled: true,
},
}, {
name: 'filesize',
label: 'File Size',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
},
] as Field[];

View File

@@ -0,0 +1,147 @@
import { Field } from '../config/types';
import { Config } from '../../config/types';
import { CollectionConfig } from '../../collections/config/types';
import { mimeTypeValidator } from '../../uploads/mimeTypeValidator';
import { IncomingUploadType } from '../../uploads/types';
type Options = {
config: Config
collection: CollectionConfig
}
const getBaseUploadFields = ({ config, collection }: Options): Field[] => {
const uploadOptions: IncomingUploadType = typeof collection.upload === 'object' ? collection.upload : {};
const mimeType: Field = {
name: 'mimeType',
label: 'MIME Type',
type: 'text',
admin: {
readOnly: true,
disabled: true,
},
};
const url: Field = {
name: 'url',
label: 'URL',
type: 'text',
admin: {
readOnly: true,
disabled: true,
},
};
const width: Field = {
name: 'width',
label: 'Width',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
};
const height: Field = {
name: 'height',
label: 'Height',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
};
const filesize: Field = {
name: 'filesize',
label: 'File Size',
type: 'number',
admin: {
readOnly: true,
disabled: true,
},
};
const filename: Field = {
name: 'filename',
label: 'File Name',
type: 'text',
admin: {
readOnly: true,
disabled: true,
},
};
let uploadFields: Field[] = [
{
...url,
hooks: {
afterRead: [
({ data }) => {
if (data?.filename) {
return `${config.serverURL}${uploadOptions.staticURL}/${data.filename}`;
}
return undefined;
},
],
},
},
filename,
mimeType,
filesize,
];
if (uploadOptions.mimeTypes) {
mimeType.validate = mimeTypeValidator(uploadOptions.mimeTypes);
}
if (uploadOptions.imageSizes) {
uploadFields = uploadFields.concat([
width,
height,
{
name: 'sizes',
label: 'Sizes',
type: 'group',
admin: {
disabled: true,
},
fields: uploadOptions.imageSizes.map((size) => ({
label: size.name,
name: size.name,
type: 'group',
admin: {
disabled: true,
},
fields: [
{
...url,
hooks: {
afterRead: [
({ data }) => {
const sizeFilename = data?.sizes?.[size.name]?.filename;
if (sizeFilename) {
return `${config.serverURL}${uploadOptions.staticURL}/${sizeFilename}`;
}
return undefined;
},
],
},
},
width,
height,
mimeType,
filesize,
filename,
],
})),
},
]);
}
return uploadFields;
};
export default getBaseUploadFields;

View File

@@ -1,4 +1,4 @@
import { Validate } from '../config/types';
import { Validate } from '../fields/config/types';
export const mimeTypeValidator = (mimeTypes: string[]): Validate => (val: string) => {
const cleanedMimeTypes = mimeTypes.map((v) => v.replace('*', ''));