Completed
Push — feature/videopress-uploader ( 6e76b8...46da43 )
by
unknown
09:27
created

update_attachment_url_for_videopress()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 3
eloc 7
c 1
b 0
f 1
nc 3
nop 2
dl 0
loc 14
rs 9.4285
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( 'jetpack_activate_module_videopress', array( $this, 'jetpack_module_activated' ) );
27
		add_action( 'jetpack_deactivate_module_videopress', array( $this, 'jetpack_module_deactivated' ) );
28
	}
29
30
	/**
31
	 * Fires on init since is_connection_owner should wait until the user is initialized by $wp->init();
32
	 */
33
	function on_init() {
34
		$options = $this->get_options();
35
36
		// Only the connection owner can configure this module.
37
		if ( $this->is_connection_owner() ) {
38
			Jetpack::enable_module_configurable( $this->module );
39
			Jetpack::module_configuration_load( $this->module, array( $this, 'jetpack_configuration_load' ) );
40
			Jetpack::module_configuration_screen( $this->module, array( $this, 'jetpack_configuration_screen' ) );
41
		}
42
43
		// Only if the current user can manage the VideoPress library and one has been connected.
44
		if ( $this->can( 'read_videos' ) && $options['blog_id'] ) {
45
			add_action( 'wp_enqueue_media', array( $this, 'enqueue_admin_scripts' ) );
46
			add_action( 'print_media_templates', array( $this, 'print_media_templates' ) );
47
48
			// Load these at priority -1 so they're fired before Core's are.
49
			add_action( 'wp_ajax_query-attachments', array( $this, 'wp_ajax_query_attachments' ), -1 );
50
			add_action( 'wp_ajax_save-attachment', array( $this, 'wp_ajax_save_attachment' ), -1 );
51
			add_action( 'wp_ajax_save-attachment-compat', array( $this, 'wp_ajax_save_attachment' ), -1 );
52
			add_action( 'wp_ajax_delete-post', array( $this, 'wp_ajax_delete_post' ), -1 );
53
54
			add_action( 'admin_menu', array( $this, 'admin_menu' ) );
55
		}
56
57
		if ( $this->can( 'upload_videos' ) && $options['blog_id'] ) {
58
			add_action( 'wp_ajax_videopress-get-upload-token', array( $this, 'wp_ajax_videopress_get_upload_token' ) );
59
			add_filter( 'plupload_default_settings', array( $this, 'videopress_pluploder_config' ) );
60
		}
61
62
		add_filter( 'videopress_shortcode_options', array( $this, 'videopress_shortcode_options' ) );
63
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
64
		add_filter( 'wp_get_attachment_url', array( $this, 'update_attachment_url_for_videopress' ), 10, 2 );
65
	}
66
67
	/**
68
	 * @param string $url
69
	 * @param int $post_id
70
	 *
71
	 * @return mixed
72
	 */
73
	public function update_attachment_url_for_videopress( $url, $post_id ) {
74
75
		$post = get_post( $post_id );
76
77
		if ( $post->post_mime_type === 'video/videopress' ) {
78
			$meta = wp_get_attachment_metadata( $post->ID );
79
80
			if ( isset( $meta['original']['url'] ) ) {
81
				$url = $meta['original']['url'];
82
			}
83
		}
84
85
		return $url;
86
	}
87
88
	/**
89
	 * Adds additional methods the WordPress xmlrpc API for handling VideoPress specific features
90
	 *
91
	 * @param array $methods
92
	 * @return array
93
	 **/
94
	public function xmlrpc_methods( $methods ) {
95
		$methods['jetpack.createMediaItem'] = array( $this, 'xmlrpc_create_media_item' );
96
		$methods['jetpack.updateVideoPressInfo'] = array( $this, 'xmlrpc_update_videopress_info' );
97
98
		return $methods;
99
	}
100
	
101
	public function xmlrpc_update_videopress_info( $vp_info ) {
102
103
		$errors = null;
104
		foreach ( $vp_info as $vp_item ) {
105
			$id = $vp_item['id'];
106
			$url = $vp_item['url'];
107
			$guid = $vp_item['guid'];
108
			$poster = $vp_item['poster'];
109
			$info = $vp_item['info'];
110
111
			$post = get_post( $id );
112
113
			if ( ! $post ) {
114
				$errors[] = array(
115
					'id' => $id,
116
					'error' => 'Post not found',
117
				);
118
119
				continue;
120
			}
121
122
			$post->guid = $url;
123
			$post->file = $url;
124
125
			wp_update_post( $post );
126
127
			$meta = wp_get_attachment_metadata( $post->ID );
128
			$meta['original']['url'] = $url;
129
			$meta['videopress'] = $info;
130
			$meta['videopress']['poster'] = $poster;
131
			$meta['videopress']['url'] = 'https://videopress.com/v/' . $guid;
132
133
			wp_update_attachment_metadata( $post->ID, $meta );
134
		}
135
136
		if ( count( $errors ) > 0 ) {
137
			return array( 'errors' => $errors );
138
139
		} else {
140
			return true;
141
		}
142
	}
