Completed
Push — feature/videopress-uploader ( a9ff31...5f48a8 )
by
unknown
09:45
created

Jetpack_VideoPress::admin_menu_library()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 13
rs 9.4285
cc 1
eloc 11
nc 1
nop 0
1
<?php
2
/**
3
 * VideoPress in Jetpack
4
 *
5
 */
6
class Jetpack_VideoPress {
7
	public $module = 'videopress';
8
	public $option_name = 'videopress';
9
	public $version = 4;
10
11
	/**
12
	 * Singleton
13
	 */
14
	public static function init() {
15
		static $instance = false;
16
17
		if ( ! $instance )
18
			$instance = new Jetpack_VideoPress;
19
20
		return $instance;
21
	}
22
23
	function __construct() {
24
		$this->version = time(); // <s>ghost</s> cache busters!
25
		add_action( 'init', array( $this, 'on_init' ) );
26
		add_action( 'admin_notices', array( __CLASS__, 'handle_editor_view_js' ) );
27
		add_action( 'jetpack_activate_module_videopress', array( $this, 'jetpack_module_activated' ) );
28
		add_action( 'jetpack_deactivate_module_videopress', array( $this, 'jetpack_module_deactivated' ) );
29
	}
30
31
	/**
32
	 * Fires on init since is_connection_owner should wait until the user is initialized by $wp->init();
33
	 */
34
	function on_init() {
35
		$options = $this->get_options();
36
37
		// Only the connection owner can configure this module.
38
		if ( $this->is_connection_owner() ) {
39
			Jetpack::enable_module_configurable( $this->module );
40
			Jetpack::module_configuration_load( $this->module, array( $this, 'jetpack_configuration_load' ) );
41
			Jetpack::module_configuration_screen( $this->module, array( $this, 'jetpack_configuration_screen' ) );
42
		}
43
44
		// Only if the current user can manage the VideoPress library and one has been connected.
45
		if ( $this->can( 'read_videos' ) && $options['blog_id'] ) {
46
			add_action( 'wp_enqueue_media', array( $this, 'enqueue_admin_scripts' ) );
47
			add_action( 'print_media_templates', array( $this, 'print_media_templates' ) );
48
49
			// Load these at priority -1 so they're fired before Core's are.
50
			add_action( 'wp_ajax_query-attachments', array( $this, 'wp_ajax_query_attachments' ), -1 );
51
			add_action( 'wp_ajax_save-attachment', array( $this, 'wp_ajax_save_attachment' ), -1 );
52
			add_action( 'wp_ajax_save-attachment-compat', array( $this, 'wp_ajax_save_attachment' ), -1 );
53
			add_action( 'wp_ajax_delete-post', array( $this, 'wp_ajax_delete_post' ), -1 );
54
55
			add_action( 'admin_menu', array( $this, 'admin_menu' ) );
56
		}
57
58
		if ( $this->can( 'upload_videos' ) && $options['blog_id'] ) {
59
			add_action( 'wp_ajax_videopress-get-upload-token', array( $this, 'wp_ajax_videopress_get_upload_token' ) );
60
			add_filter( 'plupload_default_settings', array( $this, 'videopress_pluploder_config' ) );
61
		}
62
63
		add_filter( 'videopress_shortcode_options', array( $this, 'videopress_shortcode_options' ) );
64
	}
65
66
	function wp_ajax_videopress_get_upload_token() {
67
		if ( ! $this->can( 'upload_videos' ) )
68
			return wp_send_json_error();
69
70
		$options = $this->get_options();
71
72
		$args = array(
73
			'method'  => 'POST',
74
		);
75
76
		$endpoint = "sites/{$options['blog_id']}/media/token";
77
		$result = Jetpack_Client::wpcom_json_api_request_as_blog( $endpoint, Jetpack_Client::WPCOM_JSON_API_VERSION, $args );
78
79
		$response = json_decode( $result['body'], true );
80
81
		if ( is_wp_error( $response ) )
82
			return wp_send_json_error( array( 'message' => __( 'Could not obtain a VideoPress upload token. Please try again later.', 'jetpack' ) ) );
83
84
		if ( empty( $response['upload_blog_id'] ) || empty( $response['upload_token'] ) )
85
			return wp_send_json_error( array( 'message' => __( 'Could not obtain a VideoPress upload token. Please try again later.', 'jetpack' ) ) );
86
87
		$response['upload_action_url'] = self::make_media_upload_path( $response['upload_blog_id'] );
88
89
		return wp_send_json_success( $response );
90
	}
91
92
	/**
93
	 * Get the upload api path.
94
	 *
95
	 * @param $blog_id
96
	 * @return string
97
	 */
98
	function make_media_upload_path( $blog_id ) {
99
		return sprintf(
100
			'%s://%s/rest/v%s/sites/%s/media/new',
101
			'https',
102
			JETPACK__WPCOM_JSON_API_HOST,
103
			Jetpack_Client::WPCOM_JSON_API_VERSION,
104
			$blog_id
105
		);
106
	}
107
108
	/**
109
	 * Modify the default plupload config to turn on videopress specific filters.
110
	 */
111
	function videopress_pluploder_config( $config ) {
112
		$config['filters']['videopress_check_uploads'] = 1;
113
		return $config;
114
	}
115
116
	/**
117
	 * Get VideoPress options
118
	 */
119
	function get_options() {
120
		$defaults = array(
121
			'blogs' => array(),
122
			'blog_id' => 0,
123
			'access' => '',
124
			'allow-upload' => false,
125
			'freedom' => false,
126
			'hd' => false,
127
			'meta' => array(
128
				'max_upload_size' => 0,
129
			),
130
		);
131
132
		$options = Jetpack_Options::get_option( $this->option_name, array() );
133
134
		// If options have not been saved yet, check for older VideoPress plugin options.
135
		if ( empty( $options ) ) {
136
			$options['freedom'] = (bool) get_option( 'video_player_freedom', false );
137
			$options['hd'] = (bool) get_option( 'video_player_high_quality', false );
138
		}
139
140
		$options = array_merge( $defaults, $options );
141
		return $options;
142
	}
143
144
	/**
145
	 * Update VideoPress options
146
	 */
147
	function update_options( $options ) {
148
		Jetpack_Options::update_option( $this->option_name, $options );
149
	}
150
151
	/**
152
	 * Runs when the VideoPress module is activated.
153
	 */
154
	function jetpack_module_activated() {
155
		if ( ! $this->is_connection_owner() )
156
			return;
157
158
		$options = $this->get_options();
159
160
		// Ask WordPress.com for a list of VideoPress blogs
161
		$result = $this->query( 'jetpack.vpGetBlogs' );
162
		if ( ! is_wp_error( $result ) )
163
			$options['blogs'] = $result;
164
165
		// If there's at least one available blog, let's use it.
166
		if ( is_array( $options['blogs'] ) && count( $options['blogs'] ) > 0 )
167
			$options['blog_id'] = $options['blogs'][0]['blog_id'];
168
169
		$this->update_options( $options );
170
	}
171
172
	/**
173
	 * Runs when the VideoPress module is deactivated.
174
	 */
175
	function jetpack_module_deactivated() {
176
		Jetpack_Options::delete_option( $this->option_name );
177
	}
178
179
	/**
180
	 * Remote Query
181
	 *
182
	 * Performs a remote XML-RPC query using Jetpack's IXR Client. And also
183
	 * appends some useful stuff about this setup to the query.
184
	 *
185
	 * @return the Jetpack_IXR_Client object after querying.
186
	 */
187
	function query( $method, $args = null ) {
188
		$options = $this->get_options();
189
		Jetpack::load_xml_rpc_client();
190
		$xml = new Jetpack_IXR_Client( array(
191
			'user_id' => JETPACK_MASTER_USER, // All requests are on behalf of the connection owner.
192
		) );
193
194
		$params = array(
195
			'args' => $args,
196
			'video_blog_id' => $options['blog_id'],
197
			'caps' => array(),
198
		);
199
200
		// Let Jetpack know about our local caps.
201 View Code Duplication
		foreach ( array( 'read_videos', 'edit_videos', 'delete_videos', 'upload_videos' ) as $cap )
202
			if ( $this->can( $cap ) )
203
				$params['caps'][] = $cap;
204
205
		$xml->query( $method, $params );
206
207
		if ( $xml->isError() )
208
			return new WP_Error( 'xml_rpc_error', 'An XML-RPC error has occurred.' );
209
210
		$response = $xml->getResponse();
211
212
		// If there's any metadata with the response, save it for future use.
213
		if ( is_array( $response ) && isset( $response['meta'] ) ) {
214
			$options = $this->get_options();
215
			if ( $response['meta'] !== $options['meta'] ) {
216
				$options['meta'] = array_merge( $options['meta'], $response['meta'] );
217
				$this->update_options( $options );
218
			}
219
		}
220
221
		if ( is_array( $response ) && isset( $response['result'] ) )
222
			return $response['result'];
223
224
		return $response;
225
	}
226
227
	/**
228
	 * Runs before the VideoPress Configuration screen loads, useful
229
	 * to update options and yield errors.
230
	 */
231
	function jetpack_configuration_load() {
232
		$this->enqueue_admin_scripts();
233
234
		/**
235
		 * Save configuration
236
		 */
237
		if ( ! empty( $_POST['action'] ) && $_POST['action'] == 'videopress-save' ) {
238
			check_admin_referer( 'videopress-settings' );
239
			$options = $this->get_options();
240
241
			if ( isset( $_POST['blog_id'] ) && in_array( $_POST['blog_id'], wp_list_pluck( $options['blogs'], 'blog_id' ) ) )
242
				$options['blog_id'] = $_POST['blog_id'];
243
244
			// Allow the None setting too.
245
			if ( isset( $_POST['blog_id'] ) && $_POST['blog_id'] == 0 )
246
				$options['blog_id'] = 0;
247
248
			/**
249
			 * @see $this->can()
250
			 */
251
			if ( isset( $_POST['videopress-access'] ) && in_array( $_POST['videopress-access'], array( '', 'read', 'edit', 'delete' ) ) )
252
				$options['access'] = $_POST['videopress-access'];
253
254
			$options['freedom'] = isset( $_POST['videopress-freedom'] );
255
			$options['hd'] = isset( $_POST['videopress-hd'] );
256
257
			// Allow upload only if some level of access has been granted, and uploads were allowed.
258
			$options['allow-upload'] = false;
259
			if ( ! empty( $options['access'] ) && isset( $_POST['videopress-upload'] ) )
260
				$options['allow-upload'] = true;
261
262
			$this->update_options( $options );
263
			Jetpack::state( 'message', 'module_configured' );
264
			wp_safe_redirect( Jetpack::module_configuration_url( $this->module ) );
265
		}
266
267
		/**
268
		 * Refresh the list of available WordPress.com blogs
269
		 */
270
		if ( ! empty( $_GET['videopress'] ) && $_GET['videopress'] == 'refresh-blogs' ) {
271
			check_admin_referer( 'videopress-settings' );
272
			$options = $this->get_options();
273
274
			$result = $this->query( 'jetpack.vpGetBlogs' );
275
			if ( ! is_wp_error( $result ) ) {
276
				$options['blogs'] = $result;
277
				$this->update_options( $options );
278
			}
279
280
			wp_safe_redirect( Jetpack::module_configuration_url( $this->module ) );
281
		}
282
	}
283
284
	/**
285
	 * Renders the VideoPress Configuration screen in Jetpack.
286
	 */
287
	function jetpack_configuration_screen() {
288
		$options = $this->get_options();
289
		$refresh_url = wp_nonce_url( add_query_arg( 'videopress', 'refresh-blogs' ), 'videopress-settings' );
290
		?>
291
		<div class="narrow">
292
			<form method="post" id="videopress-settings">
293
				<input type="hidden" name="action" value="videopress-save" />
294
				<?php wp_nonce_field( 'videopress-settings' ); ?>
295
296
				<table id="menu" class="form-table">
297
					<tr>
298
						<th scope="row" colspan="2">
299
							<p><?php _e( 'Please note that the VideoPress module requires a WordPress.com account with an active <a href="http://store.wordpress.com/premium-upgrades/videopress/" target="_blank">VideoPress subscription</a>.', 'jetpack' ); ?></p>
300
						</th>
301
					</tr>
302
					<tr>
303
						<th scope="row">
304
							<label><?php _e( 'Connected WordPress.com Blog', 'jetpack' ); ?></label>
305
						</th>
306
						<td>
307
							<select name="blog_id">
308
								<option value="0" <?php selected( $options['blog_id'], 0 ); ?>> <?php esc_html_e( 'None', 'jetpack' ); ?></option>
309
								<?php foreach ( $options['blogs'] as $blog ) : ?>
0 ignored issues
show
Bug introduced by
The expression $options['blogs'] of type array|integer|string|false is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
310
								<option value="<?php echo absint( $blog['blog_id'] ); ?>" <?php selected( $options['blog_id'], $blog['blog_id'] ); ?>><?php echo esc_html( $blog['name'] ); ?> (<?php echo esc_html( $blog['domain'] ); ?>)</option>
311
								<?php endforeach; ?>
312
							</select>
313
							<p class="description"><?php _e( 'Only videos from the selected blog will be available in your media library.', 'jetpack' ); ?>
314
								<?php printf( __( '<a href="%s">Click here</a> to refresh this list.', 'jetpack' ), esc_url( $refresh_url ) ); ?>
315
							</p>
316
						</td>
317
					</tr>
318
					<tr>
319
						<th scope="row">
320
							<label><?php _e( 'Video Library Access', 'jetpack' ); ?></label>
321
						</th>
322
						<td>
323
							<label><input type="radio" name="videopress-access" value="" <?php checked( '', $options['access'] ); ?> />
324
								<?php _e( 'Do not allow other users to access my VideoPress library', 'jetpack' ); ?></label><br/>
325
							<label><input type="radio" name="videopress-access" value="read" <?php checked( 'read', $options['access'] ); ?> />
326
								<?php _e( 'Allow users to access my videos', 'jetpack' ); ?></label><br/>
327
							<label><input type="radio" name="videopress-access" value="edit" <?php checked( 'edit', $options['access'] ); ?> />
328
								<?php _e( 'Allow users to access and edit my videos', 'jetpack' ); ?></label><br/>
329
							<label><input type="radio" name="videopress-access" value="delete" <?php checked( 'delete', $options['access'] ); ?> />
330
								<?php _e( 'Allow users to access, edit, and delete my videos', 'jetpack' ); ?></label><br/><br />
331
332
							<label><input type="checkbox" name="videopress-upload" value="1" <?php checked( $options['allow-upload'] ); ?> />
333
								<?php _e( 'Allow users to upload videos', 'jetpack' ); ?></label><br />
334
						</td>
335
					</tr>
336
					<tr>
337
						<th scope="row">
338
							<label for="videopress-freedom"><?php _e( 'Free formats', 'jetpack' ); ?></label>
339
						</th>
340
						<td>
341
							<label><input type="checkbox" name="videopress-freedom" id="videopress-freedom" <?php checked( $options['freedom'] ); ?> />
342
								<?php _e( 'Only display videos in free software formats', 'jetpack' ); ?></label>
343
							<p class="description"><?php _e( 'Ogg file container with Theora video and Vorbis audio. Note that some browsers are unable to play free software video formats, including Internet Explorer and Safari.', 'jetpack' ); ?></p>
344
						</td>
345
					</tr>
346
					<tr>
347
						<th scope="row">
348
							<label for="videopress-hd"><?php _e( 'Default quality', 'jetpack' ); ?></label>
349
						</th>
350
						<td>
351
							<label><input type="checkbox" name="videopress-hd" id="videopress-hd" <?php checked( $options['hd'] ); ?> />
352
								<?php _e( 'Display higher quality video by default.', 'jetpack' ); ?></label>
353
							<p class="description"><?php _e( 'This setting may be overridden for individual videos.', 'jetpack' ); ?></p>
354
						</td>
355
					</tr>
356
				</table>
357
358
				<?php submit_button(); ?>
359
			</form>
360
		</div>
361
		<?php
362
	}
363
364
	function admin_menu() {
365
		add_media_page( __( 'VideoPress Library', 'jetpack' ), __( 'VideoPress', 'jetpack' ), 'upload_files', 'videopress-library', array( $this, 'admin_menu_library' ) );
366
	}
367
368
	function admin_menu_library() {
369
		wp_enqueue_media();
370
		$this->enqueue_admin_scripts();
371
		?>
372
		<div class="wrap" style="max-width: 600px;">
373
			<?php screen_icon(); ?>
374
	        <h2><?php _e( 'VideoPress Library', 'jetpack' ); ?></h2>
375
	        <p><?php _e( 'Use the button below to browse your VideoPress Library. Note that you can also browse your VideoPress Library while editing a post or page by using the <strong>Add Media</strong> button in the post editor.', 'jetpack' ); ?></p>
376
	        <p class="hide-if-no-js"><a href="#" id="videopress-browse" class="button"><?php _e( 'Browse Your VideoPress Library', 'jetpack' ); ?></a></p>
377
	        <p class="hide-if-js description"><?php _e( 'Please enable JavaScript support in your browser to use VideoPress.', 'jetpack' ); ?></p>
378
	    </div>
379
		<?php
380
	}
381
382
	/**
383
	 * A can of coke
384
	 *
385
	 * Similar to current_user_can, but internal to VideoPress. Returns
386
	 * true if the given VideoPress capability is allowed by the given user.
387
	 */
388
	function can( $cap, $user_id = false ) {
389
		if ( ! $user_id )
390
			$user_id = get_current_user_id();
391
392
		// Connection owners are allowed to do all the things.
393
		if ( $this->is_connection_owner( $user_id ) )
394
			return true;
395
396
		/**
397
		 * The access setting can be set by the connection owner, to allow sets
398
		 * of operations to other site users. Each access value corresponds to
399
		 * an array of things they can do.
400
		 */
401
402
		$options = $this->get_options();
403
		$map = array(
404
			'read'   => array( 'read_videos' ),
405
			'edit'   => array( 'read_videos', 'edit_videos' ),
406
			'delete' => array( 'read_videos', 'edit_videos', 'delete_videos' ),
407
		);
408
409
		if ( ! array_key_exists( $options['access'], $map ) )
410
			return false;
411
412
		if ( ! in_array( $cap, $map[ $options['access'] ] ) && 'upload_videos' != $cap )
413
			return false;
414
415
		// Additional and intrenal caps checks
416
417
		if ( ! user_can( $user_id, 'upload_files' ) )
418
			return false;
419
420
		if ( 'edit_videos' == $cap && ! user_can( $user_id, 'edit_others_posts' ) )
421
			return false;
422
423
		if ( 'delete_videos' == $cap && ! user_can( $user_id, 'delete_others_posts' ) )
424
			return false;
425
426
		if ( 'upload_videos' == $cap && ! $options['allow-upload'] )
427
			return false;
428
429
		return true;
430
	}
431
432
	/**
433
	 * Returns true if the provided user is the Jetpack connection owner.
434
	 */
435
	function is_connection_owner( $user_id = false ) {
436
		if ( ! $user_id )
437
			$user_id = get_current_user_id();
438
439
		$user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
440
		return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && $user_id === $user_token->external_user_id;
441
	}
442
443
	/**
444
	 * Our custom AJAX callback for the query-attachments action
445
	 * used in the media modal. By-passed if not for VideoPress.
446
	 */
447
	function wp_ajax_query_attachments() {
448
449
		// Watch for VideoPress calls
450
		if ( ! isset( $_POST['query']['videopress'] ) )
451
			return;
452
453
		if ( ! $this->can( 'read_videos' ) )
454
			return wp_send_json_error( 'permission denied' );
455
456
		// Get and sanitize query arguments.
457
		$query_args = $this->sanitize_wp_query_args( $_POST['query'] );
458
459
		// Fire a remote WP_Query
460
		$result = $this->query( 'jetpack.vpQuery', $query_args );
461
462
		if ( is_wp_error( $result ) )
463
			return wp_send_json_error( 'xml rpc request error' );
464
465
		$items = $result;
466
467
		foreach ( $items as $key => $item ) {
468
469
			// Check local permissions
470
			if ( ! $this->can( 'edit_videos' ) )
471
				unset( $item['vp_nonces']['update'] );
472
473
			if ( ! $this->can( 'delete_videos' ) )
474
				unset( $item['vp_nonces']['delete'] );
475
476
			// Add a second pair of nonces for the .org blog.
477
			$item['nonces'] = array();
478 View Code Duplication
			if ( ! empty( $item['vp_nonces']['update'] ) )
479
				$item['nonces']['update'] = wp_create_nonce( 'update-videopress-post_' . $item['id'] );
480
481 View Code Duplication
			if ( ! empty( $item['vp_nonces']['delete'] ) )
482
				$item['nonces']['delete'] = wp_create_nonce( 'delete-videopress-post_' . $item['id'] );
483
484
			$item['vp_embed'] = videopress_shortcode_callback( array(
485
				$item['vp_guid'],
486
				'autoplay' => true,
487
				'flashonly' => true,
488
				'w' => 440,
489
			) );
490
491
			$items[ $key ] = $item;
492
		}
493
494
		wp_send_json_success( $items );
495
	}
496
497
	/**
498
	 * Sanitize user-provided WP_Query arguments
499
	 *
500
	 * These might be sent to the VideoPress server, for a remote WP_Query
501
	 * call so let's make sure they're sanitized and safe to send.
502
	 */
503
	function sanitize_wp_query_args( $args ) {
504
		$args = shortcode_atts( array(
505
			'posts_per_page' => 40,
506
			'orderby' => 'date',
507
			'order' => 'desc',
508
			'paged' => 1,
509
			's' => '',
510
		), (array) $args, 'wpvideo' );
511
512
		$args['posts_per_page'] = absint( $args['posts_per_page'] );
513
514
		$args['orderby'] = strtolower( $args['orderby'] );
515
		$args['orderby'] = ( in_array( $args['orderby'], array( 'date' ) ) ) ? $args['orderby'] : 'date';
516
517
		$args['order'] = strtolower( $args['order'] );
518
		$args['order'] = ( in_array( $args['order'], array( 'asc', 'desc' ) ) ) ? $args['order'] : 'desc';
519
520
		$args['paged'] = absint( $args['paged'] );
521
		$args['s'] = sanitize_text_field( $args['s'] );
522
		return $args;
523
	}
524
525
	/**
526
	 * Custom AJAX callback for the save-attachment action. If the request was
527
	 * not for a VideoPress object, core's fallback action will kick in.
528
	 */
529
	function wp_ajax_save_attachment() {
530
		if ( ! isset( $_POST['is_videopress'] ) )
531
			return;
532
533
		if ( ! $this->can( 'edit_videos' ) )
534
			return wp_send_json_error( 'permission denied' );
535
536
		$post_id = 0;
537 View Code Duplication
		if ( ! isset( $_POST['id'] ) || ! $post_id = absint( $_POST['id'] ) )
538
			wp_send_json_error();
539
540
		if ( ! isset( $_POST['vp_nonces']['update'] ) )
541
			wp_send_json_error();
542
543
		check_ajax_referer( 'update-videopress-post_' . $post_id, 'nonce' );
544
545
		$changes = ( ! empty( $_POST['changes'] ) ) ? (array) $_POST['changes'] : array();
546
		$changes = shortcode_atts( array(
547
			'title' => null,
548
			'caption' => null,
549
			'description' => null,
550
551
			'vp_share' => null,
552
			'vp_rating' => null,
553
		), $changes, 'wpvideo' );
554
555
		if ( ! is_null( $changes['vp_share'] ) )
556
			$changes['vp_share'] = (bool) $changes['vp_share'];
557
558
		if ( ! is_null( $changes['vp_rating'] ) )
559
			$changes['vp_rating'] = ( array_key_exists( $changes['vp_rating'], $this->get_available_ratings() ) ) ? $changes['vp_rating'] : null;
560
561
		// Remove null-values
562
		foreach ( $changes as $key => $value )
563
			if ( is_null( $value ) )
564
				unset( $changes[ $key ] );
565
566
		$result = $this->query( 'jetpack.vpSaveAttachment', array(
567
			'post_id' => $post_id,
568
			'changes' => $changes,
569
			'nonce' => $_POST['vp_nonces']['update'],
570
		) );
571
572
		if ( is_wp_error( $result ) )
573
			return wp_send_json_error( 'xml rpc request error' );
574
575
		wp_send_json_success();
576
	}
577
578
	/**
579
	 * Custom AJAX callback for the delete-post action, only for VideoPress objects.
580
	 */
581
	function wp_ajax_delete_post() {
582
		if ( ! isset( $_POST['is_videopress'] ) )
583
			return;
584
585
		if ( ! $this->can( 'delete_videos' ) )
586
			return wp_send_json_error( 'permission denied' );
587
588
		$post_id = 0;
589 View Code Duplication
		if ( ! isset( $_POST['id'] ) || ! $post_id = absint( $_POST['id'] ) )
590
			wp_send_json_error();
591
592
		if ( ! isset( $_POST['vp_nonces']['delete'] ) )
593
			wp_send_json_error();
594
595
		check_ajax_referer( 'delete-videopress-post_' . $post_id );
596
597
		$result = $this->query( 'jetpack.vpDeleteAttachment', array(
598
			'post_id' => $post_id,
599
			'nonce' => $_POST['vp_nonces']['delete'],
600
		) );
601
602
		if ( is_wp_error( $result ) )
603
			return wp_send_json_error( 'xml rpc request error' );
604
605
		wp_send_json_success();
606
	}
607
608
	/**
609
	 * Register VideoPress admin scripts.
610
	 */
611
	function enqueue_admin_scripts() {
612
		if ( did_action( 'videopress_enqueue_admin_scripts' ) )
613
			return;
614
615
		wp_enqueue_script( 'videopress-uploader', plugins_url( 'js/videopress-uploader.js', __FILE__) , array( 'jquery', 'wp-plupload' ), $this->version );
616
		wp_enqueue_script( 'videopress-admin', plugins_url( 'js/videopress-admin.js', __FILE__ ), array( 'jquery', 'media-views', 'media-models' ), $this->version );
617
		wp_enqueue_style( 'videopress-admin', plugins_url( 'videopress-admin.css', __FILE__ ), array(), $this->version );
618
619
		$caps = array();
620 View Code Duplication
		foreach( array( 'read_videos', 'edit_videos', 'delete_videos', 'upload_videos' ) as $cap )
621
			$caps[ $cap ] = $this->can( $cap );
622
623
		$l10n = array(
624
			'selectVideoFile' => __( 'Please select a video file to upload.', 'jetpack' ),
625
			'videoUploading' => __( 'Your video is uploading... Please do not close this window.', 'jetpack' ),
626
			'unknownError' => __( 'An unknown error has occurred. Please try again later.', 'jetpack' ),
627
			'videoUploaded' => __( 'Your video has successfully been uploaded. It will appear in your VideoPress Library shortly.', 'jetpack' ),
628
			'VideoPressLibraryRouter' => __( 'VideoPress Library', 'jetpack' ),
629
			'uploadVideoRouter' => __( 'Upload a Video', 'jetpack' ),
630
			'insertVideoButton' => __( 'Insert Video', 'jetpack' ),
631
632
		);
633
634
		wp_localize_script( 'videopress-admin', 'VideoPressAdminSettings', array(
635
			'caps' => $caps,
636
			'l10n' => $l10n,
637
		) );
638
		
639
		/**
640
		 * Fires after VideoPress scripts are enqueued in the dashboard.
641
		 *
642
		 * @since 2.5.0
643
		 */
644
		do_action( 'videopress_enqueue_admin_scripts' );
645
	}
646
647
	/**
648
	 * Get an array of available ratings. Keys are options, values are labels.
649
	 */
650
	function get_available_ratings() {
651
		return array(
652
			'G' => 'G',
653
			'PG-13' => 'PG-13',
654
			'R-17' => 'R',
655
			'X-18' => 'X',
656
		);
657
	}
658
659
	/**
660
	 * Additional VideoPress media templates.
661
	 */
662
	function print_media_templates() {
663
		$options = $this->get_options();
664
		?>
665
		<script type="text/html" id="tmpl-videopress-attachment">
666
			<# if ( data.vp_ogg_url ) { #>
667
			<label class="setting vp-setting">
668
				<span><?php _e( 'Ogg File URL', 'jetpack' ); ?></span>
669
				<input type="text" value="{{ data.vp_ogg_url }}" onclick="this.focus();this.select();" readonly />
670
				<p class="help"><?php _e( 'Location of the Ogg video file.', 'jetpack' ); ?></p>
671
			</label>
672
			<# } #>
673
674
			<label class="setting vp-setting">
675
				<span><?php _e( 'Share', 'jetpack' ); ?></span>
676
				<input class="vp-checkbox" type="checkbox" <# if ( '1' === data.vp_share ) { #>checked<# } #> <# if ( ! data.can.save ) { #>disabled<# } #> />
677
				<label>
678
					<?php _e( 'Display share menu and allow viewers to embed or download this video', 'jetpack' ); ?>
679
				</label>
680
				<input class="vp-checkbox-text" type="text" value="{{ data.vp_share }}" data-setting="vp_share" style="display:none;" />
681
			</label>
682
683
			<label class="setting vp-setting">
684
				<span><?php _e( 'Rating', 'jetpack' ); ?></span>
685
686
				<?php foreach ( $this->get_available_ratings() as $value => $label ) : ?>
687
				<input class="vp-radio" type="radio" name="vp-radio-group" id="vp-rating-<?php echo sanitize_html_class( $value ); ?>" value="<?php echo esc_attr( $value ); ?>"
688
					<# if ( '<?php echo esc_attr( $value ); ?>' === data.vp_rating ) { #>checked<# } #>
689
					<# if ( ! data.can.save ) { #>disabled<# } #> />
690
				<label for="vp-rating-<?php echo sanitize_html_class( $value ); ?>"><?php echo esc_html( $label ); ?></label>
691
				<?php endforeach; ?>
692
693
				<input class="vp-radio-text" type="text" value="{{ data.vp_rating }}" data-setting="vp_rating" style="display:none;" />
694
			</label>
695
696
			<label class="setting vp-setting">
697
				<span><?php _e( 'Shortcode', 'jetpack' ); ?></span>
698
				<input type="text" value="[wpvideo {{ data.vp_guid }}]" onclick="this.focus();this.select();" readonly />
699
			</label>
700
701
			<label class="setting vp-setting vp-preview">
702
				<span><?php _e( 'Preview', 'jetpack' ); ?></span>
703
				<# if ( ! data.vp_thumbnail_url ) { #>
704
					<span class="videopress-preview-unavailable"><?php esc_html_e( 'The preview is unavailable while this video is being processed.', 'jetpack' ); ?></span>
705
				<# } else { #>
706
				<a href="#" class="videopress-preview" id="videopress-thumbnail-{{ data.vp_guid }}" data-videopress-guid="{{ data.vp_guid }}"><img src="{{ data.vp_thumbnail_url }}" /></a>
707
				<# } #>
708
			</label>
709
		</script>
710
711
		<script type="text/html" id="tmpl-videopress-media-modal">
712
			<div class="videopress-modal">
713
				<p><?php _e( 'Video Preview:', 'jetpack' ); ?></p>
714
				<div class="videopress-video-container">{{{ data.video }}}</div>
715
				<p class="submit">
716
					<a class="videopress-modal-close button" href="#"><?php _e( 'Close', 'jetpack' ); ?></a>
717
				</p>
718
			</div>
719
			<div class="videopress-modal-backdrop"></div>
720
		</script>
721
722
		<script type="text/html" id="tmpl-videopress-uploader">
723
			<div class="videopress-errors"></div>
724
			<form class="videopress-upload-form" action="" method="post" target="videopress_upload_frame" enctype="multipart/form-data">
725
				<input type="hidden" name="action" value="videopress_upload" />
726
				<input type="hidden" name="videopress_blog_id" value="0" />
727
				<input type="hidden" name="videopress_token" value="0" />
728
				<?php $formats = 'ogv, mp4, m4v, mov, wmv, avi, mpg, 3gp, 3g2'; ?>
729
				<?php
730
					$max_upload_size = 0;
731
					if ( ! empty( $options['meta']['max_upload_size'] ) )
732
						$max_upload_size = absint( $options['meta']['max_upload_size'] );
733
734
					$upload_size_unit = $max_upload_size;
735
					$byte_sizes = array( 'KB', 'MB', 'GB' );
736
737
					for ( $u = -1; $upload_size_unit > 1024 && $u < count( $byte_sizes ) - 1; $u++ )
738
						$upload_size_unit /= 1024;
739
740
					if ( $u < 0 ) {
741
						$upload_size_unit = 0;
742
						$u = 0;
743
					} else {
744
						$upload_size_unit = (int) $upload_size_unit;
745
					}
746
				?>
747
				<p><?php printf( __( 'Use the form below to upload a video to your VideoPress Library. The following video formats are supported: %s. Maximum upload file size is %d%s.', 'jetpack' ), esc_html( $formats ), esc_html( $upload_size_unit ), esc_html( $byte_sizes[ $u ] ) ); ?></p>
748
749
				<input type="file" name="videopress_file" />
750
				<?php submit_button( __( 'Upload Video', 'jetpack' ) ); ?>
751
			</form>
752
			<iframe width="0" height="0" name="videopress_upload_frame"></iframe>
753
		</script>
754
		<?php
755
	}
756
757
	/**
758
	 * Filters the VideoPress shortcode options, makes sure that
759
	 * the settings set in Jetpack's VideoPress module are applied.
760
	 */
761
	function videopress_shortcode_options( $options ) {
762
		$videopress_options = $this->get_options();
763
764
		if ( false === $options['freedom'] )
765
			$options['freedom'] = $videopress_options['freedom'];
766
767
		$options['hd'] = $videopress_options['hd'];
768
769
		return $options;
770
	}
771
772
	/**
773
	 * WordPress Shortcode Editor View JS Code
774
	 */
775
	public static function handle_editor_view_js() {
776
		global $content_width;
777
		$current_screen = get_current_screen();
778
		if ( ! isset( $current_screen->id ) || $current_screen->base !== 'post' ) {
779
			return;
780
		}
781
782
		add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_view_js_templates' ) );
783
784
		wp_enqueue_script( 'videopress-editor-view', plugins_url( 'js/editor-view.js', __FILE__ ), array( 'wp-util', 'jquery' ), false, true );
785
		wp_localize_script( 'videopress-editor-view', 'vpEditorView', array(
786
			'home_url_host'     => parse_url( home_url(), PHP_URL_HOST ),
787
			'min_content_width' => VIDEOPRESS_MIN_WIDTH,
788
			'content_width'     => $content_width,
789
			'modal_labels'      => array(
790
				'title'     => __( 'VideoPress Shortcode', 'jetpack' ),
791
				'guid'      => __( 'Video GUID', 'jetpack' ),
792
				'w'         => __( 'Width (in pixels)', 'jetpack' ),
793
				'at'        => __( 'Start how many seconds in?', 'jetpack' ),
794
				'hd'        => __( 'Default to High Definition version?', 'jetpack' ),
795
				'permalink' => __( 'Link the video title to the video\'s URL on VideoPress.com?', 'jetpack' ),
796
				'autoplay'  => __( 'Autoplay video on load?', 'jetpack' ),
797
				'loop'      => __( 'Loop playback indefinitely?', 'jetpack' ),
798
				'freedom'   => __( 'Use only Open Source codecs? (this may degrade performance)', 'jetpack' ),
799
				'flashonly' => __( 'Use the legacy flash player? (not recommended)', 'jetpack' ),
800
			)
801
		) );
802
	}
803
804
	/**
805
	 * WordPress Editor Views
806
	 */
807
	public static function editor_view_js_templates() {
808
		/**
809
		 * This template uses the following parameters, and displays the video as an iframe:
810
		 *  - data.guid     // The guid of the video.
811
		 *  - data.width    // The width of the iframe.
812
		 *  - data.height   // The height of the iframe.
813
		 *  - data.urlargs  // Arguments serialized into a get string.
814
		 *
815
		 * In addition, the calling script will need to ensure that the following
816
		 * JS file is added to the header of the editor iframe:
817
		 *  - https://s0.wp.com/wp-content/plugins/video/assets/js/next/videopress-iframe.js
818
		 */
819
		?>
820
		<script type="text/html" id="tmpl-videopress_iframe_vnext">
821
			<div class="tmpl-videopress_iframe_next">
822
				<iframe style="display: block;" width="{{ data.width }}" height="{{ data.height }}" src="https://videopress.com/embed/{{ data.guid }}?{{ data.urlargs }}" frameborder='0' allowfullscreen></iframe>
823
			</div>
824
		</script>
825
		<?php
826
	}
827
}
828
829
// Initialize the module.
830
Jetpack_VideoPress::init();
831