PluginProbe
ActivityPub / 9.2.0
ActivityPub v9.2.0
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
activitypub / includes / rest / class-actor-autocomplete-controller.php

class-actor-autocomplete-controller.php in ActivityPub 9.2.0, at includes/rest/class-actor-autocomplete-controller.php

149 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Actor_Autocomplete_Controller file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Rest;
9
10 use Activitypub\Activity\Base_Object;
11 use Activitypub\Collection\Actors;
12 use Activitypub\Collection\Remote_Actors;
13
14 use function Activitypub\get_masked_wp_version;
15 use function Activitypub\get_rest_url_by_path;
16
17 /**
18 * ActivityPub Actor Autocomplete Controller.
19 *
20 * Implements the SWICG ActivityPub API actor autocomplete extension: a typeahead search over the
21 * actors this server knows (its own local actors and the remote actors it has cached), returning an
22 * ActivityStreams Collection of actor objects.
23 *
24 * @see https://swicg.github.io/activitypub-api/autocomplete
25 */
26 class Actor_Autocomplete_Controller extends \WP_REST_Controller {
27 use Verification;
28
29 /**
30 * The JSON-LD context for the actor autocomplete extension.
31 *
32 * @var string
33 */
34 const CONTEXT = 'https://swicg.github.io/activitypub-api/autocomplete';
35
36 /**
37 * The namespace of this controller's route.
38 *
39 * @var string
40 */
41 protected $namespace = ACTIVITYPUB_REST_NAMESPACE;
42
43 /**
44 * The base of this controller's route.
45 *
46 * @var string
47 */
48 protected $rest_base = 'actors/autocomplete';
49
50 /**
51 * Register routes.
52 */
53 public function register_routes() {
54 \register_rest_route(
55 $this->namespace,
56 '/' . $this->rest_base,
57 array(
58 array(
59 'methods' => \WP_REST_Server::READABLE,
60 'callback' => array( $this, 'get_items' ),
61 'permission_callback' => array( $this, 'verify_authentication' ),
62 'args' => array(
63 'q' => array(
64 'description' => 'The text typed so far.',
65 'type' => 'string',
66 'required' => true,
67 'sanitize_callback' => 'sanitize_text_field',
68 ),
69 ),
70 ),
71 )
72 );
73 }
74
75 /**
76 * Search actors matching the query.
77 *
78 * @param \WP_REST_Request $request Full details about the request.
79 * @return \WP_REST_Response|\WP_Error Collection of actor objects, or WP_Error on failure.
80 */
81 public function get_items( $request ) {
82 $query = \trim( (string) $request->get_param( 'q' ) );
83
84 /**
85 * Filters the minimum number of characters required before actors are searched.
86 *
87 * @param int $min_length The minimum query length. Default 2.
88 */
89 $min_length = (int) \apply_filters( 'activitypub_actor_autocomplete_min_length', 2 );
90
91 if ( \mb_strlen( $query ) < $min_length ) {
92 return new \WP_Error(
93 'activitypub_query_too_short',
94 \sprintf(
95 /* translators: %d: minimum number of characters */
96 \__( 'The search query must be at least %d characters long.', 'activitypub' ),
97 $min_length
98 ),
99 array( 'status' => 400 )
100 );
101 }
102
103 /**
104 * Filters the maximum number of actors returned by the autocomplete endpoint.
105 *
106 * @param int $number The maximum number of actors. Default 10.
107 * @param \WP_REST_Request $request The request object.
108 */
109 $number = \max( 1, (int) \apply_filters( 'activitypub_actor_autocomplete_number', 10, $request ) );
110
111 $items = array();
112 $seen = array();
113
114 foreach ( Actors::search( $query, $number ) as $actor ) {
115 $items[] = $actor->to_array( false );
116 $seen[ $actor->get_id() ] = true;
117 }
118
119 // Only look up as many remote actors as are still needed to fill the result set.
120 $remaining = $number - \count( $items );
121
122 if ( $remaining > 0 ) {
123 foreach ( Remote_Actors::search( $query, $remaining ) as $post ) {
124 $actor = Remote_Actors::get_actor( $post );
125 if ( \is_wp_error( $actor ) || isset( $seen[ $actor->get_id() ] ) ) {
126 continue;
127 }
128 $items[] = $actor->to_array( false );
129 $seen[ $actor->get_id() ] = true;
130 }
131 }
132
133 $response = array(
134 // JSON_LD_CONTEXT is already a context array, so merge the extension IRI in rather than nesting it.
135 '@context' => \array_merge( (array) Base_Object::JSON_LD_CONTEXT, array( self::CONTEXT ) ),
136 'id' => \add_query_arg( 'q', $query, get_rest_url_by_path( $this->rest_base ) ),
137 'generator' => 'https://wordpress.org/?v=' . get_masked_wp_version(),
138 'type' => 'Collection',
139 'totalItems' => \count( $items ),
140 'items' => $items,
141 );
142
143 $response = \rest_ensure_response( $response );
144 $response->header( 'Content-Type', 'application/activity+json; charset=' . \get_option( 'blog_charset' ) );
145
146 return $response;
147 }
148 }
149