406 lines
12 KiB
JavaScript
406 lines
12 KiB
JavaScript
/******/ "use strict";
|
|
|
|
;// ./js/src/lib/confirmation-modal.js
|
|
/**
|
|
* @package Polylang
|
|
*/
|
|
|
|
const languagesList = jQuery( '.post_lang_choice' );
|
|
|
|
// Dialog box for alerting the user about a risky changing.
|
|
const initializeConfirmationModal = () => {
|
|
// We can't use underscore or lodash in this common code because it depends of the context classic or block editor.
|
|
// Classic editor underscore is loaded, Block editor lodash is loaded.
|
|
const { __ } = wp.i18n;
|
|
|
|
// Create dialog container.
|
|
const dialogContainer = jQuery(
|
|
'<div/>',
|
|
{
|
|
id: 'pll-dialog',
|
|
style: 'display:none;'
|
|
}
|
|
).text( __( 'Are you sure you want to change the language of the current content?', 'polylang' ) );
|
|
|
|
// Put it after languages list dropdown.
|
|
// PHPCS ignore dialogContainer is a new safe HTML code generated above.
|
|
languagesList.after( dialogContainer ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
|
|
|
|
const dialogResult = new Promise(
|
|
( confirm, cancel ) => {
|
|
const confirmDialog = ( what ) => { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
|
|
switch ( what ) { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
|
|
case 'yes':
|
|
// Confirm the new language.
|
|
languagesList.data( 'old-value', languagesList.children( ':selected' ).first().val() );
|
|
confirm();
|
|
break;
|
|
case 'no':
|
|
// Revert to the old language.
|
|
languagesList.val( languagesList.data( 'old-value' ) );
|
|
cancel( 'Cancel' );
|
|
break;
|
|
}
|
|
dialogContainer.dialog( 'close' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
|
|
} // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
|
|
|
|
// Initialize dialog box in the case a language is selected but not added in the list.
|
|
const dialogOptions = {
|
|
autoOpen: false,
|
|
modal: true,
|
|
draggable: false,
|
|
resizable: false,
|
|
title: __( 'Change language', 'polylang' ),
|
|
minWidth: 600,
|
|
maxWidth: '100%',
|
|
open: function ( event, ui ) {
|
|
// Change dialog box position for rtl language
|
|
if ( jQuery( 'body' ).hasClass( 'rtl' ) ) {
|
|
jQuery( this ).parent().css(
|
|
{
|
|
right: jQuery( this ).parent().css( 'left' ),
|
|
left: 'auto'
|
|
}
|
|
);
|
|
}
|
|
},
|
|
close: function ( event, ui ) {
|
|
// When we're closing the dialog box we need to cancel the language change as we click on Cancel button.
|
|
confirmDialog( 'no' );
|
|
},
|
|
buttons: [
|
|
{
|
|
text: __( 'OK', 'polylang' ),
|
|
click: function ( event ) {
|
|
confirmDialog( 'yes' );
|
|
}
|
|
},
|
|
{
|
|
text: __( 'Cancel', 'polylang' ),
|
|
click: function ( event ) {
|
|
confirmDialog( 'no' );
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
if ( jQuery.ui.version >= '1.12.0' ) {
|
|
Object.assign( dialogOptions, { classes: { 'ui-dialog': 'pll-confirmation-modal' } } );
|
|
} else {
|
|
Object.assign( dialogOptions, { dialogClass: 'pll-confirmation-modal' } ); // jQuery UI 1.11.4 - WP < 5.6
|
|
}
|
|
|
|
dialogContainer.dialog( dialogOptions );
|
|
}
|
|
);
|
|
return { dialogContainer, dialogResult };
|
|
}
|
|
|
|
const initializeLanguageOldValue = () => {
|
|
// Keep the old language value to be able to compare to the new one and revert to it if necessary.
|
|
languagesList.attr( 'data-old-value', languagesList.children( ':selected' ).first().val() );
|
|
};
|
|
|
|
;// ./js/src/lib/metabox-autocomplete.js
|
|
/**
|
|
* @package Polylang
|
|
*/
|
|
|
|
// Translations autocomplete input box.
|
|
function initMetaboxAutoComplete() {
|
|
jQuery('.tr_lang').each(
|
|
function () {
|
|
var tr_lang = jQuery(this).attr('id').substring(8);
|
|
var td = jQuery(this).parent().parent().siblings('.pll-edit-column');
|
|
|
|
jQuery(this).autocomplete(
|
|
{
|
|
minLength: 0,
|
|
source: ajaxurl + '?action=pll_posts_not_translated' +
|
|
'&post_language=' + jQuery('.post_lang_choice').val() +
|
|
'&translation_language=' + tr_lang +
|
|
'&post_type=' + jQuery('#post_type').val() +
|
|
'&_pll_nonce=' + jQuery('#_pll_nonce').val(),
|
|
select: function (event, ui) {
|
|
jQuery('#htr_lang_' + tr_lang).val(ui.item.id);
|
|
// ui.item.link is built and come from server side and is well escaped when necessary
|
|
td.html(ui.item.link); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
|
|
},
|
|
}
|
|
);
|
|
|
|
// when the input box is emptied
|
|
jQuery(this).on(
|
|
'blur',
|
|
function () {
|
|
if ( ! jQuery(this).val() ) {
|
|
jQuery('#htr_lang_' + tr_lang).val(0);
|
|
// Value is retrieved from HTML already generated server side
|
|
td.html(td.siblings('.hidden').children().clone()); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
|
|
}
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
;// ./js/src/lib/filter-path-middleware.js
|
|
/**
|
|
* @package Polylang
|
|
*/
|
|
|
|
/**
|
|
* Filters requests for translatable entities.
|
|
* This logic is shared across all Polylang plugins.
|
|
*
|
|
* @since 3.5
|
|
*
|
|
* @param {APIFetchOptions} options
|
|
* @param {Array} filteredRoutes
|
|
* @param {CallableFunction} filter
|
|
* @returns {APIFetchOptions}
|
|
*/
|
|
const filterPathMiddleware = ( options, filteredRoutes, filter ) => {
|
|
const cleanPath = options.path.split( '?' )[0].replace(/^\/+|\/+$/g, ''); // Get path without query parameters and trim '/'.
|
|
|
|
return Object.values( filteredRoutes ).find( ( path ) => cleanPath === path ) ? filter( options ) : options;
|
|
}
|
|
|
|
/* harmony default export */ const filter_path_middleware = (filterPathMiddleware);
|
|
|
|
;// ./js/src/block-editor.js
|
|
/**
|
|
* @package Polylang
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Filter REST API requests to add the language in the request
|
|
*
|
|
* @since 2.5
|
|
*/
|
|
wp.apiFetch.use(
|
|
function ( options, next ) {
|
|
/*
|
|
* If options.url is defined, this is not a REST request but a direct call to post.php for legacy metaboxes.
|
|
* If `filteredRoutes` is not defined, return early.
|
|
*/
|
|
if ( 'undefined' !== typeof options.url || 'undefined' === typeof pllFilteredRoutes ) {
|
|
return next( options );
|
|
}
|
|
|
|
return next( filter_path_middleware( options, pllFilteredRoutes, addLanguageParameter ) );
|
|
}
|
|
);
|
|
|
|
/**
|
|
* Gets the language of the currently edited post, fallback to default language if none is found.
|
|
*
|
|
* @since 2.5
|
|
*
|
|
* @return {Element.value}
|
|
*/
|
|
function getCurrentLanguage() {
|
|
const lang = document.querySelector( '[name=post_lang_choice]' );
|
|
|
|
if ( null === lang ) {
|
|
return pllDefaultLanguage;
|
|
}
|
|
|
|
return lang.value;
|
|
}
|
|
|
|
/**
|
|
* Adds language parameter according to the current one (query string for GET, body for PUT and POST).
|
|
*
|
|
* @since 3.5
|
|
*
|
|
* @param {APIFetchOptions} options
|
|
* @returns {APIFetchOptions}
|
|
*/
|
|
function addLanguageParameter( options ) {
|
|
if ( 'undefined' === typeof options.data || null === options.data ) {
|
|
// GET
|
|
options.path += ( ( options.path.indexOf( '?' ) >= 0 ) ? '&lang=' : '?lang=' ) + getCurrentLanguage();
|
|
} else {
|
|
// PUT, POST
|
|
options.data.lang = getCurrentLanguage();
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
/**
|
|
* Handles internals of the metabox:
|
|
* Language select, autocomplete input field.
|
|
*
|
|
* @since 1.5
|
|
*
|
|
* Save post after lang choice is done and redirect to the same page for refreshing all the data.
|
|
*
|
|
* @since 2.5
|
|
*
|
|
* Link post saving after refreshing the metabox.
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
jQuery(
|
|
function ( $ ) {
|
|
// Initialize current language to be able to compare if it changes.
|
|
initializeLanguageOldValue();
|
|
|
|
|
|
// Ajax for changing the post's language in the languages metabox
|
|
$( '.post_lang_choice' ).on(
|
|
'change',
|
|
function ( event ) {
|
|
const { select, dispatch, subscribe } = wp.data;
|
|
const emptyPost = isEmptyPost();
|
|
const { addQueryArgs } = wp.url;
|
|
|
|
// Initialize the confirmation dialog box.
|
|
const confirmationModal = initializeConfirmationModal();
|
|
const { dialogContainer : dialog } = confirmationModal;
|
|
let { dialogResult } = confirmationModal;
|
|
const selectedOption = event.target; // The selected option in the dropdown list.
|
|
|
|
// Specific case for empty posts.
|
|
// Place at the beginning because window.location change triggers automatically page reloading.
|
|
if ( location.pathname.match( /post-new.php/gi ) && emptyPost ) {
|
|
reloadPageForEmptyPost( selectedOption.value );
|
|
}
|
|
|
|
// Otherwise send an ajax request to refresh the legacy metabox and set the post language with the new language.
|
|
// It needs a confirmation of the user before changing the language.
|
|
// Need to wait the ajax response before triggering the block editor post save action.
|
|
if ( $( this ).data( 'old-value' ) !== selectedOption.value && ! emptyPost ) {
|
|
dialog.dialog( 'open' );
|
|
} else {
|
|
// Update the old language with the new one to be able to compare it in the next change.
|
|
// Because the page isn't reloaded in this case.
|
|
initializeLanguageOldValue();
|
|
dialogResult = Promise.resolve();
|
|
}
|
|
|
|
dialogResult.then(
|
|
() => {
|
|
let data = { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
|
|
action: 'post_lang_choice',
|
|
lang: selectedOption.value,
|
|
post_type: $( '#post_type' ).val(),
|
|
post_id: $( '#post_ID' ).val(),
|
|
_pll_nonce: $( '#_pll_nonce' ).val()
|
|
}
|
|
|
|
// Update post language in database as soon as possible.
|
|
// Because, in addition of the block editor save process, the legacy metabox uses a post.php process to update the language and is too late compared to the page reload.
|
|
$.post(
|
|
ajaxurl,
|
|
data,
|
|
function () {
|
|
blockEditorSavePostAndReloadPage();
|
|
}
|
|
);
|
|
},
|
|
() => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
|
|
);
|
|
|
|
function isEmptyPost() {
|
|
const editor = select( 'core/editor' );
|
|
|
|
return ! editor.getEditedPostAttribute( 'title' )?.trim() && ! editor.getEditedPostContent() && ! editor.getEditedPostAttribute( 'excerpt' )?.trim();
|
|
}
|
|
|
|
/**
|
|
* Reload the block editor page for empty posts.
|
|
*
|
|
* @param {string} lang The target language code.
|
|
*/
|
|
function reloadPageForEmptyPost( lang ) {
|
|
// Change the new_lang parameter with the new language value for reloading the page
|
|
// WPCS location.search is never written in the page, just used to reload page with the right value of new_lang
|
|
// new_lang input is controlled server side in PHP. The value come from the dropdown list of language returned and escaped server side.
|
|
// Notice that window.location changing triggers automatically page reloading.
|
|
if ( -1 != location.search.indexOf( 'new_lang' ) ) {
|
|
// use regexp non capturing group to replace new_lang parameter no matter where it is and capture other parameters which can be behind it
|
|
window.location.search = window.location.search.replace( /(?:new_lang=[^&]*)(&)?(.*)/, 'new_lang=' + lang + '$1$2' ); // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
|
|
} else {
|
|
window.location.search = window.location.search + ( ( -1 != window.location.search.indexOf( '?' ) ) ? '&' : '?' ) + 'new_lang=' + lang; // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Triggers block editor post save and reload the block editor page when everything is ok.
|
|
*/
|
|
function blockEditorSavePostAndReloadPage() {
|
|
|
|
let unsubscribe = null;
|
|
const previousPost = select( 'core/editor').getCurrentPost();
|
|
|
|
// Listen if the savePost is completely done by subscribing to its events.
|
|
const savePostIsDone = new Promise(
|
|
function ( resolve, reject ) {
|
|
unsubscribe = subscribe(
|
|
function () {
|
|
const post = select( 'core/editor').getCurrentPost();
|
|
const { id, status, type } = post;
|
|
const error = select( 'core' )
|
|
.getLastEntitySaveError(
|
|
'postType',
|
|
type,
|
|
id
|
|
);
|
|
|
|
if ( error ) {
|
|
reject();
|
|
}
|
|
|
|
if ( previousPost.modified !== post.modified ) {
|
|
|
|
if ( location.pathname.match( /post-new.php/gi ) && status !== 'auto-draft' && id ) {
|
|
window.history.replaceState(
|
|
{ id },
|
|
'Post ' + id,
|
|
addQueryArgs( 'post.php', { post: id, action: 'edit' } )
|
|
);
|
|
}
|
|
resolve();
|
|
}
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
// Triggers the post save.
|
|
dispatch( 'core/editor' ).savePost();
|
|
|
|
// Process
|
|
savePostIsDone.then(
|
|
function () {
|
|
// If the post is well saved, we can reload the page
|
|
window.location.reload();
|
|
},
|
|
function () {
|
|
// If the post save failed
|
|
unsubscribe();
|
|
}
|
|
).catch(
|
|
function () {
|
|
// If an exception is thrown
|
|
unsubscribe();
|
|
}
|
|
);
|
|
};
|
|
}
|
|
);
|
|
|
|
initMetaboxAutoComplete();
|
|
}
|
|
);
|
|
|