'string', 'single' => true, 'show_in_rest' => true, // Per OBJECT (0.8.8): WordPress geeft ( $allowed, $meta_key, $object_id ) mee. 'auth_callback' => function ( $allowed = false, $meta_key = '', $object_id = 0 ) { return $object_id > 0 && current_user_can( 'edit_post', (int) $object_id ); }, ) ); } } ); /** * Verwijder de geselecteerde AIGeschikt-wijzigingen. Retourneert per type een telling. * * @param array $what ['edits'=>bool,'schema'=>bool,'blogs'=>bool]. * @return array */ function aigeschikt_reset_changes( array $what ): array { global $wpdb; $counts = array( 'edits' => 0, 'schema' => 0, 'blogs' => 0 ); if ( ! empty( $what['edits'] ) ) { $counts['edits'] = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- one-off count for the reset response. $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->postmeta WHERE meta_key = %s", '_aigeschikt_edits' ) ); delete_post_meta_by_key( '_aigeschikt_edits' ); // De site-brede URL-overlays (AIGeschikt_Rewrite_Engine::URL_OPTION: homepage, // archieven, elke URL zonder post) bleven tot 0.9.1 na een reset gewoon // draaien, en telden niet mee (audit 07-10-2026, B4). $url_edits = get_option( 'aigeschikt_url_edits', array() ); $counts['edits'] += is_array( $url_edits ) ? count( $url_edits ) : 0; delete_option( 'aigeschikt_url_edits' ); } if ( ! empty( $what['schema'] ) ) { $counts['schema'] = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- one-off count for the reset response. $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->postmeta WHERE meta_key = %s", '_aigeschikt_schema' ) ); delete_post_meta_by_key( '_aigeschikt_schema' ); delete_post_meta_by_key( '_aigeschikt_schema_mode' ); } if ( ! empty( $what['blogs'] ) ) { $ids = get_posts( array( 'post_type' => array( 'post', 'page' ), 'post_status' => array( 'publish', 'draft', 'pending', 'future', 'private' ), 'numberposts' => 500, 'fields' => 'ids', 'meta_key' => AIGESCHIKT_BLOG_MARKER, // phpcs:ignore WordPress.DB.SlowDBQuery ) ); foreach ( $ids as $id ) { if ( wp_trash_post( $id ) ) { $counts['blogs']++; } } } // Bulk-wijziging → hele full-page-cache purgen, anders blijft de oude HTML staan. if ( ! empty( $what['edits'] ) || ! empty( $what['schema'] ) ) { AIGeschikt_Cache::purge_all(); } return $counts; } /* --------------------------------------------------------------------- */ /* REST: POST /aigeschikt/v1/reset (platform-knop) */ /* --------------------------------------------------------------------- */ add_action( 'rest_api_init', 'aigeschikt_register_reset_route' ); function aigeschikt_register_reset_route() { // manage_options (0.8.1): /reset kan site-breed wissen (meta + tot 500 // posts naar de prullenbak) — dat hoort nooit achter edit_posts te staan. register_rest_route( AIGESCHIKT_REST_NS, '/reset', array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => 'aigeschikt_rest_reset', 'permission_callback' => 'aigeschikt_can_admin', ) ); } /** * POST /aigeschikt/v1/reset — body: { "edits":true, "schema":true, "blogs":false }. * * @param WP_REST_Request $request Request. * @return WP_REST_Response */ function aigeschikt_rest_reset( WP_REST_Request $request ) { $body = $request->get_json_params(); $what = array( 'edits' => ! empty( $body['edits'] ), 'schema' => ! empty( $body['schema'] ), 'blogs' => ! empty( $body['blogs'] ), ); return rest_ensure_response( array( 'ok' => true, 'removed' => aigeschikt_reset_changes( $what ), ) ); } /* --------------------------------------------------------------------- */ /* WP-admin: verwijdering AANVRAGEN bij AIGeschikt */ /* */ /* De klant wist niets meer zelf (dat kon per ongeluk ons hele werk */ /* wegvagen). In plaats daarvan stuurt deze knop een verzoek naar het */ /* platform — net als de request→approve-flow in het dashboard. Uitvoeren */ /* gebeurt daarna operator-zijdig via de REST /reset-route hierboven. */ /* --------------------------------------------------------------------- */ add_action( 'admin_post_aigeschikt_request_removal', 'aigeschikt_handle_request_removal' ); function aigeschikt_handle_request_removal() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to do that.', 'aigeschikt-connector' ), '', array( 'response' => 403 ) ); } check_admin_referer( 'aigeschikt_request_removal' ); $back = wp_get_referer() ? wp_get_referer() : admin_url( 'admin.php?page=aigeschikt' ); if ( ! aigeschikt_is_connected() ) { wp_safe_redirect( aigeschikt_melding_url( __( 'Not connected to AIGeschikt yet — connect first to be able to send a request.', 'aigeschikt-connector' ), $back ) ); exit; } $current = wp_get_current_user(); $body = array( 'edits' => ! empty( $_POST['edits'] ), 'schema' => ! empty( $_POST['schema'] ), 'blogs' => ! empty( $_POST['blogs'] ), 'note' => isset( $_POST['note'] ) ? sanitize_textarea_field( wp_unslash( $_POST['note'] ) ) : '', 'site' => home_url(), 'requested_by' => $current ? $current->user_email : '', 'site_id' => aigeschikt_site_id(), ); $resp = wp_remote_post( aigeschikt_api_url() . '/v1/removal-request', array( 'timeout' => 15, 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . aigeschikt_api_key(), ), 'body' => wp_json_encode( $body ), 'data_format' => 'body', ) ); $ok = ! is_wp_error( $resp ) && (int) wp_remote_retrieve_response_code( $resp ) < 300; $msg = $ok ? __( 'Removal request sent to AIGeschikt. We roll the changes back and report back to you.', 'aigeschikt-connector' ) : __( 'Sending failed — try again later or contact AIGeschikt.', 'aigeschikt-connector' ); wp_safe_redirect( aigeschikt_melding_url( $msg, $back ) ); exit; }