Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like WPCOM_JSON_API_Endpoint often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WPCOM_JSON_API_Endpoint, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 9 | abstract class WPCOM_JSON_API_Endpoint { |
||
| 10 | // The API Object |
||
| 11 | public $api; |
||
| 12 | |||
| 13 | // The link-generating utility class |
||
| 14 | public $links; |
||
| 15 | |||
| 16 | public $pass_wpcom_user_details = false; |
||
| 17 | |||
| 18 | // One liner. |
||
| 19 | public $description; |
||
| 20 | |||
| 21 | // Object Grouping For Documentation (Users, Posts, Comments) |
||
| 22 | public $group; |
||
| 23 | |||
| 24 | // Stats extra value to bump |
||
| 25 | public $stat; |
||
| 26 | |||
| 27 | // HTTP Method |
||
| 28 | public $method = 'GET'; |
||
| 29 | |||
| 30 | // Minimum version of the api for which to serve this endpoint |
||
| 31 | public $min_version = '0'; |
||
| 32 | |||
| 33 | // Maximum version of the api for which to serve this endpoint |
||
| 34 | public $max_version = WPCOM_JSON_API__CURRENT_VERSION; |
||
| 35 | |||
| 36 | // Path at which to serve this endpoint: sprintf() format. |
||
| 37 | public $path = ''; |
||
| 38 | |||
| 39 | // Identifiers to fill sprintf() formatted $path |
||
| 40 | public $path_labels = array(); |
||
| 41 | |||
| 42 | // Accepted query parameters |
||
| 43 | public $query = array( |
||
| 44 | // Parameter name |
||
| 45 | 'context' => array( |
||
| 46 | // Default value => description |
||
| 47 | 'display' => 'Formats the output as HTML for display. Shortcodes are parsed, paragraph tags are added, etc..', |
||
| 48 | // Other possible values => description |
||
| 49 | 'edit' => 'Formats the output for editing. Shortcodes are left unparsed, significant whitespace is kept, etc..', |
||
| 50 | ), |
||
| 51 | 'http_envelope' => array( |
||
| 52 | 'false' => '', |
||
| 53 | 'true' => 'Some environments (like in-browser JavaScript or Flash) block or divert responses with a non-200 HTTP status code. Setting this parameter will force the HTTP status code to always be 200. The JSON response is wrapped in an "envelope" containing the "real" HTTP status code and headers.', |
||
| 54 | ), |
||
| 55 | 'pretty' => array( |
||
| 56 | 'false' => '', |
||
| 57 | 'true' => 'Output pretty JSON', |
||
| 58 | ), |
||
| 59 | 'meta' => "(string) Optional. Loads data from the endpoints found in the 'meta' part of the response. Comma-separated list. Example: meta=site,likes", |
||
| 60 | 'fields' => '(string) Optional. Returns specified fields only. Comma-separated list. Example: fields=ID,title', |
||
| 61 | // Parameter name => description (default value is empty) |
||
| 62 | 'callback' => '(string) An optional JSONP callback function.', |
||
| 63 | ); |
||
| 64 | |||
| 65 | // Response format |
||
| 66 | public $response_format = array(); |
||
| 67 | |||
| 68 | // Request format |
||
| 69 | public $request_format = array(); |
||
| 70 | |||
| 71 | // Is this endpoint still in testing phase? If so, not available to the public. |
||
| 72 | public $in_testing = false; |
||
| 73 | |||
| 74 | // Is this endpoint still allowed if the site in question is flagged? |
||
| 75 | public $allowed_if_flagged = false; |
||
| 76 | |||
| 77 | // Is this endpoint allowed if the site is red flagged? |
||
| 78 | public $allowed_if_red_flagged = false; |
||
| 79 | |||
| 80 | /** |
||
| 81 | * @var string Version of the API |
||
| 82 | */ |
||
| 83 | public $version = ''; |
||
| 84 | |||
| 85 | /** |
||
| 86 | * @var string Example request to make |
||
| 87 | */ |
||
| 88 | public $example_request = ''; |
||
| 89 | |||
| 90 | /** |
||
| 91 | * @var string Example request data (for POST methods) |
||
| 92 | */ |
||
| 93 | public $example_request_data = ''; |
||
| 94 | |||
| 95 | /** |
||
| 96 | * @var string Example response from $example_request |
||
| 97 | */ |
||
| 98 | public $example_response = ''; |
||
| 99 | |||
| 100 | /** |
||
| 101 | * @var bool Set to true if the endpoint implements its own filtering instead of the standard `fields` query method |
||
| 102 | */ |
||
| 103 | public $custom_fields_filtering = false; |
||
| 104 | |||
| 105 | /** |
||
| 106 | * @var bool Set to true if the endpoint accepts all cross origin requests. You probably should not set this flag. |
||
| 107 | */ |
||
| 108 | public $allow_cross_origin_request = false; |
||
| 109 | |||
| 110 | /** |
||
| 111 | * @var bool Set to true if the endpoint can recieve unauthorized POST requests. |
||
| 112 | */ |
||
| 113 | public $allow_unauthorized_request = false; |
||
| 114 | |||
| 115 | /** |
||
| 116 | * @var bool Set to true if the endpoint should accept site based (not user based) authentication. |
||
| 117 | */ |
||
| 118 | public $allow_jetpack_site_auth = false; |
||
| 119 | |||
| 120 | function __construct( $args ) { |
||
| 121 | $defaults = array( |
||
| 122 | 'in_testing' => false, |
||
| 123 | 'allowed_if_flagged' => false, |
||
| 124 | 'allowed_if_red_flagged' => false, |
||
| 125 | 'description' => '', |
||
| 126 | 'group' => '', |
||
| 127 | 'method' => 'GET', |
||
| 128 | 'path' => '/', |
||
| 129 | 'min_version' => '0', |
||
| 130 | 'max_version' => WPCOM_JSON_API__CURRENT_VERSION, |
||
| 131 | 'force' => '', |
||
| 132 | 'deprecated' => false, |
||
| 133 | 'new_version' => WPCOM_JSON_API__CURRENT_VERSION, |
||
| 134 | 'jp_disabled' => false, |
||
| 135 | 'path_labels' => array(), |
||
| 136 | 'request_format' => array(), |
||
| 137 | 'response_format' => array(), |
||
| 138 | 'query_parameters' => array(), |
||
| 139 | 'version' => 'v1', |
||
| 140 | 'example_request' => '', |
||
| 141 | 'example_request_data' => '', |
||
| 142 | 'example_response' => '', |
||
| 143 | 'required_scope' => '', |
||
| 144 | 'pass_wpcom_user_details' => false, |
||
| 145 | 'custom_fields_filtering' => false, |
||
| 146 | 'allow_cross_origin_request' => false, |
||
| 147 | 'allow_unauthorized_request' => false, |
||
| 148 | 'allow_jetpack_site_auth' => false, |
||
| 149 | ); |
||
| 150 | |||
| 151 | $args = wp_parse_args( $args, $defaults ); |
||
| 152 | |||
| 153 | $this->in_testing = $args['in_testing']; |
||
|
|
|||
| 154 | |||
| 155 | $this->allowed_if_flagged = $args['allowed_if_flagged']; |
||
| 156 | $this->allowed_if_red_flagged = $args['allowed_if_red_flagged']; |
||
| 157 | |||
| 158 | $this->description = $args['description']; |
||
| 159 | $this->group = $args['group']; |
||
| 160 | $this->stat = $args['stat']; |
||
| 161 | $this->force = $args['force']; |
||
| 162 | $this->jp_disabled = $args['jp_disabled']; |
||
| 163 | |||
| 164 | $this->method = $args['method']; |
||
| 165 | $this->path = $args['path']; |
||
| 166 | $this->path_labels = $args['path_labels']; |
||
| 167 | $this->min_version = $args['min_version']; |
||
| 168 | $this->max_version = $args['max_version']; |
||
| 169 | $this->deprecated = $args['deprecated']; |
||
| 170 | $this->new_version = $args['new_version']; |
||
| 171 | |||
| 172 | $this->pass_wpcom_user_details = $args['pass_wpcom_user_details']; |
||
| 173 | $this->custom_fields_filtering = (bool) $args['custom_fields_filtering']; |
||
| 174 | |||
| 175 | $this->allow_cross_origin_request = (bool) $args['allow_cross_origin_request']; |
||
| 176 | $this->allow_unauthorized_request = (bool) $args['allow_unauthorized_request']; |
||
| 177 | $this->allow_jetpack_site_auth = (bool) $args['allow_jetpack_site_auth']; |
||
| 178 | |||
| 179 | $this->version = $args['version']; |
||
| 180 | |||
| 181 | $this->required_scope = $args['required_scope']; |
||
| 182 | |||
| 183 | View Code Duplication | if ( $this->request_format ) { |
|
| 184 | $this->request_format = array_filter( array_merge( $this->request_format, $args['request_format'] ) ); |
||
| 185 | } else { |
||
| 186 | $this->request_format = $args['request_format']; |
||
| 187 | } |
||
| 188 | |||
| 189 | View Code Duplication | if ( $this->response_format ) { |
|
| 190 | $this->response_format = array_filter( array_merge( $this->response_format, $args['response_format'] ) ); |
||
| 191 | } else { |
||
| 192 | $this->response_format = $args['response_format']; |
||
| 193 | } |
||
| 194 | |||
| 195 | if ( false === $args['query_parameters'] ) { |
||
| 196 | $this->query = array(); |
||
| 197 | } elseif ( is_array( $args['query_parameters'] ) ) { |
||
| 198 | $this->query = array_filter( array_merge( $this->query, $args['query_parameters'] ) ); |
||
| 199 | } |
||
| 200 | |||
| 201 | $this->api = WPCOM_JSON_API::init(); // Auto-add to WPCOM_JSON_API |
||
| 202 | $this->links = WPCOM_JSON_API_Links::getInstance(); |
||
| 203 | |||
| 204 | /** Example Request/Response ******************************************/ |
||
| 205 | |||
| 206 | // Examples for endpoint documentation request |
||
| 207 | $this->example_request = $args['example_request']; |
||
| 208 | $this->example_request_data = $args['example_request_data']; |
||
| 209 | $this->example_response = $args['example_response']; |
||
| 210 | |||
| 211 | $this->api->add( $this ); |
||
| 212 | } |
||
| 213 | |||
| 214 | // Get all query args. Prefill with defaults |
||
| 215 | function query_args( $return_default_values = true, $cast_and_filter = true ) { |
||
| 224 | |||
| 225 | // Get POST body data |
||
| 226 | function input( $return_default_values = true, $cast_and_filter = true ) { |
||
| 276 | |||
| 277 | function cast_and_filter( $data, $documentation, $return_default_values = false, $for_output = false ) { |
||
| 338 | |||
| 339 | /** |
||
| 340 | * Casts $value according to $type. |
||
| 341 | * Handles fallbacks for certain values of $type when $value is not that $type |
||
| 342 | * Currently, only handles fallback between string <-> array (two way), from string -> false (one way), and from object -> false (one way), |
||
| 343 | * and string -> object (one way) |
||
| 344 | * |
||
| 345 | * Handles "child types" - array:URL, object:category |
||
| 346 | * array:URL means an array of URLs |
||
| 347 | * object:category means a hash of categories |
||
| 348 | * |
||
| 349 | * Handles object typing - object>post means an object of type post |
||
| 350 | */ |
||
| 351 | function cast_and_filter_item( &$return, $type, $key, $value, $types = array(), $for_output = false ) { |
||
| 673 | |||
| 674 | function parse_types( $text ) { |
||
| 694 | |||
| 695 | /** |
||
| 696 | * Checks if the endpoint is publicly displayable |
||
| 697 | */ |
||
| 698 | function is_publicly_documentable() { |
||
| 701 | |||
| 702 | /** |
||
| 703 | * Auto generates documentation based on description, method, path, path_labels, and query parameters. |
||
| 704 | * Echoes HTML. |
||
| 705 | */ |
||
| 706 | function document( $show_description = true ) { |
||
| 822 | |||
| 823 | function add_http_build_query_to_php_content_example( $matches ) { |
||
| 830 | |||
| 831 | /** |
||
| 832 | * Recursively generates the <dl>'s to document item descriptions. |
||
| 833 | * Echoes HTML. |
||
| 834 | */ |
||
| 835 | function generate_doc_description( $item ) { |
||
| 853 | |||
| 854 | /** |
||
| 855 | * Auto generates documentation based on description, method, path, path_labels, and query parameters. |
||
| 856 | * Echoes HTML. |
||
| 857 | */ |
||
| 858 | function generate_documentation() { |
||
| 939 | |||
| 940 | function user_can_view_post( $post_id ) { |
||
| 1004 | |||
| 1005 | /** |
||
| 1006 | * Returns author object. |
||
| 1007 | * |
||
| 1008 | * @param $author user ID, user row, WP_User object, comment row, post row |
||
| 1009 | * @param $show_email output the author's email address? |
||
| 1010 | * |
||
| 1011 | * @return (object) |
||
| 1012 | */ |
||
| 1013 | function get_author( $author, $show_email = false ) { |
||
| 1116 | |||
| 1117 | function get_media_item( $media_id ) { |
||
| 1118 | $media_item = get_post( $media_id ); |
||
| 1119 | |||
| 1120 | if ( !$media_item || is_wp_error( $media_item ) ) |
||
| 1121 | return new WP_Error( 'unknown_media', 'Unknown Media', 404 ); |
||
| 1122 | |||
| 1123 | $response = array( |
||
| 1124 | 'id' => strval( $media_item->ID ), |
||
| 1125 | 'date' => (string) $this->format_date( $media_item->post_date_gmt, $media_item->post_date ), |
||
| 1126 | 'parent' => $media_item->post_parent, |
||
| 1127 | 'link' => wp_get_attachment_url( $media_item->ID ), |
||
| 1128 | 'title' => $media_item->post_title, |
||
| 1129 | 'caption' => $media_item->post_excerpt, |
||
| 1130 | 'description' => $media_item->post_content, |
||
| 1131 | 'metadata' => wp_get_attachment_metadata( $media_item->ID ), |
||
| 1132 | ); |
||
| 1133 | |||
| 1134 | if ( defined( 'IS_WPCOM' ) && IS_WPCOM && is_array( $response['metadata'] ) && ! empty( $response['metadata']['file'] ) ) { |
||
| 1135 | remove_filter( '_wp_relative_upload_path', 'wpcom_wp_relative_upload_path', 10 ); |
||
| 1136 | $response['metadata']['file'] = _wp_relative_upload_path( $response['metadata']['file'] ); |
||
| 1137 | add_filter( '_wp_relative_upload_path', 'wpcom_wp_relative_upload_path', 10, 2 ); |
||
| 1138 | } |
||
| 1139 | |||
| 1140 | $response['meta'] = (object) array( |
||
| 1141 | 'links' => (object) array( |
||
| 1142 | 'self' => (string) $this->links->get_media_link( $this->api->get_blog_id_for_output(), $media_id ), |
||
| 1143 | 'help' => (string) $this->links->get_media_link( $this->api->get_blog_id_for_output(), $media_id, 'help' ), |
||
| 1144 | 'site' => (string) $this->links->get_site_link( $this->api->get_blog_id_for_output() ), |
||
| 1145 | ), |
||
| 1146 | ); |
||
| 1147 | |||
| 1148 | return (object) $response; |
||
| 1149 | } |
||
| 1150 | |||
| 1151 | function get_media_item_v1_1( $media_id ) { |
||
| 1152 | $media_item = get_post( $media_id ); |
||
| 1153 | |||
| 1154 | if ( ! $media_item || is_wp_error( $media_item ) ) |
||
| 1155 | return new WP_Error( 'unknown_media', 'Unknown Media', 404 ); |
||
| 1156 | |||
| 1157 | $file = basename( wp_get_attachment_url( $media_item->ID ) ); |
||
| 1158 | $file_info = pathinfo( $file ); |
||
| 1159 | $ext = $file_info['extension']; |
||
| 1160 | |||
| 1161 | $response = array( |
||
| 1162 | 'ID' => $media_item->ID, |
||
| 1163 | 'URL' => wp_get_attachment_url( $media_item->ID ), |
||
| 1164 | 'guid' => $media_item->guid, |
||
| 1165 | 'date' => (string) $this->format_date( $media_item->post_date_gmt, $media_item->post_date ), |
||
| 1166 | 'post_ID' => $media_item->post_parent, |
||
| 1167 | 'author_ID' => (int) $media_item->post_author, |
||
| 1168 | 'file' => $file, |
||
| 1169 | 'mime_type' => $media_item->post_mime_type, |
||
| 1170 | 'extension' => $ext, |
||
| 1171 | 'title' => $media_item->post_title, |
||
| 1172 | 'caption' => $media_item->post_excerpt, |
||
| 1173 | 'description' => $media_item->post_content, |
||
| 1174 | 'alt' => get_post_meta( $media_item->ID, '_wp_attachment_image_alt', true ), |
||
| 1175 | 'thumbnails' => array() |
||
| 1176 | ); |
||
| 1177 | |||
| 1178 | View Code Duplication | if ( in_array( $ext, array( 'jpg', 'jpeg', 'png', 'gif' ) ) ) { |
|
| 1179 | $metadata = wp_get_attachment_metadata( $media_item->ID ); |
||
| 1180 | if ( isset( $metadata['height'], $metadata['width'] ) ) { |
||
| 1181 | $response['height'] = $metadata['height']; |
||
| 1182 | $response['width'] = $metadata['width']; |
||
| 1183 | } |
||
| 1184 | |||
| 1185 | if ( isset( $metadata['sizes'] ) ) { |
||
| 1186 | /** |
||
| 1187 | * Filter the thumbnail sizes available for each attachment ID. |
||
| 1188 | * |
||
| 1189 | * @module json-api |
||
| 1190 | * |
||
| 1191 | * @since 3.9.0 |
||
| 1192 | * |
||
| 1193 | * @param array $metadata['sizes'] Array of thumbnail sizes available for a given attachment ID. |
||
| 1194 | * @param string $media_id Attachment ID. |
||
| 1195 | */ |
||
| 1196 | $sizes = apply_filters( 'rest_api_thumbnail_sizes', $metadata['sizes'], $media_id ); |
||
| 1197 | if ( is_array( $sizes ) ) { |
||
| 1198 | foreach ( $sizes as $size => $size_details ) { |
||
| 1199 | $response['thumbnails'][ $size ] = dirname( $response['URL'] ) . '/' . $size_details['file']; |
||
| 1200 | } |
||
| 1201 | } |
||
| 1202 | } |
||
| 1203 | |||
| 1204 | if ( isset( $metadata['image_meta'] ) ) { |
||
| 1205 | $response['exif'] = $metadata['image_meta']; |
||
| 1206 | } |
||
| 1207 | } |
||
| 1208 | |||
| 1209 | View Code Duplication | if ( in_array( $ext, array( 'mp3', 'm4a', 'wav', 'ogg' ) ) ) { |
|
| 1210 | $metadata = wp_get_attachment_metadata( $media_item->ID ); |
||
| 1211 | $response['length'] = $metadata['length']; |
||
| 1212 | $response['exif'] = $metadata; |
||
| 1213 | } |
||
| 1214 | |||
| 1215 | View Code Duplication | if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ) ) ) { |
|
| 1216 | $metadata = wp_get_attachment_metadata( $media_item->ID ); |
||
| 1217 | if ( isset( $metadata['height'], $metadata['width'] ) ) { |
||
| 1218 | $response['height'] = $metadata['height']; |
||
| 1219 | $response['width'] = $metadata['width']; |
||
| 1220 | } |
||
| 1221 | |||
| 1222 | if ( isset( $metadata['length'] ) ) { |
||
| 1223 | $response['length'] = $metadata['length']; |
||
| 1224 | } |
||
| 1225 | |||
| 1226 | // add VideoPress info |
||
| 1227 | if ( function_exists( 'video_get_info_by_blogpostid' ) ) { |
||
| 1228 | $info = video_get_info_by_blogpostid( $this->api->get_blog_id_for_output(), $media_id ); |
||
| 1229 | |||
| 1230 | // Thumbnails |
||
| 1231 | if ( function_exists( 'video_format_done' ) && function_exists( 'video_image_url_by_guid' ) ) { |
||
| 1232 | $response['thumbnails'] = array( 'fmt_hd' => '', 'fmt_dvd' => '', 'fmt_std' => '' ); |
||
| 1233 | foreach ( $response['thumbnails'] as $size => $thumbnail_url ) { |
||
| 1234 | if ( video_format_done( $info, $size ) ) { |
||
| 1235 | $response['thumbnails'][ $size ] = video_image_url_by_guid( $info->guid, $size ); |
||
| 1236 | } else { |
||
| 1237 | unset( $response['thumbnails'][ $size ] ); |
||
| 1238 | } |
||
| 1239 | } |
||
| 1240 | } |
||
| 1241 | |||
| 1242 | $response['videopress_guid'] = $info->guid; |
||
| 1243 | $response['videopress_processing_done'] = true; |
||
| 1244 | if ( '0000-00-00 00:00:00' == $info->finish_date_gmt ) { |
||
| 1245 | $response['videopress_processing_done'] = false; |
||
| 1246 | } |
||
| 1247 | } |
||
| 1248 | } |
||
| 1249 | |||
| 1250 | $response['thumbnails'] = (object) $response['thumbnails']; |
||
| 1251 | |||
| 1252 | $response['meta'] = (object) array( |
||
| 1253 | 'links' => (object) array( |
||
| 1254 | 'self' => (string) $this->links->get_media_link( $this->api->get_blog_id_for_output(), $media_id ), |
||
| 1255 | 'help' => (string) $this->links->get_media_link( $this->api->get_blog_id_for_output(), $media_id, 'help' ), |
||
| 1256 | 'site' => (string) $this->links->get_site_link( $this->api->get_blog_id_for_output() ), |
||
| 1257 | ), |
||
| 1258 | ); |
||
| 1259 | |||
| 1260 | // add VideoPress link to the meta |
||
| 1261 | View Code Duplication | if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ) ) ) { |
|
| 1262 | if ( function_exists( 'video_get_info_by_blogpostid' ) ) { |
||
| 1263 | $response['meta']->links->videopress = (string) $this->links->get_link( '/videos/%s', $response['videopress_guid'], '' ); |
||
| 1264 | } |
||
| 1265 | } |
||
| 1266 | |||
| 1267 | View Code Duplication | if ( $media_item->post_parent > 0 ) { |
|
| 1268 | $response['meta']->links->parent = (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $media_item->post_parent ); |
||
| 1269 | } |
||
| 1270 | |||
| 1271 | return (object) $response; |
||
| 1272 | } |
||
| 1273 | |||
| 1274 | function get_taxonomy( $taxonomy_id, $taxonomy_type, $context ) { |
||
| 1284 | |||
| 1285 | function format_taxonomy( $taxonomy, $taxonomy_type, $context ) { |
||
| 1286 | // Permissions |
||
| 1287 | View Code Duplication | switch ( $context ) { |
|
| 1288 | case 'edit' : |
||
| 1289 | $tax = get_taxonomy( $taxonomy_type ); |
||
| 1290 | if ( !current_user_can( $tax->cap->edit_terms ) ) |
||
| 1291 | return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 ); |
||
| 1292 | break; |
||
| 1293 | case 'display' : |
||
| 1294 | if ( -1 == get_option( 'blog_public' ) && ! current_user_can( 'read' ) ) { |
||
| 1295 | return new WP_Error( 'unauthorized', 'User cannot view taxonomy', 403 ); |
||
| 1296 | } |
||
| 1297 | break; |
||
| 1298 | default : |
||
| 1299 | return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 ); |
||
| 1300 | } |
||
| 1301 | |||
| 1302 | $response = array(); |
||
| 1303 | $response['ID'] = (int) $taxonomy->term_id; |
||
| 1304 | $response['name'] = (string) $taxonomy->name; |
||
| 1305 | $response['slug'] = (string) $taxonomy->slug; |
||
| 1306 | $response['description'] = (string) $taxonomy->description; |
||
| 1307 | $response['post_count'] = (int) $taxonomy->count; |
||
| 1308 | |||
| 1309 | if ( 'category' === $taxonomy_type ) |
||
| 1310 | $response['parent'] = (int) $taxonomy->parent; |
||
| 1311 | |||
| 1312 | $response['meta'] = (object) array( |
||
| 1313 | 'links' => (object) array( |
||
| 1314 | 'self' => (string) $this->links->get_taxonomy_link( $this->api->get_blog_id_for_output(), $taxonomy->slug, $taxonomy_type ), |
||
| 1315 | 'help' => (string) $this->links->get_taxonomy_link( $this->api->get_blog_id_for_output(), $taxonomy->slug, $taxonomy_type, 'help' ), |
||
| 1316 | 'site' => (string) $this->links->get_site_link( $this->api->get_blog_id_for_output() ), |
||
| 1317 | ), |
||
| 1318 | ); |
||
| 1319 | |||
| 1320 | return (object) $response; |
||
| 1321 | } |
||
| 1322 | |||
| 1323 | /** |
||
| 1324 | * Returns ISO 8601 formatted datetime: 2011-12-08T01:15:36-08:00 |
||
| 1325 | * |
||
| 1326 | * @param $date_gmt (string) GMT datetime string. |
||
| 1327 | * @param $date (string) Optional. Used to calculate the offset from GMT. |
||
| 1328 | * |
||
| 1329 | * @return string |
||
| 1330 | */ |
||
| 1331 | function format_date( $date_gmt, $date = null ) { |
||
| 1332 | return WPCOM_JSON_API_Date::format_date( $date_gmt, $date ); |
||
| 1333 | } |
||
| 1334 | |||
| 1335 | /** |
||
| 1336 | * Parses a date string and returns the local and GMT representations |
||
| 1337 | * of that date & time in 'YYYY-MM-DD HH:MM:SS' format without |
||
| 1338 | * timezones or offsets. If the parsed datetime was not localized to a |
||
| 1339 | * particular timezone or offset we will assume it was given in GMT |
||
| 1340 | * relative to now and will convert it to local time using either the |
||
| 1341 | * timezone set in the options table for the blog or the GMT offset. |
||
| 1342 | * |
||
| 1343 | * @param datetime string |
||
| 1344 | * |
||
| 1345 | * @return array( $local_time_string, $gmt_time_string ) |
||
| 1346 | */ |
||
| 1347 | function parse_date( $date_string ) { |
||
| 1348 | $date_string_info = date_parse( $date_string ); |
||
| 1349 | if ( is_array( $date_string_info ) && 0 === $date_string_info['error_count'] ) { |
||
| 1350 | // Check if it's already localized. Can't just check is_localtime because date_parse('oppossum') returns true; WTF, PHP. |
||
| 1351 | if ( isset( $date_string_info['zone'] ) && true === $date_string_info['is_localtime'] ) { |
||
| 1352 | $dt_local = clone $dt_utc = new DateTime( $date_string ); |
||
| 1353 | $dt_utc->setTimezone( new DateTimeZone( 'UTC' ) ); |
||
| 1354 | return array( |
||
| 1355 | (string) $dt_local->format( 'Y-m-d H:i:s' ), |
||
| 1356 | (string) $dt_utc->format( 'Y-m-d H:i:s' ), |
||
| 1357 | ); |
||
| 1358 | } |
||
| 1359 | |||
| 1360 | // It's parseable but no TZ info so assume UTC |
||
| 1361 | $dt_local = clone $dt_utc = new DateTime( $date_string, new DateTimeZone( 'UTC' ) ); |
||
| 1362 | } else { |
||
| 1363 | // Could not parse time, use now in UTC |
||
| 1364 | $dt_local = clone $dt_utc = new DateTime( 'now', new DateTimeZone( 'UTC' ) ); |
||
| 1365 | } |
||
| 1366 | |||
| 1367 | // First try to use timezone as it's daylight savings aware. |
||
| 1368 | $timezone_string = get_option( 'timezone_string' ); |
||
| 1369 | if ( $timezone_string ) { |
||
| 1370 | $tz = timezone_open( $timezone_string ); |
||
| 1371 | if ( $tz ) { |
||
| 1372 | $dt_local->setTimezone( $tz ); |
||
| 1373 | return array( |
||
| 1374 | (string) $dt_local->format( 'Y-m-d H:i:s' ), |
||
| 1375 | (string) $dt_utc->format( 'Y-m-d H:i:s' ), |
||
| 1376 | ); |
||
| 1377 | } |
||
| 1378 | } |
||
| 1379 | |||
| 1380 | // Fallback to GMT offset (in hours) |
||
| 1381 | // NOTE: TZ of $dt_local is still UTC, we simply modified the timestamp with an offset. |
||
| 1382 | $gmt_offset_seconds = intval( get_option( 'gmt_offset' ) * 3600 ); |
||
| 1383 | $dt_local->modify("+{$gmt_offset_seconds} seconds"); |
||
| 1384 | return array( |
||
| 1385 | (string) $dt_local->format( 'Y-m-d H:i:s' ), |
||
| 1386 | (string) $dt_utc->format( 'Y-m-d H:i:s' ), |
||
| 1387 | ); |
||
| 1388 | } |
||
| 1389 | |||
| 1390 | // Load the functions.php file for the current theme to get its post formats, CPTs, etc. |
||
| 1391 | function load_theme_functions() { |
||
| 1460 | |||
| 1461 | function copy_hooks( $from_hook, $to_hook, $base_paths ) { |
||
| 1482 | |||
| 1483 | function get_reflection( $callback ) { |
||
| 1504 | |||
| 1505 | /** |
||
| 1506 | * Check whether a user can view or edit a post type |
||
| 1507 | * @param string $post_type post type to check |
||
| 1508 | * @param string $context 'display' or 'edit' |
||
| 1509 | * @return bool |
||
| 1510 | */ |
||
| 1511 | function current_user_can_access_post_type( $post_type, $context='display' ) { |
||
| 1526 | |||
| 1527 | View Code Duplication | function is_post_type_allowed( $post_type ) { |
|
| 1528 | // if the post type is empty, that's fine, WordPress will default to post |
||
| 1529 | if ( empty( $post_type ) ) |
||
| 1530 | return true; |
||
| 1531 | |||
| 1532 | // allow special 'any' type |
||
| 1533 | if ( 'any' == $post_type ) |
||
| 1534 | return true; |
||
| 1535 | |||
| 1536 | // check for allowed types |
||
| 1537 | if ( in_array( $post_type, $this->_get_whitelisted_post_types() ) ) |
||
| 1538 | return true; |
||
| 1539 | |||
| 1540 | return false; |
||
| 1541 | } |
||
| 1542 | |||
| 1543 | /** |
||
| 1544 | * Gets the whitelisted post types that JP should allow access to. |
||
| 1545 | * |
||
| 1546 | * @return array Whitelisted post types. |
||
| 1547 | */ |
||
| 1548 | View Code Duplication | protected function _get_whitelisted_post_types() { |
|
| 1549 | $allowed_types = array( 'post', 'page', 'revision' ); |
||
| 1550 | |||
| 1551 | /** |
||
| 1552 | * Filter the post types Jetpack has access to, and can synchronize with WordPress.com. |
||
| 1553 | * |
||
| 1554 | * @module json-api |
||
| 1555 | * |
||
| 1556 | * @since 2.2.3 |
||
| 1557 | * |
||
| 1558 | * @param array $allowed_types Array of whitelisted post types. Default to `array( 'post', 'page', 'revision' )`. |
||
| 1559 | */ |
||
| 1560 | $allowed_types = apply_filters( 'rest_api_allowed_post_types', $allowed_types ); |
||
| 1561 | |||
| 1562 | return array_unique( $allowed_types ); |
||
| 1563 | } |
||
| 1564 | |||
| 1565 | function handle_media_creation_v1_1( $media_files, $media_urls, $media_attrs = array(), $force_parent_id = false ) { |
||
| 1566 | |||
| 1567 | add_filter( 'upload_mimes', array( $this, 'allow_video_uploads' ) ); |
||
| 1568 | |||
| 1569 | $media_ids = $errors = array(); |
||
| 1570 | $user_can_upload_files = current_user_can( 'upload_files' ); |
||
| 1571 | $media_attrs = array_values( $media_attrs ); // reset the keys |
||
| 1572 | $i = 0; |
||
| 1573 | |||
| 1574 | if ( ! empty( $media_files ) ) { |
||
| 1575 | $this->api->trap_wp_die( 'upload_error' ); |
||
| 1576 | foreach ( $media_files as $media_item ) { |
||
| 1577 | $_FILES['.api.media.item.'] = $media_item; |
||
| 1578 | View Code Duplication | if ( ! $user_can_upload_files ) { |
|
| 1579 | $media_id = new WP_Error( 'unauthorized', 'User cannot upload media.', 403 ); |
||
| 1580 | } else { |
||
| 1581 | if ( $force_parent_id ) { |
||
| 1582 | $parent_id = absint( $force_parent_id ); |
||
| 1583 | } elseif ( ! empty( $media_attrs[$i] ) && ! empty( $media_attrs[$i]['parent_id'] ) ) { |
||
| 1584 | $parent_id = absint( $media_attrs[$i]['parent_id'] ); |
||
| 1585 | } else { |
||
| 1586 | $parent_id = 0; |
||
| 1587 | } |
||
| 1588 | $media_id = media_handle_upload( '.api.media.item.', $parent_id ); |
||
| 1589 | } |
||
| 1590 | if ( is_wp_error( $media_id ) ) { |
||
| 1591 | $errors[$i]['file'] = $media_item['name']; |
||
| 1592 | $errors[$i]['error'] = $media_id->get_error_code(); |
||
| 1593 | $errors[$i]['message'] = $media_id->get_error_message(); |
||
| 1594 | } else { |
||
| 1595 | $media_ids[$i] = $media_id; |
||
| 1596 | } |
||
| 1597 | |||
| 1598 | $i++; |
||
| 1599 | } |
||
| 1600 | $this->api->trap_wp_die( null ); |
||
| 1601 | unset( $_FILES['.api.media.item.'] ); |
||
| 1602 | } |
||
| 1603 | |||
| 1604 | if ( ! empty( $media_urls ) ) { |
||
| 1605 | foreach ( $media_urls as $url ) { |
||
| 1606 | View Code Duplication | if ( ! $user_can_upload_files ) { |
|
| 1607 | $media_id = new WP_Error( 'unauthorized', 'User cannot upload media.', 403 ); |
||
| 1608 | } else { |
||
| 1609 | if ( $force_parent_id ) { |
||
| 1610 | $parent_id = absint( $force_parent_id ); |
||
| 1611 | } else if ( ! empty( $media_attrs[$i] ) && ! empty( $media_attrs[$i]['parent_id'] ) ) { |
||
| 1612 | $parent_id = absint( $media_attrs[$i]['parent_id'] ); |
||
| 1613 | } else { |
||
| 1614 | $parent_id = 0; |
||
| 1615 | } |
||
| 1616 | $media_id = $this->handle_media_sideload( $url, $parent_id ); |
||
| 1617 | } |
||
| 1618 | if ( is_wp_error( $media_id ) ) { |
||
| 1619 | $errors[$i] = array( |
||
| 1620 | 'file' => $url, |
||
| 1621 | 'error' => $media_id->get_error_code(), |
||
| 1622 | 'message' => $media_id->get_error_message(), |
||
| 1623 | ); |
||
| 1624 | } elseif ( ! empty( $media_id ) ) { |
||
| 1625 | $media_ids[$i] = $media_id; |
||
| 1626 | } |
||
| 1627 | |||
| 1628 | $i++; |
||
| 1629 | } |
||
| 1630 | } |
||
| 1631 | |||
| 1632 | if ( ! empty( $media_attrs ) ) { |
||
| 1633 | foreach ( $media_ids as $index => $media_id ) { |
||
| 1634 | if ( empty( $media_attrs[$index] ) ) |
||
| 1635 | continue; |
||
| 1636 | |||
| 1637 | $attrs = $media_attrs[$index]; |
||
| 1638 | $insert = array(); |
||
| 1639 | |||
| 1640 | // Attributes: Title, Caption, Description |
||
| 1641 | |||
| 1642 | if ( isset( $attrs['title'] ) ) { |
||
| 1643 | $insert['post_title'] = $attrs['title']; |
||
| 1644 | } |
||
| 1645 | |||
| 1646 | if ( isset( $attrs['caption'] ) ) { |
||
| 1647 | $insert['post_excerpt'] = $attrs['caption']; |
||
| 1648 | } |
||
| 1649 | |||
| 1650 | if ( isset( $attrs['description'] ) ) { |
||
| 1651 | $insert['post_content'] = $attrs['description']; |
||
| 1652 | } |
||
| 1653 | |||
| 1654 | if ( ! empty( $insert ) ) { |
||
| 1655 | $insert['ID'] = $media_id; |
||
| 1656 | wp_update_post( (object) $insert ); |
||
| 1657 | } |
||
| 1658 | |||
| 1659 | // Attributes: Alt |
||
| 1660 | |||
| 1661 | View Code Duplication | if ( isset( $attrs['alt'] ) ) { |
|
| 1662 | $alt = wp_strip_all_tags( $attrs['alt'], true ); |
||
| 1663 | update_post_meta( $media_id, '_wp_attachment_image_alt', $alt ); |
||
| 1664 | } |
||
| 1665 | |||
| 1666 | // Attributes: Artist, Album |
||
| 1667 | |||
| 1668 | $id3_meta = array(); |
||
| 1669 | |||
| 1670 | foreach ( array( 'artist', 'album' ) as $key ) { |
||
| 1671 | if ( isset( $attrs[ $key ] ) ) { |
||
| 1672 | $id3_meta[ $key ] = wp_strip_all_tags( $attrs[ $key ], true ); |
||
| 1673 | } |
||
| 1674 | } |
||
| 1675 | |||
| 1676 | if ( ! empty( $id3_meta ) ) { |
||
| 1677 | // Before updating metadata, ensure that the item is audio |
||
| 1678 | $item = $this->get_media_item_v1_1( $media_id ); |
||
| 1679 | if ( 0 === strpos( $item->mime_type, 'audio/' ) ) { |
||
| 1680 | wp_update_attachment_metadata( $media_id, $id3_meta ); |
||
| 1681 | } |
||
| 1682 | } |
||
| 1683 | } |
||
| 1684 | } |
||
| 1685 | |||
| 1686 | return array( 'media_ids' => $media_ids, 'errors' => $errors ); |
||
| 1687 | |||
| 1688 | } |
||
| 1689 | |||
| 1690 | function handle_media_sideload( $url, $parent_post_id = 0, $type = 'any' ) { |
||
| 1691 | if ( ! function_exists( 'download_url' ) || ! function_exists( 'media_handle_sideload' ) ) |
||
| 1692 | return false; |
||
| 1693 | |||
| 1694 | // if we didn't get a URL, let's bail |
||
| 1695 | $parsed = @parse_url( $url ); |
||
| 1696 | if ( empty( $parsed ) ) |
||
| 1697 | return false; |
||
| 1698 | |||
| 1699 | $tmp = download_url( $url ); |
||
| 1700 | if ( is_wp_error( $tmp ) ) { |
||
| 1701 | return $tmp; |
||
| 1702 | } |
||
| 1703 | |||
| 1704 | // First check to see if we get a mime-type match by file, otherwise, check to |
||
| 1705 | // see if WordPress supports this file as an image. If neither, then it is not supported. |
||
| 1706 | if ( ! $this->is_file_supported_for_sideloading( $tmp ) && 'image' === $type && ! file_is_displayable_image( $tmp ) ) { |
||
| 1707 | @unlink( $tmp ); |
||
| 1708 | return false; |
||
| 1709 | } |
||
| 1710 | |||
| 1711 | // emulate a $_FILES entry |
||
| 1712 | $file_array = array( |
||
| 1713 | 'name' => basename( parse_url( $url, PHP_URL_PATH ) ), |
||
| 1714 | 'tmp_name' => $tmp, |
||
| 1715 | ); |
||
| 1716 | |||
| 1717 | $id = media_handle_sideload( $file_array, $parent_post_id ); |
||
| 1718 | @unlink( $tmp ); |
||
| 1719 | |||
| 1720 | if ( is_wp_error( $id ) ) { |
||
| 1721 | return $id; |
||
| 1722 | } |
||
| 1723 | |||
| 1724 | if ( ! $id || ! is_int( $id ) ) { |
||
| 1725 | return false; |
||
| 1726 | } |
||
| 1727 | |||
| 1728 | return $id; |
||
| 1729 | } |
||
| 1730 | |||
| 1731 | /** |
||
| 1732 | * Checks that the mime type of the specified file is among those in a filterable list of mime types. |
||
| 1733 | * |
||
| 1734 | * @param string $file Path to file to get its mime type. |
||
| 1735 | * |
||
| 1736 | * @return bool |
||
| 1737 | */ |
||
| 1738 | protected function is_file_supported_for_sideloading( $file ) { |
||
| 1739 | View Code Duplication | if ( class_exists( 'finfo' ) ) { // php 5.3+ |
|
| 1740 | $finfo = new finfo( FILEINFO_MIME ); |
||
| 1741 | $mime = explode( '; ', $finfo->file( $file ) ); |
||
| 1742 | $type = $mime[0]; |
||
| 1743 | |||
| 1744 | } elseif ( function_exists( 'mime_content_type' ) ) { // PHP 5.2 |
||
| 1745 | $type = mime_content_type( $file ); |
||
| 1746 | |||
| 1747 | } else { |
||
| 1748 | return false; |
||
| 1749 | } |
||
| 1750 | |||
| 1751 | /** |
||
| 1752 | * Filter the list of supported mime types for media sideloading. |
||
| 1753 | * |
||
| 1754 | * @since 4.0.0 |
||
| 1755 | * |
||
| 1756 | * @module json-api |
||
| 1757 | * |
||
| 1758 | * @param array $supported_mime_types Array of the supported mime types for media sideloading. |
||
| 1759 | */ |
||
| 1760 | $supported_mime_types = apply_filters( 'jetpack_supported_media_sideload_types', array( |
||
| 1761 | 'image/png', |
||
| 1762 | 'image/jpeg', |
||
| 1763 | 'image/gif', |
||
| 1764 | 'image/bmp', |
||
| 1765 | 'video/quicktime', |
||
| 1766 | 'video/mp4', |
||
| 1767 | 'video/mpeg', |
||
| 1768 | 'video/ogg', |
||
| 1769 | 'video/3gpp', |
||
| 1770 | 'video/3gpp2', |
||
| 1771 | 'video/h261', |
||
| 1772 | 'video/h262', |
||
| 1773 | 'video/h264', |
||
| 1774 | 'video/x-msvideo', |
||
| 1775 | 'video/x-ms-wmv', |
||
| 1776 | 'video/x-ms-asf', |
||
| 1777 | ) ); |
||
| 1778 | |||
| 1779 | // If the type returned was not an array as expected, then we know we don't have a match. |
||
| 1780 | if ( ! is_array( $supported_mime_types ) ) { |
||
| 1781 | return false; |
||
| 1782 | } |
||
| 1783 | |||
| 1784 | return in_array( $type, $supported_mime_types ); |
||
| 1785 | } |
||
| 1786 | |||
| 1787 | function allow_video_uploads( $mimes ) { |
||
| 1847 | |||
| 1848 | function is_current_site_multi_user() { |
||
| 1860 | |||
| 1861 | function allows_cross_origin_requests() { |
||
| 1864 | |||
| 1865 | function allows_unauthorized_requests( $origin, $complete_access_origins ) { |
||
| 1868 | |||
| 1869 | function get_platform() { |
||
| 1870 | return wpcom_get_sal_platform( $this->api->token_details ); |
||
| 1871 | } |
||
| 1872 | |||
| 1873 | /** |
||
| 1874 | * Return endpoint response |
||
| 1875 | * |
||
| 1876 | * @param ... determined by ->$path |
||
| 1877 | * |
||
| 1878 | * @return |
||
| 1879 | * falsy: HTTP 500, no response body |
||
| 1880 | * WP_Error( $error_code, $error_message, $http_status_code ): HTTP $status_code, json_encode( array( 'error' => $error_code, 'message' => $error_message ) ) response body |
||
| 1881 | * $data: HTTP 200, json_encode( $data ) response body |
||
| 1882 | */ |
||
| 1883 | abstract function callback( $path = '' ); |
||
| 1884 | |||
| 1885 | |||
| 1886 | } |
||
| 1887 | |||
| 1888 | require_once( dirname( __FILE__ ) . '/json-endpoints.php' ); |
||
| 1889 |
This check looks for improperly formatted assignments.
Every assignment must have exactly one space before and one space after the equals operator.
To illustrate:
will have no issues, while
will report issues in lines 1 and 2.