settings->get()->post_types() as $type ) { Hooks::filter( "manage_{$type}_posts_columns", array( $this, 'add_column' ) ); Hooks::action( "manage_{$type}_posts_custom_column", array( $this, 'render_column' ), 10, 2 ); } Hooks::action( 'restrict_manage_posts', array( $this, 'filter_dropdown' ) ); Hooks::filter( 'posts_clauses', array( $this, 'filter_clauses' ), 10, 2 ); } /** * Put the column right after the title. * * @param mixed $columns * @return array */ public function add_column( mixed $columns ): array { $label = esc_html__( 'Languages', 'blocklingo' ); $out = array(); foreach ( (array) $columns as $key => $value ) { $out[ (string) $key ] = (string) $value; if ( 'title' === $key ) { $out[ self::COLUMN ] = $label; } } $out[ self::COLUMN ] ??= $label; return $out; } public function render_column( mixed $column, mixed $post_id ): void { if ( self::COLUMN !== $column ) { return; } $post_id = (int) $post_id; $own = $this->posts->language_of( $post_id ); $group = $this->posts->group( $post_id ); echo ''; foreach ( $this->settings->get()->languages() as $code => $lang ) { $mark = '' !== $lang->flag ? $lang->flag : strtoupper( $code ); if ( $code === $own ) { printf( '%s', esc_attr( $lang->name ), esc_html( $mark ) ); } elseif ( isset( $group[ $code ] ) ) { printf( '%s', esc_url( (string) get_edit_post_link( $group[ $code ] ) ), esc_attr( sprintf( /* translators: %s: language name. */ __( 'Edit %s translation', 'blocklingo' ), $lang->name ) ), esc_html( $mark ) ); } else { printf( '+%s', esc_url( self::new_translation_url( $post_id, $code ) ), esc_attr( sprintf( /* translators: %s: language name. */ __( 'Add %s translation (copy)', 'blocklingo' ), $lang->name ) ), esc_html( strtoupper( $code ) ) ); } } echo ''; } public static function new_translation_url( int $post_id, string $lang ): string { return wp_nonce_url( admin_url( 'admin-post.php?action=blocklingo_new_translation&post=' . $post_id . '&lang=' . rawurlencode( $lang ) ), 'blocklingo_new_translation_' . $post_id ); } /** Handler (admin-post) of the "+UK" links: copy the post and open the copy. */ public function new_translation(): void { $post_id = isset( $_GET['post'] ) ? (int) $_GET['post'] : 0; $lang = isset( $_GET['lang'] ) ? sanitize_key( wp_unslash( $_GET['lang'] ) ) : ''; check_admin_referer( 'blocklingo_new_translation_' . $post_id ); if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to do that.', 'blocklingo' ), 403 ); } $new_id = $this->duplicator->create( $post_id, $lang, Duplicator::MODE_COPY ); if ( $new_id instanceof WP_Error ) { wp_die( esc_html( $new_id->get_error_message() ) ); } wp_safe_redirect( (string) get_edit_post_link( $new_id, 'raw' ) ); exit; } public function filter_dropdown( mixed $post_type ): void { $settings = $this->settings->get(); if ( ! $settings->is_translatable_type( (string) $post_type ) ) { return; } $current = $this->requested_language(); echo ''; } /** * Limit the main query of a post list screen to the chosen language. * Untagged posts count as the default language. * * @param mixed $clauses * @return mixed */ public function filter_clauses( mixed $clauses, mixed $query ): mixed { if ( ! is_array( $clauses ) || ! $query instanceof WP_Query || ! $query->is_main_query() ) { return $clauses; } $lang = $this->requested_language(); if ( '' === $lang ) { return $clauses; } $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || 'edit' !== $screen->base ) { return $clauses; } $settings = $this->settings->get(); if ( ! $settings->is_language( $lang ) ) { return $clauses; } $table = $this->schema->translations(); $clauses['join'] .= " LEFT JOIN {$table} AS blocklingo_t ON blocklingo_t.object_type = 'post' AND blocklingo_t.object_id = {$this->db->posts}.ID"; $clauses['where'] .= $settings->default_code() === $lang ? $this->db->prepare( ' AND ( blocklingo_t.lang = %s OR blocklingo_t.lang IS NULL )', $lang ) : $this->db->prepare( ' AND blocklingo_t.lang = %s', $lang ); return $clauses; } private function requested_language(): string { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- list filter. return isset( $_GET[ self::FILTER ] ) ? sanitize_key( wp_unslash( $_GET[ self::FILTER ] ) ) : ''; } }