feat: adds dynamic url field to upload-enabled collections
This commit is contained in:
@@ -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:
|
||||
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -6,4 +6,5 @@ export type Props = {
|
||||
width?: number,
|
||||
height?: number,
|
||||
sizes?: unknown,
|
||||
url?: string
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
],
|
||||
})),
|
||||
},
|
||||
];
|
||||
@@ -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[];
|
||||
147
src/fields/baseFields/getBaseUploadFields.ts
Normal file
147
src/fields/baseFields/getBaseUploadFields.ts
Normal 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;
|
||||
@@ -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('*', ''));
|
||||
Reference in New Issue
Block a user