Файловый менеджер - Редактировать - /var/www/xthruster/html/wp-content/uploads/flags/wp-nav-menus.tar
Назад
class-wpml-nav-menu.php 0000644 00000076044 14721623671 0011107 0 ustar 00 <?php use WPML\API\Sanitize; class WPML_Nav_Menu { private $current_menu; private $current_lang; /** @var WPML_Term_Translation $term_translations */ protected $term_translations; /** @var WPML_Post_Translation $post_translations */ protected $post_translations; /** @var SitePress $sitepress */ protected $sitepress; /** @var wpdb $wpdb */ public $wpdb; /** @var WPML_Nav_Menu_Actions $nav_menu_actions */ public $nav_menu_actions; function __construct( SitePress $sitepress, wpdb $wpdb, WPML_Post_Translation $post_translations, WPML_Term_Translation $term_translations ) { $this->sitepress = $sitepress; $this->wpdb = $wpdb; $this->post_translations = $post_translations; $this->term_translations = $term_translations; $this->nav_menu_actions = new WPML_Nav_Menu_Actions( $sitepress, $wpdb, $post_translations, $term_translations ); } public function init_hooks() { if ( is_admin() ) { add_filter( 'option_nav_menu_options', array( $this, 'option_nav_menu_options' ) ); add_filter( 'wp_get_nav_menus', array( $this, 'wp_get_nav_menus_filter' ) ); } if ( $this->must_filter_menus() ) { add_filter( 'get_terms', array( $this, 'get_terms_filter' ), 1, 3 ); } add_action( 'init', array( $this, 'init' ) ); add_filter( 'wp_nav_menu_args', array( $this, 'wp_nav_menu_args_filter' ) ); add_filter( 'wp_nav_menu_items', array( $this, 'wp_nav_menu_items_filter' ) ); add_filter( 'nav_menu_meta_box_object', array( $this, '_enable_sitepress_query_filters' ), 11 ); } /** * @return bool */ private function must_filter_menus() { global $pagenow; return 'nav-menus.php' === $pagenow || 'widgets.php' === $pagenow || filter_input( INPUT_POST, 'action' ) === 'save-widget'; } function init() { /** @var WPML_Request $wpml_request_handler */ /** @var WPML_Language_Resolution $wpml_language_resolution */ global $sitepress, $sitepress_settings, $pagenow, $wpml_request_handler, $wpml_language_resolution; $this->adjust_current_language_if_required(); $default_language = $sitepress->get_default_language(); // add language controls for menus no option but javascript if ( $pagenow === 'nav-menus.php' ) { add_action( 'admin_footer', array( $this, 'nav_menu_language_controls' ), 10 ); wp_enqueue_script( 'wp_nav_menus', ICL_PLUGIN_URL . '/res/js/wp-nav-menus.js', array( 'jquery' ), ICL_SITEPRESS_VERSION, true ); wp_enqueue_style( 'wp_nav_menus_css', ICL_PLUGIN_URL . '/res/css/wp-nav-menus.css', array(), ICL_SITEPRESS_VERSION, 'all' ); // filter posts by language add_action( 'parse_query', array( $this, 'action_parse_query' ) ); } if ( is_admin() ) { $this->_set_menus_language(); $this->get_current_menu(); } if ( isset( $_POST['action'] ) && $_POST['action'] === 'menu-get-metabox' && (bool) ( $lang = $wpml_language_resolution->get_referrer_language_code() ) !== false ) { $sitepress->switch_lang( $lang ); } if ( isset( $this->current_menu['language'] ) && isset( $this->current_menu['id'] ) && $this->current_menu['id'] && $this->current_menu['language'] && $this->current_menu['language'] != $default_language && isset( $_GET['menu'] ) && empty( $_GET['lang'] ) ) { wp_redirect( admin_url( sprintf( 'nav-menus.php?menu=%d&lang=%s', $this->current_menu['id'], $this->current_menu['language'] ) ) ); } $this->current_lang = $wpml_request_handler->get_requested_lang(); if ( isset( $_POST['icl_wp_nav_menu_ajax'] ) ) { $this->ajax( $_POST ); } // for theme locations that are not translated into the current language // reflect status in the theme location navigation switcher add_action( 'admin_footer', array( $this, '_set_custom_status_in_theme_location_switcher' ) ); // filter menu by language when adjust ids is off // not on ajax calls if ( ! $sitepress_settings['auto_adjust_ids'] && ! defined( 'DOING_AJAX' ) ) { add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 ); } $this->setup_menu_item(); if ( $this->sitepress->get_wp_api()->is_core_page( 'menu-sync/menus-sync.php' ) ) { $this->setup_menu_synchronization(); } add_action( 'wp_ajax_icl_msync_confirm', array( $this, 'sync_menus_via_ajax' ) ); add_action( 'wp_ajax_wpml_get_links_for_menu_strings_translation', array( $this, 'get_links_for_menu_strings_translation_ajax' ) ); } function sync_menus_via_ajax() { if ( isset( $_POST['_icl_nonce_menu_sync'] ) && wp_verify_nonce( $_POST['_icl_nonce_menu_sync'], '_icl_nonce_menu_sync' ) ) { if ( ! session_id() ) { session_start(); } global $icl_menus_sync,$wpdb, $wpml_post_translations, $wpml_term_translations, $sitepress; include_once WPML_PLUGIN_PATH . '/inc/wp-nav-menus/menus-sync.php'; $icl_menus_sync = new ICLMenusSync( $sitepress, $wpdb, $wpml_post_translations, $wpml_term_translations ); $icl_menus_sync->init( isset( $_SESSION['wpml_menu_sync_menu'] ) ? $_SESSION['wpml_menu_sync_menu'] : null ); $results = $icl_menus_sync->do_sync( $_POST['sync'] ); $_SESSION['wpml_menu_sync_menu'] = $results; wp_send_json_success( true ); } else { wp_send_json_error( false ); } } public function get_links_for_menu_strings_translation_ajax() { global $icl_menus_sync, $wpml_post_translations, $wpml_term_translations; $nonce = isset( $_GET['_nonce'] ) ? sanitize_text_field( $_GET['_nonce'] ) : ''; if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( esc_html__( 'Unauthorized', 'sitepress' ), 401 ); return; } if ( ! wp_verify_nonce( $nonce, 'wpml_get_links_for_menu_strings_translation' ) ) { wp_send_json_error( esc_html__( 'Invalid request!', 'sitepress' ), 400 ); return; } include_once WPML_PLUGIN_PATH . '/inc/wp-nav-menus/menus-sync.php'; $icl_menus_sync = new ICLMenusSync( $this->sitepress, $this->wpdb, $wpml_post_translations, $wpml_term_translations ); wp_send_json_success( $icl_menus_sync->get_links_for_menu_strings_translation() ); } /** * @param string $menu_id */ function admin_menu_setup( $menu_id ) { if ( 'WPML' !== $menu_id ) { return; } $menu = array(); $menu['order'] = 700; $menu['page_title'] = __( 'WP Menus Sync', 'sitepress' ); $menu['menu_title'] = __( 'WP Menus Sync', 'sitepress' ); $menu['capability'] = 'wpml_manage_wp_menus_sync'; $menu['menu_slug'] = WPML_PLUGIN_FOLDER . '/menu/menu-sync/menus-sync.php'; do_action( 'wpml_admin_menu_register_item', $menu ); } /** * * Associates menus without language information with default language */ private function _set_menus_language() { global $wpdb, $sitepress; $default_language = $sitepress->get_default_language(); $untranslated_menus = $wpdb->get_col( " SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} tt LEFT JOIN {$wpdb->prefix}icl_translations i ON CONCAT('tax_', tt.taxonomy ) = i.element_type AND i.element_id = tt.term_taxonomy_id WHERE tt.taxonomy='nav_menu' AND i.language_code IS NULL" ); foreach ( (array) $untranslated_menus as $item ) { $sitepress->set_element_language_details( $item, 'tax_nav_menu', null, $default_language ); } $untranslated_menu_items = $wpdb->get_col( " SELECT p.ID FROM {$wpdb->posts} p LEFT JOIN {$wpdb->prefix}icl_translations i ON CONCAT('post_', p.post_type ) = i.element_type AND i.element_id = p.ID WHERE p.post_type = 'nav_menu_item' AND i.language_code IS NULL" ); if ( ! empty( $untranslated_menu_items ) ) { foreach ( $untranslated_menu_items as $item ) { $sitepress->set_element_language_details( $item, 'post_nav_menu_item', null, $default_language, null, true, true ); } } } function ajax( $data ) { if ( $data['icl_wp_nav_menu_ajax'] == 'translation_of' ) { $trid = isset( $data['trid'] ) ? $data['trid'] : false; echo $this->render_translation_of( $data['lang'], $trid ); } exit; } function _get_menu_language( $menu_id ) { /** @var WPML_Term_Translation $wpml_term_translations */ global $wpml_term_translations; return $menu_id ? $wpml_term_translations->lang_code_by_termid( $menu_id ) : false; } /** * * Gets first menu in a specific language * used to override nav_menu_recently_edited when a different language is selected * * @param string $lang * @return int */ function _get_first_menu( $lang ) { global $wpdb; $menu_tt_id = $wpdb->get_var( "SELECT MIN(element_id) FROM {$wpdb->prefix}icl_translations WHERE element_type='tax_nav_menu' AND language_code='" . esc_sql( $lang ) . "'" ); return $menu_tt_id ? (int) $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE term_taxonomy_id=%d", $menu_tt_id ) ) : false; } function get_current_menu() { global $sitepress, $wpml_request_handler; $nav_menu_recently_edited = get_user_option( 'nav_menu_recently_edited' ); $nav_menu_recently_edited_lang = $this->_get_menu_language( $nav_menu_recently_edited ); $current_language = $sitepress->get_current_language(); $admin_language_cookie = $wpml_request_handler->get_cookie_lang(); if ( ! isset( $_REQUEST['menu'] ) && $nav_menu_recently_edited_lang != $current_language ) { // if no menu is specified and the language is set override nav_menu_recently_edited $nav_menu_selected_id = $this->_get_first_menu( $current_language ); if ( $nav_menu_selected_id ) { update_user_option( get_current_user_id(), 'nav_menu_recently_edited', $nav_menu_selected_id ); } else { $_REQUEST['menu'] = 0; } } elseif ( ! isset( $_REQUEST['menu'] ) && ! isset( $_GET['lang'] ) && ( empty( $nav_menu_recently_edited_lang ) || $nav_menu_recently_edited_lang != $admin_language_cookie ) && ( empty( $_POST['action'] ) || $_POST['action'] != 'update' ) ) { // if no menu is specified, no language is set, override nav_menu_recently_edited if its language is different than default $nav_menu_selected_id = $this->_get_first_menu( $current_language ); update_user_option( get_current_user_id(), 'nav_menu_recently_edited', $nav_menu_selected_id ); } elseif ( isset( $_REQUEST['menu'] ) ) { $nav_menu_selected_id = $_REQUEST['menu']; } else { $nav_menu_selected_id = $nav_menu_recently_edited; } $this->current_menu['id'] = $nav_menu_selected_id; if ( $this->current_menu['id'] ) { $this->_load_menu( $this->current_menu['id'] ); } else { $this->current_menu['trid'] = isset( $_GET['trid'] ) ? (int) $_GET['trid'] : null; if ( isset( $_POST['icl_nav_menu_language'] ) ) { $this->current_menu['language'] = $_POST['icl_nav_menu_language']; } elseif ( isset( $_GET['lang'] ) ) { $this->current_menu['language'] = (int) $_GET['lang']; } else { $this->current_menu['language'] = $admin_language_cookie; } $this->current_menu['translations'] = array(); } } /** * @param bool|int $menu_id * * @return array */ function _load_menu( $menu_id = false ) { $menu_id = $menu_id ? $menu_id : $this->current_menu['id']; $menu_term_object = get_term( $menu_id, 'nav_menu' ); if ( ! empty( $menu_term_object->term_taxonomy_id ) ) { $ttid = $menu_term_object->term_taxonomy_id; $current_menu = array( 'id' => $menu_id ); $current_menu['trid'] = $this->term_translations->get_element_trid( $ttid ); $current_menu['translations'] = $current_menu['trid'] ? $this->sitepress->get_element_translations( $current_menu['trid'], 'tax_nav_menu' ) : array(); $current_menu['language'] = $this->term_translations->lang_code_by_termid( $menu_id ); } $this->current_menu = ! empty( $current_menu['translations'] ) ? $current_menu : null; return $this->current_menu; } private function get_action_icon( $css_class, $label ) { return '<span class="' . $css_class . '" title="' . esc_attr( $label ) . '"></span>'; } function nav_menu_language_controls() { global $sitepress, $wpdb; $this->_load_menu(); $default_language = $sitepress->get_default_language(); $current_lang = isset( $this->current_menu['language'] ) ? $this->current_menu['language'] : $sitepress->get_current_language(); $langsel = '<br class="clear" />'; // show translations links if this is not a new element if ( isset( $this->current_menu['id'] ) && $this->current_menu['id'] ) { $langsel .= '<div class="icl_nav_menu_text" style="float:right;">'; $langsel .= __( 'Translations:', 'sitepress' ); foreach ( $sitepress->get_active_languages() as $lang ) { if ( ! isset( $this->current_menu['language'] ) || $lang['code'] == $this->current_menu['language'] ) { continue; } if ( isset( $this->current_menu['translations'][ $lang['code'] ] ) ) { $menu_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE term_taxonomy_id=%d", $this->current_menu['translations'][ $lang['code'] ]->element_id ) ); $label = __( 'edit translation', 'sitepress' ); $tr_link = '<a style="text-decoration:none" title="' . esc_attr( $label ) . '" href="' . admin_url( 'nav-menus.php' ) . '?menu=' . $menu_id . '&lang=' . $lang['code'] . '">' . $this->get_action_icon( WPML_Post_Status_Display::ICON_TRANSLATION_EDIT, $label ) . $lang['display_name'] . '</a>'; } else { $label = __( 'add translation', 'sitepress' ); $tr_link = '<a style="text-decoration:none" title="' . esc_attr( $label ) . '" href="' . admin_url( 'nav-menus.php' ) . '?action=edit&menu=0&trid=' . $this->current_menu['trid'] . '&lang=' . $lang['code'] . '">' . $this->get_action_icon( WPML_Post_Status_Display::ICON_TRANSLATION_ADD, $label ) . esc_html( $lang['display_name'] ) . '</a>'; } $trs[] = $tr_link; } $langsel .= ' '; if ( isset( $trs ) ) { $langsel .= join( ', ', $trs ); } $langsel .= '</div><br />'; $langsel .= '<div class="icl_nav_menu_text" style="float:right; clear:right">'; $langsel .= '<div><a href="' . admin_url( 'admin.php?page=' . WPML_PLUGIN_FOLDER . '/menu/menu-sync/menus-sync.php' ) . '">' . __( 'Synchronize menus between languages.', 'sitepress' ) . '</a></div>'; $langsel .= '</div>'; } // show languages dropdown $langsel .= '<label class="menu-name-label howto"><span>' . __( 'Language', 'sitepress' ) . '</span>'; $langsel .= ' '; $langsel .= '<select name="icl_nav_menu_language" id="icl_menu_language">'; foreach ( $sitepress->get_active_languages() as $lang ) { if ( isset( $this->current_menu['translations'][ $lang['code'] ] ) && $this->current_menu['language'] != $lang['code'] ) { continue; } if ( isset( $this->current_menu['language'] ) && $this->current_menu['language'] ) { $selected = $lang['code'] == $this->current_menu['language'] ? ' selected="selected"' : ''; } else { $selected = $lang['code'] == $sitepress->get_current_language() ? ' selected="selected"' : ''; } $langsel .= '<option value="' . $lang['code'] . '"' . $selected . '>' . $lang['display_name'] . '</option>'; } $langsel .= '</select>'; $langsel .= '</label>'; if ( $current_lang !== $default_language ) { // show 'translation of' if this element is not in the default language and there are untranslated elements $langsel .= '<span id="icl_translation_of_wrap">'; $trid_current = ! empty( $this->current_menu['trid'] ) ? $this->current_menu['trid'] : ( isset( $_GET['trid'] ) ? $_GET['trid'] : 0 ); $langsel .= $this->render_translation_of( $current_lang, (int) $trid_current ); $langsel .= '</span>'; } $langsel .= '</span>'; // Add trid to form. if ( $this->current_menu && $this->current_menu['trid'] ) { $langsel .= '<input type="hidden" id="icl_nav_menu_trid" name="icl_nav_menu_trid" value="' . $this->current_menu['trid'] . '" />'; } $langsel .= ''; echo $this->render_button_language_switcher_settings(); ?> <script type="text/javascript"> jQuery(document).ready(function () { addLoadEvent(function () { var update_menu_form = jQuery('#update-nav-menu'); update_menu_form.find('.publishing-action:first').before('<?php echo addslashes_gpc( $langsel ); ?>'); jQuery('#side-sortables').before('<?php $this->languages_menu(); ?>'); <?php if ( $this->current_lang != $default_language ) : ?> jQuery('.nav-tabs .nav-tab').each(function () { jQuery(this).attr('href', jQuery(this).attr('href') + '&lang=<?php echo $this->current_lang; ?>'); }); var original_action = update_menu_form.attr('ACTION') ? update_menu_form.attr('ACTION') : ''; update_menu_form.attr('ACTION', original_action + '?lang=<?php echo $this->current_lang; ?>'); <?php endif; ?> WPML_core.wp_nav_align_inputs(); }); }); </script> <?php } function get_menus_without_translation( $lang, $trid = 0 ) { $res_query = " SELECT ts.element_id, ts.trid, t.name FROM {$this->wpdb->prefix}icl_translations ts JOIN {$this->wpdb->term_taxonomy} tx ON ts.element_id = tx.term_taxonomy_id JOIN {$this->wpdb->terms} t ON tx.term_id = t.term_id LEFT JOIN {$this->wpdb->prefix}icl_translations mo ON mo.trid = ts.trid AND mo.language_code = %s WHERE ts.element_type='tax_nav_menu' AND ts.language_code != %s AND ts.source_language_code IS NULL AND tx.taxonomy = 'nav_menu' AND ( mo.element_id IS NULL OR ts.trid = %d ) "; $res_query_prepared = $this->wpdb->prepare( $res_query, $lang, $lang, $trid ); $res = $this->wpdb->get_results( $res_query_prepared ); $menus = array(); foreach ( $res as $row ) { $menus[ $row->trid ] = $row; } return $menus; } private function render_translation_of( $lang, $trid = false ) { global $sitepress; $out = ''; if ( $sitepress->get_default_language() != $lang ) { $menus = $this->get_menus_without_translation( $lang, (int) $trid ); $disabled = empty( $this->current_menu['id'] ) && isset( $_GET['trid'] ) ? ' disabled="disabled"' : ''; $out .= '<label class="menu-name-label howto"><span>' . __( 'Translation of', 'sitepress' ) . '</span> '; $out .= '<select name="icl_translation_of" id="icl_menu_translation_of"' . $disabled . '>'; $out .= '<option value="none">--' . __( 'none', 'sitepress' ) . '--</option>'; foreach ( $menus as $mtrid => $m ) { if ( (int) $trid === (int) $mtrid ) { $selected = ' selected="selected"'; } else { $selected = ''; } $out .= '<option value="' . $m->element_id . '"' . $selected . '>' . $m->name . '</option>'; } $out .= '</select>'; $out .= '</label>'; if ( $disabled !== '' ) { $out .= '<input type="hidden" name="icl_nav_menu_trid" value="' . (int) $_GET['trid'] . '"/>'; } } return $out; } private function render_button_language_switcher_settings() { /* @var WPML_Language_Switcher $wpml_language_switcher */ global $wpml_language_switcher; $output = ''; $default_lang = $this->sitepress->get_default_language(); $default_lang_menu = isset( $this->current_menu['translations'][ $default_lang ] ) ? $this->current_menu['translations'][ $default_lang ] : null; if ( $default_lang_menu && isset( $default_lang_menu->element_id ) ) { $output = '<div id="wpml-ls-menu-management" style="display:none;">'; $output .= $wpml_language_switcher->get_button_to_edit_slot( 'menus', $default_lang_menu->element_id ); $output .= '</div>'; } return $output; } function get_menus_by_language() { global $wpdb, $sitepress; $langs = array(); $res_query = " SELECT lt.name AS language_name, l.code AS lang, COUNT(ts.translation_id) AS c FROM {$wpdb->prefix}icl_languages l JOIN {$wpdb->prefix}icl_languages_translations lt ON lt.language_code = l.code JOIN {$wpdb->prefix}icl_translations ts ON l.code = ts.language_code WHERE lt.display_language_code=%s AND l.active = 1 AND ts.element_type = 'tax_nav_menu' GROUP BY ts.language_code ORDER BY major DESC, english_name ASC "; $admin_language = $sitepress->get_admin_language(); $res_query_prepared = $wpdb->prepare( $res_query, $admin_language ); $res = $wpdb->get_results( $res_query_prepared ); foreach ( $res as $row ) { $langs[ $row->lang ] = $row; } return $langs; } function languages_menu( $echo = true ) { global $sitepress; $langs = $this->get_menus_by_language(); // include empty languages foreach ( $sitepress->get_active_languages() as $lang ) { if ( ! isset( $langs[ $lang['code'] ] ) ) { $langs[ $lang['code'] ] = new stdClass(); $langs[ $lang['code'] ]->language_name = $lang['display_name']; $langs[ $lang['code'] ]->lang = $lang['code']; } } $url = admin_url( 'nav-menus.php' ); $ls = array(); foreach ( $langs as $l ) { $class = $l->lang == $this->current_lang ? ' class="current"' : ''; $url_suffix = '?lang=' . $l->lang; $count_string = isset( $l->c ) && $l->c > 0 ? ' (' . $l->c . ')' : ''; $ls[] = '<a href="' . $url . $url_suffix . '"' . $class . '>' . esc_html( $l->language_name ) . $count_string . '</a>'; } $ls_string = '<div class="icl_lang_menu icl_nav_menu_text">'; $ls_string .= join( ' | ', $ls ); $ls_string .= '</div>'; if ( $echo ) { echo $ls_string; } return $ls_string; } function get_terms_filter( $terms, $taxonomies, $args ) { global $wpdb, $sitepress, $pagenow; // deal with the case of not translated taxonomies // we'll assume that it's called as just a single item if ( ! $sitepress->is_translated_taxonomy( $taxonomies[0] ) && 'nav_menu' !== $taxonomies[0] ) { return $terms; } // special case for determining list of menus for updating auto-add option if ( 'nav-menus.php' === $pagenow && array_key_exists( 'fields', $args ) && array_key_exists( 'action', $_POST ) && 'nav_menu' === $taxonomies[0] && 'ids' === $args['fields'] && 'update' === $_POST['action'] ) { return $terms; } if ( ! empty( $terms ) ) { $txs = array(); foreach ( $taxonomies as $t ) { $txs[] = 'tax_' . $t; } $el_types = wpml_prepare_in( $txs ); // get all term_taxonomy_id's $tt = array(); foreach ( $terms as $t ) { if ( is_object( $t ) ) { $tt[] = $t->term_taxonomy_id; } else { if ( is_numeric( $t ) ) { $tt[] = $t; } } } // filter the ones in the current language $ftt = array(); if ( ! empty( $tt ) ) { $ftt = $wpdb->get_col( $wpdb->prepare( " SELECT element_id FROM {$wpdb->prefix}icl_translations WHERE element_type IN ({$el_types}) AND element_id IN (" . wpml_prepare_in( $tt, '%d' ) . ') AND language_code=%s', $this->current_lang ) ); } foreach ( $terms as $k => $v ) { if ( isset( $v->term_taxonomy_id ) && ! in_array( $v->term_taxonomy_id, $ftt ) ) { unset( $terms[ $k ] ); } } } return array_values( $terms ); } /** * Filter posts by language. * * @param \WP_Query $q * * @return \WP_Query */ public function parse_query( $q ) { if ( ! array_key_exists( 'post_type', $q->query_vars ) ) { return $q; } if ( 'nav_menu_item' === $q->query_vars['post_type'] ) { return $q; } // Not filtering custom posts that are not translated. if ( $this->sitepress->is_translated_post_type( $q->query_vars['post_type'] ) ) { $q->query_vars['suppress_filters'] = 0; } return $q; } /** * @param \WP_Query $q * * @return void */ public function action_parse_query( $q ) { $this->parse_query( $q ); } /** * @param mixed $val * * @return mixed */ function option_nav_menu_options( $val ) { global $wpdb, $sitepress; // special case of getting menus with auto-add only in a specific language $debug_backtrace = $sitepress->get_backtrace( 5 ); // Ignore objects and limit to first 5 stack frames, since 4 is the highest index we use if ( isset( $debug_backtrace[4] ) && $debug_backtrace[4]['function'] === '_wp_auto_add_pages_to_menu' && ! empty( $val['auto_add'] ) ) { $post_lang = Sanitize::stringProp( 'icl_post_language', $_POST ); $post_lang = ! $post_lang && isset( $_POST['lang'] ) ? Sanitize::string( $_POST['lang'] ) : $post_lang; $post_lang = ! $post_lang && $this->is_duplication_mode() ? $sitepress->get_current_language() : $post_lang; if ( $post_lang ) { $val['auto_add'] = $wpdb->get_col( $wpdb->prepare( " SELECT element_id FROM {$wpdb->prefix}icl_translations WHERE element_type = 'tax_nav_menu' AND element_id IN ( " . wpml_prepare_in( $val['auto_add'], '%d' ) . ' ) AND language_code = %s', $post_lang ) ); } } return $val; } /** * @return bool */ private function is_duplication_mode() { return isset( $_POST['langs'] ); } function wp_nav_menu_args_filter( $args ) { if ( ! $args['menu'] ) { $locations = get_nav_menu_locations(); if ( isset( $args['theme_location'] ) && isset( $locations[ $args['theme_location'] ] ) ) { $args['menu'] = self::convert_nav_menu_id( $locations[ $args['theme_location'] ] ); } }; if ( ! $args['menu'] ) { remove_filter( 'theme_mod_nav_menu_locations', array( $this->nav_menu_actions, 'theme_mod_nav_menu_locations' ) ); $locations = get_nav_menu_locations(); if ( isset( $args['theme_location'] ) && isset( $locations[ $args['theme_location'] ] ) ) { $args['menu'] = self::convert_nav_menu_id( $locations[ $args['theme_location'] ] ); } add_filter( 'theme_mod_nav_menu_locations', array( $this->nav_menu_actions, 'theme_mod_nav_menu_locations' ) ); } // $args[ "menu" ] can be an object consequently to widget's call if ( is_object( $args['menu'] ) && ( ! empty( $args['menu']->term_id ) ) ) { $args['menu'] = wp_get_nav_menu_object( self::convert_nav_menu_id( $args['menu']->term_id ) ); } if ( ( ! is_object( $args['menu'] ) ) && is_numeric( $args['menu'] ) ) { $args['menu'] = wp_get_nav_menu_object( self::convert_nav_menu_id( (int) $args['menu'] ) ); } if ( ( ! is_object( $args['menu'] ) ) && is_string( $args['menu'] ) ) { $term = get_term_by( 'slug', $args['menu'], 'nav_menu' ); if ( false === $term ) { $term = get_term_by( 'name', $args['menu'], 'nav_menu' ); } if ( false !== $term ) { $args['menu'] = wp_get_nav_menu_object( self::convert_nav_menu_id( $term->term_id ) ); } } if ( ! is_object( $args['menu'] ) ) { $args['menu'] = false; } return $args; } /** * It will fallback to the original if the translation * does not exist. This is required for nav menus in * a "widget" context. * * @param int $navMenuId * * @return int */ private static function convert_nav_menu_id( $navMenuId ) { return wpml_object_id_filter( $navMenuId, 'nav_menu', true ); } function wp_nav_menu_items_filter( $items ) { $items = preg_replace( '|<li id="([^"]+)" class="menu-item menu-item-type-taxonomy"><a href="([^"]+)">([^@]+) @([^<]+)</a>|', '<li id="$1" class="menu-item menu-item-type-taxonomy"><a href="$2">$3</a>', $items ); return $items; } function _set_custom_status_in_theme_location_switcher() { global $sitepress_settings, $sitepress, $wpdb; if ( ! $sitepress_settings ) { return; } $tl = (array) get_theme_mod( 'nav_menu_locations' ); $menus_not_translated = array(); foreach ( $tl as $k => $menu ) { $menu_tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE term_id=%d AND taxonomy='nav_menu'", $menu ) ); $menu_trid = $sitepress->get_element_trid( $menu_tt_id, 'tax_nav_menu' ); $menu_translations = $sitepress->get_element_translations( $menu_trid, 'tax_nav_menu' ); if ( ! isset( $menu_translations[ $this->current_lang ] ) || ! $menu_translations[ $this->current_lang ] ) { $menus_not_translated[] = $k; } } if ( ! empty( $menus_not_translated ) ) { ?> <script type="text/javascript"> jQuery(document).ready(function () { addLoadEvent(function () { <?php foreach ( $menus_not_translated as $menu_id ) : ?> var menu_id = '<?php echo $menu_id; ?>'; var location_menu_id = jQuery('#locations-' + menu_id); if (location_menu_id.length > 0) { location_menu_id.find('option').first().html('<?php echo esc_js( __( 'not translated in current language', 'sitepress' ) ); ?>'); location_menu_id.css('font-style', 'italic'); location_menu_id.change(function () { if (jQuery(this).val() != 0) { jQuery(this).css('font-style', 'normal'); } else { jQuery(this).css('font-style', 'italic') } }); } <?php endforeach; ?> }); }); </script> <?php } } // on the nav menus when selecting pages using the pagination filter pages > 2 by language function _enable_sitepress_query_filters( $args ) { if ( isset( $args->_default_query ) ) { $args->_default_query['suppress_filters'] = false; } return $args; } function wp_get_nav_menus_filter( $menus ) { global $pagenow; if ( is_admin() && isset( $pagenow ) && $pagenow === 'customize.php' ) { $menus = $this->unfilter_non_default_language_menus( $menus ); } return $menus; } private function setup_menu_item() { add_action( 'wpml_admin_menu_configure', array( $this, 'admin_menu_setup' ) ); } private function setup_menu_synchronization() { global $icl_menus_sync, $wpml_post_translations, $wpml_term_translations; include_once WPML_PLUGIN_PATH . '/inc/wp-nav-menus/menus-sync.php'; $icl_menus_sync = new ICLMenusSync( $this->sitepress, $this->wpdb, $wpml_post_translations, $wpml_term_translations ); } private function unfilter_non_default_language_menus( $menus ) { global $sitepress, $wpml_term_translations; $default_language = $sitepress->get_default_language(); foreach ( $menus as $index => $menu ) { $menu_ttid = is_object( $menu ) ? $menu->term_taxonomy_id : $menu; /** @var WPML_Term_Translation $wpml_term_translations */ $menu_language = $wpml_term_translations->get_element_lang_code( $menu_ttid ); if ( $menu_language != $default_language && $menu_language != null ) { unset( $menus[ $index ] ); } } return $menus; } private function adjust_current_language_if_required() { global $pagenow; if ( $pagenow === 'nav-menus.php' && isset( $_GET['menu'] ) && $_GET['menu'] ) { $current_lang = $this->sitepress->get_current_language(); $menu_lang = $this->_get_menu_language( (int) $_GET['menu'] ); if ( $menu_lang && ( $current_lang !== $menu_lang ) ) { $this->sitepress->switch_lang( $menu_lang ); $_GET['lang'] = $menu_lang; } } } } menus-sync.php 0000644 00000052241 14721623671 0007373 0 ustar 00 <?php class ICLMenusSync extends WPML_Menu_Sync_Functionality { public $menus; public $is_preview = false; public $sync_data = false; public $string_translation_links = array(); public $operations = array(); /** @var WPML_Menu_Item_Sync $menu_item_sync */ private $menu_item_sync; /** * @param SitePress $sitepress * @param wpdb $wpdb * @param WPML_Post_Translation $post_translations * @param WPML_Term_Translation $term_translations */ function __construct( &$sitepress, &$wpdb, &$post_translations, &$term_translations ) { parent::__construct( $sitepress, $wpdb, $post_translations, $term_translations ); $this->menu_item_sync = new WPML_Menu_Item_Sync( $this->sitepress, $this->wpdb, $this->post_translations, $this->term_translations ); $this->init_hooks(); } function init_hooks() { add_action( 'init', array( $this, 'init' ), 20 ); if ( isset( $_GET['updated'] ) ) { add_action( 'admin_notices', array( $this, 'admin_notices' ) ); } } function init( $previous_menu = false ) { $this->sitepress->switch_lang( $this->sitepress->get_default_language() ); $action = filter_input( INPUT_POST, 'action' ); $nonce = (string) filter_input( INPUT_POST, '_icl_nonce_menu_sync' ); if ( $action && ! wp_verify_nonce( $nonce, '_icl_nonce_menu_sync' ) ) { wp_send_json_error( 'Invalid nonce' ); } $this->menu_item_sync->cleanup_broken_page_items(); if ( ! session_id() ) { session_start(); } if ( $action === 'icl_msync_preview' ) { $this->is_preview = true; $this->sync_data = isset( $_POST['sync'] ) ? array_map( 'stripslashes_deep', $_POST['sync'] ) : false; $previous_menu = isset( $_SESSION['wpml_menu_sync_menu'] ) ? $_SESSION['wpml_menu_sync_menu'] : null; } if ( $previous_menu ) { $this->menus = $previous_menu; } else { $this->get_menus_tree(); $_SESSION['wpml_menu_sync_menu'] = $this->menus; } $this->sitepress->switch_lang(); } function get_menu_names() { $menu_names = array(); global $sitepress, $wpdb; $menus = $wpdb->get_results( $wpdb->prepare( " SELECT tm.term_id, tm.name FROM {$wpdb->terms} tm JOIN {$wpdb->term_taxonomy} tx ON tx.term_id = tm.term_id JOIN {$wpdb->prefix}icl_translations tr ON tr.element_id = tx.term_taxonomy_id AND tr.element_type='tax_nav_menu' WHERE tr.language_code=%s ", $sitepress->get_default_language() ) ); if ( $menus ) { foreach ( $menus as $menu ) { $menu_names[] = $menu->name; } } return $menu_names; } function get_menus_tree() { global $sitepress, $wpdb; $menus = $wpdb->get_results( $wpdb->prepare( " SELECT tm.term_id, tm.name FROM {$wpdb->terms} tm JOIN {$wpdb->term_taxonomy} tx ON tx.term_id = tm.term_id JOIN {$wpdb->prefix}icl_translations tr ON tr.element_id = tx.term_taxonomy_id AND tr.element_type='tax_nav_menu' WHERE tr.language_code=%s ", $sitepress->get_default_language() ) ); if ( $menus ) { foreach ( $menus as $menu ) { $this->menus[ $menu->term_id ] = array( 'name' => $menu->name, 'items' => $this->get_menu_items( $menu->term_id, true ), 'translations' => $this->get_menu_translations( $menu->term_id ), ); } $this->add_ghost_entries(); $this->set_new_menu_order(); } } private function get_menu_options( $menu_id ) { $menu_options = get_option( 'nav_menu_options' ); $options = array( 'auto_add' => isset( $menu_options['auto_add'] ) && in_array( $menu_id, $menu_options['auto_add'] ), ); return $options; } public function add_ghost_entries() { if ( is_array( $this->menus ) ) { foreach ( $this->menus as $menu_id => $menu ) { if ( ! is_array( $menu['translations'] ) ) { continue; } foreach ( $menu['translations'] as $language => $tmenu ) { if ( ! empty( $tmenu ) ) { $valid_items = array_filter( $this->menus[ $menu_id ]['items'], function ( $item ) use ( $language ) { return $item && isset( $item['translations'][ $language ]['ID'] ); } ); foreach ( $tmenu['items'] as $titem ) { // Has a place in the default menu? $exists = false; foreach ( $valid_items as $item ) { if ( (int) $item['translations'][ $language ]['ID'] === (int) $titem['ID'] ) { $exists = true; } } if ( ! $exists ) { $this->menus[ $menu_id ]['translations'][ $language ]['deleted_items'][] = array( 'ID' => $titem['ID'], 'title' => $titem['title'], 'menu_order' => $titem['menu_order'], ); } } } } } } } public function set_new_menu_order() { if ( ! is_array( $this->menus ) ) { return; } foreach ( $this->menus as $menu_id => $menu ) { $menu_index_by_lang = array(); foreach ( $menu['items'] as $item_id => $item ) { $valid_translations = array_filter( $item['translations'], function ( $item ) { return $item && $item['ID']; } ); foreach ( $valid_translations as $language => $item_translation ) { $new_menu_order = empty( $menu_index_by_lang[ $language ] ) ? 1 : $menu_index_by_lang[ $language ] + 1; $menu_index_by_lang[ $language ] = $new_menu_order; $this->menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['menu_order_new'] = $new_menu_order; } } } } function do_sync( array $data ) { $this->menus = isset( $this->menus ) ? $this->menus : array(); $this->menus = empty( $data['menu_translation'] ) ? $this->menus : $this->menu_item_sync->sync_menu_translations( $data['menu_translation'], $this->menus ); if ( ! empty( $data['options_changed'] ) ) { $this->menu_item_sync->sync_menu_options( $data['options_changed'] ); } if ( ! empty( $data['del'] ) ) { $this->menu_item_sync->sync_deleted_menus( $data['del'] ); } $this->menus = empty( $data['mov'] ) ? $this->menus : $this->menu_item_sync->sync_moved_items( $data['mov'], $this->menus ); $this->menus = empty( $data['add'] ) ? $this->menus : $this->menu_item_sync->sync_added_items( $data['add'], $this->menus ); if ( ! empty( $data['label_changed'] ) ) { $this->menu_item_sync->sync_caption( $data['label_changed'] ); } if ( ! empty( $data['url_changed'] ) ) { $this->menu_item_sync->sync_urls( $data['url_changed'] ); } if ( ! empty( $data['label_missing'] ) ) { $this->menu_item_sync->sync_missing_captions( $data['label_missing'] ); } if ( ! empty( $data['url_missing'] ) ) { $this->menu_item_sync->sync_urls_to_add( $data['url_missing'] ); } $this->menu_item_sync->sync_custom_fields( $this->menus ); $this->menus = isset( $this->menus ) ? $this->menu_item_sync->sync_menu_order( $this->menus ) : $this->menus; $this->menu_item_sync->cleanup_broken_page_items(); return $this->menus; } function render_items_tree_default( $menu_id, $parent = 0, $depth = 0 ) { global $sitepress; $active_language_codes = array_keys( $sitepress->get_active_languages() ); $need_sync = 0; $default_language = $sitepress->get_default_language(); foreach ( $this->menus[ $menu_id ]['items'] as $item ) { // deleted items #2 (menu order beyond) static $d2_items = array(); $deleted_items = array(); if ( isset( $this->menus[ $menu_id ]['translation'] ) && is_array( $this->menus[ $menu_id ]['translation'] ) ) { foreach ( $this->menus[ $menu_id ]['translations'] as $language => $tmenu ) { if ( ! isset( $d2_items[ $language ] ) ) { $d2_items[ $language ] = array(); } if ( ! empty( $this->menus[ $menu_id ]['translations'][ $language ]['deleted_items'] ) ) { foreach ( $this->menus[ $menu_id ]['translations'][ $language ]['deleted_items'] as $deleted_item ) { if ( ! in_array( $deleted_item['ID'], $d2_items[ $language ] ) && $deleted_item['menu_order'] > count( $this->menus[ $menu_id ]['items'] ) ) { $deleted_items[ $language ][] = $deleted_item; $d2_items[ $language ][] = $deleted_item['ID']; } } } } } if ( $deleted_items ) { ?> <tr> <td> </td> <?php foreach ( $sitepress->get_active_languages() as $language ) : if ( $language['code'] == $default_language ) { continue; } ?> <td> <?php if ( isset( $deleted_items[ $language['code'] ] ) ) : ?> <?php $need_sync ++; ?> <?php foreach ( $deleted_items[ $language['code'] ] as $deleted_item ) : ?> <?php echo str_repeat( ' - ', $depth ); ?><span class="icl_msync_item icl_msync_del"><?php echo esc_html( $deleted_item['title'] ); ?></span> <input type="hidden" name="sync[del][<?php echo esc_attr( $menu_id ); ?>][<?php echo esc_attr( $language['code'] ); ?>][<?php echo esc_attr( $deleted_item['ID'] ); ?>]" value="<?php echo esc_attr( $deleted_item['title'] ); ?>"/> <?php $this->operations['del'] = empty( $this->operations['del'] ) ? 1 : $this->operations['del'] ++; ?> <br/> <?php endforeach; ?> <?php else : ?> <?php endif; ?> </td> <?php endforeach; ?> </tr> <?php } // show deleted item? static $mo_added = array(); $deleted_items = array(); if ( isset( $this->menus[ $menu_id ]['translation'] ) && is_array( $this->menus[ $menu_id ]['translation'] ) ) { foreach ( $this->menus[ $menu_id ]['translations'] as $language => $tmenu ) { if ( ! isset( $mo_added[ $language ] ) ) { $mo_added[ $language ] = array(); } if ( ! empty( $this->menus[ $menu_id ]['translations'][ $language ]['deleted_items'] ) ) { foreach ( $this->menus[ $menu_id ]['translations'][ $language ]['deleted_items'] as $deleted_item ) { if ( ! in_array( $item['menu_order'], $mo_added[ $language ] ) && $deleted_item['menu_order'] == $item['menu_order'] ) { $deleted_items[ $language ] = $deleted_item; $mo_added[ $language ][] = $item['menu_order']; $need_sync ++; } } } } } $this->render_deleted_items( $deleted_items, $need_sync, $depth, $menu_id ); if ( $item['parent'] == $parent ) { ?> <tr> <td> <?php echo str_repeat( ' - ', $depth ) . $item['title']; ?> </td> <?php foreach ( $active_language_codes as $lang_code ) { if ( $lang_code === $default_language ) { continue; } ?> <td> <?php $item_translation = $item['translations'][ $lang_code ]; $item_id = $item['ID']; echo str_repeat( ' - ', $depth ); $need_sync ++; if ( ! empty( $item_translation['ID'] ) ) { // item translation exists $item_sync_needed = false; if ( $item_translation['menu_order'] != $item_translation['menu_order_new'] || $item_translation['depth'] != $item['depth'] ) { // MOVED echo '<span class="icl_msync_item icl_msync_mov">' . esc_html( $item_translation['title'] ) . '</span>'; echo '<input type="hidden" name="sync[mov][' . esc_attr( (string) $menu_id ) . '][' . esc_attr( (string) $item['ID'] ) . '][' . esc_attr( (string) $lang_code ) . '][' . esc_attr( (string) $item_translation['menu_order_new'] ) . ']" value="' . esc_attr( (string) $item_translation['title'] ) . '" />'; $this->operations['mov'] = empty( $this->operations['mov'] ) ? 1 : $this->operations['mov'] ++; $item_sync_needed = true; } if ( $item_translation['label_missing'] ) { $this->index_changed( 'label_missing', $item_id, $item_translation['title'], $menu_id, $lang_code ); $item_sync_needed = true; } if ( $item_translation['label_changed'] ) { $this->index_changed( 'label_changed', $item_id, $item_translation['title'], $menu_id, $lang_code ); $item_sync_needed = true; } if ( $item_translation['url_missing'] ) { $this->index_changed( 'url_missing', $item_id, $item_translation['url'], $menu_id, $lang_code ); $item_sync_needed = true; } if ( $item_translation['url_changed'] ) { $this->index_changed( 'url_changed', $item_id, $item_translation['url'], $menu_id, $lang_code ); $item_sync_needed = true; } if ( ! $item_sync_needed ) { // NO CHANGE $need_sync --; echo esc_html( $item_translation['title'] ); } } elseif ( $item_translation && 'custom' === $item_translation['object_type'] ) { // item translation does not exist but is a custom item that will be created echo '<span class="icl_msync_item icl_msync_add">' . esc_html( $item_translation['title'] ) . ' @' . esc_html( (string) $lang_code ) . '</span>'; echo '<input type="hidden" name="sync[add][' . esc_attr( $menu_id ) . '][' . esc_attr( $item['ID'] ) . '][' . esc_attr( (string) $lang_code ) . ']" value="' . esc_attr( $item_translation['title'] . ' @' . $lang_code ) . '" />'; $this->incOperation( 'add' ); } elseif ( ! empty( $item_translation['object_id'] ) ) { // item translation does not exist but translated object does if ( $item_translation['parent_not_translated'] ) { echo '<span class="icl_msync_item icl_msync_not">' . esc_html( $item_translation['title'] ) . '</span>'; $this->operations['not'] = empty( $this->operations['not'] ) ? 1 : $this->operations['not'] ++; } elseif ( ! icl_object_id( $item['ID'], 'nav_menu_item', false, (string) $lang_code ) ) { // item translation does not exist but translated object does echo '<span class="icl_msync_item icl_msync_add">' . esc_html( $item_translation['title'] ) . '</span>'; echo '<input type="hidden" name="sync[add][' . esc_attr( $menu_id ) . '][' . esc_attr( $item['ID'] ) . '][' . esc_attr( (string) $lang_code ) . ']" value="' . esc_attr( $item_translation['title'] ) . '" />'; $this->incOperation( 'add' ); } else { $need_sync --; } } elseif ( $item_translation && 'post_type_archive' === $item_translation['object_type'] ) { // item translation does not exist but is a post type archive item that will be created echo '<span class="icl_msync_item icl_msync_add">' . esc_html( $item_translation['title'] ) . ' @' . esc_html( (string) $lang_code ) . '</span>'; echo '<input type="hidden" name="sync[add][' . esc_attr( $menu_id ) . '][' . esc_attr( $item['ID'] ) . '][' . esc_attr( (string) $lang_code ) . ']" value="' . esc_attr( $item_translation['title'] . ' @' . $lang_code ) . '" />'; $this->incOperation( 'add' ); } else { // item translation and object translation do not exist echo '<i class="inactive">' . esc_html__( 'Not translated', 'sitepress' ) . '</i>'; $need_sync --; } ?> </td> <?php } ?> </tr> <?php if ( $this->_item_has_children( $menu_id, $item['ID'] ) ) { $need_sync += $this->render_items_tree_default( $menu_id, $item['ID'], $depth + 1 ); } } } if ( $depth == 0 ) { $this->render_option_update( $active_language_codes, $default_language, $menu_id, $need_sync ); } return $need_sync; } private function render_option_update( $active_language_codes, $default_language, $menu_id, &$need_sync ) { ?> <tr> <?php foreach ( $active_language_codes as $lang_code ) { ?> <td> <?php if ( $lang_code === $default_language ) { esc_html_e( 'Menu Option: auto_add', 'sitepress' ); continue; } $menu_options = $this->get_menu_options( $menu_id ); $translated_id = $this->get_translated_menu( $menu_id, $lang_code ); $change = false; if ( ! isset( $translated_id['id'] ) || $menu_options != $this->get_menu_options( $translated_id['id'] ) ) { $need_sync ++; $change = true; } if ( $change ) { $this->index_changed( 'options_changed', 'auto_add', $menu_options['auto_add'], $menu_id, $lang_code, $change ); } else { echo esc_html( $menu_options['auto_add'] ); } } ?> </td> <?php } private function render_deleted_items( $deleted_items, &$need_sync, $depth, $menu_id ) { global $sitepress; if ( $deleted_items ) { ?> <tr> <td> </td> <?php foreach ( $sitepress->get_active_languages() as $language ) : if ( $language['code'] === $sitepress->get_default_language() ) { continue; } ?> <td> <?php if ( isset( $deleted_items[ $language['code'] ] ) ) : ?> <?php $need_sync ++; ?> <?php echo str_repeat( ' - ', $depth ); ?><span class="icl_msync_item icl_msync_del"><?php echo esc_html( $deleted_items[ $language['code'] ]['title'] ); ?></span> <input type="hidden" name="sync[del][<?php echo esc_attr( $menu_id ); ?>][<?php echo esc_attr( $language['code'] ); ?>][<?php echo esc_attr( $deleted_items[ $language['code'] ]['ID'] ); ?>]" value="<?php echo esc_attr( $deleted_items[ $language['code'] ]['title'] ); ?>"/> <?php $this->operations['del'] = empty( $this->operations['del'] ) ? 1 : $this->operations['del'] ++; ?> <?php else : ?> <?php endif; ?> </td> <?php endforeach; ?> </tr> <?php } } private function index_changed( $index, $item_id, $item_translation, $menu_id, $lang_code, $change = true ) { $this->string_translation_links[ $this->menus[ $menu_id ]['name'] ] = 1; $additional_class = $change ? 'icl_msync_' . $index : ''; echo '<span class="icl_msync_item ' . esc_attr( $additional_class ) . '">' . ( ! $item_translation ? 0 : esc_html( $item_translation ) ) . '</span>' . '<input type="hidden" name="sync[' . esc_attr( $index ) . '][' . esc_attr( $menu_id ) . '][' . esc_attr( $item_id ) . '][' . esc_attr( $lang_code ) . ']" value="' . esc_attr( $item_translation ) . '" />'; if ( $change ) { $this->operations[ $index ] = empty( $this->operations[ $index ] ) ? 1 : $this->operations[ $index ] ++; } } function _item_has_children( $menu_id, $item_id ) { $has = false; foreach ( $this->menus[ $menu_id ]['items'] as $item ) { if ( $item['parent'] == $item_id ) { $has = true; } } return $has; } function get_item_depth( $menu_id, $item_id ) { $depth = 0; $parent = 0; do { foreach ( $this->menus[ $menu_id ]['items'] as $item ) { if ( $item['ID'] == $item_id ) { $parent = $item['parent']; if ( $parent > 0 ) { $depth++; $item_id = $parent; } else { break; } } } } while ( $parent > 0 ); return $depth; } function admin_notices() { echo '<div class="updated"><p>' . esc_html__( 'Menu(s) syncing complete.', 'sitepress' ) . '</p></div>'; } public function display_menu_links_to_string_translation() { $menu_links_data = $this->get_links_for_menu_strings_translation(); if ( count( $menu_links_data ) > 0 ) { echo '<p>'; esc_html_e( "Your menu includes custom items, which you need to translate using WPML's String Translation.", 'sitepress' ); echo '<br/>'; esc_html_e( '1. Translate these strings: ', 'sitepress' ); $i = 0; foreach ( $menu_links_data['items'] as $menu_name => $menu_url ) { if ( $i > 0 ) { echo ', '; } echo '<a href="' . esc_url( $menu_url ) . '">' . esc_html( $menu_name ) . '</a>' . PHP_EOL; $i ++; } echo '<br/>'; esc_html_e( "2. When you're done translating, return here and run the menu synchronization again. This will use the strings that you translated to update the menus.", 'sitepress' ); echo '</p>'; } } public function get_links_for_menu_strings_translation() { $menu_links = array(); $wpml_st_folder = $this->sitepress->get_wp_api()->constant( 'WPML_ST_FOLDER' ); if ( $wpml_st_folder ) { $wpml_st_contexts = icl_st_get_contexts( false ); $wpml_st_contexts = wp_list_pluck( $wpml_st_contexts, 'context' ); $menu_names = $this->get_menu_names(); foreach ( $menu_names as $k => $menu_name ) { if ( ! in_array( $menu_name . WPML_Menu_Sync_Functionality::STRING_CONTEXT_SUFFIX, $wpml_st_contexts, true ) ) { unset( $menu_names[ $k ] ); } } if ( ! empty( $menu_names ) ) { $menu_url_base = add_query_arg( 'page', urlencode( $wpml_st_folder . '/menu/string-translation.php' ), 'admin.php' ); foreach ( $menu_names as $menu_name ) { $menu_url = add_query_arg( 'context', urlencode( $menu_name . WPML_Menu_Sync_Functionality::STRING_CONTEXT_SUFFIX ), $menu_url_base ); $menu_links[ $menu_name ] = $menu_url; } } } $response = array(); if ( $menu_links ) { $response = array( 'label' => esc_html__( 'Translate menu strings and URLs for:', 'sitepress' ), 'items' => $menu_links, ); } return $response; } private function incOperation( $mode ) { $this->operations[ $mode ] = empty( $this->operations[ $mode ] ) ? 1 : $this->operations[ $mode ] ++; } } TranslateMenu.php 0000644 00000005021 14721623671 0010046 0 ustar 00 <?php namespace WPML\Core\Menu; use WPML\LIB\WP\Hooks; use WPML\LIB\WP\Post; use function WPML\FP\spreadArgs; class Translate implements \IWPML_Frontend_Action { public function add_hooks() { Hooks::onFilter( 'wp_get_nav_menu_items', 10, 2 ) ->then( spreadArgs( [ self::class, 'translate' ] ) ); } /** * @param array $items An array of menu item post objects. * @param \WP_Term $menu The menu object. * * @return array */ public static function translate( $items, $menu ) { if ( self::doesNotHaveMenuInCurrentLanguage( $menu ) ) { $items = wpml_collect( $items ) ->filter( [ self::class, 'hasTranslation' ] ) ->map( [ self::class, 'translateItem' ] ) ->filter( [ self::class, 'canView' ] ) ->values() ->toArray(); } return $items; } /** * @param \WP_Post $item Menu item - post object. * * @return bool */ public static function hasTranslation( $item ) { global $sitepress; return 'post_type' !== $item->type || (bool) self::getTranslatedId( $item ) || $sitepress->is_display_as_translated_post_type( $item->object ); } /** * @param \WP_Post $item Menu item - post object. * * @return \WP_Post */ public static function translateItem( $item ) { if ( 'post_type' === $item->type ) { $translatedId = self::getTranslatedId( $item, true ); $post = Post::get( $translatedId ); if ( ! $post instanceof \WP_Post ) { return $item; } foreach ( get_object_vars( $post ) as $key => $value ) { // We won't send the translated ID, since it affects front-end styles negatively. if ( ! in_array( $key, [ 'menu_order', 'post_type', 'ID' ] ) ) { $item->$key = $value; } } $item->object_id = (string) $translatedId; $item->title = $item->post_title; } return $item; } /** * @param \WP_Post $item Menu item - post object. * * @return bool */ public static function canView( $item ) { return current_user_can( 'administrator' ) || 'post_type' !== $item->type || 'draft' !== $item->post_status; } /** * @param \WP_Term $menu The menu object. * * @return bool */ private static function doesNotHaveMenuInCurrentLanguage( $menu ) { return ! wpml_object_id_filter( $menu->term_id, 'nav_menu' ); } /** * @param \WP_Post $item Menu item - post object. * @param bool $return_original_if_missing * @return int|null */ private static function getTranslatedId( $item, $return_original_if_missing = false ) { return wpml_object_id_filter( $item->object_id, $item->object, $return_original_if_missing ); } } menu-item-sync.class.php 0000644 00000035034 14721623671 0011251 0 ustar 00 <?php use WPML\FP\Fns; use WPML\FP\Lst; use WPML\FP\Maybe; use WPML\FP\Obj; use WPML\FP\Relation; use WPML\LIB\WP\Hooks; use function WPML\Container\make; use function WPML\FP\pipe; class WPML_Menu_Item_Sync extends WPML_Menu_Sync_Functionality { /** @var array $labels_to_add */ private $labels_to_add = array(); /** @var array $urls_to_add */ private $urls_to_add = array(); /** * @var string */ const MENU_ITEM_POST_TYPE = 'post_nav_menu_item'; /** * @return int the number of removed broken page items */ function cleanup_broken_page_items() { return $this->wpdb->query( " DELETE o FROM {$this->wpdb->term_relationships} o JOIN {$this->wpdb->postmeta} pm ON pm.post_id = o.object_id JOIN {$this->wpdb->posts} p ON p.ID = pm.post_id JOIN {$this->wpdb->postmeta} pm_type ON pm_type.post_id = pm.post_id WHERE p.post_type = 'nav_menu_item' AND pm.meta_key = '_menu_item_object_id' AND pm_type.meta_key = '_menu_item_type' AND pm_type.meta_value = 'post_type' AND pm.meta_value = 0" ); } function sync_deleted_menus( $deleted_data ) { foreach ( $deleted_data as $languages ) { foreach ( $languages as $items ) { foreach ( $items as $item_id => $name ) { wp_delete_post( $item_id, true ); $delete_trid = $this->post_translations->get_element_trid( $item_id ); if ( $delete_trid ) { $this->sitepress->delete_element_translation( $delete_trid, 'post_nav_menu_item' ); } } } } } function sync_menu_options( $options_data ) { foreach ( $options_data as $menu_id => $translations ) { foreach ( $translations as $language => $option ) { $translated_menu_id = $this->term_translations->term_id_in( $menu_id, $language ); if ( isset( $option['auto_add'] ) ) { $nav_menu_option = (array) get_option( 'nav_menu_options' ); $nav_menu_option['auto_add'] = isset( $nav_menu_option['auto_add'] ) ? $nav_menu_option['auto_add'] : array(); if ( $option['auto_add'] && ! in_array( $translated_menu_id, $nav_menu_option['auto_add'] ) ) { $nav_menu_option['auto_add'][] = $translated_menu_id; } elseif ( ! $option['auto_add'] && false !== ( $key = array_search( $translated_menu_id, $nav_menu_option['auto_add'] ) ) ) { unset( $nav_menu_option['auto_add'][ $key ] ); } /** * We need to disable Sitepress::get_term_adjust_id hook to avoid overriding menu_ids * present in $nav_menu_option['auto_add'] by their original menu_ids. */ $filterUnExistingMenuIds = function () use ( $nav_menu_option ) { return array_intersect( $nav_menu_option['auto_add'], wp_get_nav_menus( [ 'fields' => 'ids' ] ) ); }; $disableAdjustTermIds = Fns::always( true ); $nav_menu_option['auto_add'] = Hooks::callWithFilter( $filterUnExistingMenuIds, 'wpml_disable_term_adjust_id', $disableAdjustTermIds ); update_option( 'nav_menu_options', array_filter( $nav_menu_option ) ); wp_defer_term_counting( false ); do_action( 'wp_update_nav_menu', $translated_menu_id ); } } } } public function sync_menu_order( array $menus ) { global $wpdb; foreach ( $menus as $menu_id => $menu ) { $menu_index_by_lang = array(); foreach ( $menu['items'] as $item_id => $item ) { $valid_translations = array_filter( $item['translations'], function ( $item ) { return $item && $item['ID']; } ); foreach ( $valid_translations as $language => $item_translation ) { $new_menu_order = empty( $menu_index_by_lang[ $language ] ) ? 1 : $menu_index_by_lang[ $language ] + 1; $menu_index_by_lang[ $language ] = $new_menu_order; if ( $new_menu_order != $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['menu_order'] ) { $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['menu_order'] = $new_menu_order; $wpdb->update( $wpdb->posts, [ 'menu_order' => $new_menu_order ], [ 'ID' => $item_translation['ID'] ] ); } } } } return $menus; } function sync_added_items( array $added_data, array $menus ) { global $wpdb; foreach ( $added_data as $menu_id => $items ) { foreach ( $items as $language => $translations ) { foreach ( $translations as $item_id => $name ) { $trid = $this->get_or_set_trid( $item_id, $this->sitepress->get_default_language() ); $translated_object = $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]; $menu_name = $this->get_menu_name( $menu_id ); $object_type = $translated_object['object_type']; $object_title = $translated_object['title']; $object_url = $translated_object['url']; $icl_st_label_exists = false; $icl_st_url_exists = false; if ( $object_type === 'custom' && function_exists( 'icl_t' ) ) { $item = new stdClass(); $item->url = $object_url; $item->ID = $item_id; $item->post_title = $object_title; list( $object_title, $object_url ) = $this->icl_t_menu_item( $menu_name, $item, $language, $icl_st_label_exists, $icl_st_url_exists ); } $menu_data = array( 'menu-item-db-id' => 0, 'menu-item-object-id' => $translated_object['object_id'], 'menu-item-object' => $translated_object['object'], 'menu-item-parent-id' => 0, 'menu-item-position' => 0, 'menu-item-type' => $object_type, 'menu-item-title' => $object_title, 'menu-item-url' => $object_url, 'menu-item-description' => '', 'menu-item-attr-title' => $translated_object['attr-title'], 'menu-item-target' => $translated_object['target'], 'menu-item-classes' => ( $translated_object['classes'] ? implode( ' ', $translated_object['classes'] ) : '' ), 'menu-item-xfn' => $translated_object['xfn'], 'menu-item-status' => 'publish', ); $translated_menu_id = $menus[ $menu_id ]['translations'][ $language ]['id']; remove_filter( 'get_term', array( $this->sitepress, 'get_term_adjust_id' ), 1 ); $translated_item_id = wp_update_nav_menu_item( $translated_menu_id, 0, $menu_data ); // set language explicitly since the 'wp_update_nav_menu_item' is still TBD $this->sitepress->set_element_language_details( $translated_item_id, 'post_nav_menu_item', $trid, $language ); $menu_tax_id_prepared = $wpdb->prepare( "SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE term_id=%d AND taxonomy='nav_menu' LIMIT 1", $translated_menu_id ); $menu_tax_id = $wpdb->get_var( $menu_tax_id_prepared ); if ( $translated_item_id && $menu_tax_id ) { $rel_prepared = $wpdb->prepare( "SELECT object_id FROM {$wpdb->term_relationships} WHERE object_id=%d AND term_taxonomy_id=%d LIMIT 1", $translated_item_id, $menu_tax_id ); $rel = $wpdb->get_var( $rel_prepared ); if ( ! $rel ) { $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $translated_item_id, 'term_taxonomy_id' => $menu_tax_id, ) ); } } $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['ID'] = $translated_item_id; } } } $this->fix_hierarchy_added_items( $added_data ); return $menus; } function sync_moved_items( array $moved_data, array $menus ) { global $wpdb; foreach ( $moved_data as $menu_id => $items ) { foreach ( $items as $language => $changes ) { foreach ( $changes as $item_id => $details ) { $trid = $this->get_or_set_trid( $item_id, $this->sitepress->get_default_language() ); $translated_item_id = $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['ID']; $new_menu_order = key( $details ); $menus[ $menu_id ]['items'][ $item_id ]['translations'][ $language ]['menu_order'] = $new_menu_order; $wpdb->update( $wpdb->posts, array( 'menu_order' => $new_menu_order ), array( 'ID' => $translated_item_id ) ); if ( $this->post_translations->get_element_trid( $translated_item_id ) != $trid ) { $this->sitepress->set_element_language_details( $translated_item_id, 'post_nav_menu_item', $trid, $language ); } $translated_menu_id = $menus[ $menu_id ]['translations'][ $language ]['id']; $this->assign_orphan_item_to_menu( $translated_item_id, $translated_menu_id, $language ); } } } $this->fix_hierarchy_moved_items( $moved_data ); return $menus; } /** * @param int $item_id * @param int $menu_id */ private function assign_orphan_item_to_menu( $item_id, $menu_id, $language ) { $this->sitepress->switch_lang( $language ); if ( ! wp_get_object_terms( $item_id, 'nav_menu' ) ) { wp_set_object_terms( $item_id, array( $menu_id ), 'nav_menu' ); } $this->sitepress->switch_lang(); } function sync_caption( $label_change_data ) { foreach ( $label_change_data as $languages ) { foreach ( $languages as $language => $items ) { foreach ( $items as $item_id => $name ) { $trid = $this->sitepress->get_element_trid( $item_id, 'post_nav_menu_item' ); if ( $trid ) { $item_translations = $this->sitepress->get_element_translations( $trid, 'post_nav_menu_item', true ); if ( isset( $item_translations[ $language ] ) ) { $translated_item = get_post( $item_translations[ $language ]->element_id ); if ( $translated_item && $translated_item->post_title != $name ) { $translated_item->post_title = $name; /** @phpstan-ignore-next-line WP doc issue. */ wp_update_post( $translated_item ); } } } } } } } function sync_urls( $url_change_data ) { foreach ( $url_change_data as $languages ) { foreach ( $languages as $language => $items ) { foreach ( $items as $item_id => $url ) { $trid = $this->sitepress->get_element_trid( $item_id, 'post_nav_menu_item' ); if ( $trid ) { $item_translations = $this->sitepress->get_element_translations( $trid, 'post_nav_menu_item', true ); if ( isset( $item_translations[ $language ] ) ) { $translated_item_id = $item_translations[ $language ]->element_id; if ( $url ) { update_post_meta( $translated_item_id, '_menu_item_url', $url ); } } } } } } } function sync_missing_captions( $label_missing ) { foreach ( $label_missing as $menu_id => $languages ) { foreach ( $languages as $items ) { foreach ( $items as $item_id => $name ) { if ( ! in_array( $menu_id . '-' . $item_id, $this->labels_to_add ) ) { $item = get_post( $item_id ); icl_register_string( $this->get_menu_name( $menu_id ) . WPML_Menu_Sync_Functionality::STRING_CONTEXT_SUFFIX, WPML_Menu_Sync_Functionality::STRING_NAME_LABEL_PREFIX . $item_id, $item->post_title ); $this->labels_to_add[] = $menu_id . '-' . $item_id; } } } } } function sync_urls_to_add( $url_missing_data ) { foreach ( $url_missing_data as $menu_id => $languages ) { foreach ( $languages as $items ) { foreach ( $items as $item_id => $url ) { if ( ! in_array( $menu_id . '-' . $item_id, $this->urls_to_add ) ) { icl_register_string( $this->get_menu_name( $menu_id ) . WPML_Menu_Sync_Functionality::STRING_CONTEXT_SUFFIX, WPML_Menu_Sync_Functionality::STRING_NAME_URL_PREFIX . $item_id, $url ); $this->urls_to_add[] = $menu_id . '-' . $item_id; } } } } } /** * @param array $menus Registered menus. */ public function sync_custom_fields( $menus ) { $syncMenuItem = function ( $menuItemId ) { $this->sync_custom_fields_set_to_copy( $menuItemId ); $this->sync_custom_fields_set_to_copy_once( $menuItemId ); }; $syncMenu = pipe( Obj::prop( 'items' ), Obj::keys(), Fns::each( $syncMenuItem ) ); Fns::each( $syncMenu, $menus ); } /** * @param int $menuItemId */ private function sync_custom_fields_set_to_copy( $menuItemId ) { $copy = new WPML_Sync_Custom_Fields( new WPML_Translation_Element_Factory( $this->sitepress ), $this->sitepress->get_custom_fields_translation_settings( WPML_COPY_CUSTOM_FIELD ) ); $copy->sync_all_custom_fields( $menuItemId ); } /** * @param int $menuItemId */ private function sync_custom_fields_set_to_copy_once( $menuItemId ) { $getItemTranslations = function( $menuItemId ) { return $this->sitepress->get_element_translations( $this->sitepress->get_element_trid( $menuItemId, self::MENU_ITEM_POST_TYPE ), self::MENU_ITEM_POST_TYPE ); }; Maybe::of( $menuItemId ) ->map( $getItemTranslations ) ->map( Lst::pluck( 'element_id' ) ) ->map( Fns::reject( Relation::equals( $menuItemId ) ) ) ->map( Fns::map( [ make( WPML_Copy_Once_Custom_Field::class ), 'copy' ] ) ); } private function fix_hierarchy_added_items( $added_data ) { foreach ( $added_data as $menu_id => $items ) { foreach ( $items as $language => $translations ) { foreach ( $translations as $item_id => $name ) { $this->fix_hierarchy_for_item( $item_id, $language ); } } } } private function fix_hierarchy_moved_items( $moved_data ) { foreach ( $moved_data as $menu_id => $items ) { foreach ( $items as $language => $changes ) { foreach ( $changes as $item_id => $details ) { $this->fix_hierarchy_for_item( $item_id, $language ); } } } } private function fix_hierarchy_for_item( $item_id, $language ) { $parent_item = get_post_meta( $item_id, '_menu_item_menu_item_parent', true ); $translated_item_id = $this->post_translations->element_id_in( $item_id, $language ); $translated_parent_menu_item_id = $this->post_translations->element_id_in( $parent_item, $language ); $translated_parent_menu_item_id = $translated_parent_menu_item_id == $translated_item_id ? false : $translated_parent_menu_item_id; update_post_meta( $translated_item_id, '_menu_item_menu_item_parent', $translated_parent_menu_item_id ); } private function get_or_set_trid( $item_id, $language_code ) { $trid = $this->post_translations->get_element_trid( $item_id ); if ( ! $trid ) { $this->sitepress->set_element_language_details( $item_id, 'post_nav_menu_item', false, $language_code ); $trid = $this->post_translations->get_element_trid( $item_id ); } return $trid; } } wpml-menu-sync-functionality.class.php 0000644 00000046451 14721623671 0014165 0 ustar 00 <?php use WPML\FP\Lst; abstract class WPML_Menu_Sync_Functionality extends WPML_Full_Translation_API { const STRING_CONTEXT_SUFFIX = ' menu'; const STRING_NAME_LABEL_PREFIX = 'Menu Item Label '; const STRING_NAME_URL_PREFIX = 'Menu Item URL '; private $menu_items_cache; /** * @param SitePress $sitepress * @param wpdb $wpdb * @param WPML_Post_Translation $post_translations * @param WPML_Terms_Translations $term_translations */ function __construct( &$sitepress, &$wpdb, &$post_translations, &$term_translations ) { parent::__construct( $sitepress, $wpdb, $post_translations, $term_translations ); $this->menu_items_cache = array(); } function get_menu_items( $menu_id, $translations = true ) { $key = $menu_id . '-'; if ( $translations ) { $key .= 'trans'; } else { $key .= 'no-trans'; } if ( ! isset( $this->menu_items_cache[ $key ] ) ) { if ( ! isset( $this->menu_items_cache[ $menu_id ] ) ) { $this->menu_items_cache[ $menu_id ] = wp_get_nav_menu_items( (int) $menu_id ); } $items = $this->menu_items_cache[ $menu_id ]; $menu_items = array(); foreach ( $items as $item ) { $item->object_type = get_post_meta( $item->ID, '_menu_item_type', true ); $_item_add = array( 'ID' => $item->ID, 'menu_order' => $item->menu_order, 'parent' => $item->menu_item_parent, 'object' => $item->object, 'url' => $item->url, 'object_type' => $item->object_type, 'object_id' => empty( $item->object_id ) ? get_post_meta( $item->ID, '_menu_item_object_id', true ) : $item->object_id, 'title' => $item->title, 'depth' => $this->get_menu_item_depth( $item->ID ), ); if ( $translations ) { $_item_add['translations'] = $this->get_menu_item_translations( $item, $menu_id ); } $menu_items[ $item->ID ] = $_item_add; } $this->menu_items_cache[ $key ] = $menu_items; } return $this->menu_items_cache[ $key ]; } function sync_menu_translations( $menu_trans_data, $menus ) { global $wpdb; foreach ( $menu_trans_data as $menu_id => $translations ) { foreach ( $translations as $language => $name ) { $_POST['icl_translation_of'] = $wpdb->get_var( $wpdb->prepare( " SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE term_id=%d AND taxonomy='nav_menu' LIMIT 1", $menu_id ) ); $_POST['icl_nav_menu_language'] = $language; $menu_indentation = ''; $menu_increment = 0; do { $new_menu_id = wp_update_nav_menu_object( 0, array( 'menu-name' => $name . $menu_indentation . ( $menu_increment ? $menu_increment : '' ), ) ); $menu_increment = $menu_increment != '' ? $menu_increment + 1 : 2; $menu_indentation = '-'; } while ( is_wp_error( $new_menu_id ) && $menu_increment < 10 ); $menus[ $menu_id ]['translations'][ $language ] = array( 'id' => $new_menu_id ); } } return $menus; } /** * @param \stdClass $item * @param int $menu_id * * @return array */ function get_menu_item_translations( $item, $menu_id ) { $languages = array_keys( $this->sitepress->get_active_languages() ); $item_translations = $this->post_translations->get_element_translations( $item->ID ); $languages = array_diff( $languages, array( $this->sitepress->get_default_language() ) ); $translations = array_fill_keys( $languages, false ); foreach ( $languages as $lang_code ) { $item->object_type = property_exists( $item, 'object_type' ) ? $item->object_type : $item->type; $translated_object_id = (int) icl_object_id( $item->object_type === 'post_type_archive' ? $item->ID : $item->object_id, Lst::includes( $item->object_type, [ 'custom', 'post_type_archive' ] ) ? 'nav_menu_item' : $item->object, false, $lang_code ); if ( ! $translated_object_id && $item->object_type !== 'custom' && $item->object_type !== 'post_type_archive' ) { continue; } $translated_object_title = ''; $translated_object_url = $item->url; $icl_st_label_exists = true; $icl_st_url_exists = true; $label_changed = false; $url_changed = false; if ( $item->object_type === 'post_type' ) { list( $translated_object_id, $item_translations ) = $this->maybe_reload_post_item( $translated_object_id, $item_translations, $item, $lang_code ); $translated_object = get_post( $translated_object_id ); if ( $translated_object->post_status === 'trash' ) { $translated_object_id = false; } else { $translated_object_title = $translated_object->post_title; } } elseif ( $item->object_type === 'taxonomy' ) { $translated_object = get_term( $translated_object_id, get_post_meta( $item->ID, '_menu_item_object', true ) ); $translated_object_title = $translated_object->name; } elseif ( $item->object_type === 'custom' ) { $translated_object_title = $item->post_title; if ( defined( 'WPML_ST_PATH' ) ) { list( $translated_object_url, $translated_object_title, $url_changed, $label_changed ) = $this->st_actions( $lang_code, $menu_id, $item, $translated_object_id, $translated_object_title, $translated_object_url, $icl_st_label_exists, $icl_st_url_exists ); } } elseif ( $item->object_type === 'post_type_archive' ) { if ( $translated_object_id ) { $translated_object = get_post( $translated_object_id ); $translated_object_title = $translated_object->post_title; } else { $translated_object_title = $item->post_title; } } $this->fix_assignment_to_menu( $item_translations, (int) $menu_id ); $this->fix_language_conflicts(); $translated_item_id = isset( $item_translations[ $lang_code ] ) ? (int) $item_translations[ $lang_code ] : false; $item_depth = $this->get_menu_item_depth( $translated_item_id ); if ( $translated_item_id ) { $translated_item = get_post( $translated_item_id ); $translated_object_title = ! empty( $translated_item->post_title ) && ! $icl_st_label_exists ? $translated_item->post_title : $translated_object_title; $translate_item_parent_item_id = (int) get_post_meta( $translated_item_id, '_menu_item_menu_item_parent', true ); if ( $item->menu_item_parent > 0 && $translate_item_parent_item_id != $this->post_translations->element_id_in( $item->menu_item_parent, $lang_code ) ) { $translate_item_parent_item_id = 0; $item_depth = 0; } $translation = array( 'menu_order' => $translated_item->menu_order, 'parent' => $translate_item_parent_item_id, ); } else { $translation = array( 'menu_order' => ( $item->object_type === 'custom' ? $item->menu_order : 0 ), 'parent' => 0, ); } $translation['ID'] = $translated_item_id; $translation['depth'] = $item_depth; $translation['parent_not_translated'] = $this->is_parent_not_translated( $item, $lang_code ); $translation['object'] = $item->object; $translation['object_type'] = $item->object_type; $translation['object_id'] = $translated_object_id; $translation['title'] = $translated_object_title; $translation['url'] = $translated_object_url; $translation['target'] = $item->target; $translation['classes'] = $item->classes; $translation['xfn'] = $item->xfn; $translation['attr-title'] = $item->attr_title; $translation['label_changed'] = $label_changed; $translation['url_changed'] = $url_changed; $translation['label_missing'] = ! $icl_st_label_exists; $translation['url_missing'] = ! $icl_st_url_exists; $translations[ $lang_code ] = $translation; } return $translations; } /** * Synchronises a page menu item's translations' trids according to the trids of the pages they link to. * * @param object $menu_item * * @return int number of affected menu item translations */ function sync_page_menu_item_trids( $menu_item ) { $changed = 0; if ( $menu_item->object_type === 'post_type' ) { $translations = $this->post_translations->get_element_translations( $menu_item->ID ); if ( (bool) $translations === true ) { get_post_meta( $menu_item->menu_item_parent, '_menu_item_object_id', true ); $orphans = $this->wpdb->get_results( $this->get_page_orphan_sql( array_keys( $translations ), $menu_item->ID ) ); if ( (bool) $orphans === true ) { $trid = $this->post_translations->get_element_trid( $menu_item->ID ); foreach ( $orphans as $orphan ) { $this->sitepress->set_element_language_details( $orphan->element_id, 'post_nav_menu_item', $trid, $orphan->language_code ); $changed ++; } } } } return $changed; } /** * @param int $menu_id * @param bool $include_original * * @return bool|array */ function get_menu_translations( $menu_id, $include_original = false ) { $languages = array_keys( $this->sitepress->get_active_languages() ); $translations = array(); foreach ( $languages as $lang_code ) { if ( $include_original || $lang_code !== $this->sitepress->get_default_language() ) { $menu_translated_id = $this->term_translations->term_id_in( $menu_id, $lang_code ); $menu_data = array(); if ( $menu_translated_id ) { /** @var \stdClass $menu_object */ $menu_object = $this->wpdb->get_row( $this->wpdb->prepare( " SELECT t.term_id, t.name FROM {$this->wpdb->terms} t JOIN {$this->wpdb->term_taxonomy} x ON t.term_id = t.term_id WHERE t.term_id = %d AND x.taxonomy='nav_menu' LIMIT 1", $menu_translated_id ) ); $current_lang = $this->sitepress->get_current_language(); $this->sitepress->switch_lang( $lang_code, false ); $menu_data = array( 'id' => $menu_object->term_id, 'name' => $menu_object->name, 'items' => $this->get_menu_items( $menu_translated_id, false ), ); $this->sitepress->switch_lang( $current_lang, false ); } $translations[ $lang_code ] = $menu_data; } } return $translations; } protected function get_menu_name( $menu_id ) { $menu = wp_get_nav_menu_object( $menu_id ); return $menu ? $menu->name : false; } /** * @param int $menu_id * @param string|false $language_code * * @return bool */ protected function get_translated_menu( $menu_id, $language_code = false ) { $language_code = $language_code ? $language_code : $this->sitepress->get_default_language(); $menus = $this->get_menu_translations( $menu_id, true ); return isset( $menus[ $language_code ] ) ? $menus[ $language_code ] : false; } /** * We need to register the string first in the default language * to avoid it being "auto-registered" in English * * @param string $menu_name * @param WP_Post|stdClass $item * @param string $lang * @param bool $has_label_translation * @param bool $has_url_translation * * @return array */ protected function icl_t_menu_item( $menu_name, $item, $lang, &$has_label_translation, &$has_url_translation ) { $default_lang = $this->sitepress->get_default_language(); $label = $item->post_title; $url = $item->url; if ( $lang !== $default_lang ) { icl_register_string( $menu_name . self::STRING_CONTEXT_SUFFIX, self::STRING_NAME_LABEL_PREFIX . $item->ID, $label, false, $default_lang ); $label = icl_t( $menu_name . self::STRING_CONTEXT_SUFFIX, self::STRING_NAME_LABEL_PREFIX . $item->ID, $label, $has_label_translation, true, $lang ); icl_register_string( $menu_name . self::STRING_CONTEXT_SUFFIX, self::STRING_NAME_URL_PREFIX . $item->ID, $url, false, $default_lang ); $url = icl_t( $menu_name . self::STRING_CONTEXT_SUFFIX, self::STRING_NAME_URL_PREFIX . $item->ID, $url, $has_url_translation, true, $lang ); } return array( $label, $url ); } /** * @param object $item * @param string $lang_code * * @return int */ private function is_parent_not_translated( $item, $lang_code ) { if ( $item->menu_item_parent > 0 ) { $item_parent_object_id = get_post_meta( $item->menu_item_parent, '_menu_item_object_id', true ); $item_parent_object = get_post_meta( $item->menu_item_parent, '_menu_item_object', true ); $parent_element_type = $item_parent_object === 'custom' ? 'nav_menu_item' : $item_parent_object; $parent_translated = icl_object_id( $item_parent_object_id, $parent_element_type, false, $lang_code ); } return isset( $parent_translated ) && ! $parent_translated ? 1 : 0; } private function get_page_orphan_sql( $existing_languages, $menu_item_id ) { $wpdb = &$this->wpdb; return $wpdb->prepare( "SELECT it.element_id, it.language_code FROM {$wpdb->prefix}icl_translations it JOIN {$wpdb->posts} pt ON pt.ID = it.element_id AND pt.post_type = 'nav_menu_item' AND it.element_type = 'post_nav_menu_item' AND it.language_code NOT IN (" . wpml_prepare_in( $existing_languages ) . ") JOIN {$wpdb->prefix}icl_translations io ON io.element_id = %d AND io.element_type = 'post_nav_menu_item' AND io.trid != it.trid JOIN {$wpdb->posts} po ON po.ID = io.element_id AND po.post_type = 'nav_menu_item' JOIN {$wpdb->postmeta} mo ON mo.post_id = po.ID AND mo.meta_key = '_menu_item_object_id' JOIN {$wpdb->postmeta} mt ON mt.post_id = pt.ID AND mt.meta_key = '_menu_item_object_id' JOIN {$wpdb->prefix}icl_translations page_t ON mt.meta_value = page_t.element_id AND page_t.element_type = 'post_page' JOIN {$wpdb->prefix}icl_translations page_o ON mo.meta_value = page_o.element_id AND page_o.trid = page_t.trid WHERE ( SELECT COUNT(count.element_id) FROM {$wpdb->prefix}icl_translations count WHERE count.trid = it.trid ) = 1", $menu_item_id ); } private function maybe_reload_post_item( $translated_object_id, $item_translations, $item, $lang_code ) { if ( $this->sync_page_menu_item_trids( $item ) > 0 ) { $item_translations = $this->post_translations->get_element_translations( $item->ID ); $translated_object_id = $this->post_translations->element_id_in( $item->object_id, $lang_code ); $translated_object_id = $translated_object_id === null ? false : $translated_object_id; } return array( $translated_object_id, $item_translations ); } private function get_menu_item_depth( $item_id ) { $depth = 0; do { $object_parent = get_post_meta( $item_id, '_menu_item_menu_item_parent', true ); if ( $object_parent == $item_id ) { $depth = 0; break; } elseif ( $object_parent ) { $item_id = $object_parent; $depth ++; } } while ( $object_parent > 0 ); return $depth; } private function st_actions( $lang_code, $menu_id, $item, $translated_object_id, $translated_object_title, $translated_object_url, &$icl_st_label_exists, &$icl_st_url_exists ) { if ( ! function_exists( 'icl_translate' ) ) { require WPML_ST_PATH . '/inc/functions.php'; } $this->sitepress->switch_lang( $lang_code ); $label_changed = false; $url_changed = false; $menu_name = $this->get_menu_name( $menu_id ); $translated_object_title_t = ''; $translated_object_url_t = ''; $translated_menu_id = $this->term_translations->term_id_in( $menu_id, $lang_code ); if ( function_exists( 'icl_t' ) ) { list( $translated_object_title_t, $translated_object_url_t ) = $this->icl_t_menu_item( $menu_name, $item, $lang_code, $icl_st_label_exists, $icl_st_url_exists ); } else { $translated_object_title_t = $item->post_title . ' @' . $lang_code; $translated_object_url_t = $item->url; } $this->sitepress->switch_lang(); if ( $translated_object_id ) { $translated_object = get_post( $translated_object_id ); $label_changed = $translated_object_title_t != $translated_object->post_title; $url_changed = $translated_object_url_t != get_post_meta( $translated_object_id, '_menu_item_url', true ); $translated_object_title = $icl_st_label_exists ? $translated_object_title_t : $translated_object_title; $translated_object_url = $icl_st_url_exists ? $translated_object_url_t : $translated_object_url; } return array( $translated_object_url, $translated_object_title, $url_changed, $label_changed, ); } /** * @param array<string,int> $item_translations * @param int $menu_id */ private function fix_assignment_to_menu( $item_translations, $menu_id ) { foreach ( $item_translations as $lang_code => $item_id ) { $correct_menu_id = $this->term_translations->term_id_in( $menu_id, $lang_code ); if ( $correct_menu_id ) { $ttid_trans = $this->wpdb->get_var( $this->wpdb->prepare( " SELECT tt.term_taxonomy_id FROM {$this->wpdb->term_taxonomy} tt LEFT JOIN {$this->wpdb->term_relationships} tr ON tt.term_taxonomy_id = tr.term_taxonomy_id AND tr.object_id = %d WHERE tt.taxonomy = 'nav_menu' AND tt.term_id = %d AND tr.term_taxonomy_id IS NULL LIMIT 1", $item_id, $correct_menu_id ) ); if ( $ttid_trans ) { $this->wpdb->insert( $this->wpdb->term_relationships, array( 'object_id' => $item_id, 'term_taxonomy_id' => $ttid_trans, ) ); } } } } /** * Removes potentially mis-assigned menu items from their menu, whose language differs from that of their * associated menu. */ private function fix_language_conflicts() { $wrong_items = $this->wpdb->get_results( " SELECT r.object_id, t.term_taxonomy_id FROM {$this->wpdb->term_relationships} r JOIN {$this->wpdb->prefix}icl_translations ip JOIN {$this->wpdb->posts} p ON ip.element_type = CONCAT('post_', p.post_type) AND ip.element_id = p.ID AND ip.element_id = r.object_id JOIN {$this->wpdb->prefix}icl_translations it JOIN {$this->wpdb->term_taxonomy} t ON it.element_type = CONCAT('tax_', t.taxonomy) AND it.element_id = t.term_taxonomy_id AND it.element_id = r.term_taxonomy_id WHERE p.post_type = 'nav_menu_item' AND t.taxonomy = 'nav_menu' AND ip.language_code != it.language_code" ); foreach ( $wrong_items as $item ) { $this->wpdb->delete( $this->wpdb->term_relationships, array( 'object_id' => $item->object_id, 'term_taxonomy_id' => $item->term_taxonomy_id, ) ); } } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.3-4ubuntu2.24 | Генерация страницы: 0.01 |
proxy
|
phpinfo
|
Настройка