143
	
144
	public function xmlrpc_create_media_item( $media ) {
145
		$created_items = array();
146
147
		foreach ( $media as $media_item ) {
148
			$post = array(
149
				'post_type'   => 'attachment',
150
				'post_mime_type' => 'video/videopress',
151
				'post_title' => sanitize_title( basename( $media_item['url'] ) ),
152
				'post_content' => '',
153
			);
154
155
			$media_id = wp_insert_post( $post );
156
157
			wp_update_attachment_metadata( $media_id, array(
158
				'original' => array(
159
					'url' => $media_item['url'],
160
					'file' => $media_item['file'],
161
					'mime_type' => $media_item['type'],
162
				),
163
			) );
164
165
			$created_items[] = array(
166
				'id' => $media_id,
167
				'post' => get_post( $media_id ),
168
			);
169
		}
170
171
		return array( 'media' => $created_items );
172
	}
173
174
	function wp_ajax_videopress_get_upload_token() {
175
		if ( ! $this->can( 'upload_videos' ) ) {
176
			return wp_send_json_error();
177
		}
178
179
		$options = $this->get_options();
180
181
		$args = array(
182
			'method'  => 'POST',
183
		);
184
185
		$endpoint = "sites/{$options['id']}/media/token";
186
		$result = Jetpack_Client::wpcom_json_api_request_as_blog( $endpoint, Jetpack_Client::WPCOM_JSON_API_VERSION, $args );
187
188
		if ( is_wp_error( $result ) ) {
189
			return wp_send_json_error( array( 'message' => __( 'Could not obtain a VideoPress upload token. Please try again later.', 'jetpack' ) ) );
190
		}
191
192
		$response = json_decode( $result['body'], true );
193
194
		if ( empty( $response['upload_blog_id'] ) || empty( $response['upload_token'] ) ) {
195
			return wp_send_json_error( array( 'message' => __( 'Could not obtain a VideoPress upload token. Please try again later.', 'jetpack' ) ) );
196
		}
197
198
		$response['upload_action_url'] = self::make_media_upload_path( $response['upload_blog_id'] );
199
200
		return wp_send_json_success( $response );
201
	}
202
203
	/**
204
	 * Get the upload api path.
205
	 *
206
	 * @param $blog_id
207
	 * @return string
208
	 */
209
	function make_media_upload_path( $blog_id ) {
210
		return sprintf(
211
			'%s://%s/rest/v%s/videos/%s/new',
212
			'https',
213
			'public-api.wordpress.com', //JETPACK__WPCOM_JSON_API_HOST,
214
			Jetpack_Client::WPCOM_JSON_API_VERSION,
215
			$blog_id
216
		);
217
	}
218
219
	/**
220
	 * Modify the default plupload config to turn on videopress specific filters.
221
	 */
222
	function videopress_pluploder_config( $config ) {
223
		$config['filters']['videopress_check_uploads'] = 1;
224
		return $config;
225
	}
226
227
	/**
228
	 * Get VideoPress options
229
	 */
230
	function get_options() {
231
		$defaults = array(
232
			'blogs' => array(),
233
			'blog_id' => 0,
234
			'access' => '',
235
			'allow-upload' => false,
236
			'freedom' => false,
237
			'hd' => false,
238
			'meta' => array(
239
				'max_upload_size' => 0,
240
			),
241
		);
242
243
		$options = Jetpack_Options::get_option( $this->option_name, array() );
244
245
		// If options have not been saved yet, check for older VideoPress plugin options.
246
		if ( empty( $options ) ) {
247
			$options['freedom'] = (bool) get_option( 'video_player_freedom', false );
248
			$options['hd'] = (bool) get_option( 'video_player_high_quality', false );
249
		}
250
251
		$options = array_merge( $defaults, $options );
252
		$options['id'] = Jetpack_Options::get_option( 'id' );
253
254
		return $options;
255
	}
256
257
	/**
258
	 * Update VideoPress options
259
	 */
260
	function update_options( $options ) {
261
		Jetpack_Options::update_option( $this->option_name, $options );
262
	}
