'clanfront_tourney', 'post_status' => 'publish', 'posts_per_page' => 100, 'no_found_rows' => true, ) ); } /** Navigation links only appear when the section is on and has something to show. */ function clanfront_has_tournaments() { return (bool) clanfront_tournament_list(); } /* ---------- Content type ---------- */ add_action( 'init', function () { register_post_type( 'clanfront_tourney', array( 'labels' => clanfront_labels( 'Tournament', 'Tournaments', array( 'menu_name' => 'Tournaments' ) ), 'public' => true, 'has_archive' => 'tournaments', 'rewrite' => array( 'slug' => 'tournament', 'with_front' => false ), 'supports' => array( 'title', 'editor' ), 'show_in_menu' => CLANFRONT_MENU, 'show_in_rest' => false, ) ); } ); // Rewrite rules for the new types are refreshed once after an update. add_action( 'init', function () { if ( get_option( 'clanfront_version' ) !== CLANFRONT_VERSION ) { flush_rewrite_rules( false ); update_option( 'clanfront_version', CLANFRONT_VERSION ); } }, 99 ); /* ---------- Tournaments: bracket ---------- */ function clanfront_tourney_names( $post ) { $raw = (string) get_post_meta( $post->ID, 'clanfront_participants', true ); $names = array(); foreach ( preg_split( '/\r\n|\r|\n/', $raw ) as $line ) { $line = trim( sanitize_text_field( $line ), " \n\r\t\v\x00" ); if ( '' !== $line ) { $names[] = $line; } } return array_slice( $names, 0, 32 ); } /** Seed positions for a bracket of $size slots (a power of two): 1 plays the lowest seed, and so on. */ function clanfront_seed_order( $size ) { $order = array( 1 ); while ( count( $order ) < $size ) { $n = count( $order ) * 2; $new = array(); foreach ( $order as $s ) { $new[] = $s; $new[] = $n + 1 - $s; } $order = $new; } return $order; } /** * Work out every match from the entrants and the saved winners. * * $results is keyed "round-match" (both counting from 1 / 0): array( 'w' => 'a'|'b', 'sa' => '', 'sb' => '' ). * Returns array( 'rounds' => [ round => [ match => [ a, b, w, sa, sb, bye ] ] ], 'champion' => name or '' ). */ function clanfront_bracket( $names, $results = array() ) { $n = count( $names ); if ( $n < 2 ) { return array( 'rounds' => array(), 'champion' => '' ); } $size = 2; while ( $size < $n ) { $size *= 2; } $total = (int) round( log( $size, 2 ) ); $order = clanfront_seed_order( $size ); $rounds = array(); for ( $r = 1; $r <= $total; $r++ ) { $count = $size / pow( 2, $r ); for ( $m = 0; $m < $count; $m++ ) { if ( 1 === $r ) { $sa = $order[ 2 * $m ]; $sb = $order[ ( 2 * $m ) + 1 ]; $a = $sa <= $n ? $names[ $sa - 1 ] : null; $b = $sb <= $n ? $names[ $sb - 1 ] : null; } else { $a = $rounds[ $r - 1 ][ 2 * $m ]['winner']; $b = $rounds[ $r - 1 ][ ( 2 * $m ) + 1 ]['winner']; } $bye = ( 1 === $r ) && ( null === $a || null === $b ); $key = $r . '-' . $m; $saved = isset( $results[ $key ] ) && is_array( $results[ $key ] ) ? $results[ $key ] : array(); $w = isset( $saved['w'] ) ? $saved['w'] : ''; $winner = null; if ( $bye ) { $w = null === $a ? 'b' : 'a'; $winner = 'a' === $w ? $a : $b; } elseif ( null !== $a && null !== $b && ( 'a' === $w || 'b' === $w ) ) { $winner = 'a' === $w ? $a : $b; } else { $w = ''; } $rounds[ $r ][ $m ] = array( 'a' => $a, 'b' => $b, 'w' => $w, 'winner' => $winner, 'sa' => isset( $saved['sa'] ) ? (string) $saved['sa'] : '', 'sb' => isset( $saved['sb'] ) ? (string) $saved['sb'] : '', 'bye' => $bye, ); } } $final = $rounds[ $total ][0]; return array( 'rounds' => $rounds, 'champion' => null === $final['winner'] ? '' : $final['winner'], ); } function clanfront_round_name( $round, $total ) { $left = $total - $round; if ( 0 === $left ) { return 'Final'; } if ( 1 === $left ) { return 'Semifinals'; } if ( 2 === $left ) { return 'Quarterfinals'; } /* translators: %d: number of teams still in the tournament at the start of the round. */ return sprintf( 'Round of %d', (int) pow( 2, $total - $round + 1 ) ); } function clanfront_tourney_results( $post ) { $saved = get_post_meta( $post->ID, 'clanfront_results', true ); return is_array( $saved ) ? $saved : array(); } /** 'Upcoming', 'In progress' or 'Finished'. */ function clanfront_tourney_status( $post ) { $bracket = clanfront_bracket( clanfront_tourney_names( $post ), clanfront_tourney_results( $post ) ); if ( '' !== $bracket['champion'] ) { return 'Finished'; } $date = (string) get_post_meta( $post->ID, 'clanfront_date', true ); if ( $date && $date > wp_date( 'Y-m-d' ) ) { return 'Upcoming'; } return 'In progress'; } function clanfront_bracket_html( $bracket ) { if ( ! $bracket['rounds'] ) { return '
' . esc_html__( 'The bracket will appear here once the teams are set.', 'clanfront' ) . '
'; } $total = count( $bracket['rounds'] ); ob_start(); ?>Champion
ID, 'clanfront_date', true ); if ( $d && $d >= $today && 'Finished' !== clanfront_tourney_status( $t ) ) { $new[ $t->ID ] = $d; } else { $old[ $t->ID ] = $d; } } asort( $new ); arsort( $old ); $out = array(); foreach ( array_keys( $new ) as $id ) { $out[] = get_post( $id ); } foreach ( array_keys( $old ) as $id ) { $out[] = get_post( $id ); } return $out; } /* ---------- Visibility on the site ---------- */ /** Which section a request belongs to, or ''. */ function clanfront_competition_request() { if ( is_post_type_archive( 'clanfront_tourney' ) || is_singular( 'clanfront_tourney' ) ) { return 'tournaments'; } return ''; } // Visitors get "not found" while a section is switched off. People who can edit still see it, as a preview. add_action( 'template_redirect', function () { $section = clanfront_competition_request(); if ( '' === $section ) { return; } if ( ! clanfront_feature_on( $section ) && ! current_user_can( 'edit_posts' ) ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } }, 1 ); function clanfront_preview_notice( $section ) { if ( clanfront_feature_on( $section ) || ! current_user_can( 'edit_posts' ) ) { return ''; } return 'Only you can see this page. It is switched off for visitors: turn it on under Clanfront → Settings.
'; } /* ---------- Admin: settings notice on the list screens ---------- */ add_action( 'admin_notices', function () { $screen = get_current_screen(); if ( ! $screen ) { return; } $map = array( 'edit-clanfront_tourney' => array( 'tournaments', 'Tournaments' ), ); if ( ! isset( $map[ $screen->id ] ) || clanfront_feature_on( $map[ $screen->id ][0] ) ) { return; } printf( '%s is switched off, so visitors cannot see it yet. Turn it on under Clanfront → Settings when you are ready.
One per line, between 2 and 32. The first line is the top seed. Seed 1 meets the lowest seed, so the best entries meet the weakest in round one. If the number is not 4, 8, 16 or 32, the top seeds get a free pass (a "bye") in round one. The large text box above is the description and rules, shown above the bracket.
Pick the winner of each match and, if you like, type the scores. Click Update to save. Winners move on to the next round by themselves. If you change an earlier result or the list of teams, the results that depended on it are cleared, so pick them again.
'; foreach ( $bracket['rounds'] as $r => $matches ) { echo '| %s | Free pass to the next round | ||||
| %s | vs | %s | Waiting for earlier results | ||
| %3$s | vs | %4$s | |||
Champion: ' . esc_html( $bracket['champion'] ) . '
'; } } /* ---------- Admin: saving ---------- */ add_action( 'save_post_clanfront_tourney', function ( $post_id ) { if ( ! clanfront_can_save( $post_id ) ) { return; } update_post_meta( $post_id, 'clanfront_date', clanfront_post_date( 'clanfront_date' ) ); update_post_meta( $post_id, 'clanfront_game', clanfront_post_text( 'clanfront_game' ) ); update_post_meta( $post_id, 'clanfront_prize', clanfront_post_text( 'clanfront_prize' ) ); update_post_meta( $post_id, 'clanfront_link', clanfront_clean_url( clanfront_post_text( 'clanfront_link' ) ) ); update_post_meta( $post_id, 'clanfront_link_label', clanfront_post_text( 'clanfront_link_label' ) ); update_post_meta( $post_id, 'clanfront_participants', sanitize_textarea_field( (string) clanfront_post_value( 'clanfront_participants' ) ) ); // Results: accept a winner only where both sides are known, round by round. $names = clanfront_tourney_names( get_post( $post_id ) ); $posted = clanfront_post_value( 'clanfront_br' ); $posted = is_array( $posted ) ? $posted : array(); $clean = array(); $bracket = clanfront_bracket( $names, $clean ); $total = count( $bracket['rounds'] ); for ( $r = 1; $r <= $total; $r++ ) { $bracket = clanfront_bracket( $names, $clean ); foreach ( $bracket['rounds'][ $r ] as $m => $match ) { $key = $r . '-' . $m; if ( $match['bye'] || null === $match['a'] || null === $match['b'] || empty( $posted[ $key ] ) || ! is_array( $posted[ $key ] ) ) { continue; } $row = $posted[ $key ]; // The form remembers who was playing. If an earlier result or the team list changed since, this old result no longer applies. $an = isset( $row['an'] ) && is_scalar( $row['an'] ) ? sanitize_text_field( (string) $row['an'] ) : ''; $bn = isset( $row['bn'] ) && is_scalar( $row['bn'] ) ? sanitize_text_field( (string) $row['bn'] ) : ''; if ( $an !== $match['a'] || $bn !== $match['b'] ) { continue; } $w = isset( $row['w'] ) && is_scalar( $row['w'] ) ? (string) $row['w'] : ''; $sa = isset( $row['sa'] ) && is_scalar( $row['sa'] ) ? mb_substr( sanitize_text_field( (string) $row['sa'] ), 0, 8 ) : ''; $sb = isset( $row['sb'] ) && is_scalar( $row['sb'] ) ? mb_substr( sanitize_text_field( (string) $row['sb'] ), 0, 8 ) : ''; if ( '' === $w && '' === $sa && '' === $sb ) { continue; } $clean[ $key ] = array( 'w' => ( 'a' === $w || 'b' === $w ) ? $w : '', 'sa' => $sa, 'sb' => $sb, ); } } update_post_meta( $post_id, 'clanfront_results', $clean ); } ); /* ---------- Admin: list columns ---------- */ add_filter( 'manage_clanfront_tourney_posts_columns', function ( $cols ) { return clanfront_columns( $cols, array( 'clanfront_when' => 'Starts', 'clanfront_game' => 'Game', 'clanfront_status' => 'Status', ) ); } ); add_action( 'manage_clanfront_tourney_posts_custom_column', function ( $col, $post_id ) { switch ( $col ) { case 'clanfront_when': $d = (string) get_post_meta( $post_id, 'clanfront_date', true ); echo esc_html( $d ? clanfront_format_date( $d ) : '—' ); break; case 'clanfront_game': echo esc_html( (string) get_post_meta( $post_id, 'clanfront_game', true ) ?: '—' ); break; case 'clanfront_status': echo esc_html( clanfront_tourney_status( get_post( $post_id ) ) ); break; } }, 10, 2 );