post_type, ThreeRB_Settings::value( 'post_types' ), true ) ) {
return false;
}
if ( 'publish' === $post->post_status && '' === $post->post_password ) {
return true;
}
if ( '' !== $post->post_password ) {
return current_user_can( 'edit_post', $post->ID );
}
return current_user_can( 'read_post', $post->ID );
}
/**
* Ist die Box öffentlich (für Suchmaschinen und Caches)?
*
* @param int $post_id Beitrag.
* @return bool
*/
public static function is_public( $post_id ) {
$post = get_post( (int) $post_id );
return $post && 'publish' === $post->post_status && '' === $post->post_password && in_array( $post->post_type, ThreeRB_Settings::value( 'post_types' ), true );
}
/**
* Volle oder kompakte Box.
*
* @param int $post_id Beitrag.
* @param array $args variant (full|compact), display (bars|stars), style (theme|night).
* @return string
*/
public static function box( $post_id, array $args = array() ) {
if ( ! self::visible( $post_id ) ) {
return '';
}
$box = ThreeRB_Box::get( $post_id );
if ( ! ThreeRB_Box::has_rating( $box ) ) {
return '';
}
return self::html( (int) $post_id, $box, $args, true );
}
/**
* Vorschau für den Reiter Design: dieselbe Vorlage mit Beispieldaten.
*
* @param string $display bars, stars oder profile.
* @param string $style theme oder night, leer = Einstellung.
* @return string
*/
public static function preview( $display, $style = '' ) {
$box = ThreeRB_Box::clean(
array(
'item' => array(
'type' => 'product',
'sub' => __( 'Product · Tools', '3task-review-box' ),
),
'scale' => 'p10',
'step' => 0.1,
'criteria' => array(
array(
'label' => __( 'Torque', '3task-review-box' ),
'weight' => 30,
'score' => 9,
'measure' => '62 Nm',
),
array(
'label' => __( 'Battery', '3task-review-box' ),
'weight' => 25,
'score' => 8,
'measure' => '41 min',
),
array(
'label' => __( 'Handling', '3task-review-box' ),
'weight' => 25,
'score' => 8.5,
),
array(
'label' => __( 'Value for money', '3task-review-box' ),
'weight' => 20,
'score' => 7,
),
),
'pros' => array( __( 'Drives 8 mm screws without pilot holes', '3task-review-box' ) ),
'cons' => array( __( 'LED dazzles', '3task-review-box' ) ),
'tested' => __( 'Two weeks on a building site', '3task-review-box' ),
)
)['box'];
$box['score'] = null;
$box['item']['name'] = __( 'Cordless drill X18', '3task-review-box' );
$box['summary'] = '';
$box['place'] = array(
'top' => false,
'bottom' => false,
'sidebar' => false,
);
return self::html(
0,
$box,
array(
'display' => $display,
'style' => $style,
),
false
);
}
/**
* HTML der vollen oder kompakten Box aus Box-Daten.
*
* @param int $post_id Beitrag (0 in der Vorschau).
* @param array $box Box.
* @param array $args variant, display, style.
* @param bool $template Theme-Vorlage erlauben.
* @return string
*/
private static function html( $post_id, array $box, array $args, $template ) {
self::assets();
$s = ThreeRB_Settings::get();
$display = self::wahl( isset( $args['display'] ) ? $args['display'] : '', $box['display'], $s['display'], array( 'bars', 'stars', 'profile' ) );
$style = self::wahl( isset( $args['style'] ) ? $args['style'] : '', $box['style'], $s['style'], array( 'theme', 'night' ) );
$gesamt = ThreeRB_Score::total( $box );
$klassen = array( 'trb' );
if ( 'stars' === $display ) {
$klassen[] = 'trb--sterne';
}
if ( 'profile' === $display ) {
$klassen[] = 'trb--profil';
}
if ( 'night' === $style ) {
$klassen[] = 'trb--nacht';
}
$farbe = self::accent( $style );
$stil = $farbe ? sprintf( '--trb-accent:%1$s;--trb-on-accent:%2$s;--trb-accent-text:%3$s', $farbe, self::on_accent( $farbe ), '' !== self::accent_text( $farbe ) ? $farbe : 'currentColor' ) : '';
$stil .= sprintf( '%1$s--trb-radius:%2$dpx;--trb-stern:%3$s', '' !== $stil ? ';' : '', (int) $s['radius'], $s['star_color'] );
$ebene = in_array( $box['heading'], array( 'h2', 'h3', 'h4' ), true ) ? $box['heading'] : $s['heading'];
$name = '' !== $box['item']['name'] ? $box['item']['name'] : get_the_title( $post_id );
if ( isset( $args['variant'] ) && 'compact' === $args['variant'] ) {
return self::kompakt( $box, $gesamt, $name, $klassen, $stil, $display );
}
$id = 'trb-' . (int) $post_id . '-' . wp_rand( 100, 999 );
$html = sprintf( '', esc_attr( implode( ' ', $klassen ) ), esc_attr( $stil ), esc_attr( $id . '-t' ), esc_attr( (string) wp_json_encode( self::daten( $box ) ) ) );
// Kriterien mit Note.
$kriterien = array_values(
array_filter(
$box['criteria'],
function ( $k ) {
return null !== $k['score'];
}
)
);
// Kopf mit Notenkachel, im Profil mit Ring und Netzplan.
$titel = sprintf( '<%1$s id="%2$s">%3$s%1$s>', $ebene, esc_attr( $id . '-t' ), esc_html( $name ) );
if ( '' !== $box['item']['sub'] ) {
$titel .= '' . esc_html( $box['item']['sub'] ) . '
';
}
if ( '' !== $box['tested'] ) {
/* translators: %s: how the item was tested. */
$titel .= '' . self::icon( 'test' ) . '' . esc_html( sprintf( __( 'How we tested: %s', '3task-review-box' ), $box['tested'] ) ) . '
';
}
if ( 'profile' === $display ) {
$html .= '' . self::ring( $box, $gesamt ) . '
' . $titel . '
';
if ( count( $kriterien ) >= 3 ) {
$html .= '
' . self::netz( $kriterien, $box ) . '
';
}
$html .= '
';
} else {
$html .= '' . $titel . '
';
$html .= self::kachel( $box, $gesamt, $display );
$html .= '
';
}
if ( $kriterien ) {
$html .= '';
foreach ( $kriterien as $k ) {
$anteil = $k['score'] / ThreeRB_Score::max( $box['scale'] );
$html .= '
' . esc_html( $k['label'] ) . '';
if ( count( $kriterien ) > 1 ) {
$html .= '' . esc_html( (string) $k['weight'] ) . ' %';
}
$html .= '';
$html .= 'stars' === $display ? self::sterne( $anteil, $box['scale'] ) : '';
$html .= '' . esc_html( self::zahl( $k['score'], $box ) ) . '';
$html .= '' . esc_html( $k['measure'] ) . '
';
}
$html .= '
';
if ( count( $kriterien ) > 1 ) {
$html .= self::gewichtung( $id, $kriterien, $box );
}
}
if ( $gesamt['overridden'] ) {
/* translators: %s: reason for the score set by hand. */
$html .= '' . esc_html( sprintf( __( 'Total score set by the editors: %s', '3task-review-box' ), $box['override']['reason'] ) ) . '
';
}
// Stark und Schwach.
if ( $box['pros'] || $box['cons'] ) {
$html .= '';
$html .= '
' . esc_html__( 'Strengths', '3task-review-box' ) . '
';
foreach ( $box['pros'] as $pro ) {
$html .= '- ' . self::icon( 'ja' ) . '' . esc_html( $pro ) . '
';
}
$html .= '
' . esc_html__( 'Weaknesses', '3task-review-box' ) . '
';
foreach ( $box['cons'] as $con ) {
$html .= '- ' . self::icon( 'nein' ) . '' . esc_html( $con ) . '
';
}
$html .= '
';
}
// Fazit, Für wen, Links.
if ( '' !== $box['verdict'] || $box['good_for'] || $box['bad_for'] || $box['links'] || $box['alternatives'] ) {
$html .= '';
if ( '' !== $box['verdict'] ) {
$html .= '
' . esc_html__( 'Verdict:', '3task-review-box' ) . ' ' . wp_kses(
$box['verdict'],
array(
'p' => array(),
'br' => array(),
'strong' => array(),
'em' => array(),
'a' => array( 'href' => true ),
)
) . '
';
}
if ( $box['good_for'] || $box['bad_for'] ) {
$html .= '
';
foreach ( $box['good_for'] as $x ) {
/* translators: %s: audience the item suits. */
$html .= '' . self::icon( 'ja' ) . esc_html( $x ) . '';
}
foreach ( $box['bad_for'] as $x ) {
$html .= '' . self::icon( 'nein' ) . esc_html( $x ) . '';
}
$html .= '
';
}
if ( $box['alternatives'] ) {
$namen = array();
foreach ( $box['alternatives'] as $alt ) {
$namen[] = '' !== $alt['url'] ? sprintf( '
%2$s', esc_url( $alt['url'] ), esc_html( $alt['label'] ) ) : esc_html( $alt['label'] );
}
$html .= '
' . esc_html__( 'Alternatives:', '3task-review-box' ) . ' ' . implode( ', ', $namen ) . '
';
}
if ( $box['links'] ) {
$html .= '
';
foreach ( $box['links'] as $l ) {
$html .= sprintf( '
%4$s%5$s', esc_url( $l['url'] ), $l['affiliate'] ? 'sponsored noopener' : 'noopener', $l['primary'] ? 'trb-haupt' : 'trb-zweit', esc_html( $l['label'] ), self::icon( 'extern' ) );
}
$html .= '
';
}
$html .= '
';
}
$fuss = self::hinweis( $box );
if ( '' !== $fuss ) {
$html .= '' . esc_html( $fuss ) . '
';
}
$html .= '';
$html = '
' . $html . '
';
return $template ? self::theme_template( $post_id, $box, $gesamt, $html ) : $html;
}
/**
* Vorlage aus dem Theme (3task-review-box/box.php), falls vorhanden. Der Pfad kommt nur aus locate_template()
* mit festem Namen, nie aus Daten. Die Vorlage bekommt $threerb_post_id, $threerb_box, $threerb_score und
* $threerb_html (die Standardausgabe, schon escaped).
*
* @param int $post_id Beitrag.
* @param array $box Box.
* @param array $gesamt Ergebnis.
* @param string $html Standardausgabe.
* @return string
*/
private static function theme_template( $post_id, array $box, array $gesamt, $html ) {
$datei = locate_template( array( '3task-review-box/box.php' ), false, false );
if ( '' === $datei ) {
return $html;
}
$threerb_post_id = (int) $post_id;
$threerb_box = $box;
$threerb_score = array(
'value' => $gesamt['value'],
'share' => $gesamt['share'],
'best' => ThreeRB_Score::max( $box['scale'] ),
'display' => self::zahl( $gesamt['value'], $box ),
'word' => self::wort( $gesamt['share'] ),
);
$threerb_html = $html;
ob_start();
include $datei;
return (string) ob_get_clean();
}
/**
* Kurzurteil mit Note für den Anfang des Beitrags (Weg C).
*
* @param int $post_id Beitrag.
* @return string
*/
public static function summary( $post_id ) {
if ( ! self::visible( $post_id ) ) {
return '';
}
$box = ThreeRB_Box::get( $post_id );
if ( ! ThreeRB_Box::has_rating( $box ) ) {
return '';
}
self::assets();
$gesamt = ThreeRB_Score::total( $box );
$text = '' !== $box['summary'] ? $box['summary'] : wp_html_excerpt( wp_strip_all_tags( $box['verdict'] ), 220, '…' );
$farbe = self::accent( self::wahl( '', $box['style'], ThreeRB_Settings::value( 'style' ), array( 'theme', 'night' ) ) );
$vars = $farbe ? '--trb-accent:' . $farbe . ';--trb-accent-text:' . ( '' !== self::accent_text( $farbe ) ? $farbe : 'currentColor' ) : '';
$html = '';
$html .= '
' . esc_html( self::zahl( $gesamt['value'], $box ) ) . '' . esc_html( self::von( $box ) ) . '
';
$html .= '
' . esc_html__( 'In short:', '3task-review-box' ) . ' ' . esc_html( $text );
if ( $box['place']['bottom'] ) {
$html .= ' ' . esc_html__( 'Jump to verdict', '3task-review-box' ) . ' ↓';
}
$html .= '
';
return $html;
}
/**
* Nur die Note als Plakette.
*
* @param int $post_id Beitrag.
* @return string
*/
public static function score( $post_id ) {
if ( ! self::visible( $post_id ) ) {
return '';
}
$box = ThreeRB_Box::get( $post_id );
if ( ! ThreeRB_Box::has_rating( $box ) ) {
return '';
}
self::assets();
$gesamt = ThreeRB_Score::total( $box );
$wort = self::wort( $gesamt['share'] );
return '' . esc_html( self::zahl( $gesamt['value'], $box ) ) . ( '' !== $wort ? ' ' . esc_html( $wort ) . '' : '' ) . '';
}
/**
* Rangliste: veröffentlichte Beiträge ohne Passwort, nach Gesamtnote oder nach einem Kriterium,
* beste oder schwächste zuerst, als Liste, Tabelle oder Karten, wahlweise mit Seitenzahlen.
* Gleiche Note bedeutet gleichen Platz (1, 2, 3, 3, 5). Verglichen wird immer der Anteil an der eigenen Skala.
*
* @param array $args count 3 bis 20 (je Seite), postType, term (Kategorie), tag (Schlagwort), order desc|asc,
* criterion (Name eines Kriteriums), display list|table|cards, stars, paged.
* @return string
*/
public static function toplist( array $args ) {
$a = self::toplist_args( $args );
if ( '' === $a['postType'] ) {
return '';
}
$abfrage = array(
'post_type' => $a['postType'],
'post_status' => 'publish',
'has_password' => false,
'ignore_sticky_posts' => true,
'meta_key' => ThreeRB_Box::META_SCORE, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Sortierung über den kleinen Spiegel.
'update_post_term_cache' => false,
);
$steuer = array();
if ( $a['term'] > 0 ) {
$steuer[] = array(
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $a['term'],
);
}
if ( $a['tag'] > 0 ) {
$steuer[] = array(
'taxonomy' => 'post_tag',
'field' => 'term_id',
'terms' => $a['tag'],
);
}
if ( $steuer ) {
$abfrage['tax_query'] = $steuer; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Filter der Rangliste.
}
// Alle Kandidaten mit Anteil holen (höchstens 500), sortieren, Plätze vergeben, dann die Seite schneiden.
$abfrage['posts_per_page'] = 500; // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page -- nur IDs und Spiegel, Rangliste braucht die Plätze davor.
$abfrage['fields'] = 'ids';
$abfrage['no_found_rows'] = true;
$abfrage['orderby'] = array(
'meta_value_num' => 'asc' === $a['order'] ? 'ASC' : 'DESC',
'date' => 'DESC',
);
$eintraege = array();
foreach ( ( new WP_Query( $abfrage ) )->posts as $pos => $id ) {
$id = (int) $id;
$box = ThreeRB_Box::get( $id );
if ( ! ThreeRB_Box::has_rating( $box ) || ! self::is_public( $id ) ) {
continue;
}
$gesamt = ThreeRB_Score::total( $box );
$wert = $gesamt['value'];
$anteil = $gesamt['share'];
if ( '' !== $a['criterion'] ) {
$treffer = null;
foreach ( $box['criteria'] as $k ) {
if ( null !== $k['score'] && 0 === strcasecmp( trim( $k['label'] ), $a['criterion'] ) ) {
$treffer = $k;
break;
}
}
if ( null === $treffer ) {
continue;
}
$wert = (float) $treffer['score'];
$anteil = $wert / ThreeRB_Score::max( $box['scale'] );
}
$eintraege[] = array(
'id' => $id,
'box' => $box,
'wert' => $wert,
'anteil' => (int) round( $anteil * 1000 ),
'pos' => $pos,
);
}
if ( ! $eintraege ) {
return '';
}
$richtung = 'asc' === $a['order'] ? 1 : -1;
usort(
$eintraege,
function ( $x, $y ) use ( $richtung ) {
if ( $x['anteil'] !== $y['anteil'] ) {
return $richtung * ( $x['anteil'] <=> $y['anteil'] );
}
return $x['pos'] <=> $y['pos'];
}
);
$platz = 0;
$vor = null;
foreach ( $eintraege as $i => $e ) {
if ( $e['anteil'] !== $vor ) {
$platz = $i + 1;
$vor = $e['anteil'];
}
$eintraege[ $i ]['platz'] = $platz;
}
$seiten = (int) ceil( count( $eintraege ) / $a['count'] );
$seite = 1;
if ( $a['paged'] && isset( $_GET['trb_seite'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nur Blättern in einer öffentlichen Liste.
$roh = sanitize_text_field( wp_unslash( $_GET['trb_seite'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$seite = ctype_digit( $roh ) ? max( 1, min( $seiten, (int) $roh ) ) : 1;
}
$teil = array_slice( $eintraege, ( $seite - 1 ) * $a['count'], $a['count'] );
self::assets();
$html = self::toplist_html( $teil, $a );
if ( $a['paged'] && $seiten > 1 ) {
$links = paginate_links(
array(
'base' => esc_url_raw( add_query_arg( 'trb_seite', '%#%' ) ),
'format' => '',
'current' => $seite,
'total' => $seiten,
'prev_text' => __( 'Previous', '3task-review-box' ),
'next_text' => __( 'Next', '3task-review-box' ),
)
);
$html .= '';
}
return '' . $html . '
';
}
/**
* Geprüfte Argumente der Rangliste (feste Listen, Ganzzahlen, Längen).
*
* @param array $args Roh.
* @return array
*/
private static function toplist_args( array $args ) {
$typen = (array) ThreeRB_Settings::value( 'post_types' );
$typ = isset( $args['postType'] ) && in_array( $args['postType'], $typen, true ) ? $args['postType'] : ( in_array( 'post', $typen, true ) ? 'post' : (string) reset( $typen ) );
$krit = isset( $args['criterion'] ) && is_scalar( $args['criterion'] ) ? mb_substr( trim( sanitize_text_field( (string) $args['criterion'] ) ), 0, 60 ) : '';
return array(
'count' => max( 3, min( 20, isset( $args['count'] ) ? (int) $args['count'] : 5 ) ),
'postType' => $typ,
'term' => isset( $args['term'] ) ? max( 0, (int) $args['term'] ) : 0,
'tag' => isset( $args['tag'] ) ? max( 0, (int) $args['tag'] ) : 0,
'order' => isset( $args['order'] ) && 'asc' === $args['order'] ? 'asc' : 'desc',
'criterion' => $krit,
'display' => isset( $args['display'] ) && in_array( $args['display'], array( 'list', 'table', 'cards' ), true ) ? $args['display'] : 'list',
'stars' => ! empty( $args['stars'] ) && ThreeRB_Settings::bool( $args['stars'] ),
'paged' => ! empty( $args['paged'] ) && ThreeRB_Settings::bool( $args['paged'] ),
);
}
/**
* HTML der Rangliste in der gewählten Darstellung.
*
* @param array $teil Einträge dieser Seite.
* @param array $a Argumente.
* @return string
*/
private static function toplist_html( array $teil, array $a ) {
$note = function ( $e ) use ( $a ) {
$box = $e['box'];
$html = '' . esc_html( self::zahl( $e['wert'], $box ) ) . ( '' !== self::von( $box ) ? ' ' . esc_html( self::von( $box ) ) . '' : '' ) . '';
if ( $a['stars'] ) {
$html .= self::sterne( $e['anteil'] / 1000, $box['scale'] );
}
return $html;
};
$name = function ( $e ) {
return '' !== $e['box']['item']['name'] ? $e['box']['item']['name'] : get_the_title( $e['id'] );
};
$kopf = '' !== $a['criterion']
/* translators: %s: criterion name. */
? sprintf( __( 'Score for %s', '3task-review-box' ), $a['criterion'] )
: __( 'Score', '3task-review-box' );
if ( 'table' === $a['display'] ) {
$html = '| ' . esc_html__( 'Rank', '3task-review-box' ) . ' | ' . esc_html__( 'Item', '3task-review-box' ) . ' | ' . esc_html( $kopf ) . ' |
';
foreach ( $teil as $e ) {
$html .= '| ' . (int) $e['platz'] . ' | ' . esc_html( $name( $e ) ) . ' | ' . $note( $e ) . ' |
';
}
return $html . '
';
}
if ( 'cards' === $a['display'] ) {
$html = '';
foreach ( $teil as $e ) {
$box = $e['box'];
$text = '' !== $box['summary'] ? $box['summary'] : wp_html_excerpt( wp_strip_all_tags( $box['verdict'] ), 160, '…' );
$bild = get_the_post_thumbnail(
$e['id'],
'medium',
array(
'class' => 'trb-top-bild',
'loading' => 'lazy',
'alt' => '',
)
);
$html .= '- ' . $bild;
$html .= '
' . (int) $e['platz'] . '';
/* translators: %d: rank. */
$html .= '
' . esc_html( sprintf( __( 'Rank %d:', '3task-review-box' ), $e['platz'] ) ) . ' ' . esc_html( $name( $e ) ) . '
';
$html .= '' . $note( $e ) . '
';
if ( '' !== $text ) {
$html .= '' . esc_html( $text ) . '
';
}
$html .= ' ';
}
return $html . '
';
}
$html = '';
foreach ( $teil as $e ) {
$wort = '' === $a['criterion'] ? self::wort( $e['anteil'] / 1000 ) : '';
$html .= '- ' . (int) $e['platz'] . '';
$html .= '' . esc_html( $name( $e ) ) . '';
$html .= '' . $note( $e ) . '';
$html .= '' !== $wort ? '' . esc_html( $wort ) . '' : '' . esc_html( $a['criterion'] ) . '';
$html .= '
';
}
return $html . '
';
}
/**
* Autoren-Rangliste: wer hat die meisten veröffentlichten Bewertungen, mit mittlerer Note (Anteil × 10).
*
* @param array $args count 3 bis 20, postType.
* @return string
*/
public static function reviewers( array $args ) {
$anzahl = max( 3, min( 20, isset( $args['count'] ) ? (int) $args['count'] : 5 ) );
$typen = (array) ThreeRB_Settings::value( 'post_types' );
if ( ! $typen ) {
return '';
}
$ids = get_posts(
array(
'post_type' => $typen,
'post_status' => 'publish',
'has_password' => false,
'posts_per_page' => 2000, // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page -- nur IDs mit Spiegel, für die Zählung je Autor.
'fields' => 'ids',
'no_found_rows' => true,
'meta_key' => ThreeRB_Box::META_SCORE, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- nur Beiträge mit Bewertung.
)
);
$autoren = array();
foreach ( $ids as $id ) {
$autor = (int) get_post_field( 'post_author', (int) $id );
if ( $autor < 1 ) {
continue;
}
if ( ! isset( $autoren[ $autor ] ) ) {
$autoren[ $autor ] = array(
'n' => 0,
'summe' => 0,
);
}
++$autoren[ $autor ]['n'];
$autoren[ $autor ]['summe'] += (int) get_post_meta( (int) $id, ThreeRB_Box::META_SCORE, true );
}
if ( ! $autoren ) {
return '';
}
uasort(
$autoren,
function ( $x, $y ) {
return $y['n'] <=> $x['n'];
}
);
self::assets();
$html = '';
$platz = 0;
foreach ( array_slice( $autoren, 0, $anzahl, true ) as $autor => $z ) {
$nutzer = get_userdata( $autor );
if ( ! $nutzer ) {
continue;
}
$mittel = number_format_i18n( $z['summe'] / $z['n'] / 100, 1 );
/* translators: %d: number of reviews. */
$text = sprintf( _n( '%d review', '%d reviews', $z['n'], '3task-review-box' ), $z['n'] );
++$platz;
$html .= '- ' . (int) $platz . '';
$html .= '' . esc_html( $nutzer->display_name ) . '';
$html .= '' . esc_html( $text ) . '';
/* translators: %s: average score out of 10. */
$html .= '' . esc_html( sprintf( __( 'average %s out of 10', '3task-review-box' ), $mittel ) ) . '
';
}
return $html . '
';
}
/**
* Zahl im Format der Seite, mit Prozentzeichen bei Prozent-Skala.
*
* @param float $wert Wert.
* @param array $box Box.
* @return string
*/
public static function zahl( $wert, array $box ) {
if ( 'pct' === $box['scale'] ) {
return number_format_i18n( $wert, 0 ) . ' %';
}
return number_format_i18n( $wert, ( 1.0 === (float) $box['step'] ) ? 0 : 1 );
}
/**
* Wortnote in der Sprache der Seite, leer wenn abgeschaltet.
*
* @param float $anteil Anteil 0 bis 1.
* @return string
*/
public static function wort( $anteil ) {
if ( ! ThreeRB_Settings::value( 'words' ) ) {
return '';
}
$texte = array(
'outstanding' => __( 'outstanding', '3task-review-box' ),
'very_good' => __( 'very good', '3task-review-box' ),
'good' => __( 'good', '3task-review-box' ),
'satisfactory' => __( 'fair', '3task-review-box' ),
'sufficient' => __( 'sufficient', '3task-review-box' ),
'poor' => __( 'poor', '3task-review-box' ),
);
/**
* Texte der Wortnoten ändern.
*
* @param array $texte Schlüssel outstanding, very_good, good, satisfactory, sufficient, poor.
*/
$texte = apply_filters( 'threerb_word_labels', $texte );
$schluessel = ThreeRB_Score::word( $anteil );
return isset( $texte[ $schluessel ] ) ? (string) $texte[ $schluessel ] : '';
}
/**
* Hauptfarbe: Nacht aus den Einstellungen, sonst die Hauptfarbe aus theme.json, sonst leer (CSS-Vorgabe).
*
* @param string $style theme oder night.
* @return string Farbe #rrggbb oder leer.
*/
public static function accent( $style ) {
if ( 'night' === $style ) {
return (string) ThreeRB_Settings::value( 'night_accent' );
}
$eigen = (string) ThreeRB_Settings::value( 'accent' );
if ( '' !== $eigen ) {
return $eigen;
}
if ( function_exists( 'wp_get_global_settings' ) ) {
$palette = wp_get_global_settings( array( 'color', 'palette', 'theme' ) );
if ( is_array( $palette ) ) {
foreach ( array( 'primary', 'accent', 'accent-1', 'contrast-2' ) as $slug ) {
foreach ( $palette as $farbe ) {
if ( isset( $farbe['slug'], $farbe['color'] ) && $slug === $farbe['slug'] && preg_match( '/^#[0-9a-fA-F]{6}$/', $farbe['color'] ) ) {
return strtolower( $farbe['color'] );
}
}
}
}
}
return '';
}
/**
* Schriftfarbe auf der Hauptfarbe: weiß oder dunkel, je nachdem was mehr Kontrast hat (WCAG).
*
* @param string $farbe #rrggbb.
* @return string
*/
public static function on_accent( $farbe ) {
$dunkel = '#0d0f1a';
return self::kontrast( '#ffffff', $farbe ) >= self::kontrast( $dunkel, $farbe ) ? '#ffffff' : $dunkel;
}
/**
* Hauptfarbe als Schriftfarbe auf hellem Grund, nur wenn sie dort lesbar ist (WCAG 4,5:1), sonst leer.
*
* @param string $farbe #rrggbb.
* @return string
*/
public static function accent_text( $farbe ) {
return ( '' !== $farbe && self::kontrast( $farbe, '#ffffff' ) >= 4.5 ) ? $farbe : '';
}
/**
* Kontrastverhältnis zweier Farben nach WCAG 2.
*
* @param string $a #rrggbb.
* @param string $b #rrggbb.
* @return float
*/
public static function kontrast( $a, $b ) {
$la = self::luminanz( $a );
$lb = self::luminanz( $b );
return round( ( max( $la, $lb ) + 0.05 ) / ( min( $la, $lb ) + 0.05 ), 2 );
}
/**
* Relative Luminanz.
*
* @param string $farbe #rrggbb.
* @return float
*/
private static function luminanz( $farbe ) {
$teile = array( substr( $farbe, 1, 2 ), substr( $farbe, 3, 2 ), substr( $farbe, 5, 2 ) );
$werte = array();
foreach ( $teile as $hex ) {
$c = hexdec( $hex ) / 255;
$werte[] = $c <= 0.03928 ? $c / 12.92 : pow( ( $c + 0.055 ) / 1.055, 2.4 );
}
return 0.2126 * $werte[0] + 0.7152 * $werte[1] + 0.0722 * $werte[2];
}
/**
* CSS und Skript einreihen.
*/
public static function assets() {
wp_enqueue_style( 'threerb-box' );
wp_enqueue_script( 'threerb-weigh' );
}
/**
* Notenkachel.
*
* @param array $box Box.
* @param array $gesamt Ergebnis von total().
* @param string $display bars oder stars.
* @return string
*/
private static function kachel( array $box, array $gesamt, $display ) {
$wort = self::wort( $gesamt['share'] );
$zahl = self::zahl( $gesamt['value'], $box );
$label = '' !== $wort
/* translators: 1: score, 2: scale, 3: word rating. */
? sprintf( __( 'Editor score %1$s %2$s, %3$s', '3task-review-box' ), $zahl, self::von( $box ), $wort )
/* translators: 1: score, 2: scale. */
: sprintf( __( 'Editor score %1$s %2$s', '3task-review-box' ), $zahl, self::von( $box ) );
$html = '' . esc_html( $zahl ) . '';
if ( 'stars' === $display ) {
$html .= self::sterne( $gesamt['share'], $box['scale'] );
}
$html .= '' . esc_html( self::von( $box ) ) . '';
if ( '' !== $wort ) {
$html .= '' . esc_html( $wort ) . '';
}
$html .= '' . esc_html__( 'Your score', '3task-review-box' ) . '
';
return $html;
}
/**
* Kompakte Box für die Stelle im Text.
*
* @param array $box Box.
* @param array $gesamt Ergebnis.
* @param string $name Name.
* @param array $klassen Klassen.
* @param string $stil Inline-Variablen.
* @param string $display Darstellung.
* @return string
*/
private static function kompakt( array $box, array $gesamt, $name, array $klassen, $stil, $display ) {
$klassen[] = 'trb-kompakt';
$wort = self::wort( $gesamt['share'] );
$text = '' !== $box['summary'] ? $box['summary'] : wp_html_excerpt( wp_strip_all_tags( $box['verdict'] ), 160, '…' );
$html = '';
$html .= '
' . esc_html( self::zahl( $gesamt['value'], $box ) ) . '';
if ( 'stars' === $display ) {
$html .= self::sterne( $gesamt['share'], $box['scale'] );
}
$html .= '' . esc_html( self::von( $box ) ) . '
';
$html .= '
' . esc_html( $name ) . ( '' !== $wort ? ' · ' . esc_html( $wort ) : '' ) . '';
if ( '' !== $text ) {
$html .= '
' . esc_html( $text ) . '';
}
if ( $box['place']['bottom'] ) {
$html .= '
' . esc_html__( 'Full verdict', '3task-review-box' ) . ' ↓';
}
$html .= '
';
return '' . $html . '
';
}
/**
* „Deine Gewichtung“: Knopf und Regler, das Skript rechnet im Browser.
*
* @param string $id Box-ID.
* @param array $kriterien Kriterien.
* @param array $box Box.
* @return string
*/
private static function gewichtung( $id, array $kriterien, array $box ) {
$html = '';
$html .= '
' . esc_html__( 'What matters to you? Move the sliders and your score updates. Nothing is saved or sent.', '3task-review-box' ) . '
';
foreach ( $kriterien as $i => $k ) {
$rid = $id . '-r' . $i;
$html .= '
';
}
$html .= '
' . esc_html__( 'Your score', '3task-review-box' ) . ':
';
return $html;
}
/**
* Daten für das Skript.
*
* @param array $box Box.
* @return array
*/
private static function daten( array $box ) {
$c = array();
foreach ( $box['criteria'] as $k ) {
if ( null !== $k['score'] ) {
$c[] = array( (int) $k['weight'], (float) $k['score'] );
}
}
return array(
'c' => $c,
'max' => ThreeRB_Score::max( $box['scale'] ),
'pct' => 'pct' === $box['scale'],
'dec' => ( 1.0 === (float) $box['step'] || 'pct' === $box['scale'] ) ? 0 : 1,
'sep' => self::dezimal(),
);
}
/**
* Dezimaltrennzeichen der Seite.
*
* @return string
*/
private static function dezimal() {
global $wp_locale;
return ( isset( $wp_locale->number_format['decimal_point'] ) && '' !== $wp_locale->number_format['decimal_point'] ) ? $wp_locale->number_format['decimal_point'] : '.';
}
/**
* „von 10“, „von 5“ oder leer bei Prozent.
*
* @param array $box Box.
* @return string
*/
private static function von( array $box ) {
if ( 'pct' === $box['scale'] ) {
return '';
}
/* translators: %d: maximum of the scale, e.g. 10. */
return sprintf( __( 'out of %d', '3task-review-box' ), ThreeRB_Score::max( $box['scale'] ) );
}
/**
* Notenring für den Look „Profil“. Die Zahl steht als Text darin, der Ring ist Schmuck.
*
* @param array $box Box.
* @param array $gesamt Ergebnis.
* @return string
*/
private static function ring( array $box, array $gesamt ) {
$umfang = 2 * M_PI * 42;
$wort = self::wort( $gesamt['share'] );
$zahl = self::zahl( $gesamt['value'], $box );
$label = '' !== $wort
/* translators: 1: score, 2: scale, 3: word for the score. */
? sprintf( __( 'Editor score %1$s %2$s, %3$s', '3task-review-box' ), $zahl, self::von( $box ), $wort )
/* translators: 1: score, 2: scale. */
: sprintf( __( 'Editor score %1$s %2$s', '3task-review-box' ), $zahl, self::von( $box ) );
$html = '';
$html .= '', number_format( $umfang * $gesamt['share'], 1, '.', '' ), number_format( $umfang, 1, '.', '' ) );
$html .= '' . esc_html( $zahl ) . '' . esc_html( '' !== $wort ? $wort : self::von( $box ) ) . '
';
return $html;
}
/**
* Netzplan als SVG, serverseitig, mit Textalternative aus Namen und Noten.
*
* @param array $kriterien Kriterien mit Note (mindestens drei).
* @param array $box Box.
* @return string
*/
private static function netz( array $kriterien, array $box ) {
$n = count( $kriterien );
$mitte = 75;
$radius = 52;
$max = ThreeRB_Score::max( $box['scale'] );
$punkt = function ( $i, $faktor ) use ( $n, $mitte, $radius ) {
$winkel = -M_PI / 2 + $i * 2 * M_PI / $n;
return array( $mitte + cos( $winkel ) * $radius * $faktor, $mitte + sin( $winkel ) * $radius * $faktor );
};
$fmt = function ( $wert ) {
return number_format( $wert, 1, '.', '' );
};
$netz = function ( $faktor ) use ( $n, $punkt, $fmt ) {
$p = array();
for ( $i = 0; $i < $n; $i++ ) {
list( $x, $y ) = $punkt( $i, $faktor );
$p[] = $fmt( $x ) . ',' . $fmt( $y );
}
return '';
};
$achsen = '';
$namen = '';
$form = array();
$alt = array();
foreach ( $kriterien as $i => $k ) {
list( $x, $y ) = $punkt( $i, 1 );
$achsen .= '';
list( $fx, $fy ) = $punkt( $i, max( 0, min( 1, $k['score'] / $max ) ) );
$form[] = $fmt( $fx ) . ',' . $fmt( $fy );
$winkel = -M_PI / 2 + $i * 2 * M_PI / $n;
$anker = abs( cos( $winkel ) ) < .3 ? 'middle' : ( cos( $winkel ) > 0 ? 'start' : 'end' );
$lx = $mitte + cos( $winkel ) * ( $radius + 12 ) + ( 'start' === $anker ? -6 : ( 'end' === $anker ? 6 : 0 ) );
$ly = $mitte + sin( $winkel ) * ( $radius + 12 ) + 4;
$namen .= '' . esc_html( mb_strlen( $k['label'] ) > 18 ? mb_substr( $k['label'], 0, 17 ) . '…' : $k['label'] ) . '';
$alt[] = $k['label'] . ' ' . self::zahl( $k['score'], $box );
}
/* translators: %s: list of criteria with scores. */
$label = sprintf( __( 'Profile chart: %s', '3task-review-box' ), implode( ', ', $alt ) );
return '';
}
/**
* Sternreihe, fein gefüllt. Die Zahl steht immer daneben.
*
* @param float $anteil Anteil 0 bis 1.
* @param string $scale Skala.
* @return string
*/
private static function sterne( $anteil, $scale ) {
$zehn = 's10' === $scale;
if ( ThreeRB_Settings::value( 'half_stars' ) ) {
$anzahl = $zehn ? 10 : 5;
$anteil = round( $anteil * $anzahl * 2 ) / ( $anzahl * 2 );
}
return '';
}
/**
* Kennzeichnung unter der Box.
*
* @param array $box Box.
* @return string
*/
private static function hinweis( array $box ) {
$h = $box['disclosure'];
$saetze = array();
if ( $h['loan'] ) {
$saetze[] = __( 'Review unit on loan from the manufacturer. Our verdict is independent.', '3task-review-box' );
}
if ( $h['gift'] ) {
$saetze[] = __( 'Review unit provided by the manufacturer and kept by us.', '3task-review-box' );
}
if ( $h['paid'] ) {
$saetze[] = __( 'Paid partnership. The verdict is our own.', '3task-review-box' );
}
if ( $h['affiliate'] ) {
$saetze[] = __( 'Buy links are affiliate links: we may earn a commission at no extra cost to you.', '3task-review-box' );
}
if ( '' !== $h['text'] ) {
$saetze[] = $h['text'];
}
return implode( ' ', $saetze );
}
/**
* Erste nicht leere Wahl aus Aufruf, Box und Einstellung.
*
* @param string $aufruf Wert aus Shortcode/Block.
* @param string $box Wert der Box.
* @param string $einstellung Wert der Einstellungen.
* @param array $erlaubt Erlaubte Werte.
* @return string
*/
private static function wahl( $aufruf, $box, $einstellung, array $erlaubt ) {
foreach ( array( $aufruf, $box, $einstellung ) as $wert ) {
if ( in_array( $wert, $erlaubt, true ) ) {
return $wert;
}
}
return $erlaubt[0];
}
/**
* Inline-SVG (Tabler Icons, MIT), dekorativ.
*
* @param string $name Icon.
* @return string
*/
private static function icon( $name ) {
$pfade = array(
'ja' => '',
'nein' => '',
'test' => '',
'extern' => '',
'regler' => '',
);
if ( ! isset( $pfade[ $name ] ) ) {
return '';
}
return '';
}
}