263
264
	/**
265
	 * Runs when the VideoPress module is activated.
266
	 */
267
	function jetpack_module_activated() {
268
		if ( ! $this->is_connection_owner() )
269
			return;
270
271
		$options = $this->get_options();
272
273
		// Ask WordPress.com for a list of VideoPress blogs
274
		$result = $this->query( 'jetpack.vpGetBlogs' );
275
		if ( ! is_wp_error( $result ) )
276
			$options['blogs'] = $result;
277
278
		// If there's at least one available blog, let's use it.
279
		if ( is_array( $options['blogs'] ) && count( $options['blogs'] ) > 0 )
280
			$options['blog_id'] = $options['blogs'][0]['blog_id'];
281
282
		$this->update_options( $options );
283
	}
284
285
	/**
286
	 * Runs when the VideoPress module is deactivated.
287
	 */
288
	function jetpack_module_deactivated() {
289
		Jetpack_Options::delete_option( $this->option_name );
290
	}
291
292
	/**
293
	 * Remote Query
294
	 *
295
	 * Performs a remote XML-RPC query using Jetpack's IXR Client. And also
296
	 * appends some useful stuff about this setup to the query.
297
	 *
298
	 * @return the Jetpack_IXR_Client object after querying.
299
	 */
300
	function query( $method, $args = null ) {
301
		$options = $this->get_options();
302
		Jetpack::load_xml_rpc_client();
303
		$xml = new Jetpack_IXR_Client( array(
304
			'user_id' => JETPACK_MASTER_USER, // All requests are on behalf of the connection owner.
305
		) );
306
307
		$params = array(
308
			'args' => $args,
309
			'video_blog_id' => $options['blog_id'],
310
			'caps' => array(),
311
		);
312
313
		// Let Jetpack know about our local caps.
314 View Code Duplication
		foreach ( array( 'read_videos', 'edit_videos', 'delete_videos', 'upload_videos' ) as $cap )
315
			if ( $this->can( $cap ) )
316
				$params['caps'][] = $cap;
317
318
		$xml->query( $method, $params );
319
320
		if ( $xml->isError() )
321
			return new WP_Error( 'xml_rpc_error', 'An XML-RPC error has occurred.' );
322
323
		$response = $xml->getResponse();
324
325
		// If there's any metadata with the response, save it for future use.
326
		if ( is_array( $response ) && isset( $response['meta'] ) ) {
327
			$options = $this->get_options();
328
			if ( $response['meta'] !== $options['meta'] ) {
329
				$options['meta'] = array_merge( $options['meta'], $response['meta'] );
330
				$this->update_options( $options );
331
			}
332
		}
333
334
		if ( is_array( $response ) && isset( $response['result'] ) )
335
			return $response['result'];
336
337
		return $response;
338
	}
339
340
	/**
341
	 * Runs before the VideoPress Configuration screen loads, useful
342
	 * to update options and yield errors.
343
	 */
344
	function jetpack_configuration_load() {
345
		$this->enqueue_admin_scripts();
346
347
		/**
348
		 * Save configuration
349
		 */
350
		if ( ! empty( $_POST['action'] ) && $_POST['action'] == 'videopress-save' ) {
351
			check_admin_referer( 'videopress-settings' );
352
			$options = $this->get_options();
353
354
			if ( isset( $_POST['blog_id'] ) && in_array( $_POST['blog_id'], wp_list_pluck( $options['blogs'], 'blog_id' ) ) )
355
				$options['blog_id'] = $_POST['blog_id'];
356
357
			// Allow the None setting too.
358
			if ( isset( $_POST['blog_id'] ) && $_POST['blog_id'] == 0 )
359
				$options['blog_id'] = 0;
360
361
			/**
362
			 * @see $this->can()
363
			 */
364
			if ( isset( $_POST['videopress-access'] ) && in_array( $_POST['videopress-access'], array( '', 'read', 'edit', 'delete' ) ) )
365
				$options['access'] = $_POST['videopress-access'];
366
367
			$options['freedom'] = isset( $_POST['videopress-freedom'] );
368
			$options['hd'] = isset( $_POST['videopress-hd'] );
369
370
			// Allow upload only if some level of access has been granted, and uploads were allowed.
371
			$options['allow-upload'] = false;
372
			if ( ! empty( $options['access'] ) && isset( $_POST['videopress-upload'] ) )
373
				$options['allow-upload'] = true;
374
375
			$this->update_options( $options );
376
			Jetpack::state( 'message', 'module_configured' );
377
			wp_safe_redirect( Jetpack::module_configuration_url( $this->module ) );
378
		}
379
380
		/**
381
		 * Refresh the list of available WordPress.com blogs
382
		 */
383
		if ( ! empty( $_GET['videopress'] ) && $_GET['videopress'] == 'refresh-blogs' ) {
384
			check_admin_referer( 'videopress-settings' );
385
			$options = $this->get_options();
386
387
			$result = $this->query( 'jetpack.vpGetBlogs' );
388
			if ( ! is_wp_error( $result ) ) {
389
				$options['blogs'] = $result;
390
				$this->update_options( $options );
391
			}
392
393
			wp_safe_redirect( Jetpack::module_configuration_url( $this->module ) );
394
		}
395
	}
