-
Notifications
You must be signed in to change notification settings - Fork 4k
/
media-upload.js
183 lines (171 loc) · 6.16 KB
/
media-upload.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/**
* External Dependencies
*/
import { compact, flatMap, forEach, get, has, includes, map, noop, startsWith } from 'lodash';
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import { __, sprintf } from '@wordpress/i18n';
/**
* Browsers may use unexpected mime types, and they differ from browser to browser.
* This function computes a flexible array of mime types from the mime type structured provided by the server.
* Converts { jpg|jpeg|jpe: "image/jpeg" } into [ "image/jpeg", "image/jpg", "image/jpeg", "image/jpe" ]
* The computation of this array instead of directly using the object,
* solves the problem in chrome where mp3 files have audio/mp3 as mime type instead of audio/mpeg.
* https://bugs.chromium.org/p/chromium/issues/detail?id=227004
*
* @param {?Object} wpMimeTypesObject Mime type object received from the server.
* Extensions are keys separated by '|' and values are mime types associated with an extension.
*
* @return {?Array} An array of mime types or the parameter passed if it was "falsy".
*/
export function getMimeTypesArray( wpMimeTypesObject ) {
if ( ! wpMimeTypesObject ) {
return wpMimeTypesObject;
}
return flatMap( wpMimeTypesObject, ( mime, extensionsString ) => {
const [ type ] = mime.split( '/' );
const extensions = extensionsString.split( '|' );
return [ mime, ...map( extensions, ( extension ) => `${ type }/${ extension }` ) ];
} );
}
/**
* Media Upload is used by audio, image, gallery, video, and file blocks to
* handle uploading a media file when a file upload button is activated.
*
* TODO: future enhancement to add an upload indicator.
*
* @param {Object} $0 Parameters object passed to the function.
* @param {string} $0.allowedType The type of media that can be uploaded, or '*' to allow all.
* @param {?Object} $0.additionalData Additional data to include in the request.
* @param {Array} $0.filesList List of files.
* @param {?number} $0.maxUploadFileSize Maximum upload size in bytes allowed for the site.
* @param {Function} $0.onError Function called when an error happens.
* @param {Function} $0.onFileChange Function called each time a file or a temporary representation of the file is available.
* @param {?Object} $0.allowedMimeTypes List of allowed mime types and file extensions.
*/
export function mediaUpload( {
allowedType,
additionalData = {},
filesList,
maxUploadFileSize,
onError = noop,
onFileChange,
allowedMimeTypes = null,
} ) {
// Cast filesList to array
const files = [ ...filesList ];
const filesSet = [];
const setAndUpdateFiles = ( idx, value ) => {
filesSet[ idx ] = value;
onFileChange( compact( filesSet ) );
};
// Allowed type specified by consumer
const isAllowedType = ( fileType ) => {
return ( allowedType === '*' ) || startsWith( fileType, `${ allowedType }/` );
};
// Allowed types for the current WP_User
const allowedMimeTypesForUser = getMimeTypesArray( allowedMimeTypes );
const isAllowedMimeTypeForUser = ( fileType ) => {
return includes( allowedMimeTypesForUser, fileType );
};
files.forEach( ( mediaFile, idx ) => {
// verify if user is allowed to upload this mime type
if ( allowedMimeTypesForUser && ! isAllowedMimeTypeForUser( mediaFile.type ) ) {
onError( {
code: 'MIME_TYPE_NOT_ALLOWED_FOR_USER',
message: [
<strong key="filename">{ mediaFile.name }</strong>,
': ',
__( 'Sorry, this file type is not permitted for security reasons.' ),
],
file: mediaFile,
} );
return;
}
// Check if the block supports this mime type
if ( ! isAllowedType( mediaFile.type ) ) {
onError( {
code: 'MIME_TYPE_NOT_SUPPORTED_FOR_BLOCK',
message: [
<strong key="filename">{ mediaFile.name }</strong>,
': ',
__( 'Sorry, this file type is not supported by this block.' ),
],
file: mediaFile,
} );
return;
}
// verify if file is greater than the maximum file upload size allowed for the site.
if ( maxUploadFileSize && mediaFile.size > maxUploadFileSize ) {
onError( {
code: 'SIZE_ABOVE_LIMIT',
message: sprintf(
// translators: %s: file name
__( '%s exceeds the maximum upload size for this site.' ),
mediaFile.name
),
file: mediaFile,
} );
return;
}
// Set temporary URL to create placeholder media file, this is replaced
// with final file from media gallery when upload is `done` below
filesSet.push( { url: window.URL.createObjectURL( mediaFile ) } );
onFileChange( filesSet );
return createMediaFromFile( mediaFile, additionalData )
.then( ( savedMedia ) => {
const mediaObject = {
alt: savedMedia.alt_text,
caption: get( savedMedia, [ 'caption', 'raw' ], '' ),
id: savedMedia.id,
link: savedMedia.link,
title: savedMedia.title.raw,
url: savedMedia.source_url,
mediaDetails: {},
};
if ( has( savedMedia, [ 'media_details', 'sizes' ] ) ) {
mediaObject.mediaDetails.sizes = get( savedMedia, [ 'media_details', 'sizes' ], {} );
}
setAndUpdateFiles( idx, mediaObject );
} )
.catch( ( error ) => {
// Reset to empty on failure.
setAndUpdateFiles( idx, null );
let message;
if ( has( error, [ 'message' ] ) ) {
message = get( error, [ 'message' ] );
} else {
message = sprintf(
// translators: %s: file name
__( 'Error while uploading file %s to the media library.' ),
mediaFile.name
);
}
onError( {
code: 'GENERAL',
message,
file: mediaFile,
} );
} );
} );
}
/**
* @param {File} file Media File to Save.
* @param {?Object} additionalData Additional data to include in the request.
*
* @return {Promise} Media Object Promise.
*/
function createMediaFromFile( file, additionalData ) {
// Create upload payload
const data = new window.FormData();
data.append( 'file', file, file.name || file.type.replace( '/', '.' ) );
data.append( 'title', file.name ? file.name.replace( /\.[^.]+$/, '' ) : file.type.replace( '/', '.' ) );
forEach( additionalData, ( ( value, key ) => data.append( key, value ) ) );
return apiFetch( {
path: '/wp/v2/media',
body: data,
method: 'POST',
} );
}