| @@ -492,9 +492,9 @@ | ||
| 492 | 492 | if ( ! check_ajax_referer( 'faq_cat_order_nonce', 'nonce', false ) ) { |
| 493 | 493 | wp_send_json_error( __( 'Nonce Failed', 'betterdocs' ) ); |
| 494 | 494 | } |
| 495 | 495 | |
| 496 | - if ( ! current_user_can( 'edit_others_posts' ) ) { | |
| 496 | + if ( ! $this->can_manage_faqs() ) { | |
| 497 | 497 | wp_send_json_error( __( 'You don\'t have permission to manage FAQ groups.', 'betterdocs' ) ); |
| 498 | 498 | } |
| 499 | 499 | |
| 500 | 500 | $base_index = isset( $_POST['base_index'] ) ? intval( $_POST['base_index'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing |
| @@ -567,8 +567,39 @@ | ||
| 567 | 567 | |
| 568 | 568 | return $pieces; |
| 569 | 569 | } |
| 570 | 570 | |
| 571 | + /** | |
| 572 | + * Whether the current user may manage FAQ groups and FAQs. | |
| 573 | + * | |
| 574 | + * Two capabilities, either of which is enough. Nothing that could reach | |
| 575 | + * these routes before can be turned away now — the gate only widens. | |
| 576 | + * | |
| 577 | + * - `edit_others_posts` is the original gate, kept verbatim for backward | |
| 578 | + * compatibility: every site that granted FAQ Builder access by granting a | |
| 579 | + * core editor-level role keeps working exactly as it did. | |
| 580 | + * - `edit_others_docs` is the BetterDocs-side answer, added in 4.9.0. The | |
| 581 | + * `betterdocs_faq` post type is registered with | |
| 582 | + * `capability_type => [ 'doc', 'docs' ]` and `map_meta_cap => true`, so | |
| 583 | + * WordPress already governs individual FAQ posts with the docs capability | |
| 584 | + * family — the routes were the one place that asked for a *core* post | |
| 585 | + * capability instead. A documentation-only role (BetterDocs' own `editor` | |
| 586 | + * bucket, or anything Pro's `article_roles` grants) could edit every FAQ | |
| 587 | + * post through `wp/v2/betterdocs_faq` and still get a 403 from the FAQ | |
| 588 | + * Builder's own API. | |
| 589 | + * | |
| 590 | + * It is also what lines the REST routes up with the MCP FAQ abilities, which | |
| 591 | + * are gated on `edit_others_docs`: an agent that may create an FAQ through | |
| 592 | + * `bd-create-faq` reaches these same routes underneath. | |
| 593 | + * | |
| 594 | + * @since 4.9.0 | |
| 595 | + * | |
| 596 | + * @return bool | |
| 597 | + */ | |
| 598 | + private function can_manage_faqs() { | |
| 599 | + return current_user_can( 'edit_others_posts' ) || current_user_can( 'edit_others_docs' ); | |
| 600 | + } | |
| 601 | + | |
| 571 | 602 | public function register_api_endpoint() { |
| 572 | 603 | register_rest_route( |
| 573 | 604 | $this->namespace, |
| 574 | 605 | '/faq/sample_data', |
| @@ -575,9 +606,9 @@ | ||
| 575 | 606 | [ |
| 576 | 607 | 'methods' => [ 'POST' ], |
| 577 | 608 | 'callback' => [ $this, 'create_faq_sample' ], |
| 578 | 609 | 'permission_callback' => function () { |
| 579 | - return current_user_can( 'edit_others_posts' ); | |
| 610 | + return $this->can_manage_faqs(); | |
| 580 | 611 | } |
| 581 | 612 | ] |
| 582 | 613 | ); |
| 583 | 614 | |
| @@ -587,9 +618,9 @@ | ||
| 587 | 618 | [ |
| 588 | 619 | 'methods' => [ 'GET' ], |
| 589 | 620 | 'callback' => [ $this, 'fetch_faq_posts' ], |
| 590 | 621 | 'permission_callback' => function () { |
| 591 | - return current_user_can( 'edit_others_posts' ); | |
| 622 | + return $this->can_manage_faqs(); | |
| 592 | 623 | } |
| 593 | 624 | ] |
| 594 | 625 | ); |
| 595 | 626 | |
| @@ -599,9 +630,9 @@ | ||
| 599 | 630 | [ |
| 600 | 631 | 'methods' => [ 'POST' ], |
| 601 | 632 | 'callback' => [ $this, 'create_faq_category' ], |
| 602 | 633 | 'permission_callback' => function () { |
| 603 | - return current_user_can( 'edit_others_posts' ); | |
| 634 | + return $this->can_manage_faqs(); | |
| 604 | 635 | } |
| 605 | 636 | ] |
| 606 | 637 | ); |
| 607 | 638 | |
| @@ -611,9 +642,9 @@ | ||
| 611 | 642 | [ |
| 612 | 643 | 'methods' => [ 'POST' ], |
| 613 | 644 | 'callback' => [ $this, 'update_faq_category' ], |
| 614 | 645 | 'permission_callback' => function () { |
| 615 | - return current_user_can( 'edit_others_posts' ); | |
| 646 | + return $this->can_manage_faqs(); | |
| 616 | 647 | } |
| 617 | 648 | ] |
| 618 | 649 | ); |
| 619 | 650 | |
| @@ -623,9 +654,9 @@ | ||
| 623 | 654 | [ |
| 624 | 655 | 'methods' => [ 'POST' ], |
| 625 | 656 | 'callback' => [ $this, 'delete_faq_category' ], |
| 626 | 657 | 'permission_callback' => function () { |
| 627 | - return current_user_can( 'edit_others_posts' ); | |
| 658 | + return $this->can_manage_faqs(); | |
| 628 | 659 | } |
| 629 | 660 | ] |
| 630 | 661 | ); |
| 631 | 662 | |
| @@ -635,9 +666,9 @@ | ||
| 635 | 666 | [ |
| 636 | 667 | 'methods' => [ 'POST' ], |
| 637 | 668 | 'callback' => [ $this, 'create_betterdocs_faq' ], |
| 638 | 669 | 'permission_callback' => function () { |
| 639 | - return current_user_can( 'edit_others_posts' ); | |
| 670 | + return $this->can_manage_faqs(); | |
| 640 | 671 | } |
| 641 | 672 | ] |
| 642 | 673 | ); |
| 643 | 674 | |
| @@ -647,9 +678,9 @@ | ||
| 647 | 678 | [ |
| 648 | 679 | 'methods' => [ 'POST' ], |
| 649 | 680 | 'callback' => [ $this, 'update_betterdocs_faq' ], |
| 650 | 681 | 'permission_callback' => function () { |
| 651 | - return current_user_can( 'edit_others_posts' ); | |
| 682 | + return $this->can_manage_faqs(); | |
| 652 | 683 | } |
| 653 | 684 | ] |
| 654 | 685 | ); |
| 655 | 686 | |
| @@ -659,9 +690,9 @@ | ||
| 659 | 690 | [ |
| 660 | 691 | 'methods' => [ 'POST' ], |
| 661 | 692 | 'callback' => [ $this, 'delete_betterdocs_faq' ], |
| 662 | 693 | 'permission_callback' => function () { |
| 663 | - return current_user_can( 'edit_others_posts' ); | |
| 694 | + return $this->can_manage_faqs(); | |
| 664 | 695 | } |
| 665 | 696 | ] |
| 666 | 697 | ); |
| 667 | 698 | |
| @@ -671,9 +702,9 @@ | ||
| 671 | 702 | [ |
| 672 | 703 | 'methods' => [ 'POST' ], |
| 673 | 704 | 'callback' => [ $this, 'update_category_status' ], |
| 674 | 705 | 'permission_callback' => function () { |
| 675 | - return current_user_can( 'edit_others_posts' ); | |
| 706 | + return $this->can_manage_faqs(); | |
| 676 | 707 | } |
| 677 | 708 | ] |
| 678 | 709 | ); |
| 679 | 710 | |
| @@ -683,9 +714,9 @@ | ||
| 683 | 714 | [ |
| 684 | 715 | 'methods' => [ 'POST' ], |
| 685 | 716 | 'callback' => [ $this, 'update_faq_category_order' ], |
| 686 | 717 | 'permission_callback' => function () { |
| 687 | - return current_user_can( 'edit_others_posts' ); | |
| 718 | + return $this->can_manage_faqs(); | |
| 688 | 719 | } |
| 689 | 720 | ] |
| 690 | 721 | ); |
| 691 | 722 | |
| @@ -695,9 +726,9 @@ | ||
| 695 | 726 | [ |
| 696 | 727 | 'methods' => [ 'POST' ], |
| 697 | 728 | 'callback' => [ $this, 'update_faq_order_by_category' ], |
| 698 | 729 | 'permission_callback' => function () { |
| 699 | - return current_user_can( 'edit_others_posts' ); | |
| 730 | + return $this->can_manage_faqs(); | |
| 700 | 731 | } |
| 701 | 732 | ] |
| 702 | 733 | ); |
| 703 | 734 | |
| @@ -707,9 +738,9 @@ | ||
| 707 | 738 | [ |
| 708 | 739 | 'methods' => [ 'POST' ], |
| 709 | 740 | 'callback' => [ $this, 'update_faq_order_preference' ], |
| 710 | 741 | 'permission_callback' => function () { |
| 711 | - return current_user_can( 'edit_others_posts' ); | |
| 742 | + return $this->can_manage_faqs(); | |
| 712 | 743 | } |
| 713 | 744 | ] |
| 714 | 745 | ); |
| 715 | 746 | |
| @@ -719,9 +750,9 @@ | ||
| 719 | 750 | [ |
| 720 | 751 | 'methods' => [ 'GET' ], |
| 721 | 752 | 'callback' => [ $this, 'get_uncategorised_faq' ], |
| 722 | 753 | 'permission_callback' => function () { |
| 723 | - return current_user_can( 'edit_others_posts' ); | |
| 754 | + return $this->can_manage_faqs(); | |
| 724 | 755 | } |
| 725 | 756 | ] |
| 726 | 757 | ); |
| 727 | 758 | |
| @@ -731,9 +762,9 @@ | ||
| 731 | 762 | [ |
| 732 | 763 | 'methods' => [ 'GET' ], |
| 733 | 764 | 'callback' => [ $this, 'category_search' ], |
| 734 | 765 | 'permission_callback' => function () { |
| 735 | - return current_user_can( 'edit_others_posts' ); | |
| 766 | + return $this->can_manage_faqs(); | |
| 736 | 767 | }, |
| 737 | 768 | 'args' => [ |
| 738 | 769 | 'title' => [ |
| 739 | 770 | 'type' => 'string', |
| @@ -1325,9 +1356,9 @@ | ||
| 1325 | 1356 | // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query, WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- intentional NOT-IN scan to find FAQs without any group. |
| 1326 | 1357 | $posts = get_posts( |
| 1327 | 1358 | [ |
| 1328 | 1359 | 'post_type' => 'betterdocs_faq', |
| 1329 | - 'post_status' => current_user_can( 'edit_others_posts' ) ? [ 'publish', 'draft' ] : 'publish', | |
| 1360 | + 'post_status' => $this->can_manage_faqs() ? [ 'publish', 'draft' ] : 'publish', | |
| 1330 | 1361 | 'posts_per_page' => -1, |
| 1331 | 1362 | 'tax_query' => $tax_query, |
| 1332 | 1363 | 'meta_query' => $meta_query, |
| 1333 | 1364 | ] |