namespace, '/' . $this->rest_base . '/ad/(?P[\d]+)', [ [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_ad_assignments' ], 'permission_callback' => [ $this, 'get_ad_assignments_permission_check' ], 'args' => [ 'ad_id' => [ 'validate_callback' => function( $param ) { return is_numeric( $param ); }, 'required' => true, ], ], ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/group/(?P[\d]+)', [ [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_group_assignments' ], 'permission_callback' => [ $this, 'get_group_assignments_permission_check' ], 'args' => [ 'group_id' => [ 'validate_callback' => function( $param ) { return is_numeric( $param ); }, 'required' => true, ], ], ], ] ); } /** * Permission check for the ad assignments endpoint. * * @param WP_REST_Request $request * * @return bool|WP_Error */ public function get_ad_assignments_permission_check( WP_REST_Request $request ): bool|WP_Error { $ad_id = (int) $request->get_param( 'ad_id' ); // A new (unsaved) ad has no post yet - fall back to the type-level capability. $allowed = 0 === $ad_id ? current_user_can( adpresso_get_capability( 'edit_ad' ) ) : current_user_can( 'edit_post', $ad_id ); if ( ! $allowed ) { return new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to view these assignments.', 'adpresso' ), [ 'status' => 403 ] ); } return true; } /** * Callback function to get all assignment data for a single ad. * * @param WP_REST_Request $request * * @return WP_REST_Response */ public function get_ad_assignments( WP_REST_Request $request ): WP_REST_Response { $ad_id = (int) $request->get_param( 'ad_id' ); $group_repository = adpresso_get_group_repository(); $placement_repository = adpresso_get_placement_repository(); // Get all available groups $all_groups = $group_repository->get_all(); // Placements per group, from the same placement list the target column needs anyway (no extra queries). $placements = $placement_repository->get_all( [ 'any' ], false ); $group_placement_counts = []; foreach ( $placements as $placement ) { $item = $placement->get_item(); if ( $item instanceof Group ) { $group_placement_counts[ $item->get_id() ] = ( $group_placement_counts[ $item->get_id() ] ?? 0 ) + 1; } } // Get the IDs of groups this ad is assigned to $assigned_group_ids = []; if ( $ad_id > 0 ) { $assigned_group_ids = $group_repository->find_by_contained_ad( $ad_id, [ 'publish', 'draft' ], false, true ); } // Prepare the group array for the response, adding the 'is_assigned_to_ad' flag. $groups_response = []; foreach ( $all_groups as $group ) { $groups_response[] = [ 'id' => $group->get_id(), 'title' => $group->get_title(), 'placement_count' => $group_placement_counts[ $group->get_id() ] ?? 0, 'is_assigned_to_ad' => in_array( $group->get_id(), $assigned_group_ids, true ), 'image' => SvgLoader::get( ADPRESSO_ABSPATH . $group->get_image(), [ 'class' => 'adpresso-icon-main' ] ), ]; } // Sort the groups: assigned groups first, then alphabetically by title usort( $groups_response, function( $a, $b ) { if ( $a['is_assigned_to_ad'] !== $b['is_assigned_to_ad'] ) { return $b['is_assigned_to_ad'] <=> $a['is_assigned_to_ad']; // true (1) comes before false (0) } return strcasecmp( $a['title'], $b['title'] ); // Secondary sort by title } ); // Get the placement IDs for the singular ad context $start = microtime( true ); $assigned_placement_ids = []; if ( $ad_id > 0 ) { $assigned_placement_ids = $placement_repository->get_by_using_item( $ad_id, 'ad', [ 'any' ], false, true ); } $placements_response = []; foreach ( $placements as $placement ) { $placement_id = $placement->get_id(); $item = $placement->get_item(); $placements_response[] = [ 'id' => $placement_id, 'title' => $placement->get_title(), 'type_label' => $placement->get_type_object() ? $placement->get_type_object()->get_title() : $placement->get_type(), 'item_id' => $item ? $item->get_id() : 0, 'image' => $placement->get_image(), ]; } $end = microtime( true ); usort( $placements_response, function( $a, $b ) use ( $assigned_placement_ids ) { $a_is_assigned = in_array( $a['id'], $assigned_placement_ids, true ); $b_is_assigned = in_array( $b['id'], $assigned_placement_ids, true ); if ( $a_is_assigned !== $b_is_assigned ) { return $b_is_assigned <=> $a_is_assigned; // true (1) comes befor false (0) } return strcasecmp( $a['title'], $b['title'] ); // Second sorting for the title. } ); $response_data = [ 'groups' => $groups_response, 'placements' => $placements_response, 'singular_ad_assignments' => [ 'placement_ids' => $assigned_placement_ids, // The "single" icon of the group list for the "Singular ad" card. 'image' => SvgLoader::get( ADPRESSO_ABSPATH . 'admin/assets/img/groups/ap-ui-grp-single.svg', [ 'class' => 'adpresso-icon-main' ] ), ], 'total_time' => ( $end - $start ) * 1000 . 'ms', ]; return new WP_REST_Response( $response_data, 200 ); } /** * Permission check for the Group-Assignments endpoint. * * @param WP_REST_Request $request * * @return bool|WP_Error */ public function get_group_assignments_permission_check( WP_REST_Request $request ): bool|WP_Error { $group_id = (int) $request->get_param( 'group_id' ); if ( ! current_user_can( 'edit_post', $group_id ) ) { return new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to view these assignments.', 'adpresso' ), [ 'status' => 403 ] ); } return true; } /** * Callback function to get all assignment data for a single group. * * @param WP_REST_Request $request * * @return WP_REST_Response */ public function get_group_assignments( WP_REST_Request $request ): WP_REST_Response { $group_id = (int) $request->get_param( 'group_id' ); $placement_repository = adpresso_get_placement_repository(); $placements = $placement_repository->get_all( [ 'any' ], false ); $placements_response = []; $assigned_placement_ids = $placement_repository->get_by_using_item( $group_id, 'group', [ 'any' ], false, true ); foreach ( $placements as $placement ) { $item = $placement->get_item(); $placements_response[] = [ 'id' => $placement->get_id(), 'title' => $placement->get_title(), 'type_label' => $placement->get_type_object() ? $placement->get_type_object()->get_title() : $placement->get_type(), 'item_id' => $item ? $item->get_id() : 0, // Important for the replacement logic 'image' => $placement->get_image(), ]; } // sort the placements: assigned first, then alphabetically by title usort( $placements_response, function( $a, $b ) use ( $assigned_placement_ids ) { $a_is_assigned = in_array( $a['id'], $assigned_placement_ids, true ); $b_is_assigned = in_array( $b['id'], $assigned_placement_ids, true ); if ( $a_is_assigned !== $b_is_assigned ) { return $b_is_assigned <=> $a_is_assigned; // true (1) comes before false (0) } return strcasecmp( $a['title'], $b['title'] ); // Second sorting for the title. } ); $response_data = [ 'placements' => $placements_response, 'assigned_placement_ids' => $assigned_placement_ids, ]; return new WP_REST_Response( $response_data, 200 ); } }