From 7ed52d98d76d0999882707f88e88d8cd577bbd05 Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 13 Nov 2023 17:21:06 +0100 Subject: [PATCH 1/2] (feat:) in the REST API controllers support passing all request params to WP_Query --- src/Base/RestAPI/Controllers/BaseController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Base/RestAPI/Controllers/BaseController.php b/src/Base/RestAPI/Controllers/BaseController.php index ec0b5b8..da56a0e 100644 --- a/src/Base/RestAPI/Controllers/BaseController.php +++ b/src/Base/RestAPI/Controllers/BaseController.php @@ -55,10 +55,10 @@ protected function addPaginator(array $data, WP_Query $query): array */ protected function getPaginatorParams(WP_REST_Request $request, int $limit = 10): array { - return [ + return array_merge($request->get_params(), [ 'posts_per_page' => $request->get_param('limit') ?: $limit, 'paged' => $request->get_param('page') ?: 0 - ]; + ]); } /** From 9b0fc04debf292d79328484fa97aa3a296b0bfbf Mon Sep 17 00:00:00 2001 From: Mike van den Hoek Date: Thu, 4 Jan 2024 16:24:32 +0100 Subject: [PATCH 2/2] (feat): validate passed query params before initiating WP_Query --- src/Base/Repositories/AbstractRepository.php | 75 ++++++++++++++----- .../RestAPI/Controllers/BaseController.php | 35 +++++++-- src/Base/RestAPI/SharedFields/ItemsField.php | 8 +- 3 files changed, 87 insertions(+), 31 deletions(-) diff --git a/src/Base/Repositories/AbstractRepository.php b/src/Base/Repositories/AbstractRepository.php index 2fa8e71..174c687 100644 --- a/src/Base/Repositories/AbstractRepository.php +++ b/src/Base/Repositories/AbstractRepository.php @@ -7,11 +7,11 @@ namespace OWC\PDC\Base\Repositories; use Closure; -use WP_Post; -use WP_Query; +use OWC\PDC\Base\Exceptions\PropertyNotExistsException; use OWC\PDC\Base\Support\CreatesFields; use OWC\PDC\Base\Support\Traits\QueryHelpers; -use OWC\PDC\Base\Exceptions\PropertyNotExistsException; +use WP_Post; +use WP_Query; /** * PDC item object with default quering and methods. @@ -98,8 +98,6 @@ public function __construct() /** * Get all the items from the database. - * - * @return array */ public function all(): array { @@ -107,26 +105,22 @@ public function all(): array 'post_type' => [$this->posttype], ]); - $this->query = new WP_Query($args); + $this->query = new WP_Query($this->cleanParams($args)); return array_map([$this, 'transform'], $this->getQuery()->posts); } /** * Find a particular pdc item by ID. - * - * @param int $id - * - * @return array */ - public function find(int $id) + public function find(int $id): ?array { $args = array_merge($this->queryArgs, [ 'p' => $id, 'post_type' => [$this->posttype], ]); - $this->query = new WP_Query($args); + $this->query = new WP_Query($this->cleanParams($args)); if (empty($this->getQuery()->posts)) { return null; @@ -137,19 +131,15 @@ public function find(int $id) /** * Find a particular pdc item by slug. - * - * @param string $slug - * - * @return array|null */ - public function findBySlug(string $slug) + public function findBySlug(string $slug): ?array { $args = array_merge($this->queryArgs, [ 'name' => $slug, 'post_type' => [$this->posttype], ]); - $this->query = new WP_Query($args); + $this->query = new WP_Query($this->cleanParams($args)); if (empty($this->getQuery()->posts)) { return null; @@ -158,6 +148,53 @@ public function findBySlug(string $slug) return $this->transform(reset($this->getQuery()->posts)); } + protected function cleanParams(array $args): array + { + $args = $this->validatePostStatusParam($args); + $args = $this->cleanWronglyNestedQueryParams($args, 'tax_query'); + $args = $this->cleanWronglyNestedQueryParams($args, 'meta_query'); + + return $args; + } + + protected function validatePostStatusParam(array $args): array + { + if (empty($args['post_status'])) { + return $args; + } + + if (! is_string($args['post_status']) && ! is_array($args['post_status'])) { + unset($args['post_status']); + + return $args; + } + + if (is_string($args['post_status'])) { + $args['post_status'] = [$args['post_status']]; + } + + if (! \is_user_logged_in()) { + $args['post_status'] = ['publish']; + } + + return $args; + } + + protected function cleanWronglyNestedQueryParams(array $args, string $key): array + { + if (empty($args[$key]) || ! is_array($args[$key])) { + return $args; + } + + foreach ($args[$key] as &$query) { + if (is_array($query) && ! empty($query[0])) { + $query = call_user_func_array('array_merge', $query); + } + } + + return $args; + } + /** * Get the WP_Query object. * @@ -273,7 +310,7 @@ public function transform(WP_Post $post) 'date' => $post->post_date, 'slug' => $post->post_name, 'post_status' => $post->post_status, - 'protected' => ! $this->isAllowed($post) + 'protected' => ! $this->isAllowed($post), ]; $data = $this->assignFields($data, $post); diff --git a/src/Base/RestAPI/Controllers/BaseController.php b/src/Base/RestAPI/Controllers/BaseController.php index da56a0e..92f14c6 100644 --- a/src/Base/RestAPI/Controllers/BaseController.php +++ b/src/Base/RestAPI/Controllers/BaseController.php @@ -6,9 +6,9 @@ namespace OWC\PDC\Base\RestAPI\Controllers; +use OWC\PDC\Base\Foundation\Plugin; use WP_Query; use WP_REST_Request; -use OWC\PDC\Base\Foundation\Plugin; /** * Controller which handels general quering, such as pagination. @@ -39,14 +39,14 @@ protected function addPaginator(array $data, WP_Query $query): array $page = 0 == $page ? 1 : $page; return array_merge([ - 'data' => $data + 'data' => $data, ], [ 'pagination' => [ - 'total_count' => (int) $query->found_posts, - 'total_pages' => $query->max_num_pages, + 'total_count' => (int) $query->found_posts, + 'total_pages' => $query->max_num_pages, 'current_page' => $page, - 'limit' => $query->get('posts_per_page') - ] + 'limit' => $query->get('posts_per_page'), + ], ]); } @@ -55,10 +55,29 @@ protected function addPaginator(array $data, WP_Query $query): array */ protected function getPaginatorParams(WP_REST_Request $request, int $limit = 10): array { - return array_merge($request->get_params(), [ + $params = array_merge($request->get_params(), [ 'posts_per_page' => $request->get_param('limit') ?: $limit, - 'paged' => $request->get_param('page') ?: 0 + 'paged' => $request->get_param('page') ?: 0, ]); + + return $this->validateQueryParams($params); + } + + protected function validateQueryParams(array $params): array + { + $allowedQueryParams = [ + 'include-connected', + 'tax_query', + 'meta_query', + 'posts_per_page', + 'paged', + 'post_type', + 'post_status', + ]; + + return array_filter($params, function ($param) use ($allowedQueryParams) { + return in_array($param, $allowedQueryParams); + }, ARRAY_FILTER_USE_KEY); } /** diff --git a/src/Base/RestAPI/SharedFields/ItemsField.php b/src/Base/RestAPI/SharedFields/ItemsField.php index 22d36bd..d5609c7 100644 --- a/src/Base/RestAPI/SharedFields/ItemsField.php +++ b/src/Base/RestAPI/SharedFields/ItemsField.php @@ -6,10 +6,10 @@ namespace OWC\PDC\Base\RestAPI\SharedFields; -use WP_Post; -use OWC\PDC\Base\Support\Traits\QueryHelpers; -use OWC\PDC\Base\Support\Traits\CheckPluginActive; use OWC\PDC\Base\RestAPI\ItemFields\ConnectedField; +use OWC\PDC\Base\Support\Traits\CheckPluginActive; +use OWC\PDC\Base\Support\Traits\QueryHelpers; +use WP_Post; /** * Adds connected fields to item in API. @@ -44,7 +44,7 @@ protected function extraQueryArgs(string $type): array } $query['connected_query'] = [ - 'post_status' => ['publish', 'draft'], + 'post_status' => ['publish', 'draft'], // Draft only for logged in users? ]; return $query;