function write_post($path, $blog_id, $post_id) { $new = $this->api->ends_with($path, '/new'); $args = $this->query_args(); // unhook publicize, it's hooked again later -- without this, skipping services is impossible if (defined('IS_WPCOM') && IS_WPCOM) { remove_action('save_post', array($GLOBALS['publicize_ui']->publicize, 'async_publicize_post'), 100, 2); add_action('rest_api_inserted_post', array($GLOBALS['publicize_ui']->publicize, 'async_publicize_post')); } if ($new) { $input = $this->input(true); // 'future' is an alias for 'publish' for now if (isset($input['status']) && 'future' === $input['status']) { $input['status'] = 'publish'; } if ('revision' === $input['type']) { if (!isset($input['parent'])) { return new WP_Error('invalid_input', 'Invalid request input', 400); } $input['status'] = 'inherit'; // force inherit for revision type $input['slug'] = $input['parent'] . '-autosave-v1'; } elseif (!isset($input['title']) && !isset($input['content']) && !isset($input['excerpt'])) { return new WP_Error('invalid_input', 'Invalid request input', 400); } // default to post if (empty($input['type'])) { $input['type'] = 'post'; } $post_type = get_post_type_object($input['type']); if (!$this->is_post_type_allowed($input['type'])) { return new WP_Error('unknown_post_type', 'Unknown post type', 404); } if (!empty($input['author'])) { $author_id = parent::parse_and_set_author($input['author'], $input['type']); unset($input['author']); if (is_wp_error($author_id)) { return $author_id; } } if ('publish' === $input['status']) { if (!current_user_can($post_type->cap->publish_posts)) { if (current_user_can($post_type->cap->edit_posts)) { $input['status'] = 'pending'; } else { return new WP_Error('unauthorized', 'User cannot publish posts', 403); } } } else { if (!current_user_can($post_type->cap->edit_posts)) { return new WP_Error('unauthorized', 'User cannot edit posts', 403); } } } else { $input = $this->input(false); if (!is_array($input) || !$input) { return new WP_Error('invalid_input', 'Invalid request input', 400); } // 'future' is an alias for 'publish' for now if (isset($input['status']) && 'future' === $input['status']) { $input['status'] = 'publish'; } $post = get_post($post_id); $_post_type = !empty($input['type']) ? $input['type'] : $post->post_type; $post_type = get_post_type_object($_post_type); if (!$post || is_wp_error($post)) { return new WP_Error('unknown_post', 'Unknown post', 404); } if (!current_user_can('edit_post', $post->ID)) { return new WP_Error('unauthorized', 'User cannot edit post', 403); } if (!empty($input['author'])) { $author_id = parent::parse_and_set_author($input['author'], $_post_type); unset($input['author']); if (is_wp_error($author_id)) { return $author_id; } } if (isset($input['status']) && 'publish' === $input['status'] && 'publish' !== $post->post_status && !current_user_can('publish_post', $post->ID)) { $input['status'] = 'pending'; } $last_status = $post->post_status; $new_status = isset($input['status']) ? $input['status'] : $last_status; // Make sure that drafts get the current date when transitioning to publish if not supplied in the post. $date_in_past = strtotime($post->post_date_gmt) < time(); if ('publish' === $new_status && 'draft' === $last_status && !isset($input['date_gmt']) && $date_in_past) { $input['date_gmt'] = gmdate('Y-m-d H:i:s'); } } if (function_exists('wpcom_switch_to_locale')) { // fixes calypso-pre-oss #12476: respect blog locale when creating the post slug wpcom_switch_to_locale(get_blog_lang_code($blog_id)); } // If date is set, $this->input will set date_gmt, date still needs to be adjusted f if (isset($input['date_gmt'])) { $gmt_offset = get_option('gmt_offset'); $time_with_offset = strtotime($input['date_gmt']) + $gmt_offset * HOUR_IN_SECONDS; $input['date'] = date('Y-m-d H:i:s', $time_with_offset); } if (!empty($author_id) && get_current_user_id() != $author_id) { if (!current_user_can($post_type->cap->edit_others_posts)) { return new WP_Error('unauthorized', "User is not allowed to publish others' posts.", 403); } elseif (!user_can($author_id, $post_type->cap->edit_posts)) { return new WP_Error('unauthorized', 'Assigned author cannot publish post.', 403); } } if (!is_post_type_hierarchical($post_type->name) && 'revision' !== $post_type->name) { unset($input['parent']); } /* add taxonomies by name */ $tax_input = array(); foreach (array('categories' => 'category', 'tags' => 'post_tag') as $key => $taxonomy) { if (!isset($input[$key])) { continue; } $tax_input[$taxonomy] = array(); $is_hierarchical = is_taxonomy_hierarchical($taxonomy); if (is_array($input[$key])) { $terms = $input[$key]; } else { $terms = explode(',', $input[$key]); } foreach ($terms as $term) { /** * We assume these are names, not IDs, even if they are numeric. * Note: A category named "0" will not work right. * https://core.trac.wordpress.org/ticket/9059 */ $term_info = get_term_by('name', $term, $taxonomy, ARRAY_A); if (!$term_info) { // only add a new tag/cat if the user has access to $tax = get_taxonomy($taxonomy); // see https://core.trac.wordpress.org/ticket/26409 if ('category' === $taxonomy && !current_user_can($tax->cap->edit_terms)) { continue; } else { if (!current_user_can($tax->cap->assign_terms)) { continue; } } $term_info = wp_insert_term($term, $taxonomy); } if (!is_wp_error($term_info)) { if ($is_hierarchical) { // Categories must be added by ID $tax_input[$taxonomy][] = (int) $term_info['term_id']; } else { // Tags must be added by name $tax_input[$taxonomy][] = $term; } } } } /* add taxonomies by ID */ foreach (array('categories_by_id' => 'category', 'tags_by_id' => 'post_tag') as $key => $taxonomy) { if (!isset($input[$key])) { continue; } // combine with any previous selections if (!isset($tax_input[$taxonomy]) || !is_array($tax_input[$taxonomy])) { $tax_input[$taxonomy] = array(); } $is_hierarchical = is_taxonomy_hierarchical($taxonomy); if (is_array($input[$key])) { $terms = $input[$key]; } else { $terms = explode(',', $input[$key]); } foreach ($terms as $term) { $term = (string) $term; // ctype_digit compat if (!ctype_digit($term)) { // skip anything that doesn't look like an ID continue; } $term = (int) $term; $term_info = get_term_by('id', $term, $taxonomy, ARRAY_A); if ($term_info && !is_wp_error($term_info)) { if ($is_hierarchical) { // Categories must be added by ID $tax_input[$taxonomy][] = $term; } else { // Tags must be added by name $tax_input[$taxonomy][] = $term_info['name']; } } } } if ((isset($input['categories']) || isset($input['categories_by_id'])) && empty($tax_input['category']) && 'revision' !== $post_type->name) { $tax_input['category'][] = get_option('default_category'); } unset($input['tags'], $input['categories'], $input['tags_by_id'], $input['categories_by_id']); $insert = array(); if (!empty($input['slug'])) { $insert['post_name'] = $input['slug']; unset($input['slug']); } if (isset($input['discussion'])) { $discussion = (array) $input['discussion']; foreach (array('comment', 'ping') as $discussion_type) { $discussion_open = sprintf('%ss_open', $discussion_type); $discussion_status = sprintf('%s_status', $discussion_type); if (isset($discussion[$discussion_open])) { $is_open = WPCOM_JSON_API::is_truthy($discussion[$discussion_open]); $discussion[$discussion_status] = $is_open ? 'open' : 'closed'; } if (in_array($discussion[$discussion_status], array('open', 'closed'))) { $insert[$discussion_status] = $discussion[$discussion_status]; } } } unset($input['discussion']); if (isset($input['menu_order'])) { $insert['menu_order'] = $input['menu_order']; unset($input['menu_order']); } $publicize = isset($input['publicize']) ? $input['publicize'] : null; unset($input['publicize']); $publicize_custom_message = isset($input['publicize_message']) ? $input['publicize_message'] : null; unset($input['publicize_message']); if (isset($input['featured_image'])) { $featured_image = trim($input['featured_image']); $delete_featured_image = empty($featured_image); unset($input['featured_image']); } $metadata = isset($input['metadata']) ? $input['metadata'] : null; unset($input['metadata']); $likes = isset($input['likes_enabled']) ? $input['likes_enabled'] : null; unset($input['likes_enabled']); $sharing = isset($input['sharing_enabled']) ? $input['sharing_enabled'] : null; unset($input['sharing_enabled']); $sticky = isset($input['sticky']) ? $input['sticky'] : null; unset($input['sticky']); foreach ($input as $key => $value) { $insert["post_{$key}"] = $value; } if (!empty($author_id)) { $insert['post_author'] = absint($author_id); } if (!empty($tax_input)) { $insert['tax_input'] = $tax_input; } $has_media = !empty($input['media']) ? count($input['media']) : false; $has_media_by_url = !empty($input['media_urls']) ? count($input['media_urls']) : false; if ($new) { if (isset($input['content']) && !has_shortcode($input['content'], 'gallery') && ($has_media || $has_media_by_url)) { switch ($has_media + $has_media_by_url) { case 0: // No images - do nothing. break; case 1: // 1 image - make it big $insert['post_content'] = $input['content'] = "[gallery size=full columns=1]\n\n" . $input['content']; break; default: // Several images - 3 column gallery $insert['post_content'] = $input['content'] = "[gallery]\n\n" . $input['content']; break; } } $post_id = wp_insert_post(add_magic_quotes($insert), true); } else { $insert['ID'] = $post->ID; // wp_update_post ignores date unless edit_date is set // See: http://codex.wordpress.org/Function_Reference/wp_update_post#Scheduling_posts // See: https://core.trac.wordpress.org/browser/tags/3.9.2/src/wp-includes/post.php#L3302 if (isset($input['date_gmt']) || isset($input['date'])) { $insert['edit_date'] = true; } $post_id = wp_update_post((object) $insert); } if (!$post_id || is_wp_error($post_id)) { return $post_id; } // make sure this post actually exists and is not an error of some kind (ie, trying to load media in the posts endpoint) $post_check = $this->get_post_by('ID', $post_id, $args['context']); if (is_wp_error($post_check)) { return $post_check; } if ($has_media || $has_media_by_url) { $media_files = !empty($input['media']) ? $input['media'] : array(); $media_urls = !empty($input['media_urls']) ? $input['media_urls'] : array(); $media_attrs = !empty($input['media_attrs']) ? $input['media_attrs'] : array(); $force_parent_id = $post_id; $media_results = $this->handle_media_creation_v1_1($media_files, $media_urls, $media_attrs, $force_parent_id); } // set page template for this post.. if (isset($input['page_template']) && 'page' == $post_type->name) { $page_template = $input['page_template']; $page_templates = wp_get_theme()->get_page_templates(get_post($post_id)); if (empty($page_template) || 'default' == $page_template || isset($page_templates[$page_template])) { update_post_meta($post_id, '_wp_page_template', $page_template); } } // Set like status for the post /** This filter is documented in modules/likes.php */ $sitewide_likes_enabled = (bool) apply_filters('wpl_is_enabled_sitewide', !get_option('disabled_likes')); if ($new) { if ($sitewide_likes_enabled) { if (false === $likes) { update_post_meta($post_id, 'switch_like_status', 1); } else { delete_post_meta($post_id, 'switch_like_status'); } } else { if ($likes) { update_post_meta($post_id, 'switch_like_status', 1); } else { delete_post_meta($post_id, 'switch_like_status'); } } } else { if (isset($likes)) { if ($sitewide_likes_enabled) { if (false === $likes) { update_post_meta($post_id, 'switch_like_status', 1); } else { delete_post_meta($post_id, 'switch_like_status'); } } else { if (true === $likes) { update_post_meta($post_id, 'switch_like_status', 1); } else { delete_post_meta($post_id, 'switch_like_status'); } } } } // Set sharing status of the post if ($new) { $sharing_enabled = isset($sharing) ? (bool) $sharing : true; if (false === $sharing_enabled) { update_post_meta($post_id, 'sharing_disabled', 1); } } else { if (isset($sharing) && true === $sharing) { delete_post_meta($post_id, 'sharing_disabled'); } else { if (isset($sharing) && false == $sharing) { update_post_meta($post_id, 'sharing_disabled', 1); } } } if (isset($sticky)) { if (true === $sticky) { stick_post($post_id); } else { unstick_post($post_id); } } // WPCOM Specific (Jetpack's will get bumped elsewhere // Tracks how many posts are published and sets meta // so we can track some other cool stats (like likes & comments on posts published) if (defined('IS_WPCOM') && IS_WPCOM) { if ($new && 'publish' == $input['status'] || !$new && isset($last_status) && 'publish' != $last_status && isset($new_status) && 'publish' == $new_status) { /** This action is documented in modules/widgets/social-media-icons.php */ do_action('jetpack_bump_stats_extras', 'api-insights-posts', $this->api->token_details['client_id']); update_post_meta($post_id, '_rest_api_published', 1); update_post_meta($post_id, '_rest_api_client_id', $this->api->token_details['client_id']); } } // We ask the user/dev to pass Publicize services he/she wants activated for the post, but Publicize expects us // to instead flag the ones we don't want to be skipped. proceed with said logic. // any posts coming from Path (client ID 25952) should also not publicize if ($publicize === false || isset($this->api->token_details['client_id']) && 25952 == $this->api->token_details['client_id']) { // No publicize at all, skip all by ID foreach ($GLOBALS['publicize_ui']->publicize->get_services('all') as $name => $service) { delete_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $name); $service_connections = $GLOBALS['publicize_ui']->publicize->get_connections($name); if (!$service_connections) { continue; } foreach ($service_connections as $service_connection) { update_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id, 1); } } } else { if (is_array($publicize) && count($publicize) > 0) { foreach ($GLOBALS['publicize_ui']->publicize->get_services('all') as $name => $service) { /* * We support both indexed and associative arrays: * * indexed are to pass entire services * * associative are to pass specific connections per service * * We do support mixed arrays: mixed integer and string keys (see 3rd example below). * * EG: array( 'twitter', 'facebook') will only publicize to those, ignoring the other available services * Form data: publicize[]=twitter&publicize[]=facebook * EG: array( 'twitter' => '(int) $pub_conn_id_0, (int) $pub_conn_id_3', 'facebook' => (int) $pub_conn_id_7 ) will publicize to two Twitter accounts, and one Facebook connection, of potentially many. * Form data: publicize[twitter]=$pub_conn_id_0,$pub_conn_id_3&publicize[facebook]=$pub_conn_id_7 * EG: array( 'twitter', 'facebook' => '(int) $pub_conn_id_0, (int) $pub_conn_id_3' ) will publicize to all available Twitter accounts, but only 2 of potentially many Facebook connections * Form data: publicize[]=twitter&publicize[facebook]=$pub_conn_id_0,$pub_conn_id_3 */ // Delete any stale SKIP value for the service by name. We'll add it back by ID. delete_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $name); // Get the user's connections $service_connections = $GLOBALS['publicize_ui']->publicize->get_connections($name); // if the user doesn't have any connections for this service, move on if (!$service_connections) { continue; } if (!in_array($name, $publicize) && !array_key_exists($name, $publicize)) { // Skip the whole service by adding each connection ID foreach ($service_connections as $service_connection) { update_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id, 1); } } else { if (!empty($publicize[$name])) { // Seems we're being asked to only push to [a] specific connection[s]. // Explode the list on commas, which will also support a single passed ID $requested_connections = explode(',', preg_replace('/[\\s]*/', '', $publicize[$name])); // Flag the connections we can't match with the requested list to be skipped. foreach ($service_connections as $service_connection) { if (!in_array($service_connection->meta['connection_data']->id, $requested_connections)) { update_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id, 1); } else { delete_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id); } } } else { // delete all SKIP values; it's okay to publish to all connected IDs for this service foreach ($service_connections as $service_connection) { delete_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id); } } } } } } if (!is_null($publicize_custom_message)) { if (empty($publicize_custom_message)) { delete_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS); } else { update_post_meta($post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS, trim($publicize_custom_message)); } } if (!empty($insert['post_format'])) { if ('default' !== strtolower($insert['post_format'])) { set_post_format($post_id, $insert['post_format']); } else { set_post_format($post_id, get_option('default_post_format')); } } if (isset($featured_image)) { parent::parse_and_set_featured_image($post_id, $delete_featured_image, $featured_image); } if (!empty($metadata)) { foreach ((array) $metadata as $meta) { $meta = (object) $meta; $existing_meta_item = new stdClass(); if (empty($meta->operation)) { $meta->operation = 'update'; } if (!empty($meta->value)) { if ('true' == $meta->value) { $meta->value = true; } if ('false' == $meta->value) { $meta->value = false; } } if (!empty($meta->id)) { $meta->id = absint($meta->id); $existing_meta_item = get_metadata_by_mid('post', $meta->id); } $unslashed_meta_key = wp_unslash($meta->key); // should match what the final key will be $meta->key = wp_slash($meta->key); $unslashed_existing_meta_key = wp_unslash($existing_meta_item->meta_key); $existing_meta_item->meta_key = wp_slash($existing_meta_item->meta_key); // make sure that the meta id passed matches the existing meta key if (!empty($meta->id) && !empty($meta->key)) { $meta_by_id = get_metadata_by_mid('post', $meta->id); if ($meta_by_id->meta_key !== $meta->key) { continue; // skip this meta } } switch ($meta->operation) { case 'delete': if (!empty($meta->id) && !empty($existing_meta_item->meta_key) && current_user_can('delete_post_meta', $post_id, $unslashed_existing_meta_key)) { delete_metadata_by_mid('post', $meta->id); } elseif (!empty($meta->key) && !empty($meta->previous_value) && current_user_can('delete_post_meta', $post_id, $unslashed_meta_key)) { delete_post_meta($post_id, $meta->key, $meta->previous_value); } elseif (!empty($meta->key) && current_user_can('delete_post_meta', $post_id, $unslashed_meta_key)) { delete_post_meta($post_id, $meta->key); } break; case 'add': if (!empty($meta->id) || !empty($meta->previous_value)) { continue; } elseif (!empty($meta->key) && !empty($meta->value) && current_user_can('add_post_meta', $post_id, $unslashed_meta_key) || WPCOM_JSON_API_Metadata::is_public($meta->key)) { add_post_meta($post_id, $meta->key, $meta->value); } break; case 'update': if (!isset($meta->value)) { continue; } elseif (!empty($meta->id) && !empty($existing_meta_item->meta_key) && (current_user_can('edit_post_meta', $post_id, $unslashed_existing_meta_key) || WPCOM_JSON_API_Metadata::is_public($meta->key))) { update_metadata_by_mid('post', $meta->id, $meta->value); } elseif (!empty($meta->key) && !empty($meta->previous_value) && (current_user_can('edit_post_meta', $post_id, $unslashed_meta_key) || WPCOM_JSON_API_Metadata::is_public($meta->key))) { update_post_meta($post_id, $meta->key, $meta->value, $meta->previous_value); } elseif (!empty($meta->key) && (current_user_can('edit_post_meta', $post_id, $unslashed_meta_key) || WPCOM_JSON_API_Metadata::is_public($meta->key))) { update_post_meta($post_id, $meta->key, $meta->value); } break; } } } /** This action is documented in json-endpoints/class.wpcom-json-api-update-post-endpoint.php */ do_action('rest_api_inserted_post', $post_id, $insert, $new); $return = $this->get_post_by('ID', $post_id, $args['context']); if (!$return || is_wp_error($return)) { return $return; } if (isset($input['type']) && 'revision' === $input['type']) { $return['preview_nonce'] = wp_create_nonce('post_preview_' . $input['parent']); } if (isset($sticky)) { // workaround for sticky test occasionally failing, maybe a race condition with stick_post() above $return['sticky'] = true === $sticky; } if (!empty($media_results['errors'])) { $return['media_errors'] = $media_results['errors']; } if ('publish' !== $post->post_status && isset($input['title'])) { $sal_site = $this->get_sal_post_by('ID', $post_id, $args['context']); $return['other_URLs'] = (object) $sal_site->get_permalink_suggestions($input['title']); } /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */ do_action('wpcom_json_api_objects', 'posts'); return $return; }
public function get_metadata() { $metadata = array(); foreach ((array) has_meta($this->post->ID) as $meta) { // Don't expose protected fields. $meta_key = $meta['meta_key']; $show = !WPCOM_JSON_API_Metadata::is_internal_only($meta_key) && (WPCOM_JSON_API_Metadata::is_public($meta_key) || current_user_can('edit_post_meta', $this->post->ID, $meta_key)); if ($show) { $metadata[] = array('id' => $meta['meta_id'], 'key' => $meta['meta_key'], 'value' => maybe_unserialize($meta['meta_value'])); } } if (!empty($metadata)) { return $metadata; } else { return false; } }
/** * Get a post by a specified field and value * * @param string $field * @param string $field_value * @param string $context Post use context (e.g. 'display') * @return array Post **/ function get_post_by($field, $field_value, $context = 'display') { global $blog_id; /** This filter is documented in class.json-api-endpoints.php */ $is_jetpack = true === apply_filters('is_jetpack_site', false, $blog_id); if (defined('GEO_LOCATION__CLASS') && class_exists(GEO_LOCATION__CLASS)) { $geo = call_user_func(array(GEO_LOCATION__CLASS, 'init')); } else { $geo = false; } if ('display' === $context) { $args = $this->query_args(); if (isset($args['content_width']) && $args['content_width']) { $GLOBALS['content_width'] = (int) $args['content_width']; } } if (strpos($_SERVER['HTTP_USER_AGENT'], 'wp-windows8')) { remove_shortcode('gallery', 'gallery_shortcode'); add_shortcode('gallery', array(&$this, 'win8_gallery_shortcode')); } switch ($field) { case 'name': $post_id = $this->get_post_id_by_name($field_value); if (is_wp_error($post_id)) { return $post_id; } break; default: $post_id = (int) $field_value; break; } $post = get_post($post_id, OBJECT, $context); if (!$post || is_wp_error($post)) { return new WP_Error('unknown_post', 'Unknown post', 404); } if (!$this->is_post_type_allowed($post->post_type) && (!function_exists('is_post_freshly_pressed') || !is_post_freshly_pressed($post->ID))) { return new WP_Error('unknown_post', 'Unknown post', 404); } // Permissions $capabilities = $this->get_current_user_capabilities($post); switch ($context) { case 'edit': if (!$capabilities['edit_post']) { return new WP_Error('unauthorized', 'User cannot edit post', 403); } break; case 'display': break; default: return new WP_Error('invalid_context', 'Invalid API CONTEXT', 400); } $can_view = $this->user_can_view_post($post->ID); if (!$can_view || is_wp_error($can_view)) { return $can_view; } $GLOBALS['post'] = $post; if ('display' === $context) { setup_postdata($post); } $response = array(); $fields = null; if ('display' === $context && !empty($this->api->query['fields'])) { $fields = array_fill_keys(array_map('trim', explode(',', $this->api->query['fields'])), true); } foreach (array_keys($this->post_object_format) as $key) { if ($fields !== null && !isset($fields[$key])) { continue; } switch ($key) { case 'ID': // explicitly cast all output $response[$key] = (int) $post->ID; break; case 'site_ID': $response[$key] = (int) $this->api->get_blog_id_for_output(); break; case 'author': $response[$key] = (object) $this->get_author($post, 'edit' === $context && $capabilities['edit_post']); break; case 'date': $response[$key] = (string) $this->format_date($post->post_date_gmt, $post->post_date); break; case 'modified': $response[$key] = (string) $this->format_date($post->post_modified_gmt, $post->post_modified); break; case 'title': if ('display' === $context) { $response[$key] = (string) get_the_title($post->ID); } else { $response[$key] = (string) htmlspecialchars_decode($post->post_title, ENT_QUOTES); } break; case 'URL': if ('revision' === $post->post_type) { $response[$key] = (string) esc_url_raw(get_permalink($post->post_parent)); } else { $response[$key] = (string) esc_url_raw(get_permalink($post->ID)); } break; case 'short_URL': $response[$key] = (string) esc_url_raw(wp_get_shortlink($post->ID)); break; case 'content': if ('display' === $context) { add_filter('the_password_form', array($this, 'the_password_form')); $response[$key] = (string) $this->get_the_post_content_for_display(); remove_filter('the_password_form', array($this, 'the_password_form')); } else { $response[$key] = (string) $post->post_content; } break; case 'excerpt': if ('display' === $context) { add_filter('the_password_form', array($this, 'the_password_form')); ob_start(); the_excerpt(); $response[$key] = (string) ob_get_clean(); remove_filter('the_password_form', array($this, 'the_password_form')); } else { $response[$key] = htmlspecialchars_decode((string) $post->post_excerpt, ENT_QUOTES); } break; case 'status': $response[$key] = (string) get_post_status($post->ID); break; case 'sticky': $response[$key] = (bool) is_sticky($post->ID); break; case 'slug': $response[$key] = (string) $post->post_name; break; case 'guid': $response[$key] = (string) $post->guid; break; case 'password': $response[$key] = (string) $post->post_password; if ('edit' === $context) { $response[$key] = htmlspecialchars_decode((string) $response[$key], ENT_QUOTES); } break; case 'parent': // (object|false) if ($post->post_parent) { $parent = get_post($post->post_parent); if ('display' === $context) { $parent_title = (string) get_the_title($parent->ID); } else { $parent_title = (string) htmlspecialchars_decode($post->post_title, ENT_QUOTES); } $response[$key] = (object) array('ID' => (int) $parent->ID, 'type' => (string) $parent->post_type, 'link' => (string) $this->links->get_post_link($this->api->get_blog_id_for_output(), $parent->ID), 'title' => $parent_title); } else { $response[$key] = false; } break; case 'type': $response[$key] = (string) $post->post_type; break; case 'comments_open': $response[$key] = (bool) comments_open($post->ID); break; case 'pings_open': $response[$key] = (bool) pings_open($post->ID); break; case 'likes_enabled': /** This filter is documented in modules/likes.php */ $sitewide_likes_enabled = (bool) apply_filters('wpl_is_enabled_sitewide', !get_option('disabled_likes')); $post_likes_switched = (bool) get_post_meta($post->ID, 'switch_like_status', true); $post_likes_enabled = $sitewide_likes_enabled; if ($post_likes_switched) { $post_likes_enabled = !$post_likes_enabled; } $response[$key] = (bool) $post_likes_enabled; break; case 'sharing_enabled': $show = true; /** This filter is documented in modules/sharedaddy/sharing-service.php */ $show = apply_filters('sharing_show', $show, $post); $switched_status = get_post_meta($post->ID, 'sharing_disabled', false); if (!empty($switched_status)) { $show = false; } $response[$key] = (bool) $show; break; case 'comment_count': $response[$key] = (int) $post->comment_count; break; case 'like_count': $response[$key] = (int) $this->api->post_like_count($blog_id, $post->ID); break; case 'i_like': $response[$key] = (bool) $this->api->is_liked($blog_id, $post->ID); break; case 'is_reblogged': $response[$key] = (bool) $this->api->is_reblogged($blog_id, $post->ID); break; case 'is_following': $response[$key] = (bool) $this->api->is_following($blog_id); break; case 'global_ID': $response[$key] = (string) $this->api->add_global_ID($blog_id, $post->ID); break; case 'featured_image': if ($is_jetpack && (defined('IS_WPCOM') && IS_WPCOM)) { $response[$key] = get_post_meta($post->ID, '_jetpack_featured_image', true); } else { $image_attributes = wp_get_attachment_image_src(get_post_thumbnail_id($post->ID), 'full'); if (is_array($image_attributes) && isset($image_attributes[0])) { $response[$key] = (string) $image_attributes[0]; } else { $response[$key] = ''; } } break; case 'post_thumbnail': $response[$key] = null; $thumb_id = get_post_thumbnail_id($post->ID); if (!empty($thumb_id)) { $attachment = get_post($thumb_id); if (!empty($attachment)) { $featured_image_object = $this->get_attachment($attachment); } if (!empty($featured_image_object)) { $response[$key] = (object) $featured_image_object; } } break; case 'format': $response[$key] = (string) get_post_format($post->ID); if (!$response[$key]) { $response[$key] = 'standard'; } break; case 'geo': // (object|false) if (!$geo) { $response[$key] = false; } else { $geo_data = $geo->get_geo('post', $post->ID); $response[$key] = false; if ($geo_data) { $geo_data = array_intersect_key($geo_data, array('latitude' => true, 'longitude' => true, 'address' => true, 'public' => true)); if ($geo_data) { $response[$key] = (object) array('latitude' => isset($geo_data['latitude']) ? (double) $geo_data['latitude'] : 0, 'longitude' => isset($geo_data['longitude']) ? (double) $geo_data['longitude'] : 0, 'address' => isset($geo_data['address']) ? (string) $geo_data['address'] : ''); } else { $response[$key] = false; } // Private if (!isset($geo_data['public']) || !$geo_data['public']) { if ('edit' !== $context || !$capabilities['edit_post']) { // user can't access $response[$key] = false; } } } } break; case 'menu_order': $response[$key] = (int) $post->menu_order; break; case 'publicize_URLs': $publicize_URLs = array(); $publicize = get_post_meta($post->ID, 'publicize_results', true); if ($publicize) { foreach ($publicize as $service => $data) { switch ($service) { case 'twitter': foreach ($data as $datum) { $publicize_URLs[] = esc_url_raw("https://twitter.com/{$datum['user_id']}/status/{$datum['post_id']}"); } break; case 'fb': foreach ($data as $datum) { $publicize_URLs[] = esc_url_raw("https://www.facebook.com/permalink.php?story_fbid={$datum['post_id']}&id={$datum['user_id']}"); } break; } } } $response[$key] = (array) $publicize_URLs; break; case 'tags': $response[$key] = array(); $terms = wp_get_post_tags($post->ID); foreach ($terms as $term) { if (!empty($term->name)) { $response[$key][$term->name] = $this->format_taxonomy($term, 'post_tag', 'display'); } } $response[$key] = (object) $response[$key]; break; case 'categories': $response[$key] = array(); $terms = wp_get_object_terms($post->ID, 'category', array('fields' => 'all')); foreach ($terms as $term) { if (!empty($term->name)) { $response[$key][$term->name] = $this->format_taxonomy($term, 'category', 'display'); } } $response[$key] = (object) $response[$key]; break; case 'attachments': $response[$key] = array(); $_attachments = get_posts(array('post_parent' => $post->ID, 'post_status' => 'inherit', 'post_type' => 'attachment', 'posts_per_page' => 100)); foreach ($_attachments as $attachment) { $response[$key][$attachment->ID] = $this->get_attachment($attachment); } $response[$key] = (object) $response[$key]; break; case 'metadata': // (array|false) $metadata = array(); foreach ((array) has_meta($post_id) as $meta) { // Don't expose protected fields. $show = false; if (WPCOM_JSON_API_Metadata::is_public($meta['meta_key'])) { $show = true; } if (current_user_can('edit_post_meta', $post_id, $meta['meta_key'])) { $show = true; } if (!$show) { continue; } $metadata[] = array('id' => $meta['meta_id'], 'key' => $meta['meta_key'], 'value' => maybe_unserialize($meta['meta_value'])); } if (!empty($metadata)) { $response[$key] = $metadata; } else { $response[$key] = false; } break; case 'meta': $response[$key] = (object) array('links' => (object) array('self' => (string) $this->links->get_post_link($this->api->get_blog_id_for_output(), $post->ID), 'help' => (string) $this->links->get_post_link($this->api->get_blog_id_for_output(), $post->ID, 'help'), 'site' => (string) $this->links->get_site_link($this->api->get_blog_id_for_output()), 'replies' => (string) $this->links->get_post_link($this->api->get_blog_id_for_output(), $post->ID, 'replies/'), 'likes' => (string) $this->links->get_post_link($this->api->get_blog_id_for_output(), $post->ID, 'likes/'))); break; case 'current_user_can': $response[$key] = $capabilities; break; case 'capabilities': $response[$key] = $capabilities; break; } } // WPCOM_JSON_API_Post_Endpoint::find_featured_worthy_media( $post ); // $response['featured_media'] = self::find_featured_media( $response ); unset($GLOBALS['post']); return $response; }
function callback($path = '', $blog_id = 0) { $blog_id = $this->api->switch_to_blog_and_validate_user($this->api->get_blog_id($blog_id)); if (is_wp_error($blog_id)) { return $blog_id; } $args = $this->query_args(); $is_eligible_for_page_handle = true; if ($args['number'] < 1) { $args['number'] = 20; } elseif (100 < $args['number']) { return new WP_Error('invalid_number', 'The NUMBER parameter must be less than or equal to 100.', 400); } if (isset($args['type']) && !$this->is_post_type_allowed($args['type'])) { return new WP_Error('unknown_post_type', 'Unknown post type', 404); } // Normalize post_type if (isset($args['type']) && 'any' == $args['type']) { if (version_compare($this->api->version, '1.1', '<')) { $args['type'] = array('post', 'page'); } else { // 1.1+ $args['type'] = $this->_get_whitelisted_post_types(); } } // determine statuses $status = !empty($args['status']) ? explode(',', $args['status']) : array('publish'); if (is_user_logged_in()) { $statuses_whitelist = array('publish', 'pending', 'draft', 'future', 'private', 'trash', 'any'); $status = array_intersect($status, $statuses_whitelist); } else { // logged-out users can see only published posts $statuses_whitelist = array('publish', 'any'); $status = array_intersect($status, $statuses_whitelist); if (empty($status)) { // requested only protected statuses? nothing for you here return array('found' => 0, 'posts' => array()); } // clear it (AKA published only) because "any" includes protected $status = array(); } if (isset($args['type']) && !in_array($args['type'], array('post', 'page', 'revision', 'any')) && defined('IS_WPCOM') && IS_WPCOM) { $this->load_theme_functions(); } // let's be explicit about defaulting to 'post' $args['type'] = isset($args['type']) ? $args['type'] : 'post'; // make sure the user can read or edit the requested post type(s) if (is_array($args['type'])) { $allowed_types = array(); foreach ($args['type'] as $post_type) { if ($this->current_user_can_access_post_type($post_type, $args['context'])) { $allowed_types[] = $post_type; } } if (empty($allowed_types)) { return array('found' => 0, 'posts' => array()); } $args['type'] = $allowed_types; } else { if (!$this->current_user_can_access_post_type($args['type'], $args['context'])) { return array('found' => 0, 'posts' => array()); } } $query = array('posts_per_page' => $args['number'], 'order' => $args['order'], 'orderby' => $args['order_by'], 'post_type' => $args['type'], 'post_status' => $status, 'post_parent' => isset($args['parent_id']) ? $args['parent_id'] : null, 'author' => isset($args['author']) && 0 < $args['author'] ? $args['author'] : null, 's' => isset($args['search']) ? $args['search'] : null, 'fields' => 'ids'); if (!is_user_logged_in()) { $query['has_password'] = false; } if (isset($args['meta_key'])) { $show = false; if (WPCOM_JSON_API_Metadata::is_public($args['meta_key'])) { $show = true; } if (current_user_can('edit_post_meta', $query['post_type'], $args['meta_key'])) { $show = true; } if (is_protected_meta($args['meta_key'], 'post') && !$show) { return new WP_Error('invalid_meta_key', 'Invalid meta key', 404); } $meta = array('key' => $args['meta_key']); if (isset($args['meta_value'])) { $meta['value'] = $args['meta_value']; } $query['meta_query'] = array($meta); } if ($args['sticky'] === 'include') { $query['ignore_sticky_posts'] = 1; } else { if ($args['sticky'] === 'exclude') { $sticky = get_option('sticky_posts'); if (is_array($sticky)) { $query['post__not_in'] = $sticky; } } else { if ($args['sticky'] === 'require') { $sticky = get_option('sticky_posts'); if (is_array($sticky) && !empty($sticky)) { $query['post__in'] = $sticky; } else { // no sticky posts exist return array('found' => 0, 'posts' => array()); } } } } if (isset($args['exclude'])) { $excluded_ids = (array) $args['exclude']; $query['post__not_in'] = isset($query['post__not_in']) ? array_merge($query['post__not_in'], $excluded_ids) : $excluded_ids; } if (isset($args['exclude_tree']) && is_post_type_hierarchical($args['type'])) { // get_page_children is a misnomer; it supports all hierarchical post types $page_args = array('child_of' => $args['exclude_tree'], 'post_type' => $args['type'], 'post_status' => 'publish,draft,pending,private,future,trash'); $post_descendants = get_pages($page_args); $exclude_tree = array($args['exclude_tree']); foreach ($post_descendants as $child) { $exclude_tree[] = $child->ID; } $query['post__not_in'] = isset($query['post__not_in']) ? array_merge($query['post__not_in'], $exclude_tree) : $exclude_tree; } if (isset($args['category'])) { $category = get_term_by('slug', $args['category'], 'category'); if ($category === false) { $query['category_name'] = $args['category']; } else { $query['cat'] = $category->term_id; } } if (isset($args['tag'])) { $query['tag'] = $args['tag']; } if (isset($args['page'])) { if ($args['page'] < 1) { $args['page'] = 1; } $query['paged'] = $args['page']; if ($query['paged'] !== 1) { $is_eligible_for_page_handle = false; } } else { if ($args['offset'] < 0) { $args['offset'] = 0; } $query['offset'] = $args['offset']; if ($query['offset'] !== 0) { $is_eligible_for_page_handle = false; } } if (isset($args['before'])) { $this->date_range['before'] = $args['before']; } if (isset($args['after'])) { $this->date_range['after'] = $args['after']; } if (isset($args['modified_before_gmt'])) { $this->modified_range['before'] = $args['modified_before_gmt']; } if (isset($args['modified_after_gmt'])) { $this->modified_range['after'] = $args['modified_after_gmt']; } if ($this->date_range) { add_filter('posts_where', array($this, 'handle_date_range')); } if ($this->modified_range) { add_filter('posts_where', array($this, 'handle_modified_range')); } if (isset($args['page_handle'])) { $page_handle = wp_parse_args($args['page_handle']); if (isset($page_handle['value']) && isset($page_handle['id'])) { // we have a valid looking page handle $this->page_handle = $page_handle; add_filter('posts_where', array($this, 'handle_where_for_page_handle')); } } /** * 'column' necessary for the me/posts endpoint (which extends sites/$site/posts). * Would need to be added to the sites/$site/posts definition if we ever want to * use it there. */ $column_whitelist = array('post_modified_gmt'); if (isset($args['column']) && in_array($args['column'], $column_whitelist)) { $query['column'] = $args['column']; } $this->performed_query = $query; add_filter('posts_orderby', array($this, 'handle_orderby_for_page_handle')); $wp_query = new WP_Query($query); remove_filter('posts_orderby', array($this, 'handle_orderby_for_page_handle')); if ($this->date_range) { remove_filter('posts_where', array($this, 'handle_date_range')); $this->date_range = array(); } if ($this->modified_range) { remove_filter('posts_where', array($this, 'handle_modified_range')); $this->modified_range = array(); } if ($this->page_handle) { remove_filter('posts_where', array($this, 'handle_where_for_page_handle')); } $return = array(); $excluded_count = 0; foreach (array_keys($this->response_format) as $key) { switch ($key) { case 'found': $return[$key] = (int) $wp_query->found_posts; break; case 'posts': $posts = array(); foreach ($wp_query->posts as $post_ID) { $the_post = $this->get_post_by('ID', $post_ID, $args['context']); if ($the_post && !is_wp_error($the_post)) { $posts[] = $the_post; } else { $excluded_count++; } } if ($posts) { /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */ do_action('wpcom_json_api_objects', 'posts', count($posts)); } $return[$key] = $posts; break; case 'meta': if (!is_array($args['type'])) { $return[$key] = (object) array('links' => (object) array('counts' => (string) $this->links->get_site_link($blog_id, 'post-counts/' . $args['type']))); } if ($is_eligible_for_page_handle && $return['posts']) { $last_post = end($return['posts']); reset($return['posts']); if ($return['found'] > count($return['posts']) && $last_post) { if (!isset($return[$key])) { $return[$key] = (object) array(); } $return[$key]->next_page = $this->build_page_handle($last_post, $query); } } break; } } $return['found'] -= $excluded_count; return $return; }
public function get_metadata() { $metadata = array(); foreach ((array) has_meta($this->post->ID) as $meta) { // Don't expose protected fields. $meta_key = $meta['meta_key']; $show = !WPCOM_JSON_API_Metadata::is_internal_only($meta_key) && (WPCOM_JSON_API_Metadata::is_public($meta_key) || current_user_can('edit_post_meta', $this->post->ID, $meta_key)); // Only business plan subscribers can view custom meta description if (Jetpack_SEO_Posts::DESCRIPTION_META_KEY == $meta_key && !Jetpack_SEO_Utils::is_enabled_jetpack_seo()) { $show = false; } if ($show) { $metadata[] = array('id' => $meta['meta_id'], 'key' => $meta['meta_key'], 'value' => maybe_unserialize($meta['meta_value'])); } } if (!empty($metadata)) { return $metadata; } else { return false; } }