PluginProbe
BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP / trunk
BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP vtrunk
3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 3.0.0 2.4.13 2.4.12 2.4.11 2.4.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 All 110 releases
betterlinks / includes / API / Links.php

Links.php in BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP trunk, at includes/API/Links.php

367 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace BetterLinks\API;
4 if ( ! defined( 'ABSPATH' ) ) { exit; }
5
6 use BetterLinks\Traits\ArgumentSchema;
7
8 class Links extends Controller
9 {
10 use ArgumentSchema;
11 use \BetterLinks\Traits\Links;
12 /**
13 * Initialize hooks and option name
14 */
15 public function __construct()
16 {
17 add_action('rest_api_init', [$this, 'register_routes']);
18 }
19
20 /**
21 * Register the routes for the objects of the controller.
22 */
23 public function register_routes()
24 {
25 $endpoint = '/links/';
26 $favorite_endpoint = '/links_favorite/';
27 register_rest_route($this->namespace, $endpoint, [
28 [
29 'methods' => \WP_REST_Server::READABLE,
30 'callback' => [$this, 'get_items'],
31 'permission_callback' => [$this, 'get_items_permissions_check'],
32 'args' => $this->get_links_schema(),
33 ],
34 ]);
35
36 register_rest_route($this->namespace, $endpoint, [
37 [
38 'methods' => \WP_REST_Server::CREATABLE,
39 'callback' => [$this, 'create_item'],
40 'permission_callback' => [$this, 'create_item_permissions_check'],
41 'args' => $this->get_links_schema(),
42 ],
43 ]);
44
45 register_rest_route(
46 $this->namespace,
47 $favorite_endpoint . '(?P<id>[\d]+)',
48 array(
49 'args' => array(
50 'id' => array(
51 'description' => __('Unique identifier for the object.', 'betterlinks' ),
52 'type' => 'integer',
53 ),
54 ),
55 array(
56 'methods' => \WP_REST_Server::EDITABLE,
57 'callback' => array($this, 'update_item_favorite'),
58 'permission_callback' => [$this, 'update_favorite_permissions_check'],
59 'args' => [
60 'ID' => [
61 'type' => 'integer',
62 'sanitize_callback' => 'absint',
63 ],
64 'favForAll' => [
65 'type' => 'boolean',
66 ],
67 ],
68 ),
69 )
70 );
71
72 register_rest_route(
73 $this->namespace,
74 $endpoint . '(?P<id>[\d]+)',
75 array(
76 'args' => array(
77 'id' => array(
78 'description' => __('Unique identifier for the object.', 'betterlinks' ),
79 'type' => 'integer',
80 ),
81 ),
82 array(
83 'methods' => \WP_REST_Server::READABLE,
84 'callback' => array($this, 'get_item'),
85 'permission_callback' => [$this, 'permissions_check'],
86 'args' => $this->get_links_schema(),
87 ),
88 array(
89 'methods' => \WP_REST_Server::EDITABLE,
90 'callback' => array($this, 'update_item'),
91 'permission_callback' => [$this, 'update_item_permissions_check'],
92 'args' => $this->get_links_schema(),
93 ),
94 array(
95 'methods' => \WP_REST_Server::DELETABLE,
96 'callback' => array($this, 'delete_item'),
97 'permission_callback' => [$this, 'permissions_check'],
98 'args' => array(
99 'force' => array(
100 'type' => 'boolean',
101 'default' => false,
102 'description' => __('Whether to bypass Trash and force deletion.', 'betterlinks' ),
103 ),
104 ),
105 ),
106 // 'schema' => array( $this, 'get_public_item_schema' ),
107 )
108 );
109 }
110
111 /**
112 * Get betterlinks
113 *
114 * @param WP_REST_Request $request Full data about the request.
115 * @return WP_Error|WP_REST_Request
116 */
117 public function get_items($request)
118 {
119 $cache_data = get_transient(BETTERLINKS_CACHE_LINKS_NAME);
120 if (empty($cache_data) || !json_decode($cache_data, true)) {
121 $results = \BetterLinks\Helper::get_prepare_all_links();
122 set_transient(BETTERLINKS_CACHE_LINKS_NAME, json_encode($results));
123 return new \WP_REST_Response(
124 [
125 'success' => true,
126 'cache' => false,
127 'data' => $results,
128 ],
129 200
130 );
131 }
132 return new \WP_REST_Response(
133 [
134 'success' => true,
135 'cache' => true,
136 'data' => json_decode($cache_data),
137 ],
138 200
139 );
140 }
141
142 public function get_item($request)
143 {
144 return new \WP_REST_Response(
145 [
146 'success' => true,
147 'data' => [],
148 ],
149 200
150 );
151 }
152
153 /**
154 * Create OR Update betterlinks
155 *
156 * @param WP_REST_Request $request Full data about the request.
157 * @return WP_Error|WP_REST_Request
158 */
159 public function create_item($request)
160 {
161 $request = $request->get_params();
162 delete_transient(BETTERLINKS_CACHE_LINKS_NAME);
163 $args = $this->sanitize_links_data($request['params'] ?? $request);
164
165 // Reject a short_url that would shadow a URL WordPress already serves.
166 // BL's redirect handler at init:0 wins over WP routing, so the content
167 // would silently become unreachable. Sites can opt out via the
168 // `betterlinks/skip_wp_url_collision_check` filter. Instant Redirect
169 // shadows the edited post's own permalink by design and says so with
170 // `instant_redirect_post_id`, which exempts just that one path.
171 $invalid = $this->validate_link_payload($args, false, $this->resolve_instant_redirect_post_id($request));
172 if (is_wp_error($invalid)) {
173 return $this->rejection_response($invalid);
174 }
175
176 $results = $this->insert_link($args);
177 if ($results) {
178 return new \WP_REST_Response(
179 [
180 'success' => true,
181 'data' => $results,
182 ],
183 200
184 );
185 }
186 return new \WP_REST_Response(
187 [
188 'success' => false,
189 'data' => $results,
190 ],
191 200
192 );
193 }
194
195 /**
196 * Create OR Update betterlinks
197 *
198 * @param WP_REST_Request $request Full data about the request.
199 * @return WP_Error|WP_REST_Request
200 */
201 public function update_item($request)
202 {
203 $request = $request->get_params();
204 delete_transient(BETTERLINKS_CACHE_LINKS_NAME);
205 $args = $this->sanitize_links_data($request['params'] ?? $request);
206
207 // The route carries the id in the URL, but update_link() keys off the
208 // payload; without this an id-less payload silently updates nothing and
209 // skips the checks below.
210 if (!isset($args['ID']) && isset($request['id'])) {
211 $args['ID'] = absint($request['id']);
212 }
213
214 // Same collision gate as create, plus the uniqueness check the update
215 // branch never had. Skipped when short_url is unchanged, so ordinary
216 // edits to existing links keep working.
217 $invalid = $this->validate_link_payload($args, true, $this->resolve_instant_redirect_post_id($request));
218 if (is_wp_error($invalid)) {
219 return $this->rejection_response($invalid);
220 }
221
222 $response = $this->update_link($args);
223 return new \WP_REST_Response(
224 [
225 'success' => !empty($response),
226 'data' => $response,
227 ],
228 200
229 );
230 }
231
232 /**
233 * Render a validation failure in the shape the admin app already handles
234 * for rejected writes: HTTP 200, `success: false`, and a human-readable
235 * `data.message` it can put in a toast.
236 *
237 * @param \WP_Error $error
238 * @return \WP_REST_Response
239 */
240 protected function rejection_response($error)
241 {
242 $data = $error->get_error_data();
243 return new \WP_REST_Response(
244 [
245 'success' => false,
246 'data' => [
247 'code' => $error->get_error_code(),
248 'message' => $error->get_error_message(),
249 'conflicting_post_id' => is_array($data) && isset($data['conflicting_post_id']) ? $data['conflicting_post_id'] : 0,
250 ],
251 ],
252 200
253 );
254 }
255
256 /**
257 * Update betterlinks favorite option
258 *
259 * @param WP_REST_Request $request Full data about the request.
260 * @return WP_Error|WP_REST_Request
261 */
262 public function update_item_favorite($request)
263 {
264 $request = $request->get_params();
265 delete_transient(BETTERLINKS_CACHE_LINKS_NAME);
266 if (isset($request["id"]) && isset($request["params"]) && isset($request["params"]["favForAll"])) {
267 $params = [
268 "ID" => absint($request["id"]),
269 "data" => [
270 "favForAll" => $request["params"]["favForAll"]
271 ]
272 ];
273 $result = $this->update_link_favorite($params);
274 $response = [
275 "ID" => $params["ID"],
276 "favForAll" => $params["data"]["favForAll"],
277 ];
278 return new \WP_REST_Response(
279 [
280 'success' => $result,
281 'data' => $response,
282 ]
283 );
284 }
285 }
286 /**
287 * Delete betterlinks
288 *
289 * @param WP_REST_Request $request Full data about the request.
290 * @return WP_Error|WP_REST_Request
291 */
292 public function delete_item($request)
293 {
294 $request = $request->get_params();
295 delete_transient(BETTERLINKS_CACHE_LINKS_NAME);
296 $this->delete_link($request);
297 if (isset($request['id'])) {
298 \BetterLinks\Helper::delete_link_meta($request['id'], 'keywords');
299 }
300 return new \WP_REST_Response(
301 [
302 'success' => true,
303 'data' => [
304 'term_id' => isset( $request['term_id'] ) ? $request['term_id'] : null,
305 'ID' => isset( $request['id'] ) ? $request['id'] : null,
306 ],
307 ],
308 200
309 );
310 }
311
312 /**
313 * Check if a given request has access to update a setting
314 *
315 * @param WP_REST_Request $request Full data about the request.
316 * @return WP_Error|bool
317 */
318 public function get_items_permissions_check($request)
319 {
320 return apply_filters('betterlinks/api/links_get_items_permissions_check', current_user_can('manage_options'));
321 }
322
323 /**
324 * Check if a given request has access to update a setting
325 *
326 * @param WP_REST_Request $request Full data about the request.
327 * @return WP_Error|bool
328 */
329 public function create_item_permissions_check($request)
330 {
331 return apply_filters('betterlinks/api/links_create_item_permissions_check', current_user_can('manage_options'));
332 }
333
334 /**
335 * Check if a given request has access to update a setting
336 *
337 * @param WP_REST_Request $request Full data about the request.
338 * @return WP_Error|bool
339 */
340 public function update_item_permissions_check($request)
341 {
342 return apply_filters('betterlinks/api/links_update_item_permissions_check', current_user_can('manage_options'));
343 }
344
345 /**
346 * Check if a given request has access to update a setting
347 *
348 * @param WP_REST_Request $request Full data about the request.
349 * @return WP_Error|bool
350 */
351 public function update_favorite_permissions_check($request)
352 {
353 return apply_filters('betterlinks/api/links_update_favorite_permissions_check', current_user_can('manage_options'));
354 }
355
356 /**
357 * Check if a given request has access to update a setting
358 *
359 * @param WP_REST_Request $request Full data about the request.
360 * @return WP_Error|bool
361 */
362 public function permissions_check($request)
363 {
364 return current_user_can('manage_options');
365 }
366 }
367