'POST', 'callback' => array( __CLASS__, 'rest_repair' ), 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, 'args' => array( 'offset' => array( 'type' => 'integer', 'minimum' => 0, 'default' => 0, ), 'limit' => array( 'type' => 'integer', 'minimum' => 1, 'maximum' => 500, 'default' => 200, ), ), ) ); } /** * REST-Antwort der Reparatur. * * @param WP_REST_Request $request Anfrage. * @return WP_REST_Response */ public static function rest_repair( $request ) { return rest_ensure_response( self::repair( (int) $request['offset'], (int) $request['limit'] ) ); } /** * Rechnet die Spiegelfelder eines Stapels neu, entfernt Spiegel ohne Box und meldet unlesbare Boxen. * Box-Daten selbst werden nie gelöscht. * * @param int $offset Beginn. * @param int $limit Anzahl. * @return array{checked:int, fixed:int, broken:array, next:int|null} */ public static function repair( $offset, $limit ) { $ids = self::posts( $offset, $limit + 1 ); $weiter = count( $ids ) > $limit; $ids = array_slice( $ids, 0, $limit ); $fixed = 0; $kaputt = array(); foreach ( $ids as $id ) { $roh = get_post_meta( $id, ThreeRB_Box::META_BOX, true ); if ( is_string( $roh ) && '' !== $roh && ! is_array( json_decode( $roh, true ) ) ) { $kaputt[] = $id; } $vorher = self::mirror_state( $id ); ThreeRB_Box::mirror( $id ); if ( self::mirror_state( $id ) !== $vorher ) { ++$fixed; } } return array( 'checked' => count( $ids ), 'fixed' => $fixed, 'broken' => $kaputt, 'next' => $weiter ? $offset + $limit : null, ); } /** * Stimmen die Spiegel mit den Boxen überein? (höchstens 500 Beiträge, für den Website-Zustand) * * @return int Anzahl falscher Spiegel. */ public static function mirror_errors() { $falsch = 0; foreach ( self::posts( 0, 500 ) as $id ) { $box = ThreeRB_Box::get( $id ); $gesamt = $box ? ThreeRB_Score::total( $box ) : array( 'value' => null ); $soll = null === $gesamt['value'] ? array( '', '', '' ) : array( (string) (int) round( $gesamt['share'] * 1000 ), $box['scale'], $box['item']['type'] ); if ( self::mirror_state( $id ) !== $soll ) { ++$falsch; } } return $falsch; } /** * Tests im Website-Zustand. * * @param array $tests Tests. * @return array */ public static function tests( $tests ) { $tests['direct']['threerb_schema'] = array( 'label' => __( 'Review markup is written once', '3task-review-box' ), 'test' => array( __CLASS__, 'test_schema' ), ); $tests['direct']['threerb_mirror'] = array( 'label' => __( 'Review scores for lists are up to date', '3task-review-box' ), 'test' => array( __CLASS__, 'test_mirror' ), ); return $tests; } /** * Test: fremde Plugins, die selbst Bewertungs-Markup schreiben. * * @return array */ public static function test_schema() { $fremd = ThreeRB_Diagnose::foreign( 0 ); $r = self::result( 'threerb_schema', 'good', __( 'No other rating plugin writes review markup', '3task-review-box' ), __( 'Only 3task Review Box adds review markup, so Google sees one review per page.', '3task-review-box' ) ); if ( $fremd ) { $r['status'] = 'recommended'; $r['label'] = __( 'Another rating plugin may write review markup too', '3task-review-box' ); /* translators: %s: plugin names. */ $r['description'] = '

' . esc_html( sprintf( __( 'Active: %s. Two reviews on one page can cost the review snippet. Deactivate the old plugin once its ratings are moved.', '3task-review-box' ), implode( ', ', $fremd ) ) ) . '

'; } return $r; } /** * Test: Spiegelfelder aktuell. * * @return array */ public static function test_mirror() { $falsch = self::mirror_errors(); $r = self::result( 'threerb_mirror', 'good', __( 'Review scores for lists are up to date', '3task-review-box' ), __( 'Top lists, the score column and sorting use the current scores.', '3task-review-box' ) ); if ( $falsch > 0 ) { $r['status'] = 'recommended'; $r['label'] = __( 'Some review scores for lists are out of date', '3task-review-box' ); /* translators: %d: number of posts. */ $r['description'] = '

' . esc_html( sprintf( _n( '%d post has an outdated score for lists.', '%d posts have an outdated score for lists.', $falsch, '3task-review-box' ), $falsch ) ) . '

'; $r['actions'] = sprintf( '

%2$s

', esc_url( admin_url( 'admin.php?page=threerb&tab=overview' ) ), esc_html__( 'Repair now', '3task-review-box' ) ); } return $r; } /** * Infobereich im Website-Zustand. * * @param array $info Bereiche. * @return array */ public static function info( $info ) { $s = ThreeRB_Settings::get(); $info['3task-review-box'] = array( 'label' => '3task Review Box', 'fields' => array( 'version' => array( 'label' => __( 'Version', '3task-review-box' ), 'value' => THREERB_VERSION, ), 'boxes' => array( 'label' => __( 'Posts with a rating', '3task-review-box' ), 'value' => count( self::posts( 0, 5000 ) ), ), 'scale' => array( 'label' => __( 'Default scale', '3task-review-box' ), 'value' => $s['scale'], ), 'display' => array( 'label' => __( 'Look and style', '3task-review-box' ), 'value' => $s['display'] . ', ' . $s['style'], ), 'schema' => array( 'label' => __( 'Review markup', '3task-review-box' ), 'value' => self::schema_mode(), ), 'foreign' => array( 'label' => __( 'Other rating plugins', '3task-review-box' ), 'value' => implode( ', ', ThreeRB_Diagnose::foreign( 0 ) ), ), ), ); return $info; } /** * Wer schreibt das Schema? * * @return string */ public static function schema_mode() { if ( defined( 'RANK_MATH_VERSION' ) ) { return __( 'inside the Rank Math graph', '3task-review-box' ); } if ( defined( 'WPSEO_VERSION' ) ) { return __( 'inside the Yoast SEO graph', '3task-review-box' ); } return __( 'own JSON-LD in the page head', '3task-review-box' ); } /** * Polylang: Box beim Anlegen einer Übersetzung kopieren, danach nie synchronisieren * (die Übersetzung hat eigene Texte, die Spiegel rechnet jede Sprache selbst). * * @param array $metas Schlüssel. * @param bool $sync Synchronisieren statt anlegen. * @return array */ public static function polylang( $metas, $sync = false ) { $metas = is_array( $metas ) ? $metas : array(); if ( $sync ) { return array_values( array_diff( $metas, array( ThreeRB_Box::META_BOX, ThreeRB_Box::META_QUICK, ThreeRB_Box::META_SCORE, ThreeRB_Box::META_SCALE, ThreeRB_Box::META_TYPE ) ) ); } return array_values( array_unique( array_merge( $metas, array( ThreeRB_Box::META_BOX, ThreeRB_Box::META_QUICK ) ) ) ); } /** * Beiträge mit Box, Schnellbereich oder Spiegel. * * @param int $offset Beginn. * @param int $limit Anzahl. * @return array */ public static function posts( $offset, $limit ) { return array_map( 'intval', get_posts( array( 'post_type' => 'any', 'post_status' => array( 'publish', 'future', 'draft', 'pending', 'private' ), 'posts_per_page' => max( 1, (int) $limit ), 'offset' => max( 0, (int) $offset ), 'orderby' => 'ID', 'order' => 'ASC', 'fields' => 'ids', 'no_found_rows' => true, 'update_post_term_cache' => false, 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Wartung, nur im Admin. 'relation' => 'OR', array( 'key' => ThreeRB_Box::META_BOX ), array( 'key' => ThreeRB_Box::META_QUICK ), array( 'key' => ThreeRB_Box::META_SCORE ), ), ) ) ); } /** * Aktueller Stand der drei Spiegelfelder. * * @param int $id Beitrag. * @return array */ private static function mirror_state( $id ) { return array( (string) get_post_meta( $id, ThreeRB_Box::META_SCORE, true ), (string) get_post_meta( $id, ThreeRB_Box::META_SCALE, true ), (string) get_post_meta( $id, ThreeRB_Box::META_TYPE, true ), ); } /** * Grundgerüst eines Testergebnisses. * * @param string $test Kennung. * @param string $status good, recommended, critical. * @param string $label Überschrift. * @param string $description Erklärung. * @return array */ private static function result( $test, $status, $label, $description ) { return array( 'label' => $label, 'status' => $status, 'badge' => array( 'label' => __( 'Reviews', '3task-review-box' ), 'color' => 'blue', ), 'description' => '

' . esc_html( $description ) . '

', 'actions' => '', 'test' => $test, ); } }