396
397
	/**
398
	 * Renders the VideoPress Configuration screen in Jetpack.
399
	 */
400
	function jetpack_configuration_screen() {
401
		$options = $this->get_options();
402
		$refresh_url = wp_nonce_url( add_query_arg( 'videopress', 'refresh-blogs' ), 'videopress-settings' );
403
		?>
404
		<div class="narrow">
405
			<form method="post" id="videopress-settings">
406
				<input type="hidden" name="action" value="videopress-save" />
407
				<?php wp_nonce_field( 'videopress-settings' ); ?>
408
409
				<table id="menu" class="form-table">
410
					<tr>
411
						<th scope="row" colspan="2">
412
							<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>
413
						</th>
414
					</tr>
415
					<tr>
416
						<th scope="row">
417
							<label><?php _e( 'Connected WordPress.com Blog', 'jetpack' ); ?></label>
418
						</th>
419
						<td>
420
							<select name="blog_id">
421
								<option value="0" <?php selected( $options['blog_id'], 0 ); ?>> <?php esc_html_e( 'None', 'jetpack' ); ?></option>
422
								<?php foreach ( $options['blogs'] as $blog ) : ?>
423
								<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>
424
								<?php endforeach; ?>
425
							</select>
426
							<p class="description"><?php _e( 'Only videos from the selected blog will be available in your media library.', 'jetpack' ); ?>
427
								<?php printf( __( '<a href="%s">Click here</a> to refresh this list.', 'jetpack' ), esc_url( $refresh_url ) ); ?>
428
							</p>
429
						</td>
430
					</tr>
431
					<tr>
432
						<th scope="row">
433
							<label><?php _e( 'Video Library Access', 'jetpack' ); ?></label>
434
						</th>
435
						<td>
436
							<label><input type="radio" name="videopress-access" value="" <?php checked( '', $options['access'] ); ?> />
437
								<?php _e( 'Do not allow other users to access my VideoPress library', 'jetpack' ); ?></label><br/>
438
							<label><input type="radio" name="videopress-access" value="read" <?php checked( 'read', $options['access'] ); ?> />
439
								<?php _e( 'Allow users to access my videos', 'jetpack' ); ?></label><br/>
440
							<label><input type="radio" name="videopress-access" value="edit" <?php checked( 'edit', $options['access'] ); ?> />
441
								<?php _e( 'Allow users to access and edit my videos', 'jetpack' ); ?></label><br/>
442
							<label><input type="radio" name="videopress-access" value="delete" <?php checked( 'delete', $options['access'] ); ?> />
443
								<?php _e( 'Allow users to access, edit, and delete my videos', 'jetpack' ); ?></label><br/><br />
444
445
							<label><input type="checkbox" name="videopress-upload" value="1" <?php checked( $options['allow-upload'] ); ?> />
446
								<?php _e( 'Allow users to upload videos', 'jetpack' ); ?></label><br />
447
						</td>
448
					</tr>
449
					<tr>
450
						<th scope="row">
451
							<label for="videopress-freedom"><?php _e( 'Free formats', 'jetpack' ); ?></label>
452
						</th>
453
						<td>
454
							<label><input type="checkbox" name="videopress-freedom" id="videopress-freedom" <?php checked( $options['freedom'] ); ?> />
455
								<?php _e( 'Only display videos in free software formats', 'jetpack' ); ?></label>
456
							<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>
457
						</td>
458
					</tr>
459
					<tr>
460
						<th scope="row">
461
							<label for="videopress-hd"><?php _e( 'Default quality', 'jetpack' ); ?></label>
462
						</th>
463
						<td>
464
							<label><input type="checkbox" name="videopress-hd" id="videopress-hd" <?php checked( $options['hd'] ); ?> />
465
								<?php _e( 'Display higher quality video by default.', 'jetpack' ); ?></label>
466
							<p class="description"><?php _e( 'This setting may be overridden for individual videos.', 'jetpack' ); ?></p>
467
						</td>
468
					</tr>
469
				</table>
470
471
				<?php submit_button(); ?>
472
			</form>
473
		</div>
474
		<?php
475
	}
