| @@ -11,8 +11,10 @@ | ||
| 11 | 11 | use Yoast\WP\SEO\Bulk_Editor\Application\Posts\Posts_Collector_Interface; |
| 12 | 12 | use Yoast\WP\SEO\Bulk_Editor\Application\Posts\Posts_Repository; |
| 13 | 13 | use Yoast\WP\SEO\Bulk_Editor\Domain\Posts\Posts_Query; |
| 14 | 14 | use Yoast\WP\SEO\Conditionals\No_Conditionals; |
| 15 | +use Yoast\WP\SEO\Helpers\Options_Helper; | |
| 16 | +use Yoast\WP\SEO\Helpers\Post_Type_Helper; | |
| 15 | 17 | use Yoast\WP\SEO\Helpers\User_Helper; |
| 16 | 18 | use Yoast\WP\SEO\Main; |
| 17 | 19 | use Yoast\WP\SEO\Routes\Route_Interface; |
| 18 | 20 | |
| @@ -79,8 +81,22 @@ | ||
| 79 | 81 | */ |
| 80 | 82 | private $user_helper; |
| 81 | 83 | |
| 82 | 84 | /** |
| 85 | + * The options helper. | |
| 86 | + * | |
| 87 | + * @var Options_Helper | |
| 88 | + */ | |
| 89 | + private $options_helper; | |
| 90 | + | |
| 91 | + /** | |
| 92 | + * The post type helper. | |
| 93 | + * | |
| 94 | + * @var Post_Type_Helper | |
| 95 | + */ | |
| 96 | + private $post_type_helper; | |
| 97 | + | |
| 98 | + /** | |
| 83 | 99 | * The constructor. |
| 84 | 100 | * |
| 85 | 101 | * @param Posts_Repository $posts_repository The posts repository. |
| 86 | 102 | * @param Content_Types_Repository $content_types_repository The content types repository. |
| @@ -85,19 +101,25 @@ | ||
| 85 | 101 | * @param Posts_Repository $posts_repository The posts repository. |
| 86 | 102 | * @param Content_Types_Repository $content_types_repository The content types repository. |
| 87 | 103 | * @param Content_Type_Access_Checker_Interface $content_type_access_checker The content type access checker. |
| 88 | 104 | * @param User_Helper $user_helper The user helper. |
| 105 | + * @param Options_Helper $options_helper The options helper. | |
| 106 | + * @param Post_Type_Helper $post_type_helper The post type helper. | |
| 89 | 107 | */ |
| 90 | 108 | public function __construct( |
| 91 | 109 | Posts_Repository $posts_repository, |
| 92 | 110 | Content_Types_Repository $content_types_repository, |
| 93 | 111 | Content_Type_Access_Checker_Interface $content_type_access_checker, |
| 94 | - User_Helper $user_helper | |
| 112 | + User_Helper $user_helper, | |
| 113 | + Options_Helper $options_helper, | |
| 114 | + Post_Type_Helper $post_type_helper | |
| 95 | 115 | ) { |
| 96 | 116 | $this->posts_repository = $posts_repository; |
| 97 | 117 | $this->content_types_repository = $content_types_repository; |
| 98 | 118 | $this->content_type_access_checker = $content_type_access_checker; |
| 99 | 119 | $this->user_helper = $user_helper; |
| 120 | + $this->options_helper = $options_helper; | |
| 121 | + $this->post_type_helper = $post_type_helper; | |
| 100 | 122 | } |
| 101 | 123 | |
| 102 | 124 | /** |
| 103 | 125 | * Registers routes with WordPress. |
| @@ -110,15 +132,15 @@ | ||
| 110 | 132 | self::ROUTE_PREFIX, |
| 111 | 133 | [ |
| 112 | 134 | 'methods' => 'GET', |
| 113 | 135 | 'args' => [ |
| 114 | - 'content_type' => [ | |
| 136 | + 'content_type' => [ | |
| 115 | 137 | 'required' => true, |
| 116 | 138 | 'type' => 'string', |
| 117 | 139 | 'description' => 'The content type to fetch posts for.', |
| 118 | 140 | 'sanitize_callback' => 'sanitize_text_field', |
| 119 | 141 | ], |
| 120 | - 'per_page' => [ | |
| 142 | + 'per_page' => [ | |
| 121 | 143 | 'required' => false, |
| 122 | 144 | 'type' => 'integer', |
| 123 | 145 | 'default' => self::DEFAULT_PER_PAGE, |
| 124 | 146 | 'minimum' => 1, |
| @@ -125,9 +147,9 @@ | ||
| 125 | 147 | 'maximum' => self::MAX_PER_PAGE, |
| 126 | 148 | 'description' => 'The number of posts to fetch.', |
| 127 | 149 | 'sanitize_callback' => 'absint', |
| 128 | 150 | ], |
| 129 | - 'page' => [ | |
| 151 | + 'page' => [ | |
| 130 | 152 | 'required' => false, |
| 131 | 153 | 'type' => 'integer', |
| 132 | 154 | 'default' => 1, |
| 133 | 155 | 'minimum' => 1, |
| @@ -133,9 +155,9 @@ | ||
| 133 | 155 | 'minimum' => 1, |
| 134 | 156 | 'description' => 'The page of posts to fetch.', |
| 135 | 157 | 'sanitize_callback' => 'absint', |
| 136 | 158 | ], |
| 137 | - 'search' => [ | |
| 159 | + 'search' => [ | |
| 138 | 160 | 'required' => false, |
| 139 | 161 | 'type' => 'string', |
| 140 | 162 | 'default' => '', |
| 141 | 163 | 'description' => 'The term to search posts by.', |
| @@ -140,9 +162,9 @@ | ||
| 140 | 162 | 'default' => '', |
| 141 | 163 | 'description' => 'The term to search posts by.', |
| 142 | 164 | 'sanitize_callback' => 'sanitize_text_field', |
| 143 | 165 | ], |
| 144 | - 'status' => [ | |
| 166 | + 'status' => [ | |
| 145 | 167 | 'required' => false, |
| 146 | 168 | 'type' => 'array', |
| 147 | 169 | 'default' => Posts_Collector_Interface::STATUSES, |
| 148 | 170 | 'items' => [ |
| @@ -150,8 +172,29 @@ | ||
| 150 | 172 | 'enum' => Posts_Collector_Interface::STATUSES, |
| 151 | 173 | ], |
| 152 | 174 | 'description' => 'The post statuses to include.', |
| 153 | 175 | ], |
| 176 | + 'needs_improvement' => [ | |
| 177 | + 'required' => false, | |
| 178 | + 'type' => 'array', | |
| 179 | + 'default' => [], | |
| 180 | + 'items' => [ | |
| 181 | + 'type' => 'string', | |
| 182 | + 'enum' => Posts_Collector_Interface::NEEDS_IMPROVEMENT_FIELDS, | |
| 183 | + ], | |
| 184 | + 'description' => 'The fields to filter posts by; a field matches when it is empty, or (for search fields, while SEO analysis is enabled and the content type shows Yoast\'s controls and assessments) when its score needs improvement.', | |
| 185 | + ], | |
| 186 | + 'include' => [ | |
| 187 | + 'required' => false, | |
| 188 | + 'type' => 'array', | |
| 189 | + 'default' => [], | |
| 190 | + 'maxItems' => self::MAX_PER_PAGE, | |
| 191 | + 'items' => [ | |
| 192 | + 'type' => 'integer', | |
| 193 | + 'minimum' => 1, | |
| 194 | + ], | |
| 195 | + 'description' => 'Limits the posts to these post IDs, e.g. a selection carried over from the posts overview.', | |
| 196 | + ], | |
| 154 | 197 | ], |
| 155 | 198 | 'callback' => [ $this, 'get_posts' ], |
| 156 | 199 | 'permission_callback' => [ $this, 'check_permissions' ], |
| 157 | 200 | ], |
| @@ -188,8 +231,17 @@ | ||
| 188 | 231 | if ( ! $this->content_type_access_checker->can_edit_others( $content_type ) ) { |
| 189 | 232 | $author_id = $this->user_helper->get_current_user_id(); |
| 190 | 233 | } |
| 191 | 234 | |
| 235 | + // The per-field scores only back the filter while SEO analysis is on globally and the content type | |
| 236 | + // shows Yoast's controls and assessments; otherwise they go stale and the filter falls back to the | |
| 237 | + // empty-field check. | |
| 238 | + $scores_enabled = $this->options_helper->get( 'keyword_analysis_active' ) === true | |
| 239 | + && $this->post_type_helper->has_metabox( $content_type ); | |
| 240 | + | |
| 241 | + // The schema already coerces the items to positive integers; deduplicate on top of that. | |
| 242 | + $include = \array_values( \array_unique( \array_map( 'intval', (array) $request->get_param( 'include' ) ) ) ); | |
| 243 | + | |
| 192 | 244 | $query = new Posts_Query( |
| 193 | 245 | $content_type, |
| 194 | 246 | (int) $request->get_param( 'page' ), |
| 195 | 247 | (int) $request->get_param( 'per_page' ), |
| @@ -195,8 +247,11 @@ | ||
| 195 | 247 | (int) $request->get_param( 'per_page' ), |
| 196 | 248 | (string) $request->get_param( 'search' ), |
| 197 | 249 | $statuses, |
| 198 | 250 | $author_id, |
| 251 | + (array) $request->get_param( 'needs_improvement' ), | |
| 252 | + $scores_enabled, | |
| 253 | + $include, | |
| 199 | 254 | ); |
| 200 | 255 | |
| 201 | 256 | // Posts the current user cannot edit are returned locked and without their SEO data; the per-post |
| 202 | 257 | // permission is resolved while collecting the page. |