public function test_optional_param() { register_rest_route('optional', '/test', array('methods' => array('GET'), 'callback' => '__return_null', 'args' => array('foo' => array()))); $request = new WP_REST_Request('GET', '/optional/test'); $request->set_query_params(array()); $response = $this->server->dispatch($request); $this->assertInstanceOf('WP_REST_Response', $response); $this->assertEquals(200, $response->get_status()); $this->assertArrayNotHasKey('foo', (array) $request); }
public function testUpdate() { wp_set_current_user(1); $expected = array('click_tracking' => 0, 'anon_tracking' => 0, 'license_code' => 'batman'); foreach ($expected as $setting => $value) { \ingot\testing\crud\settings::write($setting, $value); } $request = new \WP_REST_Request('POST', $this->namespaced_route); $request->set_query_params(array('click_tracking' => 'true', 'anon_tracking' => 'true', 'license_code' => 'allthespidermen')); $response = $this->server->dispatch($request); $response = rest_ensure_response($response); $this->assertEquals(200, $response->get_status()); $data = (array) $response->get_data(); foreach ($expected as $setting => $value) { $this->assertArrayHasKey($setting, $data); if ('license_code' != $setting) { $data[$setting] = intval($data[$setting]); $this->assertEquals($value, $data[$setting], $setting); } } }
/** * Add meta to an object. * * @param WP_REST_Request $request * @return WP_REST_Response|WP_Error */ public function create_item($request) { $parent_id = (int) $request['parent_id']; if (!$this->is_valid_meta_data($request['value'])) { $code = $this->parent_type === 'post' ? 'rest_post_invalid_action' : 'rest_meta_invalid_action'; // for now let's not allow updating of arrays, objects or serialized values. return new WP_Error($code, __('Invalid provided meta data for action.'), array('status' => 400)); } if (empty($request['key'])) { return new WP_Error('rest_meta_invalid_key', __('Invalid meta key.'), array('status' => 400)); } /* We should be able to update protected meta if we want to if ( is_protected_meta( $request['key'] ) ) { return new WP_Error( 'rest_meta_protected', sprintf( __( '%s is marked as a protected field.' ), $request['key'] ), array( 'status' => 403 ) ); } */ $meta_key = wp_slash($request['key']); $value = wp_slash($request['value']); $mid = add_metadata($this->parent_type, $parent_id, $meta_key, $value); if (!$mid) { return new WP_Error('rest_meta_could_not_add', __('Could not add meta.'), array('status' => 400)); } $request = new WP_REST_Request('GET'); $request->set_query_params(array('context' => 'edit', 'parent_id' => $parent_id, 'id' => $mid)); $response = rest_ensure_response($this->get_item($request)); $response->set_status(201); $data = $response->get_data(); $response->header('Location', rest_url('wp/v2' . '/' . $this->parent_base . '/' . $parent_id . '/meta/' . $data['id'])); /* This action is documented in lib/endpoints/class-wp-rest-meta-controller.php */ do_action('rest_insert_meta', $data, $request, true); return $response; }
public function test_update_post_with_categories() { wp_set_current_user(self::$editor_id); $category = wp_insert_term('Test Category', 'category'); $request = new WP_REST_Request('PUT', sprintf('/wp/v2/posts/%d', self::$post_id)); $params = $this->set_post_data(array('title' => 'Tester', 'categories' => array($category['term_id']))); $request->set_body_params($params); $response = $this->server->dispatch($request); $new_data = $response->get_data(); $this->assertEquals(array($category['term_id']), $new_data['categories']); $categories_path = ''; $links = $response->get_links(); foreach ($links['https://api.w.org/term'] as $link) { if ('category' === $link['attributes']['taxonomy']) { $categories_path = $link['href']; } } $query = parse_url($categories_path, PHP_URL_QUERY); parse_str($query, $args); $request = new WP_REST_Request('GET', $args['rest_route']); unset($args['rest_route']); $request->set_query_params($args); $response = $this->server->dispatch($request); $data = $response->get_data(); $this->assertCount(1, $data); $this->assertEquals('Test Category', $data[0]['name']); }
/** * Widget instance. * * @access public * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance The settings for the particular instance of the widget. */ public function widget($args, $instance) { $instance = array_merge($this->get_default_instance(), $instance); $exported_args = $args; unset($exported_args['before_widget']); unset($exported_args['after_widget']); $data = array('args' => $exported_args, 'posts' => null); $wp_rest_server = $this->plugin->get_rest_server(); $request = new \WP_REST_Request('GET', '/wp/v2/posts'); $request->set_query_params(array('filter' => array('posts_per_page' => $instance['number']))); $response = $wp_rest_server->dispatch($request); if (!$response->is_error()) { $data['posts'] = $wp_rest_server->response_to_data($response, true); $instance['has_more'] = $instance['number'] < $response->headers['X-WP-Total']; } $data['instance'] = $instance; echo $args['before_widget']; // WPCS: xss ok. echo '<script type="application/json">'; echo wp_json_encode($data); echo '</script>'; echo $args['after_widget']; // WPCS: xss ok. }
public function test_prepare_item() { wp_set_current_user($this->editor_id); $request = new WP_REST_Request('GET', sprintf('/wp/v2/posts/%d', $this->post_id)); $request->set_query_params(array('context' => 'edit')); $response = $this->server->dispatch($request); $this->check_get_post_response($response, 'edit'); }
/** * Retrieves a WP_REST_Request object from a full URL. * * @static * @since 4.5.0 * @access public * * @param string $url URL with protocol, domain, path and query args. * @return WP_REST_Request|false WP_REST_Request object on success, false on failure. */ public static function from_url($url) { $bits = parse_url($url); $query_params = array(); if (!empty($bits['query'])) { wp_parse_str($bits['query'], $query_params); } $api_root = rest_url(); if (get_option('permalink_structure') && 0 === strpos($url, $api_root)) { // Pretty permalinks on, and URL is under the API root $api_url_part = substr($url, strlen(untrailingslashit($api_root))); $route = parse_url($api_url_part, PHP_URL_PATH); } elseif (!empty($query_params['rest_route'])) { // ?rest_route=... set directly $route = $query_params['rest_route']; unset($query_params['rest_route']); } $request = false; if (!empty($route)) { $request = new WP_REST_Request('GET', $route); $request->set_query_params($query_params); } /** * Filter the request generated from a URL. * * @since 4.5.0 * * @param WP_REST_Request|false $request Generated request object, or false if URL * could not be parsed. * @param string $url URL the request was generated from. */ return apply_filters('rest_request_from_url', $request, $url); }
public function test_get_pages_params() { $this->factory->post->create_many(8, array('post_type' => 'page')); $request = new WP_REST_Request('GET', '/wp/v2/pages'); $request->set_query_params(array('page' => 2, 'per_page' => 4)); $response = $this->server->dispatch($request); $this->assertEquals(200, $response->get_status()); $headers = $response->get_headers(); $this->assertEquals(8, $headers['X-WP-Total']); $this->assertEquals(2, $headers['X-WP-TotalPages']); $all_data = $response->get_data(); $this->assertEquals(4, count($all_data)); foreach ($all_data as $post) { $this->assertEquals('page', $post['type']); } }
/** * Test GET /products for EDD * * @since 1.1.0 * * @group products_api * @group edd * @group price * * @covers ingot\testing\api\rest/products::get_price() */ public function testGetEDDPrice() { wp_set_current_user(1); ingot_test_data_price::edd_create_simple_download(72); $product = ingot_test_data_price::edd_create_simple_download(7); $request = new \WP_REST_Request('GET', $this->namespaced_route . '/price/' . $product->ID); $request->set_query_params(array('plugin' => 'edd')); $response = $this->server->dispatch($request); $response = rest_ensure_response($response); $this->assertEquals(200, $response->get_status()); $data = (array) $response->get_data(); $this->assertArrayHasKey('price', $data); $this->assertSame(ingot_sanitize_amount(7), $data['price']); }
function wp_enqueue_scripts() { global $post; $rest_server = rest_get_server(); if ($this->has_fee()) { wp_enqueue_style('wp-core-ui', $this->url('/css/wp-core-ui.css'), false, self::VERSION, 'screen'); wp_enqueue_style('wp-core-ui-colors', $this->url('/css/wp-core-ui-colors.css'), false, self::VERSION, 'screen'); wp_enqueue_style('wp-auth-check'); wp_enqueue_script('wp-auth-check'); wp_enqueue_script('fee-tinymce', $this->url('/vendor/tinymce.js'), array(), self::TINYMCE_VERSION, true); wp_enqueue_script('fee-tinymce-image', $this->url('/js/tinymce.image.js'), array('fee-tinymce'), self::VERSION, true); wp_enqueue_script('fee-tinymce-theme', $this->url('/js/tinymce.theme.js'), array('fee-tinymce'), self::VERSION, true); foreach (array('lists', 'paste', 'wordpress', 'wplink', 'wptextpattern', 'wpview') as $plugin) { wp_enqueue_script('fee-' . $plugin, $this->url('/vendor/' . $plugin . '.js'), array('fee-tinymce'), self::VERSION, true); } $tinymce_plugins = array('wordpress', 'feeImage', 'wptextpattern', 'wplink', 'wpview', 'paste', 'lists'); $tinymce_toolbar = array('bold', 'italic', 'strikethrough', 'link'); $tinymce = array('selector' => '.fee-content', 'plugins' => implode(' ', array_unique(apply_filters('fee_tinymce_plugins', $tinymce_plugins))), 'toolbar' => apply_filters('fee_tinymce_toolbar', $tinymce_toolbar), 'theme' => 'fee', 'inline' => true, 'relative_urls' => false, 'convert_urls' => false, 'browser_spellcheck' => true, 'placeholder' => apply_filters('fee_content_placeholder', __('Just write…')), 'wpeditimage_html5_captions' => current_theme_supports('html5', 'caption'), 'end_container_on_empty_block' => true); $request = new WP_REST_Request('GET', '/wp/v2/' . ($post->post_type === 'page' ? 'pages' : 'posts') . '/' . $post->ID); $request->set_query_params(array('context' => 'edit')); $result = $rest_server->dispatch($request); wp_enqueue_script('fee', $this->url('/js/fee.js'), array('fee-tinymce', 'wp-util', 'heartbeat', 'editor', 'wp-api', 'media-views'), self::VERSION, true); wp_localize_script('fee', 'feeData', array('tinymce' => apply_filters('fee_tinymce_config', $tinymce), 'post' => $result->get_data(), 'lock' => !wp_check_post_lock($post->ID) ? implode(':', wp_set_post_lock($post->ID)) : false, 'titlePlaceholder' => apply_filters('enter_title_here', __('Enter title here'), $post), 'editURL' => get_edit_post_link())); $request = new WP_REST_Request('GET', '/wp/v2'); $result = $rest_server->dispatch($request); wp_localize_script('wp-api', 'wpApiSettings', array('root' => esc_url_raw(get_rest_url()), 'nonce' => wp_create_nonce('wp_rest'), 'versionString' => 'wp/v2/', 'schema' => $result->get_data(), 'cacheSchema' => true)); wp_enqueue_media(array('post' => $post)); wp_deregister_script('mce-view'); wp_enqueue_script('mce-view', $this->url('/vendor/mce-view.js'), array('shortcode', 'jquery', 'media-views', 'media-audiovideo'), self::VERSION, true); wp_enqueue_script('mce-view-register', $this->url('/js/mce-view-register.js'), array('mce-view', 'fee'), self::VERSION, true); wp_localize_script('mce-view-register', 'mce_view_register', array('post_id' => $post->ID)); wp_enqueue_style('tinymce-core', $this->url('/css/tinymce.core.css'), false, self::VERSION, 'screen'); wp_enqueue_style('tinymce-view', $this->url('/css/tinymce.view.css'), false, self::VERSION, 'screen'); wp_enqueue_style('fee', $this->url('/css/fee.css'), false, self::VERSION, 'screen'); wp_enqueue_style('dashicons'); } if (current_user_can('edit_posts')) { if (is_singular()) { require_once ABSPATH . '/wp-admin/includes/post.php'; $user_id = wp_check_post_lock($post->ID); $user = get_userdata($user_id); } wp_enqueue_script('fee-adminbar', $this->url('/js/fee-adminbar.js'), array('wp-util', 'wp-api'), self::VERSION, true); wp_localize_script('fee-adminbar', 'fee_adminbar', array('lock' => is_singular() && $user_id ? $user->display_name : false, 'supportedPostTypes' => $this->get_supported_post_types(), 'postNew' => admin_url('post-new.php'), 'nonce' => wp_create_nonce('fee-new'))); } }
/** * Test deleting one item * * @since 0.4.0 * * @group rest * @group group_rest * @group group * * @covers ingot\testing\api\rest\groups::create_item() */ public function testCreateItem() { wp_set_current_user(1); $request = new \WP_REST_Request('POST', $this->namespaced_route); $request->set_query_params(array('type' => 'click', 'sub_type' => 'button_color', 'name' => 'x2')); $response = $this->server->dispatch($request); $response = rest_ensure_response($response); $this->assertEquals(201, $response->get_status()); $data = (array) $response->get_data(); $this->assertArrayHasKey('ID', $data); $group = \ingot\testing\crud\group::read($data['ID']); $fields = $this->get_fields_to_check_for(); $this->assertTrue(is_array($data)); $this->assertEquals(count($fields), count($data), var_export($data, true)); foreach ($fields as $field) { $this->assertArrayHasKey($field, $data); $this->assertSame($group[$field], $data[$field]); } }
/** * Adds the included models indicated in the request to the entity provided * @param \EEM_Base $model * @param \WP_REST_Request $rest_request * @param array $entity_array * @return array the modified entity */ protected function _include_requested_models(\EEM_Base $model, \WP_REST_Request $rest_request, $entity_array) { $includes_for_this_model = $this->explode_and_get_items_prefixed_with($rest_request->get_param('include'), ''); $includes_for_this_model = $this->_remove_model_names_from_array($includes_for_this_model); //if they passed in * or didn't specify any includes, return everything if (!in_array('*', $includes_for_this_model) && !empty($includes_for_this_model)) { if ($model->has_primary_key_field()) { //always include the primary key. ya just gotta know that at least $includes_for_this_model[] = $model->primary_key_name(); } if ($this->explode_and_get_items_prefixed_with($rest_request->get_param('calculate'), '')) { $includes_for_this_model[] = '_calculated_fields'; } $entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model)); } $relation_settings = $this->get_model_version_info()->relation_settings($model); foreach ($relation_settings as $relation_name => $relation_obj) { $related_fields_to_include = $this->explode_and_get_items_prefixed_with($rest_request->get_param('include'), $relation_name); $related_fields_to_calculate = $this->explode_and_get_items_prefixed_with($rest_request->get_param('calculate'), $relation_name); //did they specify they wanted to include a related model, or //specific fields from a related model? //or did they specify to calculate a field from a related model? if ($related_fields_to_include || $related_fields_to_calculate) { //if so, we should include at least some part of the related model $pretend_related_request = new \WP_REST_Request(); $pretend_related_request->set_query_params(array('caps' => $rest_request->get_param('caps'), 'include' => $related_fields_to_include, 'calculate' => $related_fields_to_calculate)); $pretend_related_request->add_header('no_rest_headers', true); $related_results = $this->get_entities_from_relation($entity_array[$model->primary_key_name()], $relation_obj, $pretend_related_request); $entity_array[Read::get_related_entity_name($relation_name, $relation_obj)] = $related_results instanceof \WP_Error ? null : $related_results; } } return $entity_array; }
public function test_handle_request_get_one__registration_include_answers_and_question_bare_min_from_each() { $this->set_current_user_to_new(); $r = $this->new_model_obj_with_dependencies('Registration'); $this->new_model_obj_with_dependencies('Answer', array('REG_ID' => $r->ID())); $req = new \WP_REST_Request('GET', \EED_Core_Rest_Api::ee_api_namespace . '4.8.29/registrations/' . $r->ID()); $req->set_query_params(array('include' => 'Answer.ATT_ID, Answer.Question.QST_ID')); $req->set_url_params(array('id' => $r->ID())); $response = Read::handle_request_get_one($req); $entity = $response->get_data(); $this->assertArrayHasKey('answers', $entity); $answers = $entity['answers']; foreach ($answers as $answer) { $this->assertArrayHasKey('question', $answer); } }
/** * Bulk create, update and delete items. * * @param WP_REST_Request $request Full details about the request. * @return array Of WP_Error or WP_REST_Response. */ public function batch_items($request) { /** @var WP_REST_Server $wp_rest_server */ global $wp_rest_server; // Get the request params. $items = array_filter($request->get_params()); $response = array(); // Check batch limit. $limit = $this->check_batch_limit($items); if (is_wp_error($limit)) { return $limit; } if (!empty($items['create'])) { foreach ($items['create'] as $item) { $_item = new WP_REST_Request('POST'); // Default parameters. $defaults = array(); $schema = $this->get_public_item_schema(); foreach ($schema['properties'] as $arg => $options) { if (isset($options['default'])) { $defaults[$arg] = $options['default']; } } $_item->set_default_params($defaults); // Set request parameters. $_item->set_body_params($item); $_response = $this->create_item($_item); if (is_wp_error($_response)) { $response['create'][] = array('id' => 0, 'error' => array('code' => $_response->get_error_code(), 'message' => $_response->get_error_message(), 'data' => $_response->get_error_data())); } else { $response['create'][] = $wp_rest_server->response_to_data($_response, ''); } } } if (!empty($items['update'])) { foreach ($items['update'] as $item) { $_item = new WP_REST_Request('PUT'); $_item->set_body_params($item); $_response = $this->update_item($_item); if (is_wp_error($_response)) { $response['update'][] = array('id' => $item['id'], 'error' => array('code' => $_response->get_error_code(), 'message' => $_response->get_error_message(), 'data' => $_response->get_error_data())); } else { $response['update'][] = $wp_rest_server->response_to_data($_response, ''); } } } if (!empty($items['delete'])) { foreach ($items['delete'] as $id) { $_item = new WP_REST_Request('DELETE'); $_item->set_query_params(array('id' => $id, 'force' => true)); $_response = $this->delete_item($_item); if (is_wp_error($_response)) { $response['delete'][] = array('id' => $id, 'error' => array('code' => $_response->get_error_code(), 'message' => $_response->get_error_message(), 'data' => $_response->get_error_data())); } else { $response['delete'][] = $wp_rest_server->response_to_data($_response, ''); } } } return $response; }
public function test_prepare_item() { wp_set_current_user(self::$admin_id); $request = new WP_REST_Request('GET', sprintf('/wp/v2/comments/%d', self::$approved_id)); $request->set_query_params(array('context' => 'edit')); $response = $this->server->dispatch($request); $this->assertEquals(200, $response->get_status()); $data = $response->get_data(); $this->check_comment_data($data, 'edit', $response->get_links()); }
/** * Make sure that a sanitization that transforms the argument type will not * cause the validation to fail. * * @ticket 37192 */ public function test_rest_validate_before_sanitization() { register_rest_route('test-ns', '/test', array('methods' => array('GET'), 'callback' => '__return_null', 'args' => array('someinteger' => array('validate_callback' => array($this, '_validate_as_integer_123'), 'sanitize_callback' => 'absint'), 'somestring' => array('validate_callback' => array($this, '_validate_as_string_foo'), 'sanitize_callback' => 'absint')))); $request = new WP_REST_Request('GET', '/test-ns/test'); $request->set_query_params(array('someinteger' => 123, 'somestring' => 'foo')); $response = $this->server->dispatch($request); $this->assertEquals(200, $response->get_status()); }
public static function bb_gallery_shortcode($attr, $content = '') { if (is_feed() || is_array($attr) && !empty($attr['mode']) && $attr['mode'] === 'wordpress') { # invoke the standard WordPress gallery shortcode function unset($attr['mode']); return gallery_shortcode($attr); } if (is_array($attr) && !empty($attr['mode']) && $attr['mode'] === 'get_first') { # in this mode only the first image is returned for use as a representative image for a gallery unset($attr['mode']); $get_first = TRUE; ob_start(); #TODO: set underlying SQL LIMIT to 1 } foreach (['thumbnail', 'medium', 'medium_large', 'large', 'full'] as $size) { $label = "{$size}_width"; $width = intval(get_option("{$size}_size_w")); if (!$width && $size === 'medium_large') { $width = 768; } $width = intval(1.125 * $width); if ($size === 'thumbnail') { ${$label} = $width; } else { ${$label} = $prev_width + 1; } $prev_width = $width; } ob_start(); require_once dirname(__FILE__) . '/bbg_xiv-gallery_templates_wp_rest.php'; $templates = ob_get_clean(); $post = get_post(); static $instance = 10000; # not 0 to create a different space from the WordPress "gallery" shortcode $instance++; static $bbg_xiv_data = ['version' => '1.0']; $bbg_xiv_data['ajaxurl'] = admin_url('admin-ajax.php'); $bbg_xiv_data['bbg_xiv_flex_min_width'] = get_option('bbg_xiv_flex_min_width', 128); $bbg_xiv_data['bbg_xiv_flex_min_width_for_caption'] = get_option('bbg_xiv_flex_min_width_for_caption', 96); $bbg_xiv_data['bbg_xiv_max_search_results'] = get_option('bbg_xiv_max_search_results', 250); $bbg_xiv_data['bbg_xiv_flex_min_width_for_dense_view'] = get_option('bbg_xiv_flex_min_width_for_dense_view', 1280); $bbg_xiv_data['bbg_xiv_flex_number_of_dense_view_columns'] = get_option('bbg_xiv_flex_number_of_dense_view_columns', 10); $bbg_xiv_data['bbg_xiv_carousel_interval'] = get_option('bbg_xiv_carousel_interval', 2500); $bbg_xiv_data['bbg_xiv_disable_flexbox'] = get_option('bbg_xiv_disable_flexbox', FALSE); $bbg_xiv_data['bbg_xiv_default_view'] = get_option('bbg_xiv_default_view', 'Gallery'); $bbg_xiv_data['bbg_xiv_wp_rest_api'] = self::$wp_rest_api_available && self::$use_wp_rest_api_if_available; # translations for JavaScript side $bbg_xiv_lang['Nothing Found'] = __('Nothing Found', 'bb_gallery'); $bbg_xiv_lang['Search Results for'] = __('Search Results for', 'bb_gallery'); $bbg_xiv_lang['Page'] = __('Page', 'bb_gallery'); $bbg_xiv_lang['of'] = __('of', 'bb_gallery'); $bbg_xiv_lang['Images'] = __('Images', 'bb_gallery'); $bbg_xiv_lang['to'] = __('to', 'bb_gallery'); $bbg_xiv_lang['galleryOfGalleriesTitle'] = __('Each image below represents a gallery. Please click on an image to load its gallery.', 'bb_gallery'); $default_flags = []; switch (get_option('bbg_xiv_use_tiles', 'Cover')) { case 'Cover': $default_flags[] = 'tiles'; break; case 'Contain': $default_flags[] = 'tiles'; $default_flags[] = 'contain'; break; case 'Fill': $default_flags[] = 'tiles'; $default_flags[] = 'fill'; break; } if (get_option('bbg_xiv_use_embedded_carousel', TRUE)) { $default_flags[] = 'embedded-carousel'; } if (is_array($attr)) { if (!empty($attr['mode']) && $attr['mode'] === "galleries") { # this is a proprietary mode to display altgallery entries as a gallery of representative images $gallery_icons_mode = TRUE; } if (!empty($attr['view'])) { # this sets the initial view of a gallery - gallery, carousel or tabs $default_view = $attr['view']; } if (!empty($attr['flags'])) { # flag to set embedded carousel mode $flags = $attr['flags']; } } # merge the default flags and the flags from the shortcode if (empty($flags)) { $flags = $default_flags; } else { $flags = explode(',', $flags); $flags = array_merge($default_flags, $flags); $flags = array_unique($flags); } # handle cancel flags foreach (['embedded-carousel', 'tiles', 'contain', 'fill'] as $flag) { if (($i = array_search('no-' . $flag, $flags)) !== FALSE) { unset($flags[$i]); if (($j = array_search($flag, $flags)) !== FALSE) { unset($flags[$j]); } } } $flags = implode(',', $flags); $galleries = []; if ($content) { # Unfortunately (and also I think incorrectly) the 'the_content' filter wptexturize() from formatting.php will process the parameters of shortcodes # prettifying the quote marks. So, we need to undo this mutilation and restore the original content. # Opinion: WordPress seems to love regex but regex is simply inadequate for parsing HTML! $content = preg_replace('/‘|’|“|”|′|″/', '"', $content); if (preg_match_all('#\\[altgallery\\s+title="([^"]+)"\\s+([^\\]]+)\\]#m', $content, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $gallery = $galleries[] = (object) ['title' => $match[1], 'specifiers' => $match[2]]; if (!empty($gallery_icons_mode)) { $gallery->specifiers = preg_replace_callback(['/(^|\\s+)(image)="(\\d+)"/', '/(^|\\s+)(caption)="([^"]*)"/'], function ($matches) use($gallery) { $gallery->{$matches}[2] = $matches[3]; return ''; }, $gallery->specifiers); if (empty($gallery->image)) { # no image specified so use the first image of the gallery $gallery_attr = ['mode' => 'get_first']; preg_replace_callback('/(\\w+)=("|\')(.*?)\\2/', function ($matches) use(&$gallery_attr) { $gallery_attr[$matches[1]] = $matches[3]; }, $gallery->specifiers); $attachment = self::bb_gallery_shortcode($gallery_attr); $gallery->image = self::$wp_rest_api_available && self::$use_wp_rest_api_if_available ? $attachment['id'] : $attachment->ID; } if (empty($gallery->caption)) { $gallery->caption = $gallery->title; } } } } if (!empty($gallery_icons_mode)) { // construct a 'ids' parameter with ids of gallery icons $attr['ids'] = implode(',', array_map(function ($gallery) { return $gallery->image; }, $galleries)); } } if (!empty($attr['ids'])) { // 'ids' is explicitly ordered, unless you specify otherwise. if (empty($attr['orderby'])) { $attr['orderby'] = 'post__in'; } $attr['include'] = $attr['ids']; } /** * Filter the default gallery shortcode output. * * If the filtered output isn't empty, it will be used instead of generating * the default gallery template. * * @since 2.5.0 * @since 4.2.0 The `$instance` parameter was added. * * @see gallery_shortcode() * * @param string $output The gallery output. Default empty. * @param array $attr Attributes of the gallery shortcode. * @param int $instance Unique numeric ID of this gallery shortcode instance. */ $output = apply_filters('post_gallery', '', $attr, $instance); if ($output != '') { return $output; } $atts = shortcode_atts(array('order' => 'ASC', 'orderby' => 'menu_order', 'id' => $post ? $post->ID : 0, 'size' => 'thumbnail', 'include' => '', 'exclude' => '', 'link' => '', 'bb_tags' => ''), $attr, 'gallery'); $id = intval($atts['id']); $selector = "gallery-{$instance}"; if (self::$wp_rest_api_available && self::$use_wp_rest_api_if_available) { # map gallery shortcode parameters to WP REST API parameters $orderby_map = ['menu_order' => 'menu_order', 'title' => 'title', 'post_date' => 'date', 'rand' => 'rand', 'ID' => 'id', 'post__in' => 'include']; $order_map = ['ASC' => 'asc', 'DESC' => 'desc']; # Initialize the Backbone.js collection using data from the WP REST API for the WP REST API model $attributes = ['author' => [], 'author_exclude' => [], 'menu_order' => '', 'offset' => '', 'order' => $order_map[$atts['order']], 'orderby' => $orderby_map[$atts['orderby']], 'page' => 1, 'include' => [], 'exclude' => [], 'per_page' => 10, 'slug' => '', 'parent' => '', 'parent_exclude' => '', 'status' => 'publish', 'search' => '']; if (!empty($atts['bb_tags'])) { // Translate the terms of the proprietary 'bb_tags' attribute to ids $bb_tags = array_map('trim', explode(',', $atts['bb_tags'])); $attributes['bb-tags'] = get_terms(['taxonomy' => 'bb_tags', 'slug' => $bb_tags, 'name' => $bb_tags, 'fields' => 'ids']); } else { if (!empty($atts['include'])) { $attributes['include'] = explode(',', $atts['include']); $attributes['per_page'] = count($attributes['include']); } elseif (!empty($atts['exclude'])) { $attributes['parent'] = [$id]; $attributes['exclude'] = explode(',', $atts['exclude']); $attributes['per_page'] = 1024; } else { $attributes['parent'] = [$id]; $attributes['per_page'] = 1024; } } if (!empty($get_first)) { $attributes['per_page'] = 1; } $request = new WP_REST_Request('GET', '/wp/v2/media'); $request->set_query_params($attributes); # TODO: $request may need to set some of the params below #$request->set_body_params( wp_unslash( $_POST ) ); #$request->set_file_params( $_FILES ); #$request->set_headers( $this->get_headers( wp_unslash( $_SERVER ) ) ); #$request->set_body( $this->get_raw_data() ); #$request->set_url_params( $args ); #$request->set_attributes( $handler ); #$request->set_default_params( $defaults ); self::add_additional_rest_fields(); $controller = new WP_REST_Attachments_Controller("attachment"); $attachments = $controller->get_items($request)->data; if (!empty($get_first)) { ob_end_clean(); return reset($attachments); } if (!empty($gallery_icons_mode)) { # replace title and caption for image with title and caption for gallery and also remember the gallery index foreach ($galleries as $i => $gallery) { if (empty($attachments[$i])) { # this is an error probably caused by a duplicate image id continue; } $attachment =& $attachments[$i]; if ((int) $gallery->image === (int) $attachment['id']) { # if this is not true then there probably is a duplicate image id $attachment['gallery_index'] = $i; $attachment['title']['rendered'] = $gallery->title; $attachment['caption'] = $gallery->caption; $attachment['description'] = ''; } } } $bbg_xiv_data["{$selector}-data"] = json_encode($attachments); } else { // initialize the Backbone.js collection using data for my proprietary model // Handle the proprietary 'bb_tags' attribute - this specifies a gallery by a taxonomy expression if (!empty($atts['bb_tags'])) { $bb_tags = explode(',', $atts['bb_tags']); $tax_query = array(); // search by both slug and name $tax_query['relation'] = 'OR'; $tax_query[] = array('taxonomy' => 'bb_tags', 'field' => 'slug', 'terms' => $bb_tags); $tax_query[] = array('taxonomy' => 'bb_tags', 'field' => 'name', 'terms' => $bb_tags); $_attachments = get_posts(array('post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'], 'tax_query' => $tax_query, 'posts_per_page' => empty($get_first) ? -1 : 1, 'offset' => 0)); $attachments = array(); foreach ($_attachments as $key => $val) { $attachments[$val->ID] = $_attachments[$key]; } } elseif (!empty($atts['include'])) { $_attachments = get_posts(array('include' => empty($get_first) ? $atts['include'] : (string) explode(',', $atts['include'])[0], 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'])); $attachments = array(); foreach ($_attachments as $key => $val) { $attachments[$val->ID] = $_attachments[$key]; } } elseif (!empty($atts['exclude'])) { $attachments = get_children(array('post_parent' => $id, 'exclude' => $atts['exclude'], 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'], 'numberposts' => empty($get_first) ? -1 : 1)); } else { $attachments = get_children(array('post_parent' => $id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'], 'numberposts' => empty($get_first) ? -1 : 1)); } if (!empty($get_first)) { ob_end_clean(); return reset($attachments); } #if ( empty( $attachments ) ) { # return ''; #} self::bbg_xiv_do_attachments($attachments); if (!empty($gallery_icons_mode)) { # replace title and caption for image with title and caption for gallery and also remember the gallery index foreach ($galleries as $i => $gallery) { $attachment = $attachments[$gallery->image]; $attachment->gallery_index = $i; $attachment->post_title = $gallery->title; $attachment->post_excerpt = $gallery->caption; $attachment->post_content = ''; } } $bbg_xiv_data["{$selector}-data"] = json_encode(array_values($attachments)); } wp_localize_script('bbg_xiv-gallery', 'bbg_xiv', $bbg_xiv_data); wp_localize_script('bbg_xiv-gallery', 'bbg_xiv_lang', $bbg_xiv_lang); $float = is_rtl() ? 'right' : 'left'; $size_class = sanitize_html_class($atts['size']); # The "Table View" is primarily intended for developers and should be disabled for production environmemts. $table_nav_item = ''; if (get_option('bbg_xiv_table')) { $table_nav_item = <<<EOD <li><a href="#">Table</a></li> EOD; } $translations = ['GALLERY MENU' => __('GALLERY MENU', 'bb_gallery'), 'IMAGES:' => __('IMAGES:', 'bb_gallery'), 'GALLERIES:' => __('GALLERIES:', 'bb_gallery'), 'View' => __('View', 'bb_gallery'), 'Gallery' => __('Gallery', 'bb_gallery'), 'Carousel' => __('Carousel', 'bb_gallery'), 'Justified' => __('Justified', 'bb_gallery'), 'Tabs' => __('Tabs', 'bb_gallery'), 'Dense' => __('Dense', 'bb_gallery'), 'VIEWS' => __('VIEWS', 'bb_gallery'), 'GALLERIES' => __('GALLERIES', 'bb_gallery'), 'Home' => __('Home', 'bb_gallery'), 'Titles' => __('Titles', 'bb_gallery'), 'Search Images on Site' => __('Search Images on Site', 'bb_gallery'), 'Options' => __('Options', 'bb_gallery'), 'Help' => __('Help', 'bb_gallery'), 'get help' => __('get help', 'bb_gallery'), 'configure bandwidth, carousel interval, ...' => __('configure bandwidth, carousel interval, ...', 'bb_gallery'), 'return to home gallery' => __('return to home gallery', 'bb_gallery'), 'show/hide image titles' => __('show/hide image titles', 'bb_gallery'), 'Carousel Time Interval in ms' => __('Carousel Time Interval in ms', 'bb_gallery'), 'Minimum Width for Gallery Images in px' => __('Minimum Width for Gallery Images in px', 'bb_gallery'), 'Maximum Number of Images Returned by Search' => __('Maximum Number of Images Returned by Search', 'bb_gallery'), 'Number of Columns in the Dense View' => __('Number of Columns in the Dense View', 'bb_gallery'), 'Bandwidth' => __('Bandwidth', 'bb_gallery'), 'Auto' => __('Auto', 'bb_gallery'), 'High' => __('High', 'bb_gallery'), 'Medium' => __('Medium', 'bb_gallery'), 'Low' => __('Low', 'bb_gallery'), 'Interface' => __('Interface', 'bb_gallery'), 'Mouse' => __('Mouse', 'bb_gallery'), 'Touch' => __('Touch', 'bb_gallery'), 'Save' => __('Save', 'bb_gallery'), 'Cancel' => __('Cancel', 'bb_gallery'), 'Help' => __('Help', 'bb_gallery')]; if (!$galleries) { for ($i = 1; $i <= self::$gallery_menu_items_count; $i++) { $option = get_option("bbg_xiv_gallery_menu_{$i}", ''); if (preg_match('/^"([^"]+)":(.+)$/', $option, $matches) === 1) { $galleries[] = (object) ['title' => $matches[1], 'specifiers' => $matches[2]]; } } } ob_start(); wp_nonce_field(self::$nonce_action); $nonce_field = ob_get_clean(); $output = $templates; $output .= <<<EOD <div class="bbg_xiv-bootstrap bbg_xiv-gallery"> <nav role="navigation" class="navbar navbar-inverse bbg_xiv-gallery_navbar"> <div class="navbar-header"> <button type="button" data-target="#{$selector}-navbarCollapse" data-toggle="collapse" class="navbar-toggle"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a href="#" class="navbar-brand bbg_xiv-images_brand">{$translations['GALLERY MENU']}</a> </div> <div id="{$selector}-navbarCollapse" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li class="dropdown bbg_xiv-select_view"> <a data-toggle="dropdown" class="dropdown-toggle bbg_xiv-selected_view" href="#"><span>{$translations['View']}</span> <b class="caret"></b></a> <ul role="menu" class="dropdown-menu bbg_xiv-view_menu"> <li class="dropdown-header">{$translations['VIEWS']}</li> <li class="bbg_xiv-view bbg_xiv-view_gallery active"><a data-view="Gallery" href="#">{$translations['Gallery']}</a></li> <li class="bbg_xiv-view bbg_xiv-view_carousel bbg_xiv-hide_for_gallery_icons"><a data-view="Carousel" href="#">{$translations['Carousel']}</a></li> <li class="bbg_xiv-view bbg_xiv-view_justified bbg_xiv-hide_for_gallery_icons"><a data-view="Justified" href="#">{$translations['Justified']}</a></li> <li class="bbg_xiv-view bbg_xiv-view_tabs"><a data-view="Tabs" href="#">{$translations['Tabs']}</a></li> <li class="bbg_xiv-view bbg_xiv-hide_for_gallery_icons bbg_xiv-large_viewport_only"><a data-view="Dense" href="#">{$translations['Dense']}</a></li> <!-- TODO: Add entry for new views here. --> {$table_nav_item} EOD; if ($galleries) { # output menu items for dynamically loaded galleries $output .= <<<EOD <li class="divider"></li> <li class="dropdown-header">{$translations['GALLERIES']}</li> <li class="bbg_xiv-alt_gallery bbg_xiv-alt_gallery_home active"><a data-view="gallery_home" data-specifiers='' href="#">{$translations['Home']}</a></li> EOD; foreach ($galleries as $i => $gallery) { $output .= <<<EOD <li class="bbg_xiv-alt_gallery"><a data-view="gallery_{$i}" data-specifiers='{$gallery->specifiers}' href="#">{$gallery->title}</a></li> EOD; } } $output .= <<<EOD </ul> </li> </ul> <form role="search" class="navbar-form navbar-left bbg_xiv-search_form"> <div class="form-group"> <input type="text" placeholder="{$translations['Search Images on Site']}" class="form-control"> </div> <button type="submit" class="btn btn-default bbg_xiv-search" title="start search"><span class="glyphicon glyphicon-search"></span></button> {$nonce_field} </form> <button type="button" class="btn btn-info bbg_xiv-help" title="{$translations['get help']}"> <span class="glyphicon glyphicon-question-sign"></span> <span class="bbg_xiv-navbar_button_text">{$translations['Help']}</span> </button> <button type="button" class="btn btn-info bbg_xiv-configure" title="{$translations['configure bandwidth, carousel interval, ...']}"> <span class="glyphicon glyphicon-cog"></span> <span class="bbg_xiv-navbar_button_text">{$translations['Options']}</span> </button> <button type="button" class="btn btn-info bbg_xiv-home" title="{$translations['return to home gallery']}"> <span class="glyphicon glyphicon-home"></span> <span class="bbg_xiv-navbar_button_text">{$translations['Home']}</span> </button> <button type="button" class="btn btn-info bbg_xiv-titles" title="{$translations['show/hide image titles']}"> <span class="glyphicon glyphicon-subtitles"></span> <span class="bbg_xiv-navbar_button_text">{$translations['Titles']}</span> </button> </div> </nav> EOD; # Optionally show titles of dynamically loadable galleries as tab items if ($galleries && empty($gallery_icons_mode) && get_option('bbg_xiv_use_gallery_tabs', TRUE)) { $output .= <<<EOD <!-- Gallery Tabs --> <div class="bbg_xiv-container bbg_xiv-gallery_tabs_container"> <nav role="navigation" class="navbar navbar-default"> <div class="navbar-header"> <button type="button" data-target="#gallery_tabbar_collapse" data-toggle="collapse" class="navbar-toggle"> <span class="sr-only">Toggle galleries</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a href="#" class="navbar-brand bbg_xiv-tabs_brand">{$translations['GALLERIES:']}</a> </div> <div id="gallery_tabbar_collapse" class="collapse navbar-collapse bbg_xiv-closed"> <ul class="nav nav-tabs"> <li class="bbg_xiv-tabs_title"><a href="#">{$translations['GALLERIES:']}</a></li> <li class="active"><a data-view="gallery_home" data-specifiers='' href="#">{$translations['Home']}</a></li> EOD; foreach ($galleries as $i => $gallery) { $output .= <<<EOD <li><a data-view="gallery_{$i}" data-specifiers='{$gallery->specifiers}' href="#">{$gallery->title}</a></li> EOD; } $output .= <<<EOD </ul> </div> <span class="glyphicon glyphicon-collapse-down"></span> </nav> </div> EOD; } $class_gallery_icons_mode = empty($gallery_icons_mode) ? '' : ' bbg_xiv-gallery_icons_mode'; $class_default_view = empty($default_view) ? '' : ' bbg_xiv-default_view_' . $default_view; $flags = empty($flags) ? '' : $flags; $output .= <<<EOD <!-- Search or Gallery Headings --> <div id="{$selector}-heading" class="bbg_xiv-search_header"> <span class="bbg_xiv-search_heading_first"></span><br> <button class="btn btn-primary btn-sm bbg_xiv-search_scroll_left" disabled><span class="glyphicon glyphicon-chevron-left"></span></button> <span class="bbg_xiv-search_heading_second"></span> <button class="btn btn-primary btn-sm bbg_xiv-search_scroll_right"><span class="glyphicon glyphicon-chevron-right"></span></button> </div> <div id="{$selector}-alt_gallery_heading" class="bbg_xiv-alt_gallery_header"> <span class="bbg_xiv-alt_gallery_heading"></span> </div> <div id="{$selector}" class="gallery galleryid-{$id} gallery-size-{$size_class} bbg_xiv-gallery_envelope{$class_gallery_icons_mode}{$class_default_view}" data-flags="{$flags}"> <div class="ui-loader"><span class="ui-icon-loading"></span></div> </div> <div class="bbg_xiv-configure_outer"> </div> <div class="bbg_xiv-configure_inner"> <button class="bbg_xiv-configure_close"><span class="glyphicon glyphicon-remove"></span></button> <h1>BB Gallery Options</h1> <form class="form-horizontal"> <div class="form-group"> <label for="bbg_xiv-carousel_delay" class="control-label col-sm-9 col-md-offset-2 col-md-6">{$translations['Carousel Time Interval in ms']}</label> <div class="col-sm-3 col-md-2"> <input type="number" class="form-control" id="bbg_xiv-carousel_delay" min="1000" step="100"> </div> </div> <div class="form-group"> <label for="bbg_xiv-min_image_width" class="control-label col-sm-9 col-md-offset-2 col-md-6">{$translations['Minimum Width for Gallery Images in px']}</label> <div class="col-sm-3 col-md-2"> <input type="number" class="form-control" id="bbg_xiv-min_image_width" min="32" max="1024"> </div> </div> <div class="form-group"> <label for="bbg_xiv-max_search_results" class="control-label col-sm-9 col-md-offset-2 col-md-6">{$translations['Maximum Number of Images Returned by Search']}</label> <div class="col-sm-3 col-md-2"> <input type="number" class="form-control" id="bbg_xiv-max_search_results" min="1" max="{$bbg_xiv_data['bbg_xiv_max_search_results']}"> </div> </div> <div class="form-group bbg_xiv-mouse_only_option"> <label for="bbg_xiv-columns_in_dense_view" class="control-label col-sm-9 col-md-offset-2 col-md-6">{$translations['Number of Columns in the Dense View']}</label> <div class="col-sm-3 col-md-2"> <input type="number" class="form-control" id="bbg_xiv-columns_in_dense_view" min="2" max="32"> </div> </div> <div class="form-group"> <label for="bbg_xiv-bandwidth" class="control-label col-sm-3 col-md-offset-2 col-md-2">{$translations['Bandwidth']}</label> <div class="col-sm-9 col-md-6"> <span class="bbg_xiv-radio_input"> <input type="radio" class="form-control" name="bbg_xiv-bandwidth" value="auto" id="bbg_xiv-bandwidth_auto" checked> <span class="bbg_xiv-radio_text">{$translations['Auto']}</span> </span> <span class="bbg_xiv-radio_input"> <input type="radio" class="form-control" name="bbg_xiv-bandwidth" value="normal" id="bbg_xiv-bandwidth_normal"> <span class="bbg_xiv-radio_text">{$translations['High']}</span> </span> <span class="bbg_xiv-radio_input"> <input type="radio" class="form-control" name="bbg_xiv-bandwidth" value="low" id="bbg_xiv-bandwidth_low"> <span class="bbg_xiv-radio_text">{$translations['Medium']}</span> </span> <span class="bbg_xiv-radio_input"> <input type="radio" class="form-control" name="bbg_xiv-bandwidth" value="very low" id="bbg_xiv-bandwidth_very_low"> <span class="bbg_xiv-radio_text">{$translations['Low']}</span> </span> </div> </div> <div class="form-group"> <label for="bbg_xiv-interface" class="control-label col-sm-3 col-md-offset-2 col-md-2">{$translations['Interface']}</label> <div class="col-sm-9 col-md-6"> <span class="bbg_xiv-radio_input"> <input type="radio" class="form-control" name="bbg_xiv-interface" value="auto" id="bbg_xiv-interface_auto" checked> <span class="bbg_xiv-radio_text">{$translations['Auto']}</span> </span> <span class="bbg_xiv-radio_input"> <input type="radio" class="form-control" name="bbg_xiv-interface" value="mouse" id="bbg_xiv-interface_mouse"> <span class="bbg_xiv-radio_text">{$translations['Mouse']}</span> </span> <span class="bbg_xiv-radio_input"> <input type="radio" class="form-control" name="bbg_xiv-interface" value="touch" id="bbg_xiv-interface_touch"> <span class="bbg_xiv-radio_text">{$translations['Touch']}</span> </span> <span class="bbg_xiv-radio_input"> <input type="radio" class="form-control" name="bbg_xiv-interface" value="null" id="bbg_xiv-interface_null" disabled> <span class="bbg_xiv-radio_text"></span> </span> </div> </div> <br> <div class="form-group"> <div class="col-sm-offset-4 col-sm-8"> <button type="button" class="btn btn-primary bbg_xiv-options_btn bbg_xiv-save_options">{$translations['Save']}</button> <button type="button" class="btn btn-default bbg_xiv-options_btn bbg_xiv-cancel_options">{$translations['Cancel']}</button> <button type="button" class="btn btn-info bbg_xiv-options_btn bbg_xiv-help_options">{$translations['Help']}</button> </div> </div> </form> </div> </div> EOD; return $output; }
/** * Handles serving an API request. * * Matches the current server URI to a route and runs the first matching * callback then outputs a JSON representation of the returned value. * * @since 4.4.0 * @access public * * @see WP_REST_Server::dispatch() * * @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used. * Default null. * @return false|null Null if not served and a HEAD request, false otherwise. */ public function serve_request($path = null) { $content_type = isset($_GET['_jsonp']) ? 'application/javascript' : 'application/json'; $this->send_header('Content-Type', $content_type . '; charset=' . get_option('blog_charset')); $this->send_header('X-Robots-Tag', 'noindex'); $api_root = get_rest_url(); if (!empty($api_root)) { $this->send_header('Link', '<' . esc_url_raw($api_root) . '>; rel="https://api.w.org/"'); } /* * Mitigate possible JSONP Flash attacks. * * https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ */ $this->send_header('X-Content-Type-Options', 'nosniff'); $this->send_header('Access-Control-Expose-Headers', 'X-WP-Total, X-WP-TotalPages'); $this->send_header('Access-Control-Allow-Headers', 'Authorization'); /** * Send nocache headers on authenticated requests. * * @since 4.4.0 * * @param bool $rest_send_nocache_headers Whether to send no-cache headers. */ $send_no_cache_headers = apply_filters('rest_send_nocache_headers', is_user_logged_in()); if ($send_no_cache_headers) { foreach (wp_get_nocache_headers() as $header => $header_value) { $this->send_header($header, $header_value); } } /** * Filters whether the REST API is enabled. * * @since 4.4.0 * * @param bool $rest_enabled Whether the REST API is enabled. Default true. */ $enabled = apply_filters('rest_enabled', true); /** * Filters whether jsonp is enabled. * * @since 4.4.0 * * @param bool $jsonp_enabled Whether jsonp is enabled. Default true. */ $jsonp_enabled = apply_filters('rest_jsonp_enabled', true); $jsonp_callback = null; if (!$enabled) { echo $this->json_error('rest_disabled', __('The REST API is disabled on this site.'), 404); return false; } if (isset($_GET['_jsonp'])) { if (!$jsonp_enabled) { echo $this->json_error('rest_callback_disabled', __('JSONP support is disabled on this site.'), 400); return false; } $jsonp_callback = $_GET['_jsonp']; if (!wp_check_jsonp_callback($jsonp_callback)) { echo $this->json_error('rest_callback_invalid', __('The JSONP callback function is invalid.'), 400); return false; } } if (empty($path)) { if (isset($_SERVER['PATH_INFO'])) { $path = $_SERVER['PATH_INFO']; } else { $path = '/'; } } $request = new WP_REST_Request($_SERVER['REQUEST_METHOD'], $path); $request->set_query_params(wp_unslash($_GET)); $request->set_body_params(wp_unslash($_POST)); $request->set_file_params($_FILES); $request->set_headers($this->get_headers(wp_unslash($_SERVER))); $request->set_body($this->get_raw_data()); /* * HTTP method override for clients that can't use PUT/PATCH/DELETE. First, we check * $_GET['_method']. If that is not set, we check for the HTTP_X_HTTP_METHOD_OVERRIDE * header. */ if (isset($_GET['_method'])) { $request->set_method($_GET['_method']); } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { $request->set_method($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); } $result = $this->check_authentication(); if (!is_wp_error($result)) { $result = $this->dispatch($request); } // Normalize to either WP_Error or WP_REST_Response... $result = rest_ensure_response($result); // ...then convert WP_Error across. if (is_wp_error($result)) { $result = $this->error_to_response($result); } /** * Filters the API response. * * Allows modification of the response before returning. * * @since 4.4.0 * @since 4.5.0 Applied to embedded responses. * * @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response. * @param WP_REST_Server $this Server instance. * @param WP_REST_Request $request Request used to generate the response. */ $result = apply_filters('rest_post_dispatch', rest_ensure_response($result), $this, $request); // Wrap the response in an envelope if asked for. if (isset($_GET['_envelope'])) { $result = $this->envelope_response($result, isset($_GET['_embed'])); } // Send extra data from response objects. $headers = $result->get_headers(); $this->send_headers($headers); $code = $result->get_status(); $this->set_status($code); /** * Filters whether the request has already been served. * * Allow sending the request manually - by returning true, the API result * will not be sent to the client. * * @since 4.4.0 * * @param bool $served Whether the request has already been served. * Default false. * @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response. * @param WP_REST_Request $request Request used to generate the response. * @param WP_REST_Server $this Server instance. */ $served = apply_filters('rest_pre_serve_request', false, $result, $request, $this); if (!$served) { if ('HEAD' === $request->get_method()) { return null; } // Embed links inside the request. $result = $this->response_to_data($result, isset($_GET['_embed'])); $result = wp_json_encode($result); $json_error_message = $this->get_json_last_error(); if ($json_error_message) { $json_error_obj = new WP_Error('rest_encode_error', $json_error_message, array('status' => 500)); $result = $this->error_to_response($json_error_obj); $result = wp_json_encode($result->data[0]); } if ($jsonp_callback) { // Prepend '/**/' to mitigate possible JSONP Flash attacks // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ echo '/**/' . $jsonp_callback . '(' . $result . ')'; } else { echo $result; } } return null; }
/** * Changes database results into REST API entities * @param \EEM_Base $model * @param array $db_row like results from $wpdb->get_results() * @param string $include string indicating which fields to include in the response, * including fields on related entities. * Eg, when querying for events, an include string like: * "...&include=EVT_name,EVT_desc,Datetime, Datetime.Ticket.TKT_ID, Datetime.Ticket.TKT_name, Datetime.Ticket.TKT_price" * instructs us to only include the event's name and description, * each related datetime, and each related datetime's ticket's name and price. * Eg json would be: * '{ * "EVT_ID":12, * "EVT_name":"star wars party", * "EVT_desc":"this is the party you are looking for...", * "datetimes":[{ * "DTT_ID":123,..., * "tickets":[{ * "TKT_ID":234, * "TKT_name":"student rate", * "TKT_price":32.0 * },...] * }] * }', * ie, events with all their associated datetimes * (including ones that are trashed) embedded in the json object, * and each datetime also has each associated ticket embedded in its json object. * @param string $context one of the return values from EEM_Base::valid_cap_contexts() * @return array ready for being converted into json for sending to client */ public function create_entity_from_wpdb_result($model, $db_row, $include, $context) { if ($include == null) { $include = '*'; } if ($context == null) { $context = \EEM_Base::caps_read; } $result = $model->deduce_fields_n_values_from_cols_n_values($db_row); $result = array_intersect_key($result, $this->get_model_version_info()->fields_on_model_in_this_version($model)); foreach ($result as $field_name => $raw_field_value) { $field_obj = $model->field_settings_for($field_name); $field_value = $field_obj->prepare_for_set_from_db($raw_field_value); if ($this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_ignored())) { unset($result[$field_name]); } elseif ($this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_rendered_format())) { $result[$field_name] = array('raw' => $field_obj->prepare_for_get($field_value), 'rendered' => $field_obj->prepare_for_pretty_echoing($field_value)); } elseif ($this->is_subclass_of_one($field_obj, $this->get_model_version_info()->fields_that_have_pretty_format())) { $result[$field_name] = array('raw' => $field_obj->prepare_for_get($field_value), 'pretty' => $field_obj->prepare_for_pretty_echoing($field_value)); } elseif ($field_obj instanceof \EE_Datetime_Field) { if ($raw_field_value instanceof \DateTime) { $raw_field_value = $raw_field_value->format('c'); } $result[$field_name] = mysql_to_rfc3339($raw_field_value); } else { $value_prepared = $field_obj->prepare_for_get($field_value); $result[$field_name] = $value_prepared === INF ? EE_INF_IN_DB : $value_prepared; } } if ($model instanceof \EEM_CPT_Base) { $attachment = wp_get_attachment_image_src(get_post_thumbnail_id($db_row[$model->get_primary_key_field()->get_qualified_column()]), 'full'); $result['featured_image_url'] = !empty($attachment) ? $attachment[0] : null; $result['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]); } //add links to related data $result['_links'] = array('self' => array(array('href' => $this->get_versioned_link_to(\EEH_Inflector::pluralize_and_lower($model->get_this_model_name()) . '/' . $result[$model->primary_key_name()]))), 'collection' => array(array('href' => $this->get_versioned_link_to(\EEH_Inflector::pluralize_and_lower($model->get_this_model_name()))))); global $wp_rest_server; if ($model instanceof \EEM_CPT_Base && $wp_rest_server instanceof \WP_REST_Server && $wp_rest_server->get_route_options('/wp/v2/posts')) { $result['_links'][\EED_Core_Rest_Api::ee_api_link_namespace . 'self_wp_post'] = array(array('href' => rest_url('/wp/v2/posts/' . $db_row[$model->get_primary_key_field()->get_qualified_column()]), 'single' => true)); } //filter fields if specified $includes_for_this_model = $this->extract_includes_for_this_model($include); if (!empty($includes_for_this_model)) { if ($model->has_primary_key_field()) { //always include the primary key $includes_for_this_model[] = $model->primary_key_name(); } $result = array_intersect_key($result, array_flip($includes_for_this_model)); } //add meta links and possibly include related models $relation_settings = apply_filters('FHEE__Read__create_entity_from_wpdb_result__related_models_to_include', $model->relation_settings()); foreach ($relation_settings as $relation_name => $relation_obj) { $related_model_part = $this->get_related_entity_name($relation_name, $relation_obj); if (empty($includes_for_this_model) || isset($includes_for_this_model['meta'])) { $result['_links'][\EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(array('href' => $this->get_versioned_link_to(\EEH_Inflector::pluralize_and_lower($model->get_this_model_name()) . '/' . $result[$model->primary_key_name()] . '/' . $related_model_part), 'single' => $relation_obj instanceof \EE_Belongs_To_Relation ? true : false)); } $related_fields_to_include = $this->extract_includes_for_this_model($include, $relation_name); if ($related_fields_to_include) { $pretend_related_request = new \WP_REST_Request(); $pretend_related_request->set_query_params(array('caps' => $context, 'include' => $this->extract_includes_for_this_model($include, $relation_name))); $related_results = $this->get_entities_from_relation($result[$model->primary_key_name()], $relation_obj, $pretend_related_request); $result[$related_model_part] = $related_results instanceof \WP_Error ? null : $related_results; } } $result = apply_filters('FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal', $result, $model, $context); $result_without_inaccessible_fields = Capabilities::filter_out_inaccessible_entity_fields($result, $model, $context, $this->get_model_version_info()); $this->_set_debug_info('inaccessible fields', array_keys(array_diff_key($result, $result_without_inaccessible_fields))); return apply_filters('FHEE__Read__create_entity_from_wpdb_results__entity_return', $result_without_inaccessible_fields, $model, $context); }
/** * Test date_query default * * @since 0.2.0 * * @covers \calderawp\swp_api\route::the_route() * @covers \calderawp\swp_api\route::the_search() */ public function test_date_default() { $request = new \WP_REST_Request('GET', '/swp_api/search'); $request->set_query_params(array('s' => 'Elrond')); $response = $this->server->dispatch($request); //test we have the right args $params = (array) $request->get_params(); $this->assertFalse($params['date_query']); //test for correct response code $response = rest_ensure_response($response); $this->assertEquals(200, $response->get_status()); }
/** * Embeds the links from the data into the request. * * @since 4.4.0 * @access protected * * @param array $data Data from the request. * @return array { * Data with sub-requests embedded. * * @type array [$_links] Links. * @type array [$_embedded] Embeddeds. * } */ protected function embed_links($data) { if (empty($data['_links'])) { return $data; } $embedded = array(); $api_root = rest_url(); foreach ($data['_links'] as $rel => $links) { // Ignore links to self, for obvious reasons. if ('self' === $rel) { continue; } $embeds = array(); foreach ($links as $item) { // Determine if the link is embeddable. if (empty($item['embeddable']) || strpos($item['href'], $api_root) !== 0) { // Ensure we keep the same order. $embeds[] = array(); continue; } // Run through our internal routing and serve. $route = substr($item['href'], strlen(untrailingslashit($api_root))); $query_params = array(); // Parse out URL query parameters. $parsed = parse_url($route); if (empty($parsed['path'])) { $embeds[] = array(); continue; } if (!empty($parsed['query'])) { parse_str($parsed['query'], $query_params); // Ensure magic quotes are stripped. if (get_magic_quotes_gpc()) { $query_params = stripslashes_deep($query_params); } } // Embedded resources get passed context=embed. if (empty($query_params['context'])) { $query_params['context'] = 'embed'; } $request = new WP_REST_Request('GET', $parsed['path']); $request->set_query_params($query_params); $response = $this->dispatch($request); $embeds[] = $this->response_to_data($response, false); } // Determine if any real links were found. $has_links = count(array_filter($embeds)); if ($has_links) { $embedded[$rel] = $embeds; } } if (!empty($embedded)) { $data['_embedded'] = $embedded; } return $data; }
/** * Test getting customers. * * @since 2.7.0 */ public function test_get_customers() { wp_set_current_user(1); $customer_1 = WC_Helper_Customer::create_customer(); WC_Helper_Customer::create_customer('test2', 'test2', '*****@*****.**'); $request = new WP_REST_Request('GET', '/wc/v1/customers'); $request->set_query_params(array('orderby' => 'id')); $response = $this->server->dispatch($request); $customers = $response->get_data(); $this->assertEquals(200, $response->get_status()); $this->assertEquals(2, count($customers)); $this->assertContains(array('id' => $customer_1->get_id(), 'date_created' => wc_rest_prepare_date_response(date('Y-m-d H:i:s', $customer_1->get_date_created())), 'date_modified' => wc_rest_prepare_date_response(date('Y-m-d H:i:s', $customer_1->get_date_modified())), 'email' => '*****@*****.**', 'first_name' => 'Justin', 'last_name' => '', 'role' => 'customer', 'username' => 'testcustomer', 'billing' => array('first_name' => '', 'last_name' => '', 'company' => '', 'address_1' => '123 South Street', 'address_2' => 'Apt 1', 'city' => 'Philadelphia', 'state' => 'PA', 'postcode' => '19123', 'country' => 'US', 'email' => '', 'phone' => ''), 'shipping' => array('first_name' => '', 'last_name' => '', 'company' => '', 'address_1' => '123 South Street', 'address_2' => 'Apt 1', 'city' => 'Philadelphia', 'state' => 'PA', 'postcode' => '19123', 'country' => 'US'), 'is_paying_customer' => false, 'orders_count' => 0, 'total_spent' => '0.00', 'avatar_url' => $customer_1->get_avatar_url(), 'meta_data' => array(), '_links' => array('self' => array(array('href' => rest_url('/wc/v1/customers/' . $customer_1->get_id() . ''))), 'collection' => array(array('href' => rest_url('/wc/v1/customers'))))), $customers); }