476
477
	function admin_menu() {
478
		add_media_page( __( 'VideoPress Library', 'jetpack' ), __( 'VideoPress', 'jetpack' ), 'upload_files', 'videopress-library', array( $this, 'admin_menu_library' ) );
479
	}
480
481
	function admin_menu_library() {
482
		wp_enqueue_media();
483
		$this->enqueue_admin_scripts();
484
		?>
485
		<div class="wrap" style="max-width: 600px;">
486
			<?php screen_icon(); ?>
487
	        <h2><?php _e( 'VideoPress Library', 'jetpack' ); ?></h2>
488
	        <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>
489
	        <p class="hide-if-no-js"><a href="#" id="videopress-browse" class="button"><?php _e( 'Browse Your VideoPress Library', 'jetpack' ); ?></a></p>
490
	        <p class="hide-if-js description"><?php _e( 'Please enable JavaScript support in your browser to use VideoPress.', 'jetpack' ); ?></p>
491
	    </div>
492
		<?php
493
	}
494
495
	/**
496
	 * A can of coke
497
	 *
498
	 * Similar to current_user_can, but internal to VideoPress. Returns
499
	 * true if the given VideoPress capability is allowed by the given user.
500
	 */
501
	function can( $cap, $user_id = false ) {
502
		if ( ! $user_id )
503
			$user_id = get_current_user_id();
504
505
		// Connection owners are allowed to do all the things.
506
		if ( $this->is_connection_owner( $user_id ) )
507
			return true;
508
509
		/**
510
		 * The access setting can be set by the connection owner, to allow sets
511
		 * of operations to other site users. Each access value corresponds to
512
		 * an array of things they can do.
513
		 */
514
515
		$options = $this->get_options();
516
		$map = array(
517
			'read'   => array( 'read_videos' ),
518
			'edit'   => array( 'read_videos', 'edit_videos' ),
519
			'delete' => array( 'read_videos', 'edit_videos', 'delete_videos' ),
520
		);
521
522
		if ( ! array_key_exists( $options['access'], $map ) )
523
			return false;
524
525
		if ( ! in_array( $cap, $map[ $options['access'] ] ) && 'upload_videos' != $cap )
526
			return false;
527
528
		// Additional and intrenal caps checks
529
530
		if ( ! user_can( $user_id, 'upload_files' ) )
531
			return false;
532
533
		if ( 'edit_videos' == $cap && ! user_can( $user_id, 'edit_others_posts' ) )
534
			return false;
535
536
		if ( 'delete_videos' == $cap && ! user_can( $user_id, 'delete_others_posts' ) )
537
			return false;
538
539
		if ( 'upload_videos' == $cap && ! $options['allow-upload'] )
540
			return false;
541
542
		return true;
543
	}
544
545
	/**
546
	 * Returns true if the provided user is the Jetpack connection owner.
547
	 */
548
	function is_connection_owner( $user_id = false ) {
549
		if ( ! $user_id )
550
			$user_id = get_current_user_id();
551
552
		$user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
553
		return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && $user_id === $user_token->external_user_id;
554
	}
555
556
	/**
557
	 * Our custom AJAX callback for the query-attachments action
558
	 * used in the media modal. By-passed if not for VideoPress.
559
	 */
560
	function wp_ajax_query_attachments() {
561
562
		// Watch for VideoPress calls
563
		if ( ! isset( $_POST['query']['videopress'] ) )
564
			return;
565
566
		if ( ! $this->can( 'read_videos' ) )
567
			return wp_send_json_error( 'permission denied' );
568
569
		// Get and sanitize query arguments.
570
		$query_args = $this->sanitize_wp_query_args( $_POST['query'] );
571
572
		// Fire a remote WP_Query
573
		$result = $this->query( 'jetpack.vpQuery', $query_args );
574
575
		if ( is_wp_error( $result ) )
576
			return wp_send_json_error( 'xml rpc request error' );
577
578
		$items = $result;
579
580
		foreach ( $items as $key => $item ) {
581
582
			// Check local permissions
583
			if ( ! $this->can( 'edit_videos' ) )
584
				unset( $item['vp_nonces']['update'] );
585
586
			if ( ! $this->can( 'delete_videos' ) )
587
				unset( $item['vp_nonces']['delete'] );
588
589
			// Add a second pair of nonces for the .org blog.
590
			$item['nonces'] = array();
591 View Code Duplication
			if ( ! empty( $item['vp_nonces']['update'] ) )
592
				$item['nonces']['update'] = wp_create_nonce( 'update-videopress-post_' . $item['id'] );
593
594 View Code Duplication
			if ( ! empty( $item['vp_nonces']['delete'] ) )
595
				$item['nonces']['delete'] = wp_create_nonce( 'delete-videopress-post_' . $item['id'] );
596
597
			$item['vp_embed'] = videopress_shortcode_callback( array(
598
				$item['vp_guid'],
599
				'autoplay' => true,
600
				'flashonly' => true,
601
				'w' => 440,
602
			) );
603
604
			$items[ $key ] = $item;
605
		}
606
607
		wp_send_json_success( $items );
608
	}
