PluginProbe
Restrict User Access – Ultimate Membership & Content Protection / 2.7
Restrict User Access – Ultimate Membership & Content Protection v2.7
trunk 1.3 2.0 2.1.3 2.2.3 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.5 2.6 2.6.1 2.7 2.7.1 2.8 2.8.1
restrict-user-access / app.php

app.php in Restrict User Access – Ultimate Membership & Content Protection 2.7, at app.php

776 lines 23.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Restrict User Access
4 * @author Joachim Jensen <joachim@dev.institute>
5 * @license GPLv3
6 * @copyright 2024 by Joachim Jensen
7 */
8
9 defined('ABSPATH') || exit;
10
11 final class RUA_App
12 {
13 /**
14 * Plugin version
15 */
16 const PLUGIN_VERSION = '2.7';
17
18 /**
19 * Prefix for metadata
20 * Same as wp-content-aware-engine
21 */
22 const META_PREFIX = '_ca_';
23
24 /**
25 * Post Type for restriction
26 */
27 const TYPE_RESTRICT = 'restriction';
28
29 /**
30 * Post type statuses
31 */
32 const STATUS_ACTIVE = 'publish';
33 const STATUS_INACTIVE = 'draft';
34 const STATUS_SCHEDULED = 'future';
35
36 /**
37 * Capability to manage restrictions
38 * @deprecated use capability in post type object
39 */
40 const CAPABILITY = 'manage_options';
41
42 const BASE_SCREEN = 'wprua';
43
44 const ICON_SVG = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDIwIDIwIj48ZyBmaWxsPSIjYTBhNWFhIj48cGF0aCBkPSJNMTAuMDEyIDE0LjYyNUw1Ljc4IDEyLjI3Yy0xLjkwNi42NjQtMy42MDUgMS43Ni00Ljk4IDMuMTc4IDIuMTA1IDIuNzcgNS40MzYgNC41NiA5LjE4NSA0LjU2IDMuNzY2IDAgNy4xMTItMS44MDIgOS4yMTUtNC41OTMtMS4zOC0xLjQwNC0zLjA3LTIuNDk2LTQuOTctMy4xNTRsLTQuMjE4IDIuMzY3em0tLjAwNS0xNC42M0M3LjQxMi0uMDA1IDUuMzEgMS45MSA1LjMxIDQuMjhoOS4zOTNjMC0yLjM3LTIuMS00LjI4Ni00LjY5Ni00LjI4NnptNi4xMjYgMTAuNzFjLjE1OC0uMDMyLjY0LS4yMzIuNjMtLjMzMy0uMDI1LS4yNC0uNjg2LTUuNTg0LS42ODYtNS41ODRzLS40MjItLjI3LS42ODYtLjI5M2MuMDI0LjIxLjY5IDUuNzYuNzQ1IDYuMjF6bS0xMi4yNTMgMGMtLjE1OC0uMDMyLS42NC0uMjMyLS42My0uMzMzLjAyNS0uMjQuNjg2LTUuNTg0LjY4Ni01LjU4NHMuNDItLjI3LjY4Ni0uMjkzYy0uMDIuMjEtLjY5IDUuNzYtLjc0MiA2LjIxeiIvPjxwYXRoIGQ9Ik0xMCAxMy45NjdoLjAyM2wuOTc1LS41NXYtNC4yMWMuNzgtLjM3NyAxLjMxNC0xLjE3MyAxLjMxNC0yLjA5NyAwLTEuMjg1LTEuMDM1LTIuMzIzLTIuMzItMi4zMjNTNy42NyA1LjgyNSA3LjY3IDcuMTFjMCAuOTIzLjUzNSAxLjcyIDEuMzE1IDIuMDkzVjEzLjRsMS4wMTYuNTY3em0tMS43NjQtLjk4NXYtLjAzNWMwLTMuNjEtMS4zNS02LjU4My0zLjA4My02Ljk2bC0uMDMuMy0uNTIgNC42NyAzLjYzMyAyLjAyNXptMy41Ni0uMDM1YzAgLjAxNCAwIC4wMTguMDAzLjAyM2wzLjYxLTIuMDI1LS41My00LjY4LS4wMjgtLjI3M2MtMS43MjMuNC0zLjA1NyAzLjM2Mi0zLjA1NyA2Ljk1NXoiLz48L2c+PC9zdmc+';
45
46 /**
47 * @var array
48 */
49 private $levels = [];
50
51 /**
52 * @var array<int, int>
53 */
54 private $level_extends_map = [];
55
56 /**
57 * @var array<int, int[]>
58 */
59 private $level_extended_by_map = [];
60
61 /**
62 * @var WP_DB_Updater
63 */
64 private $db_updater;
65
66 /**
67 * @var RUA_App
68 */
69 private static $_instance;
70
71 /**
72 * @var RUA_Level_Manager
73 */
74 public $level_manager;
75
76 /** @var RUA_Member_Automator[]|RUA_Collection<RUA_Member_Automator> */
77 private $level_automators;
78
79 public function __construct()
80 {
81 $this->level_manager = new RUA_Level_Manager();
82
83 $this->db_updater = new WP_DB_Updater('rua_plugin_version', self::PLUGIN_VERSION);
84
85 new RUA_Nav_Menu();
86
87 if (is_admin()) {
88 $rua_fs = rua_fs();
89 new RUA_Level_Overview();
90 new RUA_Level_Edit();
91 new RUA_Settings_Page();
92 new RUA_Admin_Screen_Account($rua_fs);
93 new RUA_Admin_Screen_Addons($rua_fs);
94
95 add_action(
96 'admin_enqueue_scripts',
97 [$this,'load_admin_scripts'],
98 999
99 );
100
101 add_action(
102 'show_user_profile',
103 [$this,'add_field_access_level']
104 );
105 add_action(
106 'edit_user_profile',
107 [$this,'add_field_access_level']
108 );
109 add_action(
110 'personal_options_update',
111 [$this,'save_user_profile']
112 );
113 add_action(
114 'edit_user_profile_update',
115 [$this,'save_user_profile']
116 );
117 add_action(
118 'delete_post',
119 [$this,'sync_level_deletion']
120 );
121 add_action(
122 'delete_user',
123 [$this, 'sync_user_deletion'],
124 1,
125 3
126 );
127
128 add_filter(
129 'manage_users_columns',
130 [$this,'add_user_column_headers']
131 );
132 add_filter(
133 'manage_users_custom_column',
134 [$this,'add_user_columns'],
135 10,
136 3
137 );
138 add_filter(
139 'cas/metadata/populate',
140 [$this,'add_levels_to_visibility']
141 );
142
143 $file = plugin_basename(plugin_dir_path(__FILE__)) . '/restrict-user-access.php';
144 add_filter(
145 'plugin_action_links_' . $file,
146 [$this,'plugin_action_links'],
147 10,
148 4
149 );
150 } else {
151 new RUA_Admin_Bar();
152 new RUA_Content_Mode();
153 }
154
155 new RUA_Query_Filters();
156
157 add_action('wpca/loaded', [$this, 'ensure_wpca_loaded']);
158
159 add_shortcode(
160 'login-form',
161 [$this,'shortcode_login_form']
162 );
163
164 add_filter(
165 'cas/user_visibility',
166 [$this,'sidebars_check_levels']
167 );
168
169 add_filter(
170 'rest_authentication_errors',
171 [$this, 'rest_api_access']
172 );
173 }
174
175 public function ensure_wpca_loaded()
176 {
177 $this->process_level_automators();
178
179 //hook early, other plugins might add dynamic caps later
180 //fixes problem with WooCommerce Orders
181 //todo: verify if this is still an issue, now that we run in wpca/loaded
182 add_filter(
183 'user_has_cap',
184 [$this,'user_level_has_cap'],
185 9,
186 4
187 );
188 }
189
190 /**
191 * Override user caps with level caps.
192 *
193 * @param array $allcaps
194 * @param string $cap
195 * @param array $args {
196 * @type string [0] Requested capability
197 * @type int [1] User ID
198 * @type WP_User [2] Associated object ID (User object)
199 * }
200 * @param WP_User $user
201 *
202 * @return array
203 */
204 public function user_level_has_cap($allcaps, $cap, $args, $user)
205 {
206 return rua_get_user($user)->get_caps($allcaps);
207 }
208
209 /**
210 * Instantiates and returns class singleton
211 *
212 * @since 0.1
213 * @return RUA_App
214 */
215 public static function instance()
216 {
217 if (!self::$_instance) {
218 self::$_instance = new self();
219 }
220 return self::$_instance;
221 }
222
223 /**
224 * @since 1.1
225 * @return WP_DB_Updater
226 */
227 public function get_updater()
228 {
229 return $this->db_updater;
230 }
231
232 /**
233 * Add Levels to sidebar visibility metadata list
234 *
235 * @since 0.12
236 * @param WPCACollection $metadata
237 */
238 public function add_levels_to_visibility($metadata)
239 {
240 $visibility = $metadata->get('visibility');
241 $list = $visibility->get_input_list();
242
243 if (isset($list['rua-levels'])) {
244 return $metadata;
245 }
246
247 $levels = $this->get_levels();
248 if ($levels) {
249 $options = [];
250 foreach ($levels as $level) {
251 $options[$level->ID] = $level->post_title;
252 }
253
254 if (!defined('CAS_App::PLUGIN_VERSION')
255 || version_compare(CAS_App::PLUGIN_VERSION, '3.8', '<')) {
256 $list = $list + $options;
257 } else {
258 $list['rua-levels'] = [
259 'label' => __('Access Levels', 'restrict-user-access'),
260 'options' => $options
261 ];
262 }
263 $visibility->set_input_list($list);
264 }
265
266 return $metadata;
267 }
268
269 /**
270 * Check if user level has access to sidebar
271 *
272 * @since 0.12
273 * @param array $visibility
274 * @return array
275 */
276 public function sidebars_check_levels($visibility)
277 {
278 $user = rua_get_user();
279
280 if (!$user->has_global_access()) {
281 return array_merge($visibility, $user->get_level_ids());
282 }
283
284 return array_merge($visibility, array_keys($this->get_levels()));
285 }
286
287 /**
288 * Get login form in shotcode
289 *
290 * @version 0.9
291 * @param array $atts
292 * @param string $content
293 * @return string
294 */
295 public function shortcode_login_form($atts, $content = null)
296 {
297 if (is_user_logged_in()) {
298 return $content;
299 }
300 $a = shortcode_atts([
301 'remember' => true,
302 'redirect' => '',
303 'form_id' => 'loginform',
304 'id_username' => 'user_login',
305 'id_password' => 'user_pass',
306 'id_remember' => 'rememberme',
307 'id_submit' => 'wp-submit',
308 'label_username' => __('Username'),
309 'label_password' => __('Password'),
310 'label_remember' => __('Remember Me'),
311 'label_log_in' => __('Log In'),
312 'value_username' => '',
313 'value_remember' => false
314 ], $atts);
315 $a['echo'] = false;
316
317 if (!$a['redirect']) {
318 $parts = parse_url(home_url());
319 $root = "{$parts['scheme']}://{$parts['host']}";
320 if (isset($parts['port']) && $parts['port']) {
321 $root .= ':' . $parts['port'];
322 }
323 if (isset($_GET['redirect_to'])) {
324 $a['redirect'] = $root . urldecode($_GET['redirect_to']);
325 } else {
326 $a['redirect'] = $root . add_query_arg(null, null);
327 }
328 }
329
330 return wp_login_form($a);
331 }
332
333 /**
334 * Add Access Level to user profile
335 *
336 * @since 0.3
337 * @param WP_User $user
338 */
339 public function add_field_access_level($user)
340 {
341 $post_type = get_post_type_object(self::TYPE_RESTRICT);
342 if (!current_user_can($post_type->cap->edit_posts) || is_network_admin()) {
343 return;
344 }
345 $rua_user = rua_get_user($user);
346 $user_levels = [];
347 $link = '<a target="_blank" href="https://dev.institute/docs/restrict-user-access/getting-started/add-level-members/">' . __('Visitor Traits', 'restrict-user-access') . '</a>';
348 foreach ($rua_user->level_memberships() as $membership) {
349 $user_levels[] = $membership->get_level_id();
350 } ?>
351 <h3><?php _e('Access Control', 'restrict-user-access'); ?>
352 </h3>
353 <table class="form-table">
354 <tr>
355 <th><label for="_ca_level"><?php _e('Level Memberships', 'restrict-user-access'); ?></label>
356 </th>
357 <td>
358 <div style="width:25em;"><select style="width:100%;" class="js-rua-levels" multiple="multiple"
359 name="_ca_level[]"
360 data-value="<?php echo esc_html(implode(',', $user_levels)); ?>"></select>
361 </div>
362 <p class="description"><?php printf(__('Access Levels provided by %s will not be listed here.', 'restrict-user-access'), $link); ?>
363 </p>
364 </td>
365 </tr>
366 </table>
367 <?php
368 }
369
370 /**
371 * Save additional data for
372 * user profile
373 *
374 * @since 0.3
375 * @param int $user_id
376 * @return void
377 */
378 public function save_user_profile($user_id)
379 {
380 $post_type = get_post_type_object(self::TYPE_RESTRICT);
381 if (!current_user_can($post_type->cap->edit_posts) || is_network_admin()) {
382 return;
383 }
384
385 $user = rua_get_user($user_id);
386 $new_levels = isset($_POST[self::META_PREFIX . 'level']) ? (array) $_POST[self::META_PREFIX . 'level'] : [];
387
388 $user_levels = [];
389 foreach ($user->level_memberships() as $membership) {
390 $user_levels[$membership->get_level_id()] = 1;
391 }
392
393 wp_defer_comment_counting(true);
394 foreach ($new_levels as $level) {
395 if (isset($user_levels[$level])) {
396 unset($user_levels[$level]);
397 } else {
398 $user->add_level($level);
399 }
400 }
401 foreach ($user_levels as $level => $value) {
402 $user->remove_level($level);
403 }
404 wp_defer_comment_counting(false);
405 }
406
407 /**
408 * Add column headers on
409 * User overview
410 *
411 * @since 0.3
412 * @param array $column
413 */
414 public function add_user_column_headers($columns)
415 {
416 $new_columns = [];
417 foreach ($columns as $key => $title) {
418 $new_columns[$key] = $title;
419 if ($key == 'role') {
420 $new_columns['level'] = __('Access Levels', 'restrict-user-access');
421 }
422 }
423 return $new_columns;
424 }
425
426 /**
427 * Add columns on user overview
428 *
429 * @since 0.3
430 * @param string $output
431 * @param string $column_name
432 * @param int $user_id
433 */
434 public function add_user_columns($output, $column_name, $user_id)
435 {
436 switch ($column_name) {
437 case 'level':
438 $level_links = [];
439 foreach (rua_get_user($user_id)->level_memberships() as $membership) {
440 $level_links[] = sprintf(
441 '<a href="%s">%s%s</a>',
442 get_edit_post_link($membership->get_level_id()),
443 $membership->level()->get_title(),
444 !$membership->is_active() ? ' (' . $membership->get_status() . ') ' : ''
445 );
446 }
447 sort($level_links);
448 $output = implode(', ', $level_links);
449 break;
450 default:
451 }
452 return $output;
453 }
454
455 /**
456 * @param int $level_id
457 *
458 * @return int[]
459 */
460 public function get_level_extends($level_id)
461 {
462 $levels = [];
463 while (isset($this->level_extends_map[$level_id])) {
464 $level_id = $this->level_extends_map[$level_id];
465 $levels[] = $level_id;
466 }
467 return $levels;
468 }
469
470 /**
471 * @param int $level_id
472 *
473 * @return int[]
474 */
475 public function get_level_extended_by($level_id)
476 {
477 $levels = [];
478 if (isset($this->level_extended_by_map[$level_id])) {
479 foreach ($this->level_extended_by_map[$level_id] as $level) {
480 $levels[] = $level;
481 $levels = array_merge($levels, $this->get_level_extended_by($level));
482 }
483 }
484 return $levels;
485 }
486
487 /**
488 * Get all levels
489 *
490 * @since 0.3
491 * @return array
492 */
493 public function get_levels()
494 {
495 if (!$this->levels) {
496 $levels = get_posts([
497 'numberposts' => -1,
498 'post_type' => self::TYPE_RESTRICT,
499 'post_status' => [
500 self::STATUS_ACTIVE,
501 self::STATUS_INACTIVE,
502 self::STATUS_SCHEDULED
503 ],
504 'update_post_meta_cache' => true
505 ]);
506 foreach ($levels as $level) {
507 $this->levels[$level->ID] = $level;
508 if ($level->post_parent) {
509 $this->level_extends_map[$level->ID] = $level->post_parent;
510
511 if (!isset($this->level_extended_by_map[$level->post_parent])) {
512 $this->level_extended_by_map[$level->post_parent] = [];
513 }
514 $this->level_extended_by_map[$level->post_parent][] = $level->ID;
515 }
516 }
517 }
518 return $this->levels;
519 }
520
521 /**
522 * Delete foreign metadata belonging to level
523 *
524 * @since 0.11.1
525 * @param int $post_id
526 * @return void
527 */
528 public function sync_level_deletion($post_id)
529 {
530 $post = get_post($post_id);
531
532 if (!$post || $post->post_type != RUA_App::TYPE_RESTRICT) {
533 return;
534 }
535
536 global $wpdb;
537
538 //Delete user levels
539 $wpdb->query($wpdb->prepare(
540 "DELETE FROM $wpdb->usermeta
541 WHERE
542 (meta_key = %s AND meta_value = %d)
543 OR
544 meta_key = %s",
545 self::META_PREFIX . 'level',
546 $post_id,
547 self::META_PREFIX . 'level_' . $post_id
548 ));
549
550 //Delete nav menu item levels
551 $wpdb->query($wpdb->prepare(
552 "DELETE FROM $wpdb->postmeta
553 WHERE
554 meta_key = %s AND meta_value = %d",
555 '_menu_item_level',
556 $post_id
557 ));
558 }
559
560 public function sync_user_deletion($id, $reassign, $user)
561 {
562 global $wpdb;
563
564 $entities = $wpdb->get_results( $wpdb->prepare( "SELECT comment_post_ID, comment_ID FROM $wpdb->comments
565 WHERE comment_type = %s AND user_id = %d",
566 RUA_User_Level::ENTITY_TYPE,
567 $id
568 ));
569
570 wp_defer_comment_counting(true);
571 foreach($entities as $entity) {
572 wp_delete_comment($entity->comment_ID, true);
573 wp_update_comment_count($entity->comment_post_ID);
574 }
575 wp_defer_comment_counting(false);
576 }
577
578 /**
579 * Add actions to plugin in Plugins screen
580 *
581 * @version 1.0
582 * @param array $actions
583 * @param string $plugin_file
584 * @param [type] $plugin_data
585 * @param [type] $context
586 * @return array
587 */
588 public function plugin_action_links($actions, $plugin_file, $plugin_data, $context)
589 {
590 $new_actions = [];
591 $new_actions['docs'] = '<a href="https://dev.institute/docs/restrict-user-access/?utm_source=plugin&amp;utm_medium=referral&amp;utm_content=plugin-list&amp;utm_campaign=rua" target="_blank">' . __('Documentation & FAQ', 'restrict-user-access') . '</a>';
592
593 return array_merge($new_actions, $actions);
594 }
595
596 /**
597 * Load scripts and styles for administration
598 *
599 * @since 0.1
600 * @param string $hook
601 * @return void
602 */
603 public function load_admin_scripts($hook)
604 {
605 $current_screen = get_current_screen();
606
607 if ($current_screen->id == 'nav-menus' || $current_screen->id == 'user-edit' || $current_screen->id == 'profile') {
608 //todo: enqueue automatically in wpcacore
609 if (wp_script_is('select2', 'registered')) {
610 wp_deregister_script('select2');
611 }
612 wp_register_script(
613 'select2',
614 plugins_url('/lib/wp-content-aware-engine/assets/js/select2.min.js', __FILE__),
615 ['jquery'],
616 '4.0.3',
617 false
618 );
619 wp_enqueue_style(self::META_PREFIX . 'condition-groups');
620
621 $levels = [];
622 foreach ($this->get_levels() as $level) {
623 $levels[] = [
624 'id' => $level->ID,
625 'text' => $level->post_title
626 ];
627 }
628 wp_enqueue_script('rua/admin/suggest-levels', plugins_url('/assets/js/suggest-levels.min.js', __FILE__), ['select2','jquery'], self::PLUGIN_VERSION);
629 wp_localize_script('rua/admin/suggest-levels', 'RUA', [
630 'search' => __('Search for Levels', 'restrict-user-access'),
631 'levels' => $levels
632 ]);
633 }
634 }
635
636 /**
637 * @return RUA_Collection|RUA_Member_Automator[]
638 */
639 public function get_level_automators()
640 {
641 if ($this->level_automators === null) {
642 $automators = [
643 new RUA_Role_Member_Automator(),
644 new RUA_Role_Sync_Member_Automator(),
645 new RUA_LoggedIn_Member_Automator(),
646 new RUA_BP_Member_Type_Member_Automator(),
647 new RUA_EDD_Product_Member_Automator(),
648 new RUA_WooProduct_Member_Automator(),
649 new RUA_GiveWP_Donation_Member_Automator()
650 ];
651
652 $this->level_automators = new RUA_Collection();
653 /** @var RUA_Member_Automator $automator */
654 foreach ($automators as $automator) {
655 if ($automator->can_enable()) {
656 $this->level_automators->put($automator->get_name(), $automator);
657 if (is_admin()) {
658 add_action(
659 'wp_ajax_rua/automator/' . $automator->get_name(),
660 [$automator,'ajax_print_content']
661 );
662 }
663 }
664 }
665 }
666 return $this->level_automators;
667 }
668
669 public function process_level_automators()
670 {
671 $metadata = $this->level_manager->metadata();
672 $levels = $this->get_levels();
673 $automators = $this->get_level_automators();
674
675 foreach ($levels as $level) {
676 if ($level->post_status != RUA_App::STATUS_ACTIVE) {
677 continue;
678 }
679
680 $automators_data = $metadata->get('member_automations')->get_data($level->ID);
681 if (empty($automators_data)) {
682 continue;
683 }
684
685 foreach ($automators_data as $automator_data) {
686 if (!isset($automator_data['value'],$automator_data['name'])) {
687 continue;
688 }
689
690 if (!$automators->has($automator_data['name'])) {
691 continue;
692 }
693
694 $automators->get($automator_data['name'])->queue($level->ID, $automator_data['value']);
695 }
696 }
697
698 foreach ($automators as $automator) {
699 if (!empty($automator->get_level_data())) {
700 $automator->add_callback();
701 }
702 }
703 }
704
705 public function rest_api_access($result)
706 {
707 //bail if auth has been handled elsewhere
708 if ($result === true || is_wp_error($result)) {
709 return $result;
710 }
711
712 if (rua_get_user()->has_global_access()) {
713 return $result;
714 }
715
716 if (!get_option('rua_rest_api_access', 1)) {
717 return $result;
718 }
719
720 //Contributor is the lowest role that should have access,
721 //since they can see content in admin area
722 if (current_user_can('edit_posts')) {
723 return $result;
724 }
725
726 $restricted = [
727 '/wp/v2/search' => true,
728 '/wp/v2/users' => true
729 ];
730
731 $ignored_post_types = [
732 'nav_menu_item' => true,
733 'wp_block' => true,
734 'wp_template' => true,
735 'wp_template_part' => true,
736 'wp_navigation' => true
737 ];
738 foreach (get_post_types(['show_in_rest' => true], 'objects') as $post_type) {
739 if (empty($post_type->rest_base)) {
740 continue;
741 }
742 if (isset($ignored_post_types[$post_type->name])) {
743 continue;
744 }
745 $restricted['/' . $post_type->rest_namespace . '/' . $post_type->rest_base] = true;
746 }
747 $ignored_taxonomies = [
748 'menu' => true,
749 ];
750 foreach (get_taxonomies(['show_in_rest' => true], 'objects') as $taxonomy) {
751 if (empty($taxonomy->rest_base)) {
752 continue;
753 }
754 if (isset($ignored_taxonomies[$post_type->name])) {
755 continue;
756 }
757 $restricted['/' . $taxonomy->rest_namespace . '/' . $taxonomy->rest_base] = true;
758 }
759
760 global $wp;
761
762 $route = $wp->query_vars['rest_route'];
763 $route = preg_replace('/(\/\d+)$/', '', $route, 1);
764
765 if (!isset($restricted[$route])) {
766 return $result;
767 }
768
769 return new WP_Error(
770 'rest_forbidden',
771 __('Sorry, you are not allowed to do that.'),
772 ['status' => rest_authorization_required_code()]
773 );
774 }
775 }
776