array( 'name' => __( 'Genocide Maps', 'fp-genocide-maps' ), 'singular_name' => __( 'Map', 'fp-genocide-maps' ), 'add_new' => __( 'Add New Map', 'fp-genocide-maps' ), 'add_new_item' => __( 'Add New Map', 'fp-genocide-maps' ), 'edit_item' => __( 'Edit Map', 'fp-genocide-maps' ), 'new_item' => __( 'New Map', 'fp-genocide-maps' ), 'view_item' => __( 'View Map', 'fp-genocide-maps' ), 'search_items' => __( 'Search Maps', 'fp-genocide-maps' ), 'not_found' => __( 'No maps found', 'fp-genocide-maps' ), 'featured_image' => __( 'Map image', 'fp-genocide-maps' ), 'set_featured_image' => __( 'Select map image', 'fp-genocide-maps' ), ), 'public' => false, 'show_ui' => true, 'show_in_menu' => true, 'show_in_rest' => false, 'exclude_from_search' => true, 'menu_icon' => 'dashicons-location-alt', 'menu_position' => 21, 'supports' => array( 'title', 'editor', 'thumbnail', 'page-attributes' ), 'hierarchical' => false, ) ); } add_action( 'init', 'fpgm_register_cpt' ); /** * Front-end assets. The stylesheet replicates the exact Divi-generated design * of the original section, so it is tiny and safe to load site-wide. */ function fpgm_enqueue_assets() { wp_enqueue_style( 'fpgm-maps', plugins_url( 'assets/css/geep-maps.css', __FILE__ ), array(), FPGM_VERSION ); wp_enqueue_script( 'fpgm-maps', plugins_url( 'assets/js/geep-maps.js', __FILE__ ), array(), FPGM_VERSION, true ); } add_action( 'wp_enqueue_scripts', 'fpgm_enqueue_assets' ); /** * [geep_maps] — image column + accordion column, mirroring the original * "texto mapa" Divi row (columns 14/24 and 2/5, 2% gap). */ function fpgm_shortcode() { $maps = get_posts( array( 'post_type' => FPGM_CPT, 'posts_per_page' => -1, 'orderby' => array( 'menu_order' => 'ASC', 'date' => 'ASC', ), 'order' => 'ASC', 'suppress_filters' => false, // Let WPML return the current language. ) ); if ( empty( $maps ) ) { return ''; } // Map images are language-independent: when a translation has no image of // its own, fall back to the original post's image (e.g. Arabic maps show // the image assigned to the English one). Setting an image on the // translation still overrides this. $get_thumb = function ( $post_id ) { $thumb = get_post_thumbnail_id( $post_id ); if ( ! $thumb ) { $original = apply_filters( 'wpml_original_element_id', null, $post_id, 'post_' . FPGM_CPT ); if ( $original && (int) $original !== (int) $post_id ) { $thumb = get_post_thumbnail_id( (int) $original ); } } return $thumb; }; // The first map starts open, so its image (if it has one) is the one // visible on load. Maps without an image simply leave the gap empty. $first_thumb = $get_thumb( $maps[0]->ID ); $active_key = $first_thumb ? (string) $first_thumb : ''; // One image per map that has one. The visible size tops out around 660px // (58% of the 90%-wide row), so tell the browser it never needs the // full-size file: with `sizes` it picks the smallest sufficient variant. $sizes = '(max-width: 767px) 100vw, 50vw'; $images_html = ''; $toggles_html = ''; $first = true; $rendered_keys = array(); foreach ( $maps as $map ) { $thumb_id = $get_thumb( $map->ID ); $map_key = ''; if ( $thumb_id ) { $map_key = (string) $thumb_id; if ( ! in_array( $map_key, $rendered_keys, true ) ) { $rendered_keys[] = $map_key; $images_html .= wp_get_attachment_image( $thumb_id, 'full', false, array( 'class' => 'geep-map-img' . ( $map_key === $active_key ? ' is-active is-shown' : '' ), 'data-map' => $map_key, 'loading' => 'lazy', 'sizes' => $sizes, ) ); } } $toggles_html .= sprintf( '
' . '

%s

' . '
%s
' . '
', $first ? 'geep-toggle--open' : 'geep-toggle--close', $first ? ' geep-toggle--first' : '', esc_attr( $map_key ), esc_attr( urldecode( $map->post_name ) ), // Readable form (Arabic slugs are stored percent-encoded). esc_html( get_the_title( $map ) ), wp_kses_post( wpautop( $map->post_content ) ) ); $first = false; } // UI labels for the lightbox, in the language of the current page. $is_ar = 'ar' === apply_filters( 'wpml_current_language', null ); $labels = array( 'copy' => $is_ar ? 'نسخ رابط الخريطة' : 'Copy map URL', 'copied' => $is_ar ? 'تم نسخ الرابط' : 'Link copied!', 'close' => $is_ar ? 'إغلاق' : 'Close', 'zoom' => $is_ar ? 'عرض الخريطة بحجم أكبر' : 'View map larger', ); return '
' . '
' . $images_html . '
' . '
' . $toggles_html . '
' . '
'; } add_shortcode( 'geep_maps', 'fpgm_shortcode' ); /* ------------------------------------------------------------------------- * Admin: list-table columns and default-image setting. * ---------------------------------------------------------------------- */ /** * Add image + order columns to the Maps list table. * * @param array $columns Existing columns. * @return array */ function fpgm_admin_columns( $columns ) { $new = array(); foreach ( $columns as $key => $label ) { if ( 'title' === $key ) { $new['fpgm_image'] = __( 'Map image', 'fp-genocide-maps' ); } $new[ $key ] = $label; } $new['fpgm_order'] = __( 'Order', 'fp-genocide-maps' ); return $new; } add_filter( 'manage_' . FPGM_CPT . '_posts_columns', 'fpgm_admin_columns' ); /** * Render the custom columns. * * @param string $column Column key. * @param int $post_id Post ID. */ function fpgm_admin_columns_content( $column, $post_id ) { if ( 'fpgm_image' === $column ) { if ( has_post_thumbnail( $post_id ) ) { echo get_the_post_thumbnail( $post_id, array( 80, 60 ), array( 'style' => 'border-radius:4px;height:auto;' ) ); } else { echo '' . esc_html__( 'No image', 'fp-genocide-maps' ) . ''; } } elseif ( 'fpgm_order' === $column ) { echo (int) get_post_field( 'menu_order', $post_id ); } } add_action( 'manage_' . FPGM_CPT . '_posts_custom_column', 'fpgm_admin_columns_content', 10, 2 ); /** * Order the admin list by menu_order, like the front end. * * @param WP_Query $query Current query. */ function fpgm_admin_order( $query ) { if ( is_admin() && $query->is_main_query() && FPGM_CPT === $query->get( 'post_type' ) && ! $query->get( 'orderby' ) ) { $query->set( 'orderby', 'menu_order' ); $query->set( 'order', 'ASC' ); } } add_action( 'pre_get_posts', 'fpgm_admin_order' ); /* ------------------------------------------------------------------------- * Activation: seed the current 8 maps (EN + AR, linked in WPML). * ---------------------------------------------------------------------- */ /** * Create the initial maps from the content that used to live in the Divi * toggles, once, on first activation. */ function fpgm_activate() { fpgm_register_cpt(); // Register the CPT as translatable in WPML *before* seeding, otherwise // wpml_set_element_language_details silently ignores the posts and the // AR/EN pairs never get linked. Mode 2 = translatable, display the // original when no translation exists (same as wpml-config.xml). global $sitepress; if ( $sitepress && method_exists( $sitepress, 'get_setting' ) ) { $sync = (array) $sitepress->get_setting( 'custom_posts_sync_option', array() ); $sync[ FPGM_CPT ] = 2; $sitepress->set_setting( 'custom_posts_sync_option', $sync, true ); } $existing = get_posts( array( 'post_type' => FPGM_CPT, 'posts_per_page' => 1, 'post_status' => 'any', 'suppress_filters' => true, ) ); if ( ! empty( $existing ) ) { return; // Already seeded. } $file = __DIR__ . '/includes/seed-data.json'; if ( ! file_exists( $file ) ) { return; } $data = json_decode( file_get_contents( $file ), true ); if ( empty( $data['en'] ) ) { return; } $has_wpml = has_filter( 'wpml_element_trid' ); foreach ( $data['en'] as $i => $item ) { $en_id = wp_insert_post( array( 'post_type' => FPGM_CPT, 'post_status' => 'publish', 'post_title' => wp_strip_all_tags( $item['title'] ), 'post_content' => $item['content'], 'menu_order' => $i + 1, ) ); if ( ! $en_id || is_wp_error( $en_id ) ) { continue; } if ( $has_wpml ) { $trid = apply_filters( 'wpml_element_trid', null, $en_id, 'post_' . FPGM_CPT ); do_action( 'wpml_set_element_language_details', array( 'element_id' => $en_id, 'element_type' => 'post_' . FPGM_CPT, 'trid' => $trid, 'language_code' => 'en', 'source_language_code' => null, ) ); // Arabic counterpart (same position in the list). if ( ! empty( $data['ar'][ $i ]['title'] ) ) { $ar_id = wp_insert_post( array( 'post_type' => FPGM_CPT, 'post_status' => 'publish', 'post_title' => wp_strip_all_tags( $data['ar'][ $i ]['title'] ), 'post_content' => $data['ar'][ $i ]['content'], 'menu_order' => $i + 1, ) ); if ( $ar_id && ! is_wp_error( $ar_id ) ) { do_action( 'wpml_set_element_language_details', array( 'element_id' => $ar_id, 'element_type' => 'post_' . FPGM_CPT, 'trid' => $trid, 'language_code' => 'ar', 'source_language_code' => 'en', ) ); } } } } } register_activation_hook( __FILE__, 'fpgm_activate' );