609
610
	/**
611
	 * Sanitize user-provided WP_Query arguments
612
	 *
613
	 * These might be sent to the VideoPress server, for a remote WP_Query
614
	 * call so let's make sure they're sanitized and safe to send.
615
	 */
616
	function sanitize_wp_query_args( $args ) {
617
		$args = shortcode_atts( array(
618
			'posts_per_page' => 40,
619
			'orderby' => 'date',
620
			'order' => 'desc',
621
			'paged' => 1,
622
			's' => '',
623
		), (array) $args, 'wpvideo' );
624
625
		$args['posts_per_page'] = absint( $args['posts_per_page'] );
626
627
		$args['orderby'] = strtolower( $args['orderby'] );
628
		$args['orderby'] = ( in_array( $args['orderby'], array( 'date' ) ) ) ? $args['orderby'] : 'date';
629
630
		$args['order'] = strtolower( $args['order'] );
631
		$args['order'] = ( in_array( $args['order'], array( 'asc', 'desc' ) ) ) ? $args['order'] : 'desc';
632
633
		$args['paged'] = absint( $args['paged'] );
634
		$args['s'] = sanitize_text_field( $args['s'] );
635
		return $args;
636
	}
637
638
	/**
639
	 * Custom AJAX callback for the save-attachment action. If the request was
640
	 * not for a VideoPress object, core's fallback action will kick in.
641
	 */
642
	function wp_ajax_save_attachment() {
643
		if ( ! isset( $_POST['is_videopress'] ) )
644
			return;
645
646
		if ( ! $this->can( 'edit_videos' ) )
647
			return wp_send_json_error( 'permission denied' );
648
649
		$post_id = 0;
650 View Code Duplication
		if ( ! isset( $_POST['id'] ) || ! $post_id = absint( $_POST['id'] ) )
651
			wp_send_json_error();
652
653
		if ( ! isset( $_POST['vp_nonces']['update'] ) )
654
			wp_send_json_error();
655
656
		check_ajax_referer( 'update-videopress-post_' . $post_id, 'nonce' );
657
658
		$changes = ( ! empty( $_POST['changes'] ) ) ? (array) $_POST['changes'] : array();
659
		$changes = shortcode_atts( array(
660
			'title' => null,
661
			'caption' => null,
662
			'description' => null,
663
664
			'vp_share' => null,
665
			'vp_rating' => null,
666
		), $changes, 'wpvideo' );
667
668
		if ( ! is_null( $changes['vp_share'] ) )
669
			$changes['vp_share'] = (bool) $changes['vp_share'];
670
671
		if ( ! is_null( $changes['vp_rating'] ) )
672
			$changes['vp_rating'] = ( array_key_exists( $changes['vp_rating'], $this->get_available_ratings() ) ) ? $changes['vp_rating'] : null;
673
674
		// Remove null-values
675
		foreach ( $changes as $key => $value )
676
			if ( is_null( $value ) )
677
				unset( $changes[ $key ] );
678
679
		$result = $this->query( 'jetpack.vpSaveAttachment', array(
680
			'post_id' => $post_id,
681
			'changes' => $changes,
682
			'nonce' => $_POST['vp_nonces']['update'],
683
		) );
684
685
		if ( is_wp_error( $result ) )
686
			return wp_send_json_error( 'xml rpc request error' );
687
688
		wp_send_json_success();
689
	}
690
691
	/**
692
	 * Custom AJAX callback for the delete-post action, only for VideoPress objects.
693
	 */
