post_id] public function hooks() { add_action( 'add_meta_boxes', [ $this, 'add_metaboxes' ] ); add_action( 'save_post', [ $this, 'save_post' ], 10, 2 ); add_filter( 'manage_pages_columns', [ $this, 'add_admin_columns' ] ); add_action( 'manage_pages_custom_column', [ $this, 'render_admin_columns' ], 10, 2 ); add_filter( 'manage_posts_columns', [ $this, 'add_admin_columns' ] ); add_action( 'manage_posts_custom_column', [ $this, 'render_admin_columns' ], 10, 2 ); add_action( 'restrict_manage_posts', [ $this, 'admin_language_filter' ] ); add_filter( 'pre_get_posts', [ $this, 'apply_admin_language_filter' ] ); // Bulk actions (Pages/Posts list) for quick language assignment. add_action( 'current_screen', [ $this, 'maybe_register_bulk_hooks' ] ); } public function get_languages() { return Settings::get_languages_option(); } public function get_default_language_code() { $langs = $this->get_languages(); $d = isset( $langs['default'] ) ? sanitize_key( $langs['default'] ) : ''; return $d; } public function get_active_languages() { $langs = $this->get_languages(); $active = []; foreach ( (array) $langs['items'] as $code => $row ) { if ( ! empty( $row['active'] ) ) { $active[ $code ] = $row; } } return $active; } public function enabled_post_types() { $s = Settings::get_settings_option(); return (array) $s['enabled_post_types']; } public function add_metaboxes() { $post_types = $this->enabled_post_types(); foreach ( $post_types as $pt ) { add_meta_box( 'abdiel_amm_language', __( 'Language', 'abdiel-multilingual-manager' ), [ $this, 'render_metabox' ], $pt, 'side', 'default' ); add_meta_box( 'abdiel_amm_translations_side', __( 'Translations', 'abdiel-multilingual-manager' ), [ $this, 'render_translations_metabox' ], $pt, 'side', 'default' ); add_meta_box( 'abdiel_amm_translations', __( 'Translations', 'abdiel-multilingual-manager' ), [ $this, 'render_translations_metabox' ], $pt, 'normal', 'default' ); } } public function render_metabox( $post ) { wp_nonce_field( 'abdiel_amm_save', 'abdiel_amm_nonce' ); $lang = get_post_meta( $post->ID, self::META_LANG, true ); $active = $this->get_active_languages(); $default = $this->get_default_language_code(); if ( empty( $active ) ) { return; } ?>
get_active_languages(); $map = get_post_meta( $post->ID, self::META_MAP, true ); if ( ! is_array( $map ) ) { $map = []; } $group = get_post_meta( $post->ID, self::META_GROUP, true ); $lang_current = get_post_meta( $post->ID, self::META_LANG, true ); if ( empty( $lang_current ) ) { $lang_current = $this->get_default_language_code(); } ?>
|
' . esc_html( $lang ) . '';
}
public function admin_language_filter() {
global $typenow;
$enabled = $this->enabled_post_types();
if ( empty( $typenow ) || ! in_array( $typenow, $enabled, true ) ) { return; }
$active = $this->get_active_languages();
$selected = '';
$filter_nonce = isset( $_GET['abdiel_amm_admin_filter_nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['abdiel_amm_admin_filter_nonce'] ) ) : '';
if ( $filter_nonce && wp_verify_nonce( $filter_nonce, 'abdiel_amm_admin_filter' ) ) {
$sel_raw = isset( $_GET['abdiel_amm_lang'] ) ? wp_unslash( $_GET['abdiel_amm_lang'] ) : '';
$selected = is_string( $sel_raw ) ? sanitize_key( $sel_raw ) : '';
}
wp_nonce_field( 'abdiel_amm_admin_filter', 'abdiel_amm_admin_filter_nonce', false );
?>
is_main_query() ) { return; }
global $pagenow, $typenow;
if ( 'edit.php' !== $pagenow ) { return; }
$enabled = $this->enabled_post_types();
if ( empty( $typenow ) || ! in_array( $typenow, $enabled, true ) ) { return; }
$filter_nonce = isset( $_GET['abdiel_amm_admin_filter_nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['abdiel_amm_admin_filter_nonce'] ) ) : '';
$q_lang_raw = isset( $_GET['abdiel_amm_lang'] ) ? wp_unslash( $_GET['abdiel_amm_lang'] ) : '';
if ( $filter_nonce && wp_verify_nonce( $filter_nonce, 'abdiel_amm_admin_filter' ) && is_string( $q_lang_raw ) && $q_lang_raw !== '' ) {
$lang = sanitize_key( $q_lang_raw );
$query->set( 'meta_query', [
[
'key' => self::META_LANG,
'value' => $lang,
],
] );
}
}
public function get_current_language_code( $post_id = 0 ) {
// If URL routing is enabled, respect explicit language in the URL.
// - Prefix mode uses the rewrite tag/query var `abdiel_lang`
// - Query mode uses `?lang=xx`
$active = $this->get_active_languages();
$q_lang = '';
// En algunos contextos (Customizer / admin temprano) $wp_query puede ser null.
global $wp_query;
// 1) Intentar idioma desde rewrite/query-var SOLO si WP_Query existe.
$rewrite_lang = '';
if ( $wp_query instanceof \WP_Query ) {
$rewrite_lang = get_query_var( 'abdiel_lang' );
}
if ( $rewrite_lang ) {
$q_lang = sanitize_key( (string) $rewrite_lang );
} else {
// 2) Fallback: ?lang=xx
$lang_raw = isset( $_GET['lang'] ) ? wp_unslash( $_GET['lang'] ) : '';
if ( is_string( $lang_raw ) && $lang_raw !== '' ) {
$q_lang = sanitize_key( $lang_raw );
}
}
// 3) Si viene un idioma valido por URL, devolverlo.
if ( $q_lang && isset( $active[ $q_lang ] ) ) {
return $q_lang;
}
// 4) Si no pasaron $post_id, intentar sacarlo del objeto consultado,
// pero SOLO si existe WP_Query. En Customizer puede no existir.
if ( $post_id === 0 && ( $wp_query instanceof \WP_Query ) ) {
$post_id = get_queried_object_id();
}
// 5) Si ya tenemos post_id, intentar idioma del meta.
if ( $post_id ) {
$lang = get_post_meta( $post_id, self::META_LANG, true );
if ( $lang ) {
return sanitize_key( $lang );
}
}
// 6) Fallback final: idioma por defecto del plugin.
return $this->get_default_language_code();
}
public function get_translation_map( $post_id = 0 ) {
// En algunos contextos (Customizer / admin temprano) $wp_query puede ser null.
global $wp_query;
if ( $post_id === 0 && ( $wp_query instanceof \WP_Query ) ) {
$post_id = get_queried_object_id();
}
if ( ! $post_id ) {
return [];
}
$map = get_post_meta( $post_id, self::META_MAP, true );
return is_array( $map ) ? $map : [];
}
public function maybe_register_bulk_hooks( $screen ) {
if ( ! is_object( $screen ) || empty( $screen->base ) ) { return; }
if ( $screen->base !== 'edit' ) { return; }
$pt = isset( $screen->post_type ) ? sanitize_key( $screen->post_type ) : '';
if ( ! $pt ) { return; }
$settings = Settings::get_settings_option();
$enabled = isset( $settings['enabled_post_types'] ) && is_array( $settings['enabled_post_types'] ) ? $settings['enabled_post_types'] : [ 'page', 'post' ];
if ( ! in_array( $pt, $enabled, true ) ) { return; }
add_filter( "bulk_actions-edit-{$pt}", [ $this, 'register_bulk_actions' ] );
add_filter( "handle_bulk_actions-edit-{$pt}", [ $this, 'handle_bulk_action' ], 10, 3 );
}
public function register_bulk_actions_for_enabled_types() {
if ( ! is_admin() ) { return; }
$settings = Settings::get_settings_option();
$enabled = isset( $settings['enabled_post_types'] ) && is_array( $settings['enabled_post_types'] ) ? $settings['enabled_post_types'] : [ 'page', 'post' ];
foreach ( $enabled as $pt ) {
$pt = sanitize_key( (string) $pt );
if ( ! $pt ) { continue; }
add_filter( "bulk_actions-edit-{$pt}", [ $this, 'register_bulk_actions' ] );
add_filter( "handle_bulk_actions-edit-{$pt}", [ $this, 'handle_bulk_action' ], 10, 3 );
}
}
public function register_bulk_actions( $actions ) {
$active = $this->get_active_languages();
if ( empty( $active ) ) { return $actions; }
// Add one action per language + unassigned.
$actions['abdiel_amm_lang_unassigned'] = __( 'Assign language: Unassigned', 'abdiel-multilingual-manager' );
foreach ( $active as $code => $row ) {
$actions[ 'abdiel_amm_lang_' . $code ] = sprintf(
/* translators: %s language name */
__( 'Assign language: %s', 'abdiel-multilingual-manager' ),
$row['name'] . ' (' . $code . ')'
);
}
return $actions;
}
public function handle_bulk_action( $redirect_to, $doaction, $post_ids ) {
if ( strpos( $doaction, 'abdiel_amm_lang_' ) !== 0 ) {
return $redirect_to;
}
// BEGIN: Bulk nonce verification (Plugin Check).
$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
$pt = '';
if ( function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
if ( is_object( $screen ) && ! empty( $screen->post_type ) ) {
$pt = sanitize_key( $screen->post_type );
}
}
if ( ! $pt ) {
$pt = isset( $GLOBALS['typenow'] ) ? sanitize_key( (string) $GLOBALS['typenow'] ) : '';
}
// WP uses 'bulk-' . $plural.
$plural = ( $pt === 'page' ) ? 'pages' : ( $pt === 'post' ? 'posts' : ( $pt ? $pt . 's' : 'posts' ) );
if ( ! $nonce || ! wp_verify_nonce( $nonce, 'bulk-' . $plural ) ) {
return $redirect_to;
}
// END: Bulk nonce verification.
$code = str_replace( 'abdiel_amm_lang_', '', $doaction );
$active = $this->get_active_languages();
$updated = 0;
foreach ( (array) $post_ids as $post_id ) {
$post_id = absint( $post_id );
if ( ! $post_id ) { continue; }
if ( $code === 'unassigned' ) {
delete_post_meta( $post_id, self::META_LANG );
$updated++;
continue;
}
if ( empty( $active[ $code ] ) ) { continue; }
update_post_meta( $post_id, self::META_LANG, $code );
$updated++;
}
$redirect_to = add_query_arg( 'abdiel_amm_bulk_updated', (string) $updated, $redirect_to );
return $redirect_to;
}
}