PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
jetpack / modules / videopress / js / videopress-media-new.js

videopress-media-new.js in Jetpack – WP Security, Backup, Speed, & Growth 16.3, at modules/videopress/js/videopress-media-new.js

345 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global plupload, pluploadL10n, ajaxurl, wpUploaderInit, uploadSuccess, wpFileError, wpQueueError, videoPressMediaNew */
2
3 /**
4 * Routes video uploads from wp-admin/media-new.php to VideoPress.
5 *
6 * The classic uploader (plupload-handlers) creates a raw plupload.Uploader
7 * rather than wp.Uploader, so the override in videopress-plupload.js never
8 * engages there. This shim registers the same videopress_check_uploads file
9 * filter and re-targets video uploads to VideoPress, then hands the local
10 * attachment ID created during the upload back to the stock uploadSuccess
11 * handler so the classic UI renders the media item as usual.
12 */
13 ( function ( $ ) {
14 if ( typeof plupload === 'undefined' ) {
15 return;
16 }
17
18 var originalOptions = {};
19
20 // Videos accepted for VideoPress upload since the page loaded, used to
21 // enforce the free plan's single-video limit within an upload session.
22 var acceptedVideoCount = 0;
23
24 // Video files in the selection currently being added to the uploader,
25 // recorded before the file filters run so the free-plan gate can block a
26 // multi-video selection before any upload starts.
27 var pendingBatchVideoCount = 0;
28
29 /**
30 * Counts the video files in an addFile() argument.
31 *
32 * @param {Array|FileList|object} file What was passed to addFile: a single
33 * file or an array-like of files.
34 * @return {number} How many of them are videos.
35 */
36 function countVideoFiles( file ) {
37 var files = file && typeof file.length === 'number' ? file : [ file ];
38 var count = 0;
39
40 for ( var i = 0; i < files.length; i++ ) {
41 var type = files[ i ] && files[ i ].type;
42
43 if ( type && type.split( '/' )[ 0 ] === 'video' ) {
44 count++;
45 }
46 }
47
48 return count;
49 }
50
51 /**
52 * Returns the upload limit data localized by the module.
53 *
54 * @return {object} The limit data.
55 */
56 function getUploadLimits() {
57 return typeof videoPressMediaNew !== 'undefined' ? videoPressMediaNew : {};
58 }
59
60 // Class of the container the upgrade notice renders in.
61 var NOTICE_CLASS = 'videopress-media-new-notice';
62
63 /**
64 * Removes any previously rendered upgrade notice.
65 */
66 function removeUpgradeNotice() {
67 var notices = document.querySelectorAll( '.' + NOTICE_CLASS );
68
69 for ( var i = 0; i < notices.length; i++ ) {
70 notices[ i ].parentNode.removeChild( notices[ i ] );
71 }
72 }
73
74 /**
75 * Shows the free-plan upgrade notice above the upload queue.
76 *
77 * The message is pre-escaped HTML built server-side, containing the link
78 * to the upgrade path. It renders in a container of its own next to the
79 * stock #media-upload-error area rather than inside it: the stock
80 * FilesAdded handler empties #media-upload-error as soon as any file of
81 * the selection is accepted, which would wipe a notice rendered there
82 * before the user could read it.
83 *
84 * @param {string} message The notice HTML.
85 */
86 function showUpgradeNotice( message ) {
87 if ( ! message ) {
88 return;
89 }
90
91 var anchor = document.getElementById( 'media-upload-error' );
92
93 if ( ! anchor || ! anchor.parentNode ) {
94 // The stock error area is better than no notice at all.
95 if ( typeof wpQueueError === 'function' ) {
96 wpQueueError( message );
97 }
98 return;
99 }
100
101 removeUpgradeNotice();
102
103 var notice = document.createElement( 'div' );
104 notice.className = 'notice notice-warning ' + NOTICE_CLASS;
105
106 var paragraph = document.createElement( 'p' );
107 // The message is localized server-side and passed through wp_kses;
108 // it only contains the upgrade link markup.
109 paragraph.innerHTML = message;
110
111 notice.appendChild( paragraph );
112 anchor.parentNode.insertBefore( notice, anchor.nextSibling );
113 }
114
115 /**
116 * Free-plan gate for a video file: the free plan includes a single video
117 * upload, so reject every video once the site already has a VideoPress
118 * video, and allow only one video per upload session otherwise.
119 *
120 * @param {plupload.File} file The video file being queued.
121 * @param {Function} cb The file filter continuation callback.
122 * @return {boolean} Whether the file was rejected.
123 */
124 function rejectedByFreePlanLimits( file, cb ) {
125 var limits = getUploadLimits();
126 var strings = limits.strings || {};
127
128 if ( limits.hasVideoPressPurchase ) {
129 return false;
130 }
131
132 if ( limits.hasUsedVideo ) {
133 showUpgradeNotice( strings.usedVideoUpload );
134 cb( false );
135 return true;
136 }
137
138 // A selection with more than one video is blocked entirely — including
139 // its first video — so no upload starts until the user upgrades.
140 if ( pendingBatchVideoCount > 1 ) {
141 showUpgradeNotice( strings.multipleVideosSelected );
142 cb( false );
143 return true;
144 }
145
146 if ( acceptedVideoCount >= 1 ) {
147 showUpgradeNotice( strings.multipleVideos );
148 cb( false );
149 return true;
150 }
151
152 return false;
153 }
154
155 /**
156 * Restores the stock upload settings saved before a VideoPress upload.
157 *
158 * @param {plupload.Uploader} up Uploader instance.
159 */
160 function restoreOriginalOptions( up ) {
161 if ( typeof originalOptions.url === 'undefined' ) {
162 return;
163 }
164
165 up.setOption( 'url', originalOptions.url );
166 up.setOption( 'file_data_name', originalOptions.file_data_name );
167 up.setOption( 'headers', originalOptions.headers );
168
169 originalOptions = {};
170 }
171
172 /**
173 * For video files, fetch a VideoPress upload token and attach it to the
174 * file before it is queued. Mirrors the videopress_check_uploads filter in
175 * videopress-uploader.js without depending on wp.media, which is not
176 * loaded on media-new.php. Non-video files get the stock max_file_size
177 * behavior this filter replaces.
178 */
179 plupload.addFileFilter( 'videopress_check_uploads', function ( maxSize, file, cb ) {
180 var self = this;
181
182 /**
183 * Rejects the file with an uploader error.
184 *
185 * @param {string} [message] Error message to display.
186 */
187 function fail( message ) {
188 self.trigger( 'Error', {
189 code: plupload.HTTP_ERROR,
190 message:
191 message ||
192 plupload.translate( 'Could not get the VideoPress token needed for uploading' ),
193 file: file,
194 } );
195 cb( false );
196 }
197
198 if ( file.type && file.type.split( '/' )[ 0 ] === 'video' ) {
199 if ( rejectedByFreePlanLimits( file, cb ) ) {
200 return;
201 }
202
203 $.post( ajaxurl, { action: 'videopress-get-upload-token', filename: file.name } )
204 .done( function ( response ) {
205 if (
206 ! response ||
207 ! response.success ||
208 ! response.data ||
209 ! response.data.upload_action_url
210 ) {
211 fail( response && response.data && response.data.message );
212 return;
213 }
214
215 file.videopress = response.data;
216 acceptedVideoCount++;
217 cb( true );
218 } )
219 .fail( function () {
220 fail();
221 } );
222
223 return;
224 }
225
226 // Handles the stock max_file_size functionality for non-video files.
227 if (
228 typeof file.size !== 'undefined' &&
229 maxSize &&
230 file.size > plupload.parseSize( maxSize )
231 ) {
232 this.trigger( 'Error', {
233 code: plupload.FILE_SIZE_ERROR,
234 message: pluploadL10n.file_exceeds_size_limit.replace( '%s', file.name ),
235 file: file,
236 } );
237 cb( false );
238 return;
239 }
240
241 cb( true );
242 } );
243
244 $( function () {
245 // plupload-handlers is a dependency of this script, so its ready
246 // callback ran first and the global `uploader` already exists.
247 if (
248 typeof wpUploaderInit === 'undefined' ||
249 typeof window.uploader === 'undefined' ||
250 ! window.uploader
251 ) {
252 return;
253 }
254
255 var uploader = window.uploader;
256
257 /**
258 * Record how many videos each selection contains before plupload runs
259 * the file filters, so the free-plan gate can reject every video of a
260 * multi-video selection. Both the file selector and drag-and-drop
261 * funnel their whole selection through a single addFile() call.
262 */
263 var stockAddFile = uploader.addFile;
264
265 uploader.addFile = function ( file, fileName ) {
266 // Each selection starts clean: a notice about a previous
267 // selection no longer applies to this one.
268 removeUpgradeNotice();
269 pendingBatchVideoCount = countVideoFiles( file );
270 return stockAddFile.call( this, file, fileName );
271 };
272
273 /**
274 * Re-target VideoPress files right before each upload starts and
275 * restore the stock settings for everything else, so mixed batches
276 * of videos and other media both land in the right place.
277 */
278 uploader.bind( 'BeforeUpload', function ( up, file ) {
279 if ( typeof file.videopress === 'undefined' ) {
280 restoreOriginalOptions( up );
281 return;
282 }
283
284 if ( typeof originalOptions.url === 'undefined' ) {
285 originalOptions.url = up.getOption( 'url' );
286 originalOptions.file_data_name = up.getOption( 'file_data_name' );
287 originalOptions.headers = up.getOption( 'headers' );
288 }
289
290 up.setOption( 'url', file.videopress.upload_action_url );
291 up.setOption( 'file_data_name', 'media[]' );
292 up.setOption( 'headers', {
293 Authorization:
294 'X_UPLOAD_TOKEN token="' +
295 file.videopress.upload_token +
296 '" blog_id="' +
297 file.videopress.upload_blog_id +
298 '"',
299 } );
300 } );
301
302 uploader.bind( 'Error', function ( up ) {
303 restoreOriginalOptions( up );
304 } );
305
306 uploader.bind( 'UploadComplete', function ( up ) {
307 restoreOriginalOptions( up );
308 } );
309
310 /**
311 * The stock FileUploaded handler calls the global uploadSuccess with
312 * the raw server response, which for VideoPress uploads is the REST
313 * API media response instead of the attachment ID async-upload.php
314 * returns. Wrap uploadSuccess to translate the former into the
315 * latter; the stock handler then loads the media item row from
316 * async-upload.php as it would for any other upload.
317 */
318 var stockUploadSuccess = uploadSuccess;
319
320 window.uploadSuccess = function ( fileObj, serverData ) {
321 if ( typeof fileObj.videopress === 'undefined' ) {
322 return stockUploadSuccess( fileObj, serverData );
323 }
324
325 var response = null;
326
327 try {
328 response = JSON.parse( serverData );
329 } catch {
330 // Handled below.
331 }
332
333 var media = response && response.media && response.media[ 0 ];
334
335 if ( ! media || ! media.ID ) {
336 var message = response && ( response.message || response.error );
337 wpFileError( fileObj, message || pluploadL10n.default_error );
338 return;
339 }
340
341 return stockUploadSuccess( fileObj, String( media.ID ) );
342 };
343 } );
344 } )( jQuery );
345