3rd-party
4 days ago
abstracts
3 months ago
admin
2 weeks ago
emails
2 weeks ago
forms
4 days ago
helper
3 months ago
promoted-jobs
4 days ago
ui
2 weeks ago
widgets
2 weeks ago
class-access-token.php
2 years ago
class-dev-tools.php
2 years ago
class-guest-session.php
2 years ago
class-guest-user.php
2 years ago
class-job-dashboard-shortcode.php
6 months ago
class-job-listing-stats.php
2 years ago
class-job-overlay.php
2 years ago
class-stats-dashboard.php
2 years ago
class-stats-script.php
3 months ago
class-stats.php
3 months ago
class-wp-job-manager-ajax.php
2 months ago
class-wp-job-manager-api.php
6 years ago
class-wp-job-manager-blocks.php
2 weeks ago
class-wp-job-manager-cache-helper.php
3 months ago
class-wp-job-manager-category-walker.php
6 years ago
class-wp-job-manager-com-api.php
2 years ago
class-wp-job-manager-data-cleaner.php
2 years ago
class-wp-job-manager-data-exporter.php
3 months ago
class-wp-job-manager-dependency-checker.php
2 years ago
class-wp-job-manager-email-notifications.php
2 years ago
class-wp-job-manager-forms.php
5 years ago
class-wp-job-manager-geocode.php
2 weeks ago
class-wp-job-manager-install.php
2 years ago
class-wp-job-manager-post-types.php
4 days ago
class-wp-job-manager-recaptcha.php
6 months ago
class-wp-job-manager-rest-api.php
2 months ago
class-wp-job-manager-shortcodes.php
2 weeks ago
class-wp-job-manager-usage-tracking-data.php
2 years ago
class-wp-job-manager-usage-tracking.php
2 years ago
class-wp-job-manager-widget.php
2 weeks ago
class-wp-job-manager.php
3 months ago
trait-singleton.php
2 years ago
class-wp-job-manager-ajax.php
502 lines
| 1 | <?php |
| 2 | /** |
| 3 | * File containing the class WP_Job_Manager_Ajax. |
| 4 | * |
| 5 | * @package wp-job-manager |
| 6 | */ |
| 7 | |
| 8 | if ( ! defined( 'ABSPATH' ) ) { |
| 9 | exit; |
| 10 | } |
| 11 | |
| 12 | /** |
| 13 | * Handles Job Manager's Ajax endpoints. |
| 14 | * |
| 15 | * @since 1.0.0 |
| 16 | */ |
| 17 | class WP_Job_Manager_Ajax { |
| 18 | |
| 19 | /** |
| 20 | * The single instance of the class. |
| 21 | * |
| 22 | * @var self |
| 23 | * @since 1.26.0 |
| 24 | */ |
| 25 | private static $instance = null; |
| 26 | |
| 27 | /** |
| 28 | * Allows for accessing single instance of class. Class should only be constructed once per call. |
| 29 | * |
| 30 | * @since 1.26.0 |
| 31 | * @static |
| 32 | * @return self Main instance. |
| 33 | */ |
| 34 | public static function instance() { |
| 35 | if ( is_null( self::$instance ) ) { |
| 36 | self::$instance = new self(); |
| 37 | } |
| 38 | return self::$instance; |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * Constructor. |
| 43 | */ |
| 44 | public function __construct() { |
| 45 | add_action( 'init', [ __CLASS__, 'add_endpoint' ] ); |
| 46 | add_action( 'template_redirect', [ __CLASS__, 'do_jm_ajax' ], 0 ); |
| 47 | |
| 48 | // JM Ajax endpoints. |
| 49 | add_action( 'job_manager_ajax_get_listings', [ $this, 'get_listings' ] ); |
| 50 | add_action( 'job_manager_ajax_upload_file', [ $this, 'upload_file' ] ); |
| 51 | |
| 52 | // BW compatible handlers. |
| 53 | add_action( 'wp_ajax_nopriv_job_manager_get_listings', [ $this, 'get_listings' ] ); |
| 54 | add_action( 'wp_ajax_job_manager_get_listings', [ $this, 'get_listings' ] ); |
| 55 | add_action( 'wp_ajax_nopriv_job_manager_upload_file', [ $this, 'upload_file' ] ); |
| 56 | add_action( 'wp_ajax_job_manager_upload_file', [ $this, 'upload_file' ] ); |
| 57 | add_action( 'wp_ajax_job_manager_search_users', [ $this, 'ajax_search_users' ] ); |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Adds endpoint for frontend Ajax requests. |
| 62 | */ |
| 63 | public static function add_endpoint() { |
| 64 | add_rewrite_tag( '%jm-ajax%', '([^/]*)' ); |
| 65 | add_rewrite_rule( 'jm-ajax/([^/]*)/?', 'index.php?jm-ajax=$matches[1]', 'top' ); |
| 66 | add_rewrite_rule( 'index.php/jm-ajax/([^/]*)/?', 'index.php?jm-ajax=$matches[1]', 'top' ); |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * Gets Job Manager's Ajax Endpoint. |
| 71 | * |
| 72 | * @param string $request Optional. |
| 73 | * @return string |
| 74 | */ |
| 75 | public static function get_endpoint( $request = '%%endpoint%%' ) { |
| 76 | if ( strstr( get_option( 'permalink_structure' ), '/index.php/' ) ) { |
| 77 | $endpoint = trailingslashit( home_url( '/index.php/jm-ajax/' . $request . '/', 'relative' ) ); |
| 78 | } elseif ( get_option( 'permalink_structure' ) ) { |
| 79 | $endpoint = trailingslashit( home_url( '/jm-ajax/' . $request . '/', 'relative' ) ); |
| 80 | } else { |
| 81 | $endpoint = add_query_arg( 'jm-ajax', $request, home_url( '/', 'relative' ) ); |
| 82 | } |
| 83 | |
| 84 | return esc_url_raw( $endpoint ); |
| 85 | } |
| 86 | |
| 87 | /** |
| 88 | * Performs Job Manager's Ajax actions. |
| 89 | */ |
| 90 | public static function do_jm_ajax() { |
| 91 | global $wp_query; |
| 92 | |
| 93 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Input is used safely. |
| 94 | if ( ! empty( $_GET['jm-ajax'] ) ) { |
| 95 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Input is used safely. |
| 96 | $wp_query->set( 'jm-ajax', sanitize_text_field( wp_unslash( $_GET['jm-ajax'] ) ) ); |
| 97 | } |
| 98 | |
| 99 | $action = $wp_query->get( 'jm-ajax' ); |
| 100 | if ( $action ) { |
| 101 | if ( ! defined( 'DOING_AJAX' ) ) { |
| 102 | define( 'DOING_AJAX', true ); |
| 103 | } |
| 104 | |
| 105 | // Not home - this is an ajax endpoint. |
| 106 | $wp_query->is_home = false; |
| 107 | |
| 108 | /** |
| 109 | * Performs an Ajax action. |
| 110 | * The dynamic part of the action, $action, is the predefined Ajax action to be performed. |
| 111 | * |
| 112 | * @since 1.23.0 |
| 113 | */ |
| 114 | do_action( 'job_manager_ajax_' . sanitize_text_field( $action ) ); |
| 115 | wp_die(); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | /** |
| 120 | * Returns Job Listings for Ajax endpoint. |
| 121 | */ |
| 122 | public function get_listings() { |
| 123 | // Get input variables. |
| 124 | // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Fetching data only; often for logged out visitors. |
| 125 | $search_location = isset( $_REQUEST['search_location'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search_location'] ) ) : ''; |
| 126 | $search_keywords = isset( $_REQUEST['search_keywords'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search_keywords'] ) ) : ''; |
| 127 | $search_categories = isset( $_REQUEST['search_categories'] ) ? wp_unslash( $_REQUEST['search_categories'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Input is sanitized below. |
| 128 | $filter_job_types = isset( $_REQUEST['filter_job_type'] ) ? array_filter( array_map( 'sanitize_title', wp_unslash( (array) $_REQUEST['filter_job_type'] ) ) ) : null; |
| 129 | $filter_post_status = isset( $_REQUEST['filter_post_status'] ) ? array_filter( array_map( 'sanitize_title', wp_unslash( (array) $_REQUEST['filter_post_status'] ) ) ) : null; |
| 130 | $order = isset( $_REQUEST['order'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ) : 'DESC'; |
| 131 | $orderby = isset( $_REQUEST['orderby'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['orderby'] ) ) : 'featured'; |
| 132 | $page = isset( $_REQUEST['page'] ) ? absint( $_REQUEST['page'] ) : 1; |
| 133 | $per_page = isset( $_REQUEST['per_page'] ) ? absint( $_REQUEST['per_page'] ) : absint( get_option( 'job_manager_per_page' ) ); |
| 134 | $filled = isset( $_REQUEST['filled'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['filled'] ) ) : null; |
| 135 | $featured = isset( $_REQUEST['featured'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['featured'] ) ) : null; |
| 136 | $remote_position = isset( $_REQUEST['remote_position'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_position'] ) ) : null; |
| 137 | $show_pagination = isset( $_REQUEST['show_pagination'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['show_pagination'] ) ) : null; |
| 138 | $featured_first = isset( $_REQUEST['featured_first'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['featured_first'] ) ) : null; |
| 139 | if ( ! isset( $_REQUEST['author'] ) ) { |
| 140 | $author = ''; |
| 141 | } elseif ( is_array( $_REQUEST['author'] ) ) { |
| 142 | // Array-shaped input is not supported via AJAX - fails closed (passes '0' so get_job_listings returns zero results). |
| 143 | $author = '0'; |
| 144 | } else { |
| 145 | $author = sanitize_text_field( wp_unslash( $_REQUEST['author'] ) ); |
| 146 | } |
| 147 | // phpcs:enable WordPress.Security.NonceVerification.Recommended |
| 148 | |
| 149 | if ( is_array( $search_categories ) ) { |
| 150 | $search_categories = array_filter( array_map( 'sanitize_text_field', array_map( 'stripslashes', $search_categories ) ) ); |
| 151 | } else { |
| 152 | $search_categories = array_filter( [ sanitize_text_field( wp_unslash( $search_categories ) ) ] ); |
| 153 | } |
| 154 | |
| 155 | // Ensure the current user can filter by post_status. Users without the capability may only |
| 156 | // narrow the query to publicly visible listings (e.g. the `[jobs post_status="publish"]` |
| 157 | // shortcode), never request unpublished statuses such as draft, pending, preview or private. |
| 158 | if ( is_array( $filter_post_status ) && ! current_user_can( \WP_Job_Manager_Post_Types::CAP_EDIT_LISTINGS ) ) { |
| 159 | /** |
| 160 | * Post statuses a visitor without listing-editing capabilities is allowed to filter by. |
| 161 | * |
| 162 | * @since 2.4.5 |
| 163 | * |
| 164 | * @param string[] $allowed_post_status Allowed post statuses. Defaults to `[ 'publish' ]`. |
| 165 | */ |
| 166 | $allowed_post_status = apply_filters( 'job_manager_get_listings_public_post_status', [ 'publish' ] ); |
| 167 | $filter_post_status = array_values( array_intersect( $filter_post_status, $allowed_post_status ) ); |
| 168 | |
| 169 | if ( empty( $filter_post_status ) ) { |
| 170 | $filter_post_status = null; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | // Browse-capability gate — match the [jobs] shortcode denial without surfacing partial results. |
| 175 | if ( ! job_manager_user_can_browse_job_listings() ) { |
| 176 | wp_send_json( |
| 177 | [ |
| 178 | 'found_jobs' => false, |
| 179 | 'showing' => '', |
| 180 | 'showing_all' => false, |
| 181 | 'showing_links' => '', |
| 182 | 'max_num_pages' => 0, |
| 183 | 'html' => '', |
| 184 | ] |
| 185 | ); |
| 186 | return; |
| 187 | } |
| 188 | |
| 189 | $types = get_job_listing_types(); |
| 190 | $job_types_filtered = ! is_null( $filter_job_types ) && count( $types ) !== count( $filter_job_types ); |
| 191 | |
| 192 | $args = [ |
| 193 | 'search_location' => $search_location, |
| 194 | 'search_keywords' => $search_keywords, |
| 195 | 'search_categories' => $search_categories, |
| 196 | 'job_types' => is_null( $filter_job_types ) || count( $types ) === count( $filter_job_types ) ? '' : $filter_job_types + [ 0 ], |
| 197 | 'post_status' => $filter_post_status, |
| 198 | 'orderby' => $orderby, |
| 199 | 'order' => $order, |
| 200 | 'featured_first' => $featured_first, |
| 201 | 'author' => $author, |
| 202 | 'offset' => ( $page - 1 ) * $per_page, |
| 203 | 'posts_per_page' => max( 1, $per_page ), // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page -- Known slow query. |
| 204 | ]; |
| 205 | |
| 206 | if ( 'true' === $filled || 'false' === $filled ) { |
| 207 | $args['filled'] = 'true' === $filled; |
| 208 | } |
| 209 | |
| 210 | if ( 'true' === $remote_position || 'false' === $remote_position ) { |
| 211 | $args['remote_position'] = 'true' === $remote_position; |
| 212 | } |
| 213 | |
| 214 | if ( 'true' === $featured || 'false' === $featured ) { |
| 215 | $args['featured'] = 'true' === $featured; |
| 216 | $args['orderby'] = 'featured' === $orderby ? 'date' : $orderby; |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Get the arguments to use when building the Job Listing WP Query. |
| 221 | * |
| 222 | * @since 1.0.0 |
| 223 | * |
| 224 | * @param array $args Arguments used for generating Job Listing query (see `get_job_listings()`). |
| 225 | */ |
| 226 | $jobs = get_job_listings( apply_filters( 'job_manager_get_listings_args', $args ) ); |
| 227 | |
| 228 | $result = [ |
| 229 | 'found_jobs' => $jobs->have_posts(), |
| 230 | 'showing' => '', |
| 231 | 'max_num_pages' => $jobs->max_num_pages, |
| 232 | ]; |
| 233 | |
| 234 | if ( ( $search_location || $search_keywords || $search_categories || $job_types_filtered ) ) { |
| 235 | // translators: Placeholder %d is the number of found search results. |
| 236 | $message = sprintf( _n( 'Search completed. Found %d matching record.', 'Search completed. Found %d matching records.', $jobs->found_posts, 'wp-job-manager' ), $jobs->found_posts ); |
| 237 | $result['showing_all'] = true; |
| 238 | } else { |
| 239 | $message = ''; |
| 240 | } |
| 241 | |
| 242 | $search_values = [ |
| 243 | 'location' => $search_location, |
| 244 | 'keywords' => $search_keywords, |
| 245 | 'categories' => $search_categories, |
| 246 | ]; |
| 247 | |
| 248 | /** |
| 249 | * Filter the message that describes the results of the search query. |
| 250 | * |
| 251 | * @since 1.0.0 |
| 252 | * |
| 253 | * @param string $message Default message that is generated when posts are found. |
| 254 | * @param array $search_values { |
| 255 | * Helpful values often used in the generation of this message. |
| 256 | * |
| 257 | * @type string $location Query used to filter by job listing location. |
| 258 | * @type string $keywords Query used to filter by general keywords. |
| 259 | * @type array $categories List of the categories to filter by. |
| 260 | * } |
| 261 | */ |
| 262 | $result['showing'] = apply_filters( 'job_manager_get_listings_custom_filter_text', $message, $search_values ); |
| 263 | |
| 264 | // Generate RSS link. |
| 265 | $result['showing_links'] = job_manager_get_filtered_links( |
| 266 | [ |
| 267 | 'filter_job_types' => $filter_job_types, |
| 268 | 'search_location' => $search_location, |
| 269 | 'search_categories' => $search_categories, |
| 270 | 'search_keywords' => $search_keywords, |
| 271 | 'author' => $author, |
| 272 | ] |
| 273 | ); |
| 274 | |
| 275 | /** |
| 276 | * Send back a response to the AJAX request without creating HTML. |
| 277 | * |
| 278 | * @since 1.26.0 |
| 279 | * |
| 280 | * @param array $result |
| 281 | * @param WP_Query $jobs |
| 282 | * @return bool True by default. Change to false to halt further response. |
| 283 | */ |
| 284 | if ( true !== apply_filters( 'job_manager_ajax_get_jobs_html_results', true, $result, $jobs ) ) { |
| 285 | /** |
| 286 | * Filters the results of the job listing Ajax query to be sent back to the client. |
| 287 | * |
| 288 | * @since 1.0.0 |
| 289 | * |
| 290 | * @param array $result { |
| 291 | * Package of the query results along with meta information. |
| 292 | * |
| 293 | * @type bool $found_jobs Whether or not jobs were found in the query. |
| 294 | * @type string $showing Description of the search query and results. |
| 295 | * @type int $max_num_pages Number of pages in the search result. |
| 296 | * @type string $html HTML representation of the search results (only if filter |
| 297 | * `job_manager_ajax_get_jobs_html_results` returns true). |
| 298 | * @type array $pagination Pagination links to use for stepping through filter results. |
| 299 | * } |
| 300 | */ |
| 301 | wp_send_json( apply_filters( 'job_manager_get_listings_result', $result, $jobs ) ); |
| 302 | |
| 303 | return; |
| 304 | } |
| 305 | |
| 306 | ob_start(); |
| 307 | |
| 308 | if ( $result['found_jobs'] ) { |
| 309 | while ( $jobs->have_posts() ) { |
| 310 | $jobs->the_post(); |
| 311 | get_job_manager_template_part( 'content', \WP_Job_Manager_Post_Types::PT_LISTING ); |
| 312 | } |
| 313 | } else { |
| 314 | get_job_manager_template_part( 'content', 'no-jobs-found' ); |
| 315 | } |
| 316 | |
| 317 | $result['html'] = ob_get_clean(); |
| 318 | |
| 319 | // Generate pagination. |
| 320 | if ( 'true' === $show_pagination ) { |
| 321 | $result['pagination'] = get_job_listing_pagination( $jobs->max_num_pages, $page ); |
| 322 | } |
| 323 | |
| 324 | /** This filter is documented in includes/class-wp-job-manager-ajax.php (above) */ |
| 325 | wp_send_json( apply_filters( 'job_manager_get_listings_result', $result, $jobs ) ); |
| 326 | } |
| 327 | |
| 328 | /** |
| 329 | * Uploads file from an Ajax request. |
| 330 | * |
| 331 | * No nonce field since the form may be statically cached. |
| 332 | */ |
| 333 | public function upload_file() { |
| 334 | if ( ! job_manager_user_can_upload_file_via_ajax() ) { |
| 335 | wp_send_json_error( __( 'You must be logged in to upload files using this method.', 'wp-job-manager' ) ); |
| 336 | return; |
| 337 | } |
| 338 | $data = [ |
| 339 | 'files' => [], |
| 340 | ]; |
| 341 | |
| 342 | if ( ! empty( $_FILES ) ) { |
| 343 | foreach ( $_FILES as $file_key => $file ) { |
| 344 | $files_to_upload = job_manager_prepare_uploaded_files( $file ); |
| 345 | foreach ( $files_to_upload as $file_to_upload ) { |
| 346 | $uploaded_file = job_manager_upload_file( |
| 347 | $file_to_upload, |
| 348 | [ |
| 349 | 'file_key' => $file_key, |
| 350 | ] |
| 351 | ); |
| 352 | |
| 353 | if ( is_wp_error( $uploaded_file ) ) { |
| 354 | $data['files'][] = [ |
| 355 | 'error' => $uploaded_file->get_error_message(), |
| 356 | ]; |
| 357 | } else { |
| 358 | $data['files'][] = $uploaded_file; |
| 359 | } |
| 360 | } |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | wp_send_json( $data ); |
| 365 | } |
| 366 | |
| 367 | /** |
| 368 | * Checks if user can search for other users in ajax call. |
| 369 | * |
| 370 | * @return bool |
| 371 | */ |
| 372 | private static function user_can_search_users() { |
| 373 | $user_can_search_users = false; |
| 374 | |
| 375 | /** |
| 376 | * Filter the capabilities that are allowed to search for users in ajax call. |
| 377 | * |
| 378 | * @since 1.32.0 |
| 379 | * |
| 380 | * @param array $user_caps Array of capabilities/roles that are allowed to search for users. |
| 381 | */ |
| 382 | $allowed_capabilities = apply_filters( 'job_manager_caps_can_search_users', [ \WP_Job_Manager_Post_Types::CAP_EDIT_LISTINGS ] ); |
| 383 | foreach ( $allowed_capabilities as $cap ) { |
| 384 | if ( current_user_can( $cap ) ) { |
| 385 | $user_can_search_users = true; |
| 386 | break; |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | /** |
| 391 | * Filters whether the current user can search for users in ajax call. |
| 392 | * |
| 393 | * @since 1.32.0 |
| 394 | * |
| 395 | * @param bool $user_can_search_users True if they are allowed, false if not. |
| 396 | */ |
| 397 | return apply_filters( 'job_manager_user_can_search_users', $user_can_search_users ); |
| 398 | } |
| 399 | |
| 400 | /** |
| 401 | * Search for users and return json. |
| 402 | */ |
| 403 | public static function ajax_search_users() { |
| 404 | check_ajax_referer( 'search-users', 'security' ); |
| 405 | |
| 406 | if ( ! self::user_can_search_users() ) { |
| 407 | wp_die( -1 ); |
| 408 | } |
| 409 | |
| 410 | $term = isset( $_GET['term'] ) ? sanitize_text_field( wp_unslash( $_GET['term'] ) ) : ''; |
| 411 | $page = isset( $_GET['page'] ) ? intval( $_GET['page'] ) : 1; |
| 412 | $per_page = 20; |
| 413 | |
| 414 | $exclude = []; |
| 415 | if ( ! empty( $_GET['exclude'] ) ) { |
| 416 | $exclude = array_map( 'intval', $_GET['exclude'] ); |
| 417 | } |
| 418 | |
| 419 | if ( empty( $term ) ) { |
| 420 | wp_die(); |
| 421 | } |
| 422 | |
| 423 | $more_exist = false; |
| 424 | $users = []; |
| 425 | |
| 426 | // Search by ID. |
| 427 | if ( is_numeric( $term ) && ! in_array( intval( $term ), $exclude, true ) ) { |
| 428 | $user = get_user_by( 'ID', intval( $term ) ); |
| 429 | if ( $user instanceof WP_User ) { |
| 430 | $users[ $user->ID ] = $user; |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | if ( empty( $users ) ) { |
| 435 | $search_args = [ |
| 436 | 'exclude' => $exclude, |
| 437 | 'search' => '*' . esc_attr( $term ) . '*', |
| 438 | 'search_columns' => [ 'user_login', 'user_email', 'user_nicename', 'display_name' ], |
| 439 | 'number' => $per_page, |
| 440 | 'paged' => $page, |
| 441 | 'orderby' => 'display_name', |
| 442 | 'order' => 'ASC', |
| 443 | ]; |
| 444 | |
| 445 | /** |
| 446 | * Modify the arguments used for `WP_User_Query` constructor. |
| 447 | * |
| 448 | * @since 1.32.0 |
| 449 | * |
| 450 | * @see https://codex.wordpress.org/Class_Reference/WP_User_Query |
| 451 | * |
| 452 | * @param array $search_args Argument array used in `WP_User_Query` constructor. |
| 453 | * @param string $term Search term. |
| 454 | * @param int[] $exclude Array of IDs to exclude. |
| 455 | * @param int $page Current page. |
| 456 | */ |
| 457 | $search_args = apply_filters( 'job_manager_search_users_args', $search_args, $term, $exclude, $page ); |
| 458 | |
| 459 | $user_query = new WP_User_Query( $search_args ); |
| 460 | $users = $user_query->get_results(); |
| 461 | $total_pages = ceil( $user_query->get_total() / $per_page ); |
| 462 | $more_exist = $total_pages > $page; |
| 463 | } |
| 464 | |
| 465 | $found_users = []; |
| 466 | |
| 467 | foreach ( $users as $user ) { |
| 468 | $found_users[ $user->ID ] = sprintf( |
| 469 | // translators: Used in user select. %1$s is the user's display name; #%2$s is the user ID; %3$s is the user email. |
| 470 | esc_html__( '%1$s (#%2$s – %3$s)', 'wp-job-manager' ), |
| 471 | htmlentities( $user->display_name ), |
| 472 | absint( $user->ID ), |
| 473 | $user->user_email |
| 474 | ); |
| 475 | } |
| 476 | |
| 477 | $response = [ |
| 478 | 'results' => $found_users, |
| 479 | 'more' => $more_exist, |
| 480 | ]; |
| 481 | |
| 482 | /** |
| 483 | * Modify the search results response for users in ajax call. |
| 484 | * |
| 485 | * @since 1.32.0 |
| 486 | * |
| 487 | * @param array $response { |
| 488 | * @type array $results Array of all found users; id => string descriptor |
| 489 | * @type boolean $more True if there is an additional page. |
| 490 | * } |
| 491 | * @param string $term Search term. |
| 492 | * @param int[] $exclude Array of IDs to exclude. |
| 493 | * @param int $page Current page. |
| 494 | */ |
| 495 | $response = apply_filters( 'job_manager_search_users_response', $response, $term, $exclude, $page ); |
| 496 | |
| 497 | wp_send_json( $response ); |
| 498 | } |
| 499 | } |
| 500 | |
| 501 | WP_Job_Manager_Ajax::instance(); |
| 502 |