first commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<?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 object $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 object $item Menu item - post object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasTranslation( $item ) {
|
||||
return $item->type !== 'post_type' || (bool) self::getTranslatedId( $item );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $item Menu item - post object
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function translateItem( $item ) {
|
||||
if ( $item->type === 'post_type' ) {
|
||||
$translatedId = self::getTranslatedId( $item );
|
||||
foreach ( get_object_vars( Post::get( $translatedId ) ) 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 object $item Menu item - post object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function canView ( $item ) {
|
||||
return current_user_can( 'administrator' ) || $item->type !== 'post_type' || $item->post_status !== 'draft';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $menu The menu object.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function doesNotHaveMenuInCurrentLanguage( $menu ) {
|
||||
return ! wpml_object_id_filter( $menu->term_id, 'nav_menu' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $item Menu item - post object
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private static function getTranslatedId( $item ) {
|
||||
return wpml_object_id_filter( $item->object_id, $item->object );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,843 @@
|
||||
<?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' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, '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;
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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( $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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
<?php
|
||||
|
||||
use WPML\FP\Fns;
|
||||
use WPML\FP\Lst;
|
||||
use WPML\FP\Maybe;
|
||||
use WPML\FP\Obj;
|
||||
use WPML\FP\Relation;
|
||||
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 ] );
|
||||
}
|
||||
$nav_menu_option['auto_add'] = array_intersect(
|
||||
$nav_menu_option['auto_add'],
|
||||
wp_get_nav_menus( array( 'fields' => 'ids' ) )
|
||||
);
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
$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 ) {
|
||||
if ( ! wp_get_object_terms( $item_id, 'nav_menu' ) ) {
|
||||
wp_set_object_terms( $item_id, array( $menu_id ), 'nav_menu' );
|
||||
}
|
||||
}
|
||||
|
||||
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->post_title != $name ) {
|
||||
$translated_item->post_title = $name;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,639 @@
|
||||
<?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( $menu_id ) . '][' . esc_attr( $item['ID'] ) . '][' . esc_attr( $lang_code ) . '][' . esc_attr( $item_translation['menu_order_new'] ) . ']" value="' . esc_attr( $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( $lang_code ) . '</span>';
|
||||
echo '<input type="hidden" name="sync[add][' . esc_attr( $menu_id ) . '][' . esc_attr( $item['ID'] ) . '][' . esc_attr( $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, $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( $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( $lang_code ) . '</span>';
|
||||
echo '<input type="hidden" name="sync[add][' . esc_attr( $menu_id ) . '][' . esc_attr( $item['ID'] ) . '][' . esc_attr( $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 ] ++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
<?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,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user