694
	function wp_ajax_delete_post() {
695
		if ( ! isset( $_POST['is_videopress'] ) )
696
			return;
697
698
		if ( ! $this->can( 'delete_videos' ) )
699
			return wp_send_json_error( 'permission denied' );
700
701
		$post_id = 0;
702 View Code Duplication
		if ( ! isset( $_POST['id'] ) || ! $post_id = absint( $_POST['id'] ) )
703
			wp_send_json_error();
704
705
		if ( ! isset( $_POST['vp_nonces']['delete'] ) )
706
			wp_send_json_error();
707
708
		check_ajax_referer( 'delete-videopress-post_' . $post_id );
709
710
		$result = $this->query( 'jetpack.vpDeleteAttachment', array(
711
			'post_id' => $post_id,
712
			'nonce' => $_POST['vp_nonces']['delete'],
713
		) );
714
715
		if ( is_wp_error( $result ) )
716
			return wp_send_json_error( 'xml rpc request error' );
717
718
		wp_send_json_success();
719
	}
720
721
	/**
722
	 * Register VideoPress admin scripts.
723
	 */
724
	function enqueue_admin_scripts() {
725
		if ( did_action( 'videopress_enqueue_admin_scripts' ) )
726
			return;
727
728
		wp_enqueue_script( 'videopress-uploader', plugins_url( 'js/videopress-uploader.js', __FILE__) , array( 'jquery', 'wp-plupload' ), $this->version );
729
		wp_enqueue_script( 'videopress-admin', plugins_url( 'js/videopress-admin.js', __FILE__ ), array( 'jquery', 'media-views', 'media-models' ), $this->version );
730
		wp_enqueue_style( 'videopress-admin', plugins_url( 'videopress-admin.css', __FILE__ ), array(), $this->version );
731
732
		$caps = array();
733 View Code Duplication
		foreach( array( 'read_videos', 'edit_videos', 'delete_videos', 'upload_videos' ) as $cap )
734
			$caps[ $cap ] = $this->can( $cap );
735
736
		$l10n = array(
737
			'selectVideoFile' => __( 'Please select a video file to upload.', 'jetpack' ),
738
			'videoUploading' => __( 'Your video is uploading... Please do not close this window.', 'jetpack' ),
739
			'unknownError' => __( 'An unknown error has occurred. Please try again later.', 'jetpack' ),
740
			'videoUploaded' => __( 'Your video has successfully been uploaded. It will appear in your VideoPress Library shortly.', 'jetpack' ),
741
			'VideoPressLibraryRouter' => __( 'VideoPress Library', 'jetpack' ),
742
			'uploadVideoRouter' => __( 'Upload a Video', 'jetpack' ),
743
			'insertVideoButton' => __( 'Insert Video', 'jetpack' ),
744
745
		);
746
747
		wp_localize_script( 'videopress-admin', 'VideoPressAdminSettings', array(
748
			'caps' => $caps,
749
			'l10n' => $l10n,
750
		) );
751
		
752
		/**
753
		 * Fires after VideoPress scripts are enqueued in the dashboard.
754
		 *
755
		 * @since 2.5.0
756
		 */
757
		do_action( 'videopress_enqueue_admin_scripts' );
758
	}
759
760
	/**
761
	 * Get an array of available ratings. Keys are options, values are labels.
762
	 */
763
	function get_available_ratings() {
764
		return array(
765
			'G' => 'G',
766
			'PG-13' => 'PG-13',
767
			'R-17' => 'R',
768
			'X-18' => 'X',
769
		);
770
	}
771
772
	/**
773
	 * Additional VideoPress media templates.
774
	 */
775
	function print_media_templates() {
776
		$options = $this->get_options();
777
		?>
778
		<script type="text/html" id="tmpl-videopress-attachment">
779
			<# if ( data.vp_ogg_url ) { #>
780
			<label class="setting vp-setting">
781
				<span><?php _e( 'Ogg File URL', 'jetpack' ); ?></span>
782
				<input type="text" value="{{ data.vp_ogg_url }}" onclick="this.focus();this.select();" readonly />
783
				<p class="help"><?php _e( 'Location of the Ogg video file.', 'jetpack' ); ?></p>
784
			</label>
785
			<# } #>
786
787
			<label class="setting vp-setting">
788
				<span><?php _e( 'Share', 'jetpack' ); ?></span>
789
				<input class="vp-checkbox" type="checkbox" <# if ( '1' === data.vp_share ) { #>checked<# } #> <# if ( ! data.can.save ) { #>disabled<# } #> />
790
				<label>
791
					<?php _e( 'Display share menu and allow viewers to embed or download this video', 'jetpack' ); ?>
792
				</label>
793
				<input class="vp-checkbox-text" type="text" value="{{ data.vp_share }}" data-setting="vp_share" style="display:none;" />
794
			</label>
795
796
			<label class="setting vp-setting">
797
				<span><?php _e( 'Rating', 'jetpack' ); ?></span>
798
799
				<?php foreach ( $this->get_available_ratings() as $value => $label ) : ?>
800
				<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 ); ?>"
