Brak opisu

class.json-api-post-base.php 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. <?php
  2. /**
  3. * This class wraps a WP_Post and proxies any undefined attributes
  4. * and methods to the wrapped class. We need to do this because at present
  5. * the WP_Post class is marked as final (in 4.5 this will change, though it's
  6. * not clear if there will be a mechanism to retrieve from the DB into the over-
  7. * ridden class dynamically).
  8. **/
  9. require_once dirname( __FILE__ ) . '/class.json-api-metadata.php';
  10. require_once dirname( __FILE__ ) . '/class.json-api-date.php';
  11. require_once ABSPATH . 'wp-admin/includes/post.php';
  12. require_once ABSPATH . 'wp-includes/post.php';
  13. abstract class SAL_Post {
  14. public $post;
  15. public $context;
  16. public $site;
  17. function __construct( $site, $post, $context ) {
  18. $this->post = $post;
  19. $this->context = $context;
  20. $this->site = $site;
  21. }
  22. public function __set( $key, $value ) {
  23. $this->post->{ $key } = $value;
  24. }
  25. public function __get( $key ) {
  26. if ( $key === 'links' ) {
  27. require_once dirname( __FILE__ ) . '/class.json-api-links.php';
  28. return WPCOM_JSON_API_Links::getInstance();
  29. }
  30. return $this->post->{ $key };
  31. }
  32. public function __call( $name, $arguments ) {
  33. if ( is_callable( array( $this->post, $name ) ) ) {
  34. return call_user_func_array( array( $this->post, $name ), $arguments );
  35. } else {
  36. trigger_error("Call to undefined method '{$name}'");
  37. }
  38. }
  39. public function __isset ( $name ) {
  40. return isset( $this->post->{ $name } );
  41. }
  42. abstract public function get_like_count();
  43. abstract public function is_liked();
  44. abstract public function is_reblogged();
  45. abstract public function is_following();
  46. abstract public function get_global_id();
  47. abstract public function get_geo();
  48. public function get_menu_order() {
  49. return (int) $this->post->menu_order;
  50. }
  51. public function get_guid() {
  52. return (string) $this->post->guid;
  53. }
  54. public function get_type() {
  55. return (string) $this->post->post_type;
  56. }
  57. public function get_terms() {
  58. $taxonomies = get_object_taxonomies( $this->post, 'objects' );
  59. $terms = array();
  60. foreach ( $taxonomies as $taxonomy ) {
  61. if ( ! $taxonomy->public && ! current_user_can( $taxonomy->cap->assign_terms ) ) {
  62. continue;
  63. }
  64. $terms[ $taxonomy->name ] = array();
  65. $taxonomy_terms = wp_get_object_terms( $this->post->ID, $taxonomy->name, array( 'fields' => 'all' ) );
  66. foreach ( $taxonomy_terms as $term ) {
  67. $formatted_term = $this->format_taxonomy( $term, $taxonomy->name, 'display' );
  68. $terms[ $taxonomy->name ][ $term->name ] = $formatted_term;
  69. }
  70. $terms[ $taxonomy->name ] = (object) $terms[ $taxonomy->name ];
  71. }
  72. return (object) $terms;
  73. }
  74. public function get_tags() {
  75. $tags = array();
  76. $terms = wp_get_post_tags( $this->post->ID );
  77. foreach ( $terms as $term ) {
  78. if ( !empty( $term->name ) ) {
  79. $tags[$term->name] = $this->format_taxonomy( $term, 'post_tag', 'display' );
  80. }
  81. }
  82. return (object) $tags;
  83. }
  84. public function get_categories() {
  85. $categories = array();
  86. $terms = wp_get_object_terms( $this->post->ID, 'category', array( 'fields' => 'all' ) );
  87. foreach ( $terms as $term ) {
  88. if ( !empty( $term->name ) ) {
  89. $categories[$term->name] = $this->format_taxonomy( $term, 'category', 'display' );
  90. }
  91. }
  92. return (object) $categories;
  93. }
  94. public function get_attachments_and_count() {
  95. $attachments = array();
  96. $_attachments = new WP_Query( array( 'post_parent' => $this->post->ID, 'post_status' => 'inherit', 'post_type' => 'attachment', 'posts_per_page' => '20' ) );
  97. foreach ( $_attachments->posts as $attachment ) {
  98. $attachments[$attachment->ID] = $this->get_media_item_v1_1( $attachment->ID );
  99. }
  100. return array( (object) $attachments, (int) $_attachments->found_posts );
  101. }
  102. public function get_metadata() {
  103. $metadata = array();
  104. foreach ( (array) has_meta( $this->post->ID ) as $meta ) {
  105. // Don't expose protected fields.
  106. $meta_key = $meta['meta_key'];
  107. $show = !( WPCOM_JSON_API_Metadata::is_internal_only( $meta_key ) )
  108. &&
  109. (
  110. WPCOM_JSON_API_Metadata::is_public( $meta_key )
  111. ||
  112. current_user_can( 'edit_post_meta', $this->post->ID , $meta_key )
  113. );
  114. if ( Jetpack_SEO_Posts::DESCRIPTION_META_KEY == $meta_key && ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
  115. $show = false;
  116. }
  117. if ( $show ) {
  118. $metadata[] = array(
  119. 'id' => $meta['meta_id'],
  120. 'key' => $meta['meta_key'],
  121. 'value' => $this->safe_maybe_unserialize( $meta['meta_value'] ),
  122. );
  123. }
  124. }
  125. return $metadata;
  126. }
  127. public function get_meta() {
  128. $meta = (object) array(
  129. 'links' => (object) array(
  130. 'self' => (string) $this->get_post_link(),
  131. 'help' => (string) $this->get_post_link( 'help' ),
  132. 'site' => (string) $this->get_site_link(),
  133. 'replies' => (string) $this->get_post_link( 'replies/' ),
  134. 'likes' => (string) $this->get_post_link( 'likes/' ),
  135. ),
  136. );
  137. $amp_permalink = get_post_meta( $this->post->ID, '_jetpack_amp_permalink', true );
  138. if ( ! empty( $amp_permalink ) ) {
  139. $meta->links->amp = (string) $amp_permalink;
  140. }
  141. // add autosave link if a more recent autosave exists
  142. if ( 'edit' === $this->context ) {
  143. $autosave = wp_get_post_autosave( $this->post->ID );
  144. if ( $autosave && $autosave->post_modified > $this->post->post_modified )
  145. $meta->links->autosave = (string) $this->get_post_link() . '/autosave';
  146. }
  147. return $meta;
  148. }
  149. public function get_current_user_capabilities() {
  150. return array(
  151. 'publish_post' => current_user_can( 'publish_post', $this->post->ID ),
  152. 'delete_post' => current_user_can( 'delete_post', $this->post->ID ),
  153. 'edit_post' => current_user_can( 'edit_post', $this->post->ID )
  154. );
  155. }
  156. public function get_revisions() {
  157. if ( 'edit' !== $this->context ) {
  158. return false;
  159. }
  160. $revisions = array();
  161. $post_revisions = wp_get_post_revisions( $this->post->ID );
  162. foreach ( $post_revisions as $_post ) {
  163. $revisions[] = $_post->ID;
  164. }
  165. return $revisions;
  166. }
  167. public function get_other_urls() {
  168. $other_urls = array();
  169. if ( 'publish' !== $this->post->post_status ) {
  170. $other_urls = $this->get_permalink_suggestions( $this->post->post_title );
  171. }
  172. return (object) $other_urls;
  173. }
  174. protected function get_site_link() {
  175. return $this->links->get_site_link( $this->site->get_id() );
  176. }
  177. protected function get_post_link( $path = null ) {
  178. return $this->links->get_post_link( $this->site->get_id(), $this->post->ID, $path );
  179. }
  180. public function get_publicize_urls() {
  181. $publicize_URLs = array();
  182. $publicize = get_post_meta( $this->post->ID, 'publicize_results', true );
  183. if ( $publicize ) {
  184. foreach ( $publicize as $service => $data ) {
  185. switch ( $service ) {
  186. case 'twitter' :
  187. foreach ( $data as $datum ) {
  188. $publicize_URLs[] = esc_url_raw( "https://twitter.com/{$datum['user_id']}/status/{$datum['post_id']}" );
  189. }
  190. break;
  191. case 'fb' :
  192. foreach ( $data as $datum ) {
  193. $publicize_URLs[] = esc_url_raw( "https://www.facebook.com/permalink.php?story_fbid={$datum['post_id']}&id={$datum['user_id']}" );
  194. }
  195. break;
  196. }
  197. }
  198. }
  199. return (array) $publicize_URLs;
  200. }
  201. public function get_page_template() {
  202. return (string) get_post_meta( $this->post->ID, '_wp_page_template', true );
  203. }
  204. // note this is overridden in jetpack-shadow
  205. public function get_featured_image() {
  206. $image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( $this->post->ID ), 'full' );
  207. if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
  208. return (string) $image_attributes[0];
  209. } else {
  210. return '';
  211. }
  212. }
  213. public function get_post_thumbnail() {
  214. $thumb = null;
  215. $thumb_id = get_post_thumbnail_id( $this->post->ID );
  216. if ( ! empty( $thumb_id ) ) {
  217. $attachment = get_post( $thumb_id );
  218. if ( ! empty( $attachment ) )
  219. $featured_image_object = $this->get_attachment( $attachment );
  220. if ( ! empty( $featured_image_object ) ) {
  221. $thumb = (object) $featured_image_object;
  222. }
  223. }
  224. return $thumb;
  225. }
  226. public function get_format() {
  227. $format = (string) get_post_format( $this->post->ID );
  228. if ( !$format ) {
  229. $format = 'standard';
  230. }
  231. return $format;
  232. }
  233. private function get_attachment( $attachment ) {
  234. $metadata = wp_get_attachment_metadata( $attachment->ID );
  235. $result = array(
  236. 'ID' => (int) $attachment->ID,
  237. 'URL' => (string) wp_get_attachment_url( $attachment->ID ),
  238. 'guid' => (string) $attachment->guid,
  239. 'mime_type' => (string) $attachment->post_mime_type,
  240. 'width' => (int) isset( $metadata['width'] ) ? $metadata['width'] : 0,
  241. 'height' => (int) isset( $metadata['height'] ) ? $metadata['height'] : 0,
  242. );
  243. if ( isset( $metadata['duration'] ) ) {
  244. $result['duration'] = (int) $metadata['duration'];
  245. }
  246. /** This filter is documented in class.jetpack-sync.php */
  247. return (object) apply_filters( 'get_attachment', $result );
  248. }
  249. public function get_date() {
  250. return (string) WPCOM_JSON_API_Date::format_date( $this->post->post_date_gmt, $this->post->post_date );
  251. }
  252. public function get_modified_date() {
  253. return (string) WPCOM_JSON_API_Date::format_date( $this->post->post_modified_gmt, $this->post->post_modified );
  254. }
  255. public function get_title() {
  256. if ( 'display' === $this->context ) {
  257. return (string) get_the_title( $this->post->ID );
  258. } else {
  259. return (string) htmlspecialchars_decode( $this->post->post_title, ENT_QUOTES );
  260. }
  261. }
  262. public function get_url() {
  263. if ( 'revision' === $this->post->post_type ) {
  264. return (string) esc_url_raw( get_permalink( $this->post->post_parent ) );
  265. } else {
  266. return (string) esc_url_raw( get_permalink( $this->post->ID ) );
  267. }
  268. }
  269. public function get_shortlink() {
  270. return (string) esc_url_raw( wp_get_shortlink( $this->post->ID ) );
  271. }
  272. public function get_content() {
  273. if ( 'display' === $this->context ) {
  274. // TODO: move this WPCOM-specific hack
  275. add_filter( 'the_password_form', array( $this, 'the_password_form' ) );
  276. $content = (string) $this->get_the_post_content_for_display();
  277. remove_filter( 'the_password_form', array( $this, 'the_password_form' ) );
  278. return $content;
  279. } else {
  280. return (string) $this->post->post_content;
  281. }
  282. }
  283. public function get_excerpt() {
  284. if ( 'display' === $this->context ) {
  285. add_filter( 'the_password_form', array( $this, 'the_password_form' ) );
  286. ob_start();
  287. the_excerpt();
  288. $response = (string) ob_get_clean();
  289. remove_filter( 'the_password_form', array( $this, 'the_password_form' ) );
  290. } else {
  291. $response = htmlspecialchars_decode( (string) $this->post->post_excerpt, ENT_QUOTES );
  292. }
  293. return $response;
  294. }
  295. public function get_status() {
  296. return (string) get_post_status( $this->post->ID );
  297. }
  298. public function is_sticky() {
  299. return (bool) is_sticky( $this->post->ID );
  300. }
  301. public function get_slug() {
  302. return (string) $this->post->post_name;
  303. }
  304. public function get_password() {
  305. $password = (string) $this->post->post_password;
  306. if ( 'edit' === $this->context ) {
  307. $password = htmlspecialchars_decode( (string) $password, ENT_QUOTES );
  308. }
  309. return $password;
  310. }
  311. public function get_parent() {
  312. if ( $this->post->post_parent ) {
  313. $parent = get_post( $this->post->post_parent );
  314. if ( 'display' === $this->context ) {
  315. $parent_title = (string) get_the_title( $parent->ID );
  316. } else {
  317. $parent_title = (string) htmlspecialchars_decode( $this->post->post_title, ENT_QUOTES );
  318. }
  319. return (object) array(
  320. 'ID' => (int) $parent->ID,
  321. 'type' => (string) $parent->post_type,
  322. 'link' => (string) $this->links->get_post_link( $this->site->get_id(), $parent->ID ),
  323. 'title' => $parent_title,
  324. );
  325. } else {
  326. return false;
  327. }
  328. }
  329. function the_password_form() {
  330. return __( 'This post is password protected.', 'jetpack' );
  331. }
  332. public function get_discussion() {
  333. return array(
  334. 'comments_open' => (bool) comments_open( $this->post->ID ),
  335. 'comment_status' => (string) $this->post->comment_status,
  336. 'pings_open' => (bool) pings_open( $this->post->ID ),
  337. 'ping_status' => (string) $this->post->ping_status,
  338. 'comment_count' => (int) $this->post->comment_count,
  339. );
  340. }
  341. public function is_likes_enabled() {
  342. /** This filter is documented in modules/likes.php */
  343. $sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
  344. $post_likes_switched = get_post_meta( $this->post->ID, 'switch_like_status', true );
  345. return $post_likes_switched || ( $sitewide_likes_enabled && $post_likes_switched !== '0' );
  346. }
  347. public function is_sharing_enabled() {
  348. $show = true;
  349. /** This filter is documented in modules/sharedaddy/sharing-service.php */
  350. $show = apply_filters( 'sharing_show', $show, $this->post );
  351. $switched_status = get_post_meta( $this->post->ID, 'sharing_disabled', false );
  352. if ( !empty( $switched_status ) )
  353. $show = false;
  354. return (bool) $show;
  355. }
  356. // No Blog ID parameter. No Post ID parameter. Depends on globals.
  357. // Expects setup_postdata() to already have been run
  358. function get_the_post_content_for_display() {
  359. global $pages, $page;
  360. $old_pages = $pages;
  361. $old_page = $page;
  362. $content = join( "\n\n", $pages );
  363. $content = preg_replace( '/<!--more(.*?)?-->/', '', $content );
  364. $pages = array( $content );
  365. $page = 1;
  366. ob_start();
  367. the_content();
  368. $return = ob_get_clean();
  369. $pages = $old_pages;
  370. $page = $old_page;
  371. return $return;
  372. }
  373. public function get_author() {
  374. if ( 0 == $this->post->post_author )
  375. return null;
  376. $show_email = $this->context === 'edit' && current_user_can( 'edit_post', $this->post->ID );
  377. $user = get_user_by( 'id', $this->post->post_author );
  378. if ( ! $user || is_wp_error( $user ) ) {
  379. trigger_error( 'Unknown user', E_USER_WARNING );
  380. return null;
  381. }
  382. // TODO factor this out
  383. if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
  384. $active_blog = get_active_blog_for_user( $user->ID );
  385. $site_id = $active_blog->blog_id;
  386. $profile_URL = "https://en.gravatar.com/{$user->user_login}";
  387. } else {
  388. $profile_URL = 'https://en.gravatar.com/' . md5( strtolower( trim( $user->user_email ) ) );
  389. $site_id = -1;
  390. }
  391. $author = array(
  392. 'ID' => (int) $user->ID,
  393. 'login' => (string) $user->user_login,
  394. 'email' => $show_email ? (string) $user->user_email : false, // (string|bool)
  395. 'name' => (string) $user->display_name,
  396. 'first_name' => (string) $user->first_name,
  397. 'last_name' => (string) $user->last_name,
  398. 'nice_name' => (string) $user->user_nicename,
  399. 'URL' => (string) esc_url_raw( $user->user_url ),
  400. 'avatar_URL' => (string) esc_url_raw( $this->get_avatar_url( $user->user_email ) ),
  401. 'profile_URL' => (string) esc_url_raw( $profile_URL )
  402. );
  403. if ($site_id > -1) {
  404. $author['site_ID'] = (int) $site_id;
  405. }
  406. return (object) $author;
  407. }
  408. protected function get_avatar_url( $email, $avatar_size = 96 ) {
  409. $avatar_url = wpcom_get_avatar_url( $email, $avatar_size, '', true );
  410. if ( ! $avatar_url || is_wp_error( $avatar_url ) ) {
  411. return '';
  412. }
  413. return esc_url_raw( htmlspecialchars_decode( $avatar_url[0] ) );
  414. }
  415. /**
  416. * Get extra post permalink suggestions
  417. * @return array array of permalink suggestions: 'permalink_URL', 'suggested_slug'
  418. */
  419. public function get_permalink_suggestions( $title ) {
  420. $suggestions = array();
  421. list( $suggestions['permalink_URL'], $suggestions['suggested_slug'] ) = get_sample_permalink( $this->post->ID, $title );
  422. return $suggestions;
  423. }
  424. private function format_taxonomy( $taxonomy, $taxonomy_type, $context ) {
  425. // Permissions
  426. switch ( $context ) {
  427. case 'edit' :
  428. $tax = get_taxonomy( $taxonomy_type );
  429. if ( !current_user_can( $tax->cap->edit_terms ) )
  430. return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 );
  431. break;
  432. case 'display' :
  433. if ( -1 == get_option( 'blog_public' ) && ! current_user_can( 'read' ) ) {
  434. return new WP_Error( 'unauthorized', 'User cannot view taxonomy', 403 );
  435. }
  436. break;
  437. default :
  438. return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 );
  439. }
  440. $response = array();
  441. $response['ID'] = (int) $taxonomy->term_id;
  442. $response['name'] = (string) $taxonomy->name;
  443. $response['slug'] = (string) $taxonomy->slug;
  444. $response['description'] = (string) $taxonomy->description;
  445. $response['post_count'] = (int) $taxonomy->count;
  446. if ( is_taxonomy_hierarchical( $taxonomy_type ) ) {
  447. $response['parent'] = (int) $taxonomy->parent;
  448. }
  449. $response['meta'] = (object) array(
  450. 'links' => (object) array(
  451. 'self' => (string) $this->links->get_taxonomy_link( $this->site->get_id(), $taxonomy->slug, $taxonomy_type ),
  452. 'help' => (string) $this->links->get_taxonomy_link( $this->site->get_id(), $taxonomy->slug, $taxonomy_type, 'help' ),
  453. 'site' => (string) $this->links->get_site_link( $this->site->get_id() ),
  454. ),
  455. );
  456. return (object) $response;
  457. }
  458. // TODO: factor this out into site
  459. private function get_media_item_v1_1( $media_id ) {
  460. $media_item = get_post( $media_id );
  461. if ( ! $media_item || is_wp_error( $media_item ) )
  462. return new WP_Error( 'unknown_media', 'Unknown Media', 404 );
  463. $file = basename( wp_get_attachment_url( $media_item->ID ) );
  464. $file_info = pathinfo( $file );
  465. $ext = $file_info['extension'];
  466. $response = array(
  467. 'ID' => $media_item->ID,
  468. 'URL' => wp_get_attachment_url( $media_item->ID ),
  469. 'guid' => $media_item->guid,
  470. 'date' => (string) WPCOM_JSON_API_Date::format_date( $media_item->post_date_gmt, $media_item->post_date ),
  471. 'post_ID' => $media_item->post_parent,
  472. 'author_ID' => (int) $media_item->post_author,
  473. 'file' => $file,
  474. 'mime_type' => $media_item->post_mime_type,
  475. 'extension' => $ext,
  476. 'title' => $media_item->post_title,
  477. 'caption' => $media_item->post_excerpt,
  478. 'description' => $media_item->post_content,
  479. 'alt' => get_post_meta( $media_item->ID, '_wp_attachment_image_alt', true ),
  480. 'thumbnails' => array()
  481. );
  482. if ( in_array( $ext, array( 'jpg', 'jpeg', 'png', 'gif', 'webp' ), true ) ) {
  483. $metadata = wp_get_attachment_metadata( $media_item->ID );
  484. if ( isset( $metadata['height'], $metadata['width'] ) ) {
  485. $response['height'] = $metadata['height'];
  486. $response['width'] = $metadata['width'];
  487. }
  488. if ( isset( $metadata['sizes'] ) ) {
  489. /**
  490. * Filter the thumbnail sizes available for each attachment ID.
  491. *
  492. * @module json-api
  493. *
  494. * @since 3.9.0
  495. *
  496. * @param array $metadata['sizes'] Array of thumbnail sizes available for a given attachment ID.
  497. * @param string $media_id Attachment ID.
  498. */
  499. $sizes = apply_filters( 'rest_api_thumbnail_sizes', $metadata['sizes'], $media_id );
  500. if ( is_array( $sizes ) ) {
  501. foreach ( $sizes as $size => $size_details ) {
  502. $response['thumbnails'][ $size ] = dirname( $response['URL'] ) . '/' . $size_details['file'];
  503. }
  504. }
  505. }
  506. if ( isset( $metadata['image_meta'] ) ) {
  507. $response['exif'] = $metadata['image_meta'];
  508. }
  509. }
  510. if ( in_array( $ext, array( 'mp3', 'm4a', 'wav', 'ogg' ) ) ) {
  511. $metadata = wp_get_attachment_metadata( $media_item->ID );
  512. if ( isset( $metadata['length'] ) ) {
  513. $response['length'] = $metadata['length'];
  514. }
  515. $response['exif'] = $metadata;
  516. }
  517. if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ) ) ) {
  518. $metadata = wp_get_attachment_metadata( $media_item->ID );
  519. if ( isset( $metadata['height'], $metadata['width'] ) ) {
  520. $response['height'] = $metadata['height'];
  521. $response['width'] = $metadata['width'];
  522. }
  523. if ( isset( $metadata['length'] ) ) {
  524. $response['length'] = $metadata['length'];
  525. }
  526. // add VideoPress info
  527. if ( function_exists( 'video_get_info_by_blogpostid' ) ) {
  528. $info = video_get_info_by_blogpostid( $this->site->get_id(), $media_id );
  529. // Thumbnails
  530. if ( function_exists( 'video_format_done' ) && function_exists( 'video_image_url_by_guid' ) ) {
  531. $response['thumbnails'] = array( 'fmt_hd' => '', 'fmt_dvd' => '', 'fmt_std' => '' );
  532. foreach ( $response['thumbnails'] as $size => $thumbnail_url ) {
  533. if ( video_format_done( $info, $size ) ) {
  534. $response['thumbnails'][ $size ] = video_image_url_by_guid( $info->guid, $size );
  535. } else {
  536. unset( $response['thumbnails'][ $size ] );
  537. }
  538. }
  539. }
  540. $response['videopress_guid'] = $info->guid;
  541. $response['videopress_processing_done'] = true;
  542. if ( '0000-00-00 00:00:00' == $info->finish_date_gmt ) {
  543. $response['videopress_processing_done'] = false;
  544. }
  545. }
  546. }
  547. $response['thumbnails'] = (object) $response['thumbnails'];
  548. $response['meta'] = (object) array(
  549. 'links' => (object) array(
  550. 'self' => (string) $this->links->get_media_link( $this->site->get_id(), $media_id ),
  551. 'help' => (string) $this->links->get_media_link( $this->site->get_id(), $media_id, 'help' ),
  552. 'site' => (string) $this->links->get_site_link( $this->site->get_id() ),
  553. ),
  554. );
  555. // add VideoPress link to the meta
  556. if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ) ) ) {
  557. if ( function_exists( 'video_get_info_by_blogpostid' ) ) {
  558. $response['meta']->links->videopress = (string) $this->links->get_link( '/videos/%s', $response['videopress_guid'], '' );
  559. }
  560. }
  561. if ( $media_item->post_parent > 0 ) {
  562. $response['meta']->links->parent = (string) $this->links->get_post_link( $this->site->get_id(), $media_item->post_parent );
  563. }
  564. return (object) $response;
  565. }
  566. /**
  567. * Temporary wrapper around maybe_unserialize() to catch exceptions thrown by unserialize().
  568. *
  569. * Can be removed after https://core.trac.wordpress.org/ticket/45895 lands in Core.
  570. *
  571. * @param string $original Serialized string.
  572. * @return string Unserialized string or original string if an exception was raised.
  573. **/
  574. protected function safe_maybe_unserialize( $original ) {
  575. try {
  576. return maybe_unserialize( $original );
  577. } catch ( Exception $e ) {
  578. return $original;
  579. }
  580. }
  581. }