401 ) ); } return true; } /** * Schrijf-permissie: ingelogd + kan posts/pagina's bewerken. * * @param WP_REST_Request $request Request. * @return true|WP_Error */ function aigeschikt_can_write( WP_REST_Request $request ) { if ( ! is_user_logged_in() ) { return new WP_Error( 'aigeschikt_unauthorized', __( 'Authentication required (Application Password).', 'aigeschikt-connector' ), array( 'status' => 401 ) ); } if ( ! current_user_can( 'edit_posts' ) ) { return new WP_Error( 'aigeschikt_forbidden', __( 'You do not have permission for this action.', 'aigeschikt-connector' ), array( 'status' => 403 ) ); } return true; } /** * Object-permissie: mag deze gebruiker DÉZE post bewerken? WordPress' eigen * `edit_post`-cap (auteurschap, post-type, status), niet het globale `edit_posts`. * Tot 0.8.7 kon een Contributor met een Application Password overlays en schema * zetten op élke post — ook die van anderen (audit 17-09-2026, M1). * * @param WP_REST_Request $request Request met een `id`-parameter. * @return true|WP_Error */ function aigeschikt_can_edit_post( WP_REST_Request $request ) { if ( ! is_user_logged_in() ) { return new WP_Error( 'aigeschikt_unauthorized', __( 'Authentication required (Application Password).', 'aigeschikt-connector' ), array( 'status' => 401 ) ); } $id = (int) $request['id']; if ( $id <= 0 || ! current_user_can( 'edit_post', $id ) ) { return new WP_Error( 'aigeschikt_forbidden', __( 'You do not have permission for this post.', 'aigeschikt-connector' ), array( 'status' => 403 ) ); } return true; } /** * Site-brede schrijfpermissie: URL-overlays (ook op de homepage), llms.txt, de * IndexNow-sleutel, de sitemap-verversing en de paginastructuur. Dat is werk op * de SITE, niet op één post: Editor-niveau (`edit_others_pages`), niet Contributor. * * @param WP_REST_Request $request Request. * @return true|WP_Error */ function aigeschikt_can_site_write( WP_REST_Request $request ) { if ( ! is_user_logged_in() ) { return new WP_Error( 'aigeschikt_unauthorized', __( 'Authentication required (Application Password).', 'aigeschikt-connector' ), array( 'status' => 401 ) ); } if ( ! current_user_can( 'edit_others_pages' ) ) { return new WP_Error( 'aigeschikt_forbidden', __( 'Site-wide changes require Editor rights (edit_others_pages).', 'aigeschikt-connector' ), array( 'status' => 403 ) ); } return true; } /** * Overlay op één post zetten of wissen: de overlay werkt op de HELE gerenderde * pagina (ook kop, menu en voet), dus schrijven is Editor-werk. Lezen blijft * `edit_post`. * * @param WP_REST_Request $request Request met een `id`-parameter. * @return true|WP_Error */ function aigeschikt_can_edit_post_editor( WP_REST_Request $request ) { $ok = aigeschikt_can_edit_post( $request ); if ( true !== $ok ) { return $ok; } if ( ! current_user_can( 'edit_others_pages' ) ) { return new WP_Error( 'aigeschikt_forbidden', __( 'Setting overlays requires Editor rights (edit_others_pages).', 'aigeschikt-connector' ), array( 'status' => 403 ) ); } return true; } /** * Pagina's maken/bijwerken: de handler eist daarnaast unfiltered_html (ruwe HTML). * * @param WP_REST_Request $request Request. * @return true|WP_Error */ function aigeschikt_can_edit_pages( WP_REST_Request $request ) { return aigeschikt_cap_check( 'edit_pages', __( 'You do not have permission to edit pages (edit_pages).', 'aigeschikt-connector' ) ); } /** * Menu-items toevoegen/verwijderen: thema-beheer (`edit_theme_options`). * * @param WP_REST_Request $request Request. * @return true|WP_Error */ function aigeschikt_can_edit_menus( WP_REST_Request $request ) { return aigeschikt_cap_check( 'edit_theme_options', __( 'You do not have permission to change menus (edit_theme_options).', 'aigeschikt-connector' ) ); } /** * Eigen concepten/blogs aanmaken: het generieke `edit_posts` IS hier de bedoelde * toets (er wordt een NIEUWE post gemaakt, geen bestaande aangeraakt). * * @param WP_REST_Request $request Request. * @return true|WP_Error */ function aigeschikt_can_create_posts( WP_REST_Request $request ) { return aigeschikt_cap_check( 'edit_posts', __( 'You do not have permission to create posts (edit_posts).', 'aigeschikt-connector' ) ); } /** * Gedeelde kern: ingelogd + één capability. * * @param string $cap Capability. * @param string $melding Foutmelding bij ontbrekend recht. * @return true|WP_Error */ function aigeschikt_cap_check( $cap, $melding ) { if ( ! is_user_logged_in() ) { return new WP_Error( 'aigeschikt_unauthorized', __( 'Authentication required (Application Password).', 'aigeschikt-connector' ), array( 'status' => 401 ) ); } if ( ! current_user_can( $cap ) ) { return new WP_Error( 'aigeschikt_forbidden', $melding, array( 'status' => 403 ) ); } return true; } /** * Beheer-permissie: alleen voor routes die de KOPPELING zelf of site-brede * content kunnen wijzigen (/claim overschrijft api_url + api_key, /reset wist * site-breed). Op edit_posts kon elke Contributor dat — 0.8.1, gemeld door de * beheerder van Finivlam (13-08-2026). * * Bewust NIET op de overige routes: het koppelaccount van bestaande klanten is * niet gegarandeerd Administrator (het onboarding-pad accepteert de * webdesigner), en blog-/schema-publicatie moet blijven werken. * * @param WP_REST_Request $request Request. * @return true|WP_Error */ function aigeschikt_can_admin( WP_REST_Request $request ) { if ( ! is_user_logged_in() ) { return new WP_Error( 'aigeschikt_unauthorized', __( 'Authentication required (Application Password).', 'aigeschikt-connector' ), array( 'status' => 401 ) ); } if ( ! current_user_can( 'manage_options' ) ) { return new WP_Error( 'aigeschikt_forbidden', __( 'Administrator rights are required for this action.', 'aigeschikt-connector' ), array( 'status' => 403 ) ); } return true; }