From 5afad7d07c5a35d5c1481e90255fd9802132f346 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Tue, 6 Jun 2023 22:18:32 +0100 Subject: [PATCH 01/20] Add methods to hide filled and expired posts - Adds two new methods to hide filled and expired posts - Will investigate to see if it's possible to do the same, but more performantly and without modifying `meta_query` --- includes/class-wp-job-manager-post-types.php | 90 ++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 88e6b2f93..8f36cfdb5 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -87,6 +87,8 @@ public function __construct() { add_action( 'transition_post_status', [ $this, 'track_job_submission' ], 10, 3 ); add_action( 'parse_query', [ $this, 'add_feed_query_args' ] ); + add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_job_listings_from_search' ] ); + add_action( 'pre_get_posts', [ $this, 'maybe_hide_expired_job_listings_from_search' ] ); // Single job content. $this->job_content_filter( true ); @@ -659,6 +661,93 @@ public function job_feed() { remove_filter( 'posts_search', 'get_job_listings_keyword_search' ); } + /** + * Maybe hide filled job listings from search. + * + * @param WP_Query $query Query object. + */ + public function maybe_hide_filled_job_listings_from_search( $query ) { + if ( ! is_admin() && $query->is_search() && 1 === absint( get_option( 'job_manager_hide_filled_positions' ) ) ) { + $meta_query = $query->get( 'meta_query' ); + + if ( ! is_array( $meta_query ) ) { + $meta_query = []; + } + + $meta_query[] = [ + 'relation' => 'OR', + [ + 'key' => '_filled', + 'value' => '0', + 'compare' => '=', + ], + [ + 'key' => '_filled', + 'compare' => 'NOT EXISTS', + ], + ]; + + $query->set( 'meta_query', $meta_query ); + } + } + + /** + * Maybe hide expired job listings from search. + * + * @param WP_Query $query Query object. + */ + public function maybe_hide_expired_job_listings_from_search( $query ) { + if ( ! is_admin() && $query->is_search() && 1 === absint( get_option( 'job_manager_hide_expired' ) ) ) { + $meta_query = $query->get( 'meta_query' ); + + if ( ! is_array( $meta_query ) ) { + $meta_query = []; + } + + $meta_query[] = [ + 'relation' => 'OR', + [ + 'key' => '_job_expires', + 'value' => gmdate( 'Y-m-d' ), + 'compare' => '>=', + 'type' => 'DATE', + ], + [ + 'key' => '_job_expires', + 'compare' => 'NOT EXISTS', + ], + ]; + + $query->set( 'meta_query', $meta_query ); + } + } + + /** + * Hide expired listings from the search results. + * + * @param array $listings + * + * @return array + */ + public function hide_expired( $listings ) { + + // Get all expired listings. + $expired_listings = get_posts( + [ + 'post_type' => 'job_listing', + 'post_status' => 'expired', + ] + ); + + // Remove expired listings from the search results. + foreach ( $expired_listings as $listing ) { + unset( $listings[ $listing->ID ] ); + } + + return $listings; + + } + /** * Adds query arguments in order to make sure that the feed properly queries the 'job_listing' type. * @@ -1898,4 +1987,5 @@ public function delete_user_add_job_listings_post_type( $types ) { return $types; } + } From 7a924f644d9aa140551a1c1ad3ded46bc8533f86 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Wed, 7 Jun 2023 09:21:09 +0100 Subject: [PATCH 02/20] Remove unused method --- includes/class-wp-job-manager-post-types.php | 26 -------------------- 1 file changed, 26 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 8f36cfdb5..27c0f1631 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -722,32 +722,6 @@ public function maybe_hide_expired_job_listings_from_search( $query ) { } } - /** - * Hide expired listings from the search results. - * - * @param array $listings - * - * @return array - */ - public function hide_expired( $listings ) { - - // Get all expired listings. - $expired_listings = get_posts( - [ - 'post_type' => 'job_listing', - 'post_status' => 'expired', - ] - ); - - // Remove expired listings from the search results. - foreach ( $expired_listings as $listing ) { - unset( $listings[ $listing->ID ] ); - } - - return $listings; - - } - /** * Adds query arguments in order to make sure that the feed properly queries the 'job_listing' type. * From 41f374d0c4e99d97dc4b4b7685df40dd738a85cf Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Tue, 13 Jun 2023 15:24:52 +0100 Subject: [PATCH 03/20] Hide expired and filled jobs - Use get_posts to retrieve a list of Job IDs that are either expired, or marked as filled - Store IDs in a transient for caching/performance - Transient for the moment is set to cache for a day - Clear transients on post update if the post status changes, or post meta is updated - Transients are then regenerated when searching if the relevant "hide" option is enabled --- includes/class-wp-job-manager-post-types.php | 108 +++++++++++-------- wp-job-manager-functions.php | 23 ++++ 2 files changed, 85 insertions(+), 46 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 27c0f1631..6a507da06 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -87,8 +87,7 @@ public function __construct() { add_action( 'transition_post_status', [ $this, 'track_job_submission' ], 10, 3 ); add_action( 'parse_query', [ $this, 'add_feed_query_args' ] ); - add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_job_listings_from_search' ] ); - add_action( 'pre_get_posts', [ $this, 'maybe_hide_expired_job_listings_from_search' ] ); + add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_expired_job_listings_from_search' ] ); // Single job content. $this->job_content_filter( true ); @@ -662,63 +661,80 @@ public function job_feed() { } /** - * Maybe hide filled job listings from search. + * Get filled jobs. * - * @param WP_Query $query Query object. + * @return array */ - public function maybe_hide_filled_job_listings_from_search( $query ) { - if ( ! is_admin() && $query->is_search() && 1 === absint( get_option( 'job_manager_hide_filled_positions' ) ) ) { - $meta_query = $query->get( 'meta_query' ); - - if ( ! is_array( $meta_query ) ) { - $meta_query = []; - } + public function get_filled_job_listings(): array { + if ( ! get_option( 'job_manager_hide_filled_positions' ) ) { + return []; + } - $meta_query[] = [ - 'relation' => 'OR', - [ - 'key' => '_filled', - 'value' => '0', - 'compare' => '=', - ], + $filled_jobs_transient = get_transient( 'hide_filled_jobs_transient' ); + if ( false === $filled_jobs_transient ) { + $filled_jobs_transient = get_posts( [ - 'key' => '_filled', - 'compare' => 'NOT EXISTS', - ], - ]; - - $query->set( 'meta_query', $meta_query ); + 'post_type' => 'job_listing', + 'fields' => 'ids', + 'meta_query' => [ + [ + 'key' => '_filled', + 'value' => '1', + 'compare' => '=', + ], + ], + ] + ); + set_transient( 'hide_filled_jobs_transient', $filled_jobs_transient, DAY_IN_SECONDS ); } + return $filled_jobs_transient; } /** - * Maybe hide expired job listings from search. + * Get expired jobs. * - * @param WP_Query $query Query object. + * @return array */ - public function maybe_hide_expired_job_listings_from_search( $query ) { - if ( ! is_admin() && $query->is_search() && 1 === absint( get_option( 'job_manager_hide_expired' ) ) ) { - $meta_query = $query->get( 'meta_query' ); - - if ( ! is_array( $meta_query ) ) { - $meta_query = []; - } + public function get_expired_jobs_listings(): array { + if ( ! get_option( 'job_manager_hide_expired' ) ) { + return []; + } - $meta_query[] = [ - 'relation' => 'OR', + $expired_jobs_transient = get_transient( 'hide_expired_jobs_transient' ); + if ( false === $expired_jobs_transient ) { + $expired_jobs_transient = get_posts( [ - 'key' => '_job_expires', - 'value' => gmdate( 'Y-m-d' ), - 'compare' => '>=', - 'type' => 'DATE', - ], - [ - 'key' => '_job_expires', - 'compare' => 'NOT EXISTS', - ], - ]; + 'post_type' => 'job_listing', + 'post_status' => 'expired', + 'fields' => 'ids', + ] + ); + set_transient( 'hide_expired_jobs_transient', $expired_jobs_transient, DAY_IN_SECONDS ); + } + return $expired_jobs_transient; + } + + /** + * Maybe exclude expired and/or filled job listings from search. + * + * @param $query WP_Query $query + * + * @return void + */ + public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $query ): void { + if ( ! get_option( 'job_manager_hide_filled_positions' ) ) { + return; + } - $query->set( 'meta_query', $meta_query ); + if ( ! get_option( 'job_manager_hide_expired' ) ) { + return; + } + + if ( ! is_admin() && $query->is_search() ) { + $jobs_to_exclude = array_merge( $this->get_filled_job_listings(), $this->get_expired_jobs_listings() ); + if ( ! empty( $jobs_to_exclude ) ) { + $query->set( 'post__not_in', $jobs_to_exclude ); + } } } diff --git a/wp-job-manager-functions.php b/wp-job-manager-functions.php index b2498acff..eee78e432 100644 --- a/wp-job-manager-functions.php +++ b/wp-job-manager-functions.php @@ -1712,3 +1712,26 @@ function job_manager_get_salary_unit_options( $include_empty = true ) { */ return apply_filters( 'job_manager_get_salary_unit_options', $options, $include_empty ); } + +/** + * Delete filled_jobs_transient and expired_jobs_transient transients when a job listing is saved or updated. + * + * @param int $post_id The ID of the post being saved. + * @param WP_Post $post The post object being saved. + */ +function delete_job_listings_transients_on_save( int $post_id, WP_Post $post ): void { + + if ( 'job_listing' === $post->post_type ) { + + if ( '1' === get_post_meta( $post_id, '_filled', true ) ) { + delete_transient( 'hide_filled_jobs_transient' ); + } + + $post_status = [ 'publish', 'expired' ]; + if ( in_array( $post->post_status, $post_status, true ) ) { + delete_transient( 'hide_expired_jobs_transient' ); + } + } + +} +add_action( 'save_post', 'delete_job_listings_transients_on_save', 10, 2 ); From 2fccb3666690098c1966b6da79b9fd3c1e3d4140 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Tue, 13 Jun 2023 15:27:14 +0100 Subject: [PATCH 04/20] Remove an 's' from 'jobs' in method name --- includes/class-wp-job-manager-post-types.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 6a507da06..36a8b5f16 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -695,7 +695,7 @@ public function get_filled_job_listings(): array { * * @return array */ - public function get_expired_jobs_listings(): array { + public function get_expired_job_listings(): array { if ( ! get_option( 'job_manager_hide_expired' ) ) { return []; } @@ -731,7 +731,7 @@ public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $qu } if ( ! is_admin() && $query->is_search() ) { - $jobs_to_exclude = array_merge( $this->get_filled_job_listings(), $this->get_expired_jobs_listings() ); + $jobs_to_exclude = array_merge( $this->get_filled_job_listings(), $this->get_expired_job_listings() ); if ( ! empty( $jobs_to_exclude ) ) { $query->set( 'post__not_in', $jobs_to_exclude ); } From eeb42e00f2aec2ea5bb7adaa7f171c4203cccefd Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Tue, 13 Jun 2023 15:45:20 +0100 Subject: [PATCH 05/20] Update maybe_hide_filled_expired_job_listings_from_search logic - Updates the early return so that return properly if neither "hide_*" option is enabled, but if one is, allow the method to continue. --- includes/class-wp-job-manager-post-types.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 36a8b5f16..bc9fc443c 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -722,11 +722,10 @@ public function get_expired_job_listings(): array { * @return void */ public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $query ): void { - if ( ! get_option( 'job_manager_hide_filled_positions' ) ) { - return; - } + $hide_filled_positions = get_option( 'job_manager_hide_filled_positions' ); + $hide_expired = get_option( 'job_manager_hide_expired' ); - if ( ! get_option( 'job_manager_hide_expired' ) ) { + if ( ! $hide_filled_positions && ! $hide_expired ) { return; } From 068a162f435005da0f035285330387f8ffef46d8 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Tue, 13 Jun 2023 21:41:21 +0100 Subject: [PATCH 06/20] Account for 'trash' post status --- wp-job-manager-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wp-job-manager-functions.php b/wp-job-manager-functions.php index eee78e432..d4c5b3152 100644 --- a/wp-job-manager-functions.php +++ b/wp-job-manager-functions.php @@ -1727,7 +1727,7 @@ function delete_job_listings_transients_on_save( int $post_id, WP_Post $post ): delete_transient( 'hide_filled_jobs_transient' ); } - $post_status = [ 'publish', 'expired' ]; + $post_status = [ 'publish', 'expired', 'trash' ]; if ( in_array( $post->post_status, $post_status, true ) ) { delete_transient( 'hide_expired_jobs_transient' ); } From d1d04d450117cb0c207f0aa4476dc63b8b2206f0 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Wed, 21 Jun 2023 10:35:00 +0100 Subject: [PATCH 07/20] Add additional query checks Add checks for: - is_main_query - is_archive - 'job_listing' is the post type To ensure that we don't impact any non-main queries, queries that don't pertain to 'job_listing', and that might also happen on archive pages. --- includes/class-wp-job-manager-post-types.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index bc9fc443c..d43860d39 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -729,7 +729,14 @@ public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $qu return; } - if ( ! is_admin() && $query->is_search() ) { + if ( + ! is_admin() + && $query->is_main_query() + && 'job_listing' === $query->get( 'post_type' ) + && $query->is_search() + || $query->is_archive() + ) { + $jobs_to_exclude = array_merge( $this->get_filled_job_listings(), $this->get_expired_job_listings() ); if ( ! empty( $jobs_to_exclude ) ) { $query->set( 'post__not_in', $jobs_to_exclude ); From cd943134120586512fec152922e5934ba8cd6cd5 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Wed, 21 Jun 2023 10:48:45 +0100 Subject: [PATCH 08/20] Remove post_type check, as search page is querying "any" - && 'job_listing' === $query->get( 'post_type' ) --- includes/class-wp-job-manager-post-types.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index d43860d39..06573c801 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -732,7 +732,6 @@ public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $qu if ( ! is_admin() && $query->is_main_query() - && 'job_listing' === $query->get( 'post_type' ) && $query->is_search() || $query->is_archive() ) { From 3fedb2bc7c13d6254316e4a6a0dd4e69be6ccdb1 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Wed, 21 Jun 2023 11:48:21 +0100 Subject: [PATCH 09/20] Refactor hide/filled - Remove now unnecessary method to retrieve expired posts by ID - Exclude expired posts by setting the query to 'publish' post status only, if $hide_expired is enabled - Removed related expired transient code - Set the get_filled_job_listings() method to retrieve all posts, rather than 5 --- includes/class-wp-job-manager-post-types.php | 44 ++++++-------------- wp-job-manager-functions.php | 5 --- 2 files changed, 12 insertions(+), 37 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 06573c801..d51e67a59 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -674,9 +674,10 @@ public function get_filled_job_listings(): array { if ( false === $filled_jobs_transient ) { $filled_jobs_transient = get_posts( [ - 'post_type' => 'job_listing', - 'fields' => 'ids', - 'meta_query' => [ + 'post_type' => 'job_listing', + 'fields' => 'ids', + 'posts_per_page' => -1, + 'meta_query' => [ [ 'key' => '_filled', 'value' => '1', @@ -691,31 +692,7 @@ public function get_filled_job_listings(): array { } /** - * Get expired jobs. - * - * @return array - */ - public function get_expired_job_listings(): array { - if ( ! get_option( 'job_manager_hide_expired' ) ) { - return []; - } - - $expired_jobs_transient = get_transient( 'hide_expired_jobs_transient' ); - if ( false === $expired_jobs_transient ) { - $expired_jobs_transient = get_posts( - [ - 'post_type' => 'job_listing', - 'post_status' => 'expired', - 'fields' => 'ids', - ] - ); - set_transient( 'hide_expired_jobs_transient', $expired_jobs_transient, DAY_IN_SECONDS ); - } - return $expired_jobs_transient; - } - - /** - * Maybe exclude expired and/or filled job listings from search. + * Maybe exclude expired and/or filled job listings from search and archive pages. * * @param $query WP_Query $query * @@ -733,12 +710,15 @@ public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $qu ! is_admin() && $query->is_main_query() && $query->is_search() - || $query->is_archive() + || $query->is_archive() && 'job_listing' === $query->get( 'post_type' ) ) { - $jobs_to_exclude = array_merge( $this->get_filled_job_listings(), $this->get_expired_job_listings() ); - if ( ! empty( $jobs_to_exclude ) ) { - $query->set( 'post__not_in', $jobs_to_exclude ); + if ( $hide_expired ) { + $query->set( 'post_status', 'publish' ); + } + + if ( $hide_filled_positions ) { + $query->set( 'post__not_in', $this->get_filled_job_listings() ); } } } diff --git a/wp-job-manager-functions.php b/wp-job-manager-functions.php index d4c5b3152..28db910c0 100644 --- a/wp-job-manager-functions.php +++ b/wp-job-manager-functions.php @@ -1726,11 +1726,6 @@ function delete_job_listings_transients_on_save( int $post_id, WP_Post $post ): if ( '1' === get_post_meta( $post_id, '_filled', true ) ) { delete_transient( 'hide_filled_jobs_transient' ); } - - $post_status = [ 'publish', 'expired', 'trash' ]; - if ( in_array( $post->post_status, $post_status, true ) ) { - delete_transient( 'hide_expired_jobs_transient' ); - } } } From fc8e201e12ce8dff5d75e59beced538f93b315c8 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Wed, 21 Jun 2023 13:31:36 +0100 Subject: [PATCH 10/20] Explicitly query 'publish' post status - When using get_posts() to retrieved posts with a _filled meta value of 1, make sure we're only querying posts with a 'publish' post status. --- includes/class-wp-job-manager-post-types.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index d51e67a59..49bddf07b 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -674,6 +674,7 @@ public function get_filled_job_listings(): array { if ( false === $filled_jobs_transient ) { $filled_jobs_transient = get_posts( [ + 'post_status' => 'publish', 'post_type' => 'job_listing', 'fields' => 'ids', 'posts_per_page' => -1, From 2522485a150ca6b525d93a3e12ebdf7567000a47 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Thu, 22 Jun 2023 11:43:09 +0100 Subject: [PATCH 11/20] Refactor how $expired posts are hidden from search - Removes $query->set() to hide $expired posts - Instead, changes the 'public' argue on register_post_status( 'expired' ) to 'true' or 'false' depending on whether we're on a search page, or the 'job_manager_hide_expired' option is enabled (or not in either case) - Also changes the filled jobs transient delete so that it triggers on 'update_post_meta', vs. 'save_post' --- includes/class-wp-job-manager-post-types.php | 35 ++++++++++++-------- wp-job-manager-functions.php | 4 +-- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 49bddf07b..51ea06600 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -87,7 +87,7 @@ public function __construct() { add_action( 'transition_post_status', [ $this, 'track_job_submission' ], 10, 3 ); add_action( 'parse_query', [ $this, 'add_feed_query_args' ] ); - add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_expired_job_listings_from_search' ] ); + add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_job_listings_from_search' ] ); // Single job content. $this->job_content_filter( true ); @@ -390,6 +390,21 @@ public function register_post_types() { */ add_feed( self::get_job_feed_name(), [ $this, 'job_feed' ] ); + /** + * This code checks if (1) we're on a search or (2) the option to hide expired job listings is enabled. + * If either condition is false, we're going to set the register_post_status() 'public' arg to false, + * otherwise it will be true. + * + * This will prevent single expired job listings from showing a '404', instead of an + * 'Expired' notice, when viewing the single job listing and not logged in. + * + * This will also address historical issues stemming from addressing the issue raised here + * https://github.com/Automattic/wpjobmanager.com/issues/420. + */ + $is_site_search = ( isset( $_GET['s'] ) ); + $is_expired_searchable = (bool) get_option( 'job_manager_hide_expired' ); + $is_expired_public = ! $is_site_search || ! $is_expired_searchable; + /** * Post status */ @@ -397,7 +412,7 @@ public function register_post_types() { 'expired', [ 'label' => _x( 'Expired', 'post status', 'wp-job-manager' ), - 'public' => true, + 'public' => $is_expired_public, 'protected' => true, 'exclude_from_search' => true, 'show_in_admin_all_list' => true, @@ -699,28 +714,20 @@ public function get_filled_job_listings(): array { * * @return void */ - public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $query ): void { + public function maybe_hide_filled_job_listings_from_search( WP_Query $query ): void { $hide_filled_positions = get_option( 'job_manager_hide_filled_positions' ); - $hide_expired = get_option( 'job_manager_hide_expired' ); - if ( ! $hide_filled_positions && ! $hide_expired ) { + if ( ! $hide_filled_positions ) { return; } if ( ! is_admin() && $query->is_main_query() - && $query->is_search() - || $query->is_archive() && 'job_listing' === $query->get( 'post_type' ) + && ( $query->is_search() || $query->is_archive() ) ) { - if ( $hide_expired ) { - $query->set( 'post_status', 'publish' ); - } - - if ( $hide_filled_positions ) { - $query->set( 'post__not_in', $this->get_filled_job_listings() ); - } + $query->set( 'post__not_in', $this->get_filled_job_listings() ); } } diff --git a/wp-job-manager-functions.php b/wp-job-manager-functions.php index 28db910c0..f73bf718c 100644 --- a/wp-job-manager-functions.php +++ b/wp-job-manager-functions.php @@ -1719,7 +1719,7 @@ function job_manager_get_salary_unit_options( $include_empty = true ) { * @param int $post_id The ID of the post being saved. * @param WP_Post $post The post object being saved. */ -function delete_job_listings_transients_on_save( int $post_id, WP_Post $post ): void { +function delete_filled_job_listing_transient_on_post_meta_update( int $post_id, WP_Post $post ): void { if ( 'job_listing' === $post->post_type ) { @@ -1729,4 +1729,4 @@ function delete_job_listings_transients_on_save( int $post_id, WP_Post $post ): } } -add_action( 'save_post', 'delete_job_listings_transients_on_save', 10, 2 ); +add_action( 'update_post_meta', 'delete_filled_job_listing_transient_on_post_meta_update', 10, 2 ); From 27685ac3c619d8afb2937b8a4a0270eb244d5cb6 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Thu, 22 Jun 2023 11:53:31 +0100 Subject: [PATCH 12/20] Nonce verification for $_GET['s'] isn't necessary here, so let's try to quiet PHPCS. The change to `update_post_meta` broke tests, so revert that for now. --- includes/class-wp-job-manager-post-types.php | 2 +- wp-job-manager-functions.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 51ea06600..a2f7ab7f8 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -401,7 +401,7 @@ public function register_post_types() { * This will also address historical issues stemming from addressing the issue raised here * https://github.com/Automattic/wpjobmanager.com/issues/420. */ - $is_site_search = ( isset( $_GET['s'] ) ); + $is_site_search = ( isset( $_GET['s'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $is_expired_searchable = (bool) get_option( 'job_manager_hide_expired' ); $is_expired_public = ! $is_site_search || ! $is_expired_searchable; diff --git a/wp-job-manager-functions.php b/wp-job-manager-functions.php index f73bf718c..616ed3656 100644 --- a/wp-job-manager-functions.php +++ b/wp-job-manager-functions.php @@ -1729,4 +1729,4 @@ function delete_filled_job_listing_transient_on_post_meta_update( int $post_id, } } -add_action( 'update_post_meta', 'delete_filled_job_listing_transient_on_post_meta_update', 10, 2 ); +add_action( 'save_post', 'delete_filled_job_listing_transient_on_post_meta_update', 10, 2 ); From d3b5123927368e7e143c9f611ae6226ff3d96b85 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Sat, 24 Jun 2023 00:01:17 +0100 Subject: [PATCH 13/20] Update issue reference --- includes/class-wp-job-manager-post-types.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index a2f7ab7f8..7b62fa8bb 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -399,7 +399,7 @@ public function register_post_types() { * 'Expired' notice, when viewing the single job listing and not logged in. * * This will also address historical issues stemming from addressing the issue raised here - * https://github.com/Automattic/wpjobmanager.com/issues/420. + * https://github.com/Automattic/WP-Job-Manager/issues/1884. */ $is_site_search = ( isset( $_GET['s'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $is_expired_searchable = (bool) get_option( 'job_manager_hide_expired' ); From d23b3a3242ab91c54286d4fccf57cd5b725ecb43 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Wed, 28 Jun 2023 09:54:27 +0100 Subject: [PATCH 14/20] Rename $is_expired_hidden variable to be more readable - Renamed to $is_expired_hidden to (hopefully) make the true/false more readable. --- includes/class-wp-job-manager-post-types.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 7b62fa8bb..4bf5606ff 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -401,9 +401,9 @@ public function register_post_types() { * This will also address historical issues stemming from addressing the issue raised here * https://github.com/Automattic/WP-Job-Manager/issues/1884. */ - $is_site_search = ( isset( $_GET['s'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $is_expired_searchable = (bool) get_option( 'job_manager_hide_expired' ); - $is_expired_public = ! $is_site_search || ! $is_expired_searchable; + $is_site_search = ( isset( $_GET['s'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $is_expired_hidden = (bool) get_option( 'job_manager_hide_expired' ); + $is_expired_public = ! $is_site_search || ! $is_expired_hidden; /** * Post status From ea63e6609cebe6de0d564deedd71abef6867bfc6 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Wed, 28 Jun 2023 09:58:08 +0100 Subject: [PATCH 15/20] Update PHP doc for the get_filled_job_listings() method to state the return plainly. --- includes/class-wp-job-manager-post-types.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 4bf5606ff..8c4e594bc 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -676,9 +676,9 @@ public function job_feed() { } /** - * Get filled jobs. + * Retrieve and return the post IDs of any job listings marked as filled. * - * @return array + * @return array Array of filled job listing post IDs. */ public function get_filled_job_listings(): array { if ( ! get_option( 'job_manager_hide_filled_positions' ) ) { From 3a210fb7152c0fb9a40c5393e60c33b13ee06db1 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Wed, 28 Jun 2023 10:08:14 +0100 Subject: [PATCH 16/20] Early return on get_filled_job_listings() method isn't necessary, as the transient will return an empty array if there are no post IDs anyway. --- includes/class-wp-job-manager-post-types.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 8c4e594bc..9a133b340 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -681,9 +681,6 @@ public function job_feed() { * @return array Array of filled job listing post IDs. */ public function get_filled_job_listings(): array { - if ( ! get_option( 'job_manager_hide_filled_positions' ) ) { - return []; - } $filled_jobs_transient = get_transient( 'hide_filled_jobs_transient' ); if ( false === $filled_jobs_transient ) { From 1a7b7da26d47c33255a23ea5e95da70773f6454a Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Sun, 2 Jul 2023 18:12:11 +0100 Subject: [PATCH 17/20] Revert how expired posts are hidden to previous implementation Setting 'public' to 'false' dynamically workes fine with $_GET['s'], however, the same method fails to return true when checking is_archive(), is_tax(), is_wpjm_*(), etc. This previous implementation is able to check conditionals as expected, keeps the code in one place, and works. --- includes/class-wp-job-manager-post-types.php | 28 +++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 9a133b340..a746714a0 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -87,7 +87,7 @@ public function __construct() { add_action( 'transition_post_status', [ $this, 'track_job_submission' ], 10, 3 ); add_action( 'parse_query', [ $this, 'add_feed_query_args' ] ); - add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_job_listings_from_search' ] ); + add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_expired_job_listings_from_search' ] ); // Single job content. $this->job_content_filter( true ); @@ -390,21 +390,6 @@ public function register_post_types() { */ add_feed( self::get_job_feed_name(), [ $this, 'job_feed' ] ); - /** - * This code checks if (1) we're on a search or (2) the option to hide expired job listings is enabled. - * If either condition is false, we're going to set the register_post_status() 'public' arg to false, - * otherwise it will be true. - * - * This will prevent single expired job listings from showing a '404', instead of an - * 'Expired' notice, when viewing the single job listing and not logged in. - * - * This will also address historical issues stemming from addressing the issue raised here - * https://github.com/Automattic/WP-Job-Manager/issues/1884. - */ - $is_site_search = ( isset( $_GET['s'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $is_expired_hidden = (bool) get_option( 'job_manager_hide_expired' ); - $is_expired_public = ! $is_site_search || ! $is_expired_hidden; - /** * Post status */ @@ -412,7 +397,7 @@ public function register_post_types() { 'expired', [ 'label' => _x( 'Expired', 'post status', 'wp-job-manager' ), - 'public' => $is_expired_public, + 'public' => true, 'protected' => true, 'exclude_from_search' => true, 'show_in_admin_all_list' => true, @@ -711,10 +696,11 @@ public function get_filled_job_listings(): array { * * @return void */ - public function maybe_hide_filled_job_listings_from_search( WP_Query $query ): void { + public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $query ): void { $hide_filled_positions = get_option( 'job_manager_hide_filled_positions' ); + $hide_expired = get_option( 'job_manager_hide_expired' ); - if ( ! $hide_filled_positions ) { + if ( ! $hide_filled_positions && ! $hide_expired ) { return; } @@ -724,6 +710,10 @@ public function maybe_hide_filled_job_listings_from_search( WP_Query $query ): v && ( $query->is_search() || $query->is_archive() ) ) { + if ( $hide_expired ) { + $query->set( 'post_status', 'publish' ); + } + $query->set( 'post__not_in', $this->get_filled_job_listings() ); } } From de746c49aacb3a010e644be3b12e6329b68401ac Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Sun, 2 Jul 2023 21:57:13 +0100 Subject: [PATCH 18/20] Delete 'hide_filled_jobs_transient' on 'updated_post_meta' action. --- includes/class-wp-job-manager-post-types.php | 19 +++++++++++++++++++ wp-job-manager-functions.php | 18 ------------------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index a746714a0..58fe83302 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -81,6 +81,7 @@ public function __construct() { add_filter( 'wp_insert_post_data', [ $this, 'fix_post_name' ], 10, 2 ); add_action( 'add_post_meta', [ $this, 'maybe_add_geolocation_data' ], 10, 3 ); add_action( 'update_post_meta', [ $this, 'update_post_meta' ], 10, 4 ); + add_action( 'updated_post_meta', [ $this, 'delete_filled_job_listing_transient' ], 10, 4 ); add_action( 'wp_insert_post', [ $this, 'maybe_add_default_meta_data' ], 10, 2 ); add_filter( 'post_types_to_delete_with_user', [ $this, 'delete_user_add_job_listings_post_type' ] ); @@ -1958,4 +1959,22 @@ public function delete_user_add_job_listings_post_type( $types ) { return $types; } + /** + * Delete the 'job_manager_hide_filled_jobs' transient when meta is updated. + * + * @param int $meta_id ID of updated metadata entry. + * @param int $object_id ID of the object metadata is for. + * @param string $meta_key Metadata key. + * @param mixed $_meta_value Metadata value. This will be a PHP-serialized string representation of the value if the value is an array, an object, or itself a PHP-serialized string. + * + * @return void + */ + public function delete_filled_job_listing_transient( $meta_id, $object_id, $meta_key, $_meta_value ) { + + if ( '_edit_lock' !== $meta_key || 'job_listing' !== get_post_type( $object_id ) ) { + return; + } + + delete_transient( 'hide_filled_jobs_transient' ); + } } diff --git a/wp-job-manager-functions.php b/wp-job-manager-functions.php index 616ed3656..b2498acff 100644 --- a/wp-job-manager-functions.php +++ b/wp-job-manager-functions.php @@ -1712,21 +1712,3 @@ function job_manager_get_salary_unit_options( $include_empty = true ) { */ return apply_filters( 'job_manager_get_salary_unit_options', $options, $include_empty ); } - -/** - * Delete filled_jobs_transient and expired_jobs_transient transients when a job listing is saved or updated. - * - * @param int $post_id The ID of the post being saved. - * @param WP_Post $post The post object being saved. - */ -function delete_filled_job_listing_transient_on_post_meta_update( int $post_id, WP_Post $post ): void { - - if ( 'job_listing' === $post->post_type ) { - - if ( '1' === get_post_meta( $post_id, '_filled', true ) ) { - delete_transient( 'hide_filled_jobs_transient' ); - } - } - -} -add_action( 'save_post', 'delete_filled_job_listing_transient_on_post_meta_update', 10, 2 ); From 82e75e67cb8136e5489f78f86872141e5155abbd Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Tue, 4 Jul 2023 14:36:21 +0100 Subject: [PATCH 19/20] Refactor maybe_hide_expired_posts() Refactors the maybe_hide_expired_posts() method to set the 'public' static to 'false', then back to 'true', during/after query generation. --- includes/class-wp-job-manager-post-types.php | 71 +++++++++++++++++--- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index 58fe83302..bd1bd5f13 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -88,7 +88,8 @@ public function __construct() { add_action( 'transition_post_status', [ $this, 'track_job_submission' ], 10, 3 ); add_action( 'parse_query', [ $this, 'add_feed_query_args' ] ); - add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_expired_job_listings_from_search' ] ); + add_action( 'pre_get_posts', [ $this, 'maybe_hide_filled_job_listings' ] ); + add_action( 'pre_get_posts', [ $this, 'maybe_hide_expired_job_listings' ] ); // Single job content. $this->job_content_filter( true ); @@ -691,17 +692,16 @@ public function get_filled_job_listings(): array { } /** - * Maybe exclude expired and/or filled job listings from search and archive pages. + * Maybe exclude filled job listings from search and archive pages. * * @param $query WP_Query $query * * @return void */ - public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $query ): void { + public function maybe_hide_filled_job_listings( WP_Query $query ): void { $hide_filled_positions = get_option( 'job_manager_hide_filled_positions' ); - $hide_expired = get_option( 'job_manager_hide_expired' ); - if ( ! $hide_filled_positions && ! $hide_expired ) { + if ( ! $hide_filled_positions ) { return; } @@ -711,14 +711,67 @@ public function maybe_hide_filled_expired_job_listings_from_search( WP_Query $qu && ( $query->is_search() || $query->is_archive() ) ) { - if ( $hide_expired ) { - $query->set( 'post_status', 'publish' ); - } - $query->set( 'post__not_in', $this->get_filled_job_listings() ); } } + /** + * Maybe exclude expired job listings from search and archive pages. + * + * @param $query WP_Query $query + * + * @return void + */ + public function maybe_hide_expired_job_listings( WP_Query $query ): void { + $hide_expired = get_option( 'job_manager_hide_expired' ); + + if ( ! $hide_expired ) { + return; + } + + if ( + ! is_admin() + && $query->is_main_query() + && ( $query->is_search() || $query->is_archive() ) + ) { + + $this->make_expired_private(); + + add_action( 'posts_selection', [ $this, 'make_expired_public' ] ); + } + } + + /** + * Make the expired post status public. + * + * @return void + * @internal + */ + public function make_expired_public(): void { + + global $wp_post_statuses; + + if ( isset( $wp_post_statuses['expired'] ) ) { + $wp_post_statuses['expired']->public = true; + } + + } + + /** + * Make the expired post status private. + * + * @return void + * @internal + */ + public function make_expired_private() { + + global $wp_post_statuses; + + if ( isset( $wp_post_statuses['expired'] ) ) { + $wp_post_statuses['expired']->public = false; + } + } + /** * Adds query arguments in order to make sure that the feed properly queries the 'job_listing' type. * From 27b3be7732a15d5d95d1981dbbb6f7ddceb4a009 Mon Sep 17 00:00:00 2001 From: Katja Paavola Date: Fri, 7 Jul 2023 10:52:06 +0100 Subject: [PATCH 20/20] Add comments explaining conditional logic in the maybe_hide_* methods. --- includes/class-wp-job-manager-post-types.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/includes/class-wp-job-manager-post-types.php b/includes/class-wp-job-manager-post-types.php index bd1bd5f13..d3df4ea0d 100644 --- a/includes/class-wp-job-manager-post-types.php +++ b/includes/class-wp-job-manager-post-types.php @@ -705,6 +705,18 @@ public function maybe_hide_filled_job_listings( WP_Query $query ): void { return; } + /** + * We want to ensure this only runs when: (1) not in admin and (2) the query is the main query and (3) the query + * is either a search query or an archive query. This is to address complications stemming from the following + * feature request: https://github.com/Automattic/WP-Job-Manager/issues/1884 + * + * See also: + * + * https://github.com/Automattic/WP-Job-Manager/pull/1570 + * https://github.com/Automattic/WP-Job-Manager/pull/2367 + * https://github.com/Automattic/WP-Job-Manager/issues/2423 + */ + if ( ! is_admin() && $query->is_main_query() @@ -729,6 +741,11 @@ public function maybe_hide_expired_job_listings( WP_Query $query ): void { return; } + /** + * We want to ensure this only runs when: (1) not in admin and (2) the query is the main query and (3) the query. + * See the comment in the maybe_hide_filled_job_listings() method for more information. + */ + if ( ! is_admin() && $query->is_main_query()