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);
 }
Exemple #2
0
 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]);
     }
 }
Exemple #12
0
 /**
  * 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('/&#8216;|&#8217;|&#8220;|&#8221;|&#8242;|&#8243;/', '"', $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;
 }
Exemple #22
-1
 /**
  * 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);
 }