801
					<# if ( '<?php echo esc_attr( $value ); ?>' === data.vp_rating ) { #>checked<# } #>
802
					<# if ( ! data.can.save ) { #>disabled<# } #> />
803
				<label for="vp-rating-<?php echo sanitize_html_class( $value ); ?>"><?php echo esc_html( $label ); ?></label>
804
				<?php endforeach; ?>
805
806
				<input class="vp-radio-text" type="text" value="{{ data.vp_rating }}" data-setting="vp_rating" style="display:none;" />
807
			</label>
808
809
			<label class="setting vp-setting">
810
				<span><?php _e( 'Shortcode', 'jetpack' ); ?></span>
811
				<input type="text" value="[wpvideo {{ data.vp_guid }}]" onclick="this.focus();this.select();" readonly />
812
			</label>
813
814
			<label class="setting vp-setting vp-preview">
815
				<span><?php _e( 'Preview', 'jetpack' ); ?></span>
816
				<# if ( ! data.vp_thumbnail_url ) { #>
817
					<span class="videopress-preview-unavailable"><?php esc_html_e( 'The preview is unavailable while this video is being processed.', 'jetpack' ); ?></span>
818
				<# } else { #>
819
				<a href="#" class="videopress-preview" id="videopress-thumbnail-{{ data.vp_guid }}" data-videopress-guid="{{ data.vp_guid }}"><img src="{{ data.vp_thumbnail_url }}" /></a>
820
				<# } #>
821
			</label>
822
		</script>
823
824
		<script type="text/html" id="tmpl-videopress-media-modal">
825
			<div class="videopress-modal">
826
				<p><?php _e( 'Video Preview:', 'jetpack' ); ?></p>
827
				<div class="videopress-video-container">{{{ data.video }}}</div>
828
				<p class="submit">
829
					<a class="videopress-modal-close button" href="#"><?php _e( 'Close', 'jetpack' ); ?></a>
830
				</p>
831
			</div>
832
			<div class="videopress-modal-backdrop"></div>
833
		</script>
834
835
		<script type="text/html" id="tmpl-videopress-uploader">
836
			<div class="videopress-errors"></div>
837
			<form class="videopress-upload-form" action="" method="post" target="videopress_upload_frame" enctype="multipart/form-data">
838
				<input type="hidden" name="action" value="videopress_upload" />
839
				<input type="hidden" name="videopress_blog_id" value="0" />
840
				<input type="hidden" name="videopress_token" value="0" />
841
				<?php $formats = 'ogv, mp4, m4v, mov, wmv, avi, mpg, 3gp, 3g2'; ?>
842
				<?php
843
					$max_upload_size = 0;
844
					if ( ! empty( $options['meta']['max_upload_size'] ) )
845
						$max_upload_size = absint( $options['meta']['max_upload_size'] );
846
847
					$upload_size_unit = $max_upload_size;
848
					$byte_sizes = array( 'KB', 'MB', 'GB' );
849
850
					for ( $u = -1; $upload_size_unit > 1024 && $u < count( $byte_sizes ) - 1; $u++ )
851
						$upload_size_unit /= 1024;
852
853
					if ( $u < 0 ) {
854
						$upload_size_unit = 0;
855
						$u = 0;
856
					} else {
857
						$upload_size_unit = (int) $upload_size_unit;
858
					}
859
				?>
860
				<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>
861
862
				<input type="file" name="videopress_file" />
863
				<?php submit_button( __( 'Upload Video', 'jetpack' ) ); ?>
864
			</form>
865
			<iframe width="0" height="0" name="videopress_upload_frame"></iframe>
866
		</script>
867
		<?php
868
	}
869
870
	/**
871
	 * Filters the VideoPress shortcode options, makes sure that
872
	 * the settings set in Jetpack's VideoPress module are applied.
873
	 */
874
	function videopress_shortcode_options( $options ) {
875
		$videopress_options = $this->get_options();
876
877
		if ( false === $options['freedom'] )
878
			$options['freedom'] = $videopress_options['freedom'];
879
880
		$options['hd'] = $videopress_options['hd'];
881
882
		return $options;
883
	}
884
885
}
886
887
// Initialize the module.
888
Jetpack_VideoPress::init();
889