");return n.disabled&&this._addClass(l,null,"ui-state-disabled"),this._setText(s,n.label),s.prepend(e(n.element).data("flag-html")),s.children("img").addClass("ui-icon"),l.append(s).appendTo(t)},a=function(t){var n=e(t).selectmenu("instance").buttonText;n.prepend(e(t).children(":selected").data("flag-html")),n.children("img").addClass("ui-icon")},c=function(t){var n=e("
");return this._setText(n,t.label),this._addClass(n,"ui-selectmenu-text"),n.prepend(e(t.element).data("flag-html")),n.children("img").addClass("ui-icon"),n};function o(e,t){var l=e.selectmenu(t).selectmenu("instance");return l._renderItem=i,n&&(l._renderButtonItem=c,l.refresh()),l}var r={width:"95%",classes:{"ui-selectmenu-menu":"pll-selectmenu-menu","ui-selectmenu-button":"pll-selectmenu-button"}},u={},d=function(e,t){a(e.target)},p=function(t,n){a(e(t.target).selectmenu("refresh"))},g=function(t,n){e(t.target).selectmenu("refresh")};if(u=n?{change:g,open:g}:{create:d,select:d,change:p,open:p},l){var h=o(e("#flag_list"),Object.assign({},r,u));e("#lang_list").on("languageChanged",(function(e,t){h.element.val(t),h._trigger("change")}))}var f=function(t,n){var l=function(t){var n=e("option:selected",t.target),l=n.val().split(":");return{slug:l[0],locale:l[1],rtl:[l[2]],flag:l[3],name:n.text().split(" - ")[0]}}(t);!function(t){e("#lang_slug").val(t.slug),e("#lang_locale").val(t.locale),e('input[name="rtl"]').val(t.rtl),e("#lang_name").val(t.name)}(l),e(t.target).trigger("languageChanged",l.flag)},v={};e("#lang_list").closest(".pll-wizard-content").length>0&&(r=Object.assign(r,{width:"100%"})),v=n?{change:f}:{create:d,select:d,change:f},s&&o(e("#lang_list"),Object.assign({},r,v)),e(".translation input").on("keydown",(function(t){"Enter"===t.key&&(t.preventDefault(),e("#submit").trigger("click"))})),e("#the-list").on("click",".configure>a",(function(){return e(".pll-configure").hide().prev().show(),e(this).closest("tr").hide().next().show(),!1})),e("#the-list").on("click",".cancel",(function(){e(this).closest("tr").hide().prev().show()})),e("#the-list").on("click",".save",(function(){var t=e(this).closest("tr"),n=t.attr("id").split("-"),l={action:"pll_save_options",pll_ajax_settings:!0,module:n[n.length-1],_pll_nonce:e("#_pll_nonce").val()};l=t.find(":input").serialize()+"&"+e.param(l),e.post(ajaxurl,l,(function(n){var l=wpAjax.parseAjaxResponse(n,"pll-ajax-response");e.each(l.responses,(function(){switch(wp.hooks.doAction("pll_settings_saved",this,t.get(0)),this.what){case"license-update":e("#pll-license-"+this.data).replaceWith(this.supplemental.html);break;case"success":t.hide().prev().show();case"error":e(".settings-error").remove(),e("h1").after(this.data),e(".notice.is-dismissible").each((function(){var t=e(this),n=e(' '),l=pll_admin.dismiss_notice||"";n.find(".screen-reader-text").text(l),t.append(n),n.on("click.wp-dismiss-notice",(function(n){n.preventDefault(),t.fadeTo(100,0,(function(){e(this).slideUp(100,(function(){e(this).remove()}))}))}))}))}}))}))})),e(".pll-configure").on("keydown",(function(t){"Enter"===t.key&&(t.preventDefault(),e(this).find(".save").trigger("click")),"Escape"===t.key&&(t.preventDefault(),e(this).find(".cancel").trigger("click"))})),e("input[name='force_lang']").on("change",(function(){function t(e,t){t?e.show():e.hide()}var n=e(this).val();t(e("#pll-domains-table"),3==n),t(e("#pll-hide-default"),3>n),t(e("#pll-rewrite"),2>n),t(e("#pll-redirect-lang"),2>n)})),e(".pll-deactivate-license").on("click",(function(){var t={action:"pll_deactivate_license",pll_ajax_settings:!0,id:e(this).attr("id"),_pll_nonce:e("#_pll_nonce").val()};e.post(ajaxurl,t,(function(t){e("#pll-license-"+t.id).replaceWith(t.html)}))})),e(".if-js-closed").removeClass("if-js-closed").addClass("closed"),"undefined"!=typeof postboxes&&postboxes.add_postbox_toggles(pagenow)}));
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/js/build/block-editor.js b/wp-content/plugins/polylang/js/build/block-editor.js
new file mode 100644
index 0000000000..ef87ebf00b
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/block-editor.js
@@ -0,0 +1,405 @@
+/******/ "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(
+ '
',
+ {
+ 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();
+ }
+);
+
diff --git a/wp-content/plugins/polylang/js/build/block-editor.min.js b/wp-content/plugins/polylang/js/build/block-editor.min.js
new file mode 100644
index 0000000000..6c5480a803
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/block-editor.min.js
@@ -0,0 +1 @@
+"use strict";const languagesList=jQuery(".post_lang_choice"),initializeConfirmationModal=()=>{const{__:t}=wp.i18n,e=jQuery("
",{id:"pll-dialog",style:"display:none;"}).text(t("Are you sure you want to change the language of the current content?","polylang"));languagesList.after(e);const a=new Promise(((a,n)=>{const i=t=>{switch(t){case"yes":languagesList.data("old-value",languagesList.children(":selected").first().val()),a();break;case"no":languagesList.val(languagesList.data("old-value")),n("Cancel")}e.dialog("close")},l={autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:t("Change language","polylang"),minWidth:600,maxWidth:"100%",open:function(t,e){jQuery("body").hasClass("rtl")&&jQuery(this).parent().css({right:jQuery(this).parent().css("left"),left:"auto"})},close:function(t,e){i("no")},buttons:[{text:t("OK","polylang"),click:function(t){i("yes")}},{text:t("Cancel","polylang"),click:function(t){i("no")}}]};jQuery.ui.version>="1.12.0"?Object.assign(l,{classes:{"ui-dialog":"pll-confirmation-modal"}}):Object.assign(l,{dialogClass:"pll-confirmation-modal"}),e.dialog(l)}));return{dialogContainer:e,dialogResult:a}},initializeLanguageOldValue=()=>{languagesList.attr("data-old-value",languagesList.children(":selected").first().val())};function initMetaboxAutoComplete(){jQuery(".tr_lang").each((function(){var t=jQuery(this).attr("id").substring(8),e=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="+t+"&post_type="+jQuery("#post_type").val()+"&_pll_nonce="+jQuery("#_pll_nonce").val(),select:function(a,n){jQuery("#htr_lang_"+t).val(n.item.id),e.html(n.item.link)}}),jQuery(this).on("blur",(function(){jQuery(this).val()||(jQuery("#htr_lang_"+t).val(0),e.html(e.siblings(".hidden").children().clone()))}))}))}const filterPathMiddleware=(t,e,a)=>{const n=t.path.split("?")[0].replace(/^\/+|\/+$/g,"");return Object.values(e).find((t=>n===t))?a(t):t},filter_path_middleware=filterPathMiddleware;function getCurrentLanguage(){const t=document.querySelector("[name=post_lang_choice]");return null===t?pllDefaultLanguage:t.value}function addLanguageParameter(t){return void 0===t.data||null===t.data?t.path+=(t.path.indexOf("?")>=0?"&lang=":"?lang=")+getCurrentLanguage():t.data.lang=getCurrentLanguage(),t}wp.apiFetch.use((function(t,e){return void 0!==t.url||"undefined"==typeof pllFilteredRoutes?e(t):e(filter_path_middleware(t,pllFilteredRoutes,addLanguageParameter))})),jQuery((function(t){initializeLanguageOldValue(),t(".post_lang_choice").on("change",(function(e){const{select:a,dispatch:n,subscribe:i}=wp.data,l=function(){const t=a("core/editor");return!t.getEditedPostAttribute("title")?.trim()&&!t.getEditedPostContent()&&!t.getEditedPostAttribute("excerpt")?.trim()}(),{addQueryArgs:o}=wp.url,r=initializeConfirmationModal(),{dialogContainer:s}=r;let{dialogResult:u}=r;const c=e.target;var d;location.pathname.match(/post-new.php/gi)&&l&&(d=c.value,-1!=location.search.indexOf("new_lang")?window.location.search=window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/,"new_lang="+d+"$1$2"):window.location.search=window.location.search+(-1!=window.location.search.indexOf("?")?"&":"?")+"new_lang="+d),t(this).data("old-value")===c.value||l?(initializeLanguageOldValue(),u=Promise.resolve()):s.dialog("open"),u.then((()=>{let e={action:"post_lang_choice",lang:c.value,post_type:t("#post_type").val(),post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,e,(function(){!function(){let t=null;const e=a("core/editor").getCurrentPost(),l=new Promise((function(n,l){t=i((function(){const t=a("core/editor").getCurrentPost(),{id:i,status:r,type:s}=t;a("core").getLastEntitySaveError("postType",s,i)&&l(),e.modified!==t.modified&&(location.pathname.match(/post-new.php/gi)&&"auto-draft"!==r&&i&&window.history.replaceState({id:i},"Post "+i,o("post.php",{post:i,action:"edit"})),n())}))}));n("core/editor").savePost(),l.then((function(){window.location.reload()}),(function(){t()})).catch((function(){t()}))}()}))}),(()=>{}))})),initMetaboxAutoComplete()}));
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/js/build/classic-editor.js b/wp-content/plugins/polylang/js/build/classic-editor.js
new file mode 100644
index 0000000000..6706eb13ef
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/classic-editor.js
@@ -0,0 +1,451 @@
+/******/ "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(
+ '
',
+ {
+ 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/classic-editor.js
+/**
+ * @package Polylang
+ */
+
+
+
+
+
+// tag suggest in metabox
+jQuery(
+ function ( $ ) {
+ $.ajaxPrefilter(
+ function ( options, originalOptions, jqXHR ) {
+ var lang = $( '.post_lang_choice' ).val();
+ if ( 'string' === typeof options.data && -1 !== options.url.indexOf( 'action=ajax-tag-search' ) && lang ) {
+ options.data = 'lang=' + lang + '&' + options.data;
+ }
+ }
+ );
+ }
+);
+
+// overrides tagBox.get
+jQuery(
+ function ( $ ) {
+ // overrides function to add the language
+ tagBox.get = function ( id ) {
+ var tax = id.substr( id.indexOf( '-' ) + 1 );
+
+ // add the language in the $_POST variable
+ var data = {
+ action: 'get-tagcloud',
+ lang: $( '.post_lang_choice' ).val(),
+ tax: tax
+ }
+
+ $.post(
+ ajaxurl,
+ data,
+ function ( r, stat ) {
+ if ( 0 == r || 'success' != stat ) {
+ r = wpAjax.broken;
+ }
+
+ // @see code from WordPress core https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/js/tags-box.js#L291
+ // @see wp_generate_tag_cloud function which generate the escaped HTML https://github.com/WordPress/WordPress/blob/a02b5cc2a8eecb8e076fbb7cf4de7bd2ec8a8eb1/wp-includes/category-template.php#L966-L975
+ r = $( '
' ).addClass( 'the-tagcloud' ).attr( 'id', 'tagcloud-' + tax ).html( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ $( 'a', r ).on(
+ 'click',
+ function () {
+ tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
+ return false;
+ }
+ );
+
+ var tagCloud = $( '#tagcloud-' + tax );
+ // add an if else condition to allow modifying the tags outputted when switching the language
+ var v = tagCloud.css( 'display' );
+ if ( v ) {
+ // See the comment above when r variable is created.
+ $( '#tagcloud-' + tax ).replaceWith( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
+ $( '#tagcloud-' + tax ).css( 'display', v );
+ }
+ else {
+ // See the comment above when r variable is created.
+ $( '#' + id ).after( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
+ }
+ }
+ );
+ }
+ }
+);
+
+jQuery(
+ function ( $ ) {
+ // collect taxonomies - code partly copied from WordPress
+ var taxonomies = new Array();
+ $( '.categorydiv' ).each(
+ function () {
+ var this_id = $( this ).attr( 'id' ), taxonomyParts, taxonomy;
+
+ taxonomyParts = this_id.split( '-' );
+ taxonomyParts.shift();
+ taxonomy = taxonomyParts.join( '-' );
+ taxonomies.push( taxonomy ); // store the taxonomy for future use
+
+ // add our hidden field in the new category form - for each hierarchical taxonomy
+ // to set the language when creating a new category
+ // html code inserted come from html code itself.
+ $( '#' + taxonomy + '-add-submit' ).before( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.before
+ $( ' ' ).attr( 'type', 'hidden' )
+ .attr( 'id', taxonomy + '-lang' )
+ .attr( 'name', 'term_lang_choice' )
+ .attr( 'value', $( '.post_lang_choice' ).val() )
+ );
+ }
+ );
+
+ // 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 ) {
+ // Initialize the confirmation dialog box.
+ const confirmationModal = initializeConfirmationModal();
+ const { dialogContainer: dialog } = confirmationModal;
+ let { dialogResult } = confirmationModal;
+ // The selected option in the dropdown list.
+ const selectedOption = event.target;
+
+ if ( $( this ).data( 'old-value' ) !== selectedOption.value && ! isEmptyPost() ) {
+ dialog.dialog( 'open' );
+ } else {
+ dialogResult = Promise.resolve();
+ }
+
+ // phpcs:disable PEAR.Functions.FunctionCallSignature.EmptyLine
+ dialogResult.then(
+ () => {
+ var lang = selectedOption.options[selectedOption.options.selectedIndex].lang; // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
+ var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
+
+ var data = { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
+ action: 'post_lang_choice',
+ lang: selectedOption.value,
+ post_type: $( '#post_type' ).val(),
+ taxonomies: taxonomies,
+ post_id: $( '#post_ID' ).val(),
+ _pll_nonce: $( '#_pll_nonce' ).val()
+ }
+
+ $.post(
+ ajaxurl,
+ data,
+ function ( response ) {
+ // Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
+ var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
+ $.each(
+ res.responses,
+ function () {
+ switch ( this.what ) {
+ case 'translations': // translations fields
+ // Data is built and come from server side and is well escaped when necessary
+ $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ initMetaboxAutoComplete();
+ break;
+ case 'taxonomy': // categories metabox for posts
+ var tax = this.data;
+ // @see wp_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L175
+ // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/class-walker-category-checklist.php#L89-L111
+ $( '#' + tax + 'checklist' ).html( this.supplemental.all ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ // @see wp_popular_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L236
+ $( '#' + tax + 'checklist-pop' ).html( this.supplemental.populars ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ // @see wp_dropdown_categories https://github.com/WordPress/WordPress/blob/5.5.1/wp-includes/category-template.php#L336
+ // which is called by PLL_Admin_Classic_Editor::post_lang_choice to generate supplemental.dropdown
+ $( '#new' + tax + '_parent' ).replaceWith( this.supplemental.dropdown ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
+ $( '#' + tax + '-lang' ).val( $( '.post_lang_choice' ).val() ); // hidden field
+ break;
+ case 'pages': // parent dropdown list for pages
+ // @see wp_dropdown_pages https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/post-template.php#L1186-L1208
+ // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/class-walker-page-dropdown.php#L88
+ $( '#parent_id' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ break;
+ case 'flag': // flag in front of the select dropdown
+ // Data is built and come from server side and is well escaped when necessary
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ break;
+ case 'permalink': // Sample permalink
+ var div = $( '#edit-slug-box' );
+ if ( '-1' != this.data && div.children().length ) {
+ // @see get_sample_permalink_html https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/post.php#L1425-L1454
+ div.html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ }
+ break;
+ }
+ }
+ );
+
+ // Update the old language with the new one to be able to compare it in the next changing.
+ initializeLanguageOldValue();
+ // modifies the language in the tag cloud
+ $( '.tagcloud-link' ).each(
+ function () {
+ var id = $( this ).attr( 'id' );
+ tagBox.get( id );
+ }
+ );
+
+ // Modifies the text direction
+ $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
+ $( '#content_ifr' ).contents().find( 'html' ).attr( 'lang', lang ).attr( 'dir', dir );
+ $( '#content_ifr' ).contents().find( 'body' ).attr( 'dir', dir );
+
+ pll.media.resetAllAttachmentsCollections();
+ }
+ )
+ },
+ () => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
+ );
+ // phpcs:enable PEAR.Functions.FunctionCallSignature.EmptyLine
+
+ function isEmptyPost() {
+ const title = $( 'input#title' ).val();
+ const content = $( 'textarea#content' ).val();
+ const excerpt = $( 'textarea#excerpt' ).val();
+
+ return ! title && ! content && ! excerpt;
+ }
+ }
+ );
+
+ initMetaboxAutoComplete();
+ }
+);
+
+/**
+ * @since 3.0
+ *
+ * @namespace pll
+ */
+var pll = window.pll || {};
+
+/**
+ * @since 3.0
+ *
+ * @namespace pll.media
+ */
+_.extend( pll, { media: {} } );
+
+/**
+ * @since 3.0
+ *
+ * @alias pll.media
+ * @memberOf pll
+ * @namespace
+ */
+var media = _.extend(
+ pll.media, /** @lends pll.media.prototype */
+ {
+ /**
+ * TODO: Find a way to delete references to Attachments collections that are not used anywhere else.
+ *
+ * @type {wp.media.model.Attachments}
+ */
+ attachmentsCollections : [],
+
+ /**
+ * Imitates { @see wp.media.query } but log all Attachments collections created.
+ *
+ * @param {Object} [props]
+ * @return {wp.media.model.Attachments}
+ */
+ query: function ( props ) {
+ var attachments = pll.media.query.delegate( props );
+
+ pll.media.attachmentsCollections.push( attachments );
+
+ return attachments;
+ },
+
+ resetAllAttachmentsCollections: function () {
+ this.attachmentsCollections.forEach(
+ function ( attachmentsCollection ) {
+ /**
+ * First reset the { @see wp.media.model.Attachments } collection.
+ * Then, if it is mirroring a { @see wp.media.model.Query } collection,
+ * refresh this one too, so it will fetch new data from the server,
+ * and then the wp.media.model.Attachments collection will synchronize with the new data.
+ */
+ attachmentsCollection.reset();
+ if (attachmentsCollection.mirroring) {
+ attachmentsCollection.mirroring._hasMore = true;
+ attachmentsCollection.mirroring.reset();
+ }
+ }
+ );
+ }
+ }
+);
+
+if ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.media ) {
+
+ /**
+ * @since 3.0
+ *
+ * @memberOf pll.media
+ */
+ media.query = _.extend(
+ media.query, /** @lends pll.media.query prototype */
+ {
+ /**
+ * @type Function References WordPress { @see wp.media.query } constructor
+ */
+ delegate: wp.media.query
+ }
+ )
+
+ // Substitute WordPress media query shortcut with our decorated function.
+ wp.media.query = media.query
+
+}
+
diff --git a/wp-content/plugins/polylang/js/build/classic-editor.min.js b/wp-content/plugins/polylang/js/build/classic-editor.min.js
new file mode 100644
index 0000000000..e2ce487492
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/classic-editor.min.js
@@ -0,0 +1 @@
+"use strict";const languagesList=jQuery(".post_lang_choice"),initializeConfirmationModal=()=>{const{__:t}=wp.i18n,a=jQuery("
",{id:"pll-dialog",style:"display:none;"}).text(t("Are you sure you want to change the language of the current content?","polylang"));languagesList.after(a);const e=new Promise(((e,l)=>{const n=t=>{switch(t){case"yes":languagesList.data("old-value",languagesList.children(":selected").first().val()),e();break;case"no":languagesList.val(languagesList.data("old-value")),l("Cancel")}a.dialog("close")},i={autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:t("Change language","polylang"),minWidth:600,maxWidth:"100%",open:function(t,a){jQuery("body").hasClass("rtl")&&jQuery(this).parent().css({right:jQuery(this).parent().css("left"),left:"auto"})},close:function(t,a){n("no")},buttons:[{text:t("OK","polylang"),click:function(t){n("yes")}},{text:t("Cancel","polylang"),click:function(t){n("no")}}]};jQuery.ui.version>="1.12.0"?Object.assign(i,{classes:{"ui-dialog":"pll-confirmation-modal"}}):Object.assign(i,{dialogClass:"pll-confirmation-modal"}),a.dialog(i)}));return{dialogContainer:a,dialogResult:e}},initializeLanguageOldValue=()=>{languagesList.attr("data-old-value",languagesList.children(":selected").first().val())};function initMetaboxAutoComplete(){jQuery(".tr_lang").each((function(){var t=jQuery(this).attr("id").substring(8),a=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="+t+"&post_type="+jQuery("#post_type").val()+"&_pll_nonce="+jQuery("#_pll_nonce").val(),select:function(e,l){jQuery("#htr_lang_"+t).val(l.item.id),a.html(l.item.link)}}),jQuery(this).on("blur",(function(){jQuery(this).val()||(jQuery("#htr_lang_"+t).val(0),a.html(a.siblings(".hidden").children().clone()))}))}))}jQuery((function(t){t.ajaxPrefilter((function(a,e,l){var n=t(".post_lang_choice").val();"string"==typeof a.data&&-1!==a.url.indexOf("action=ajax-tag-search")&&n&&(a.data="lang="+n+"&"+a.data)}))})),jQuery((function(t){tagBox.get=function(a){var e=a.substr(a.indexOf("-")+1),l={action:"get-tagcloud",lang:t(".post_lang_choice").val(),tax:e};t.post(ajaxurl,l,(function(l,n){0!=l&&"success"==n||(l=wpAjax.broken),l=t("
").addClass("the-tagcloud").attr("id","tagcloud-"+e).html(l),t("a",l).on("click",(function(){return tagBox.flushTags(t(this).closest(".inside").children(".tagsdiv"),this),!1}));var i=t("#tagcloud-"+e).css("display");i?(t("#tagcloud-"+e).replaceWith(l),t("#tagcloud-"+e).css("display",i)):t("#"+a).after(l)}))}})),jQuery((function(t){var a=new Array;t(".categorydiv").each((function(){var e,l;(e=t(this).attr("id").split("-")).shift(),l=e.join("-"),a.push(l),t("#"+l+"-add-submit").before(t(" ").attr("type","hidden").attr("id",l+"-lang").attr("name","term_lang_choice").attr("value",t(".post_lang_choice").val()))})),initializeLanguageOldValue(),t(".post_lang_choice").on("change",(function(e){const l=initializeConfirmationModal(),{dialogContainer:n}=l;let{dialogResult:i}=l;const o=e.target;t(this).data("old-value")===o.value||function(){const a=t("input#title").val(),e=t("textarea#content").val(),l=t("textarea#excerpt").val();return!a&&!e&&!l}()?i=Promise.resolve():n.dialog("open"),i.then((()=>{var e=o.options[o.options.selectedIndex].lang,l=t('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),n={action:"post_lang_choice",lang:o.value,post_type:t("#post_type").val(),taxonomies:a,post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,n,(function(a){var n=wpAjax.parseAjaxResponse(a,"pll-ajax-response");t.each(n.responses,(function(){switch(this.what){case"translations":t(".translations").html(this.data),initMetaboxAutoComplete();break;case"taxonomy":var a=this.data;t("#"+a+"checklist").html(this.supplemental.all),t("#"+a+"checklist-pop").html(this.supplemental.populars),t("#new"+a+"_parent").replaceWith(this.supplemental.dropdown),t("#"+a+"-lang").val(t(".post_lang_choice").val());break;case"pages":t("#parent_id").html(this.data);break;case"flag":t(".pll-select-flag").html(this.data);break;case"permalink":var e=t("#edit-slug-box");"-1"!=this.data&&e.children().length&&e.html(this.data)}})),initializeLanguageOldValue(),t(".tagcloud-link").each((function(){var a=t(this).attr("id");tagBox.get(a)})),t("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+l),t("#content_ifr").contents().find("html").attr("lang",e).attr("dir",l),t("#content_ifr").contents().find("body").attr("dir",l),pll.media.resetAllAttachmentsCollections()}))}),(()=>{}))})),initMetaboxAutoComplete()}));var pll=window.pll||{};_.extend(pll,{media:{}});var media=_.extend(pll.media,{attachmentsCollections:[],query:function(t){var a=pll.media.query.delegate(t);return pll.media.attachmentsCollections.push(a),a},resetAllAttachmentsCollections:function(){this.attachmentsCollections.forEach((function(t){t.reset(),t.mirroring&&(t.mirroring._hasMore=!0,t.mirroring.reset())}))}});"undefined"!=typeof wp&&void 0!==wp.media&&(media.query=_.extend(media.query,{delegate:wp.media.query}),wp.media.query=media.query);
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/js/build/languages-step.js b/wp-content/plugins/polylang/js/build/languages-step.js
new file mode 100644
index 0000000000..8077fa908a
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/languages-step.js
@@ -0,0 +1,303 @@
+/**
+ * @package Polylang
+ */
+
+jQuery(
+ function ( $ ) {
+ var addLanguageForm = $( '.languages-step' ); // Form element.
+ var languageFields = $( '#language-fields' ); // Element where to append hidden fields for creating language.
+ var languagesTable = $( '#languages' ); // Table element contains languages list to create.
+ var languagesListTable = $( '#languages tbody' ); // Table rows with languages list to create.
+ var definedLanguagesListTable = $( '#defined-languages tbody' ); // Table rows with already defined languages list.
+ var languagesList = $( '#lang_list' ); // Select form element with predefined languages without already created languages.
+ var nextStepButton = $( '[name="save_step"]' ); // The button for continuing to the next step.
+ var messagesContainer = $( '#messages' ); // Element where to display error messages.
+ var languagesMap = new Map(); // Languages map object for managing the languages to create.
+ var dialog = $( '#dialog' ); // Dialog box for alerting the language selected has not been added to the list.
+
+ /**
+ * Add a language in the list to create it in Polylang settings
+ *
+ * @param {object} language The language object
+ */
+ function addLanguage( language ) {
+ // language properties come from the select dropdown which is built server side and well escaped.
+ // see template view-wizard-step-languages.php.
+ var languageValueHtml = $( ' ' ).text( language.text ).prepend( language.flagUrl ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ var languageTrashIconHtml = $( '
' )
+ .append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ $( '
' )
+ .addClass( 'dashicons dashicons-trash' )
+ .attr( 'data-language', language.locale )
+ .append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ $( '
' )
+ .addClass( 'screen-reader-text' )
+ .text( pll_wizard_params.i18n_remove_language_icon )
+ )
+ );
+ // see the comment and the hardcoded code above. languageTrashIconHtml and languageValueHtml are safe.
+ var languageLineHtml = $( '
' ).prepend( languageTrashIconHtml ).prepend( languageValueHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ var languageFieldHtml = $( '
' ).attr(
+ {
+ type: 'hidden',
+ name: 'languages[]'
+ }
+ ).val( language.locale );
+
+ languagesList.val( '' );
+ languagesList.selectmenu( 'refresh' ); // Refresh jQuery selectmenu widget after changing the value.
+
+ languagesMap.set( language.locale, language );
+
+ // see above how languageLineHtml is built.
+ languagesListTable.append( languageLineHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ // Bind click event on trash icon.
+ languagesListTable.on(
+ 'click',
+ 'span[data-language=' + language.locale + ']',
+ function ( event ) {
+ event.preventDefault();
+ // Remove line in languages table.
+ $( this ).parents( 'tr' ).remove();
+ // Remove input field.
+ var languageField = languageFields.children( 'input[value=' + $( this ).data( 'language' ) + ']' ).remove();
+ // If there is no more languages hide languages table.
+ if ( languagesListTable.children().length <= 0 ) {
+ languagesTable.hide();
+ }
+ // Remove language from the Map.
+ languagesMap.delete( $( this ).data( 'language' ) );
+ // Hide error message.
+ hideError();
+ }
+ );
+ // see above how languageFieldHtml is built.
+ // Add hidden input field for posting the form.
+ languageFields.append( languageFieldHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+
+ }
+
+ /**
+ * Display an error message
+ *
+ * @param {string} message The message to display
+ */
+ function showError( message ) {
+ messagesContainer.empty();
+ // html is hardcoded and use of jQuery text method which is safe to add message value.
+ // In addition message is i18n value which is initialized server side in PLL_Wizard::add_step_languages and correctly escaped.
+ messagesContainer.prepend( $( '
' ).addClass( 'error' ).text( message ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ }
+
+ /**
+ * Hide all error messages and fields in error
+ */
+ function hideError() {
+ messagesContainer.empty();
+ addLanguageForm.find( '.error' ).removeClass( 'error field-in-error' );
+ }
+
+ /**
+ * Style the field to indicate where the error is
+ *
+ * @param {object} field The jQuery element which is in error
+ */
+ function showFieldInError( field ) {
+ field.addClass( 'error field-in-error' );
+ }
+
+ /**
+ * Focus on a specific element
+ *
+ * @param {object} field The jQuery element which will be focused
+ */
+ function focusOnField( field ) {
+ field.trigger( 'focus' );
+ }
+
+ /**
+ * Disable a specific button
+ *
+ * @param {object} button
+ */
+ function disableButton( button ){
+ button.prop( 'disabled', true );
+ // Because the button is disabled we need to add the value of the button to ensure it will pass in the request.
+ addLanguageForm.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ $( '
' ).prop(
+ {
+ type: 'hidden',
+ name: button.prop( 'name' ),
+ value: button.prop( 'value' )
+ }
+ )
+ );
+ }
+
+ /**
+ * Remove error when a new selection is done in languages list.
+ */
+ languagesList.on(
+ 'selectmenuchange',
+ function () {
+ hideError();;
+ }
+ );
+ /**
+ * Bind click event on "Add language" button
+ */
+ $( '#add-language' ).on(
+ 'click',
+ function ( event ) {
+ hideError();
+ var selectedOption = event.currentTarget.form.lang_list.options[event.currentTarget.form.lang_list.selectedIndex];
+ if ( '' !== selectedOption.value && ! languagesMap.has( selectedOption.value ) ) {
+ addLanguage(
+ {
+ locale: selectedOption.value,
+ text: selectedOption.innerText,
+ name: $( selectedOption ).data( 'language-name' ),
+ flagUrl: $( selectedOption ).data( 'flag-html' )
+ }
+ );
+ // Show table of languages.
+ languagesTable.show();
+ // Put back the focus on the select language field after clicking on "Add language button".
+ focusOnField( $( '#lang_list-button' ) );
+ } else {
+ var message = pll_wizard_params.i18n_no_language_selected;
+ if ( languagesMap.has( selectedOption.value ) ) {
+ message = pll_wizard_params.i18n_language_already_added;
+ }
+ showError( message );
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
+ focusOnField( $( '#lang_list-button' ) );
+
+ }
+ }
+ );
+
+ /**
+ * Bind submit event on "add_lang" form
+ */
+ addLanguageForm.on(
+ 'submit',
+ function ( event ) {
+ // Verify if there is at least one language.
+ var isLanguagesAlreadyDefined = definedLanguagesListTable.children().length > 0;
+ var selectedLanguage = $( '#lang_list' ).val();
+ if ( languagesMap.size <= 0 && ! isLanguagesAlreadyDefined ) {
+ if ( '' === selectedLanguage ) {
+ showError( pll_wizard_params.i18n_no_language_added );
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
+ focusOnField( $( '#lang_list-button' ) );
+ } else {
+ showError( pll_wizard_params.i18n_add_language_needed );
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
+ focusOnField( $( '#add-language' ) ); // Put the focus on the "Add language" button.
+ }
+ return false;
+ }
+ // Verify if the language has been added in the list otherwise display a dialog box to confirm what to do.
+ if ( '' !== selectedLanguage ) {
+ // Verify we don't add a duplicate language before opening the dialog box otherwise display an error message.
+ if ( ! languagesMap.has( selectedLanguage ) ) {
+ dialog.dialog( 'open' );
+ } else {
+ showError( pll_wizard_params.i18n_language_already_added );
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
+ focusOnField( $( '#lang_list-button' ) );
+ }
+ return false;
+ }
+ disableButton( nextStepButton );
+ }
+ );
+
+ // Is there an error return by PHP ?
+ var searchParams = new URLSearchParams( document.location.search );
+ if ( searchParams.has( 'activate_error' ) ) {
+ // If the error code exists, display it.
+ if ( undefined !== pll_wizard_params[ searchParams.get( 'activate_error' ) ] ) {
+ showError( pll_wizard_params[ searchParams.get( 'activate_error' ) ] );
+ }
+ }
+
+ function confirmDialog( what ) {
+ switch ( what ) {
+ case 'yes':
+ var selectedOption = $( '#lang_list' ).children( ':selected' );
+ addLanguage(
+ {
+ locale: selectedOption[0].value,
+ text: selectedOption[0].innerText,
+ name: $( selectedOption ).data( 'language-name' ),
+ flagUrl: $( selectedOption ).data( 'flag-html' )
+ }
+ );
+ break;
+ case 'no':
+ // Empty select form field and submit again the form.
+ languagesList.val( '' );
+ break;
+ case 'ignore':
+ }
+ dialog.dialog( 'close' );
+ if ( 'ignore' === what ) {
+ focusOnField( $( '#lang_list-button' ) );
+ } else {
+ addLanguageForm.submit();
+ }
+ }
+
+ // Initialize dialog box in the case a language is selected but not added in the list.
+ dialog.dialog(
+ {
+ autoOpen: false,
+ modal: true,
+ draggable: false,
+ resizable: false,
+ title: pll_wizard_params.i18n_dialog_title,
+ minWidth: 600,
+ maxWidth: '100%',
+ open: function ( event, ui ) {
+ // Change dialog box position for rtl language
+ if ( $( 'body' ).hasClass( 'rtl' ) ) {
+ $( this ).parent().css(
+ {
+ right: $( this ).parent().css( 'left' ),
+ left: 'auto'
+ }
+ );
+ }
+ // Display language name and flag information in dialog box.
+ $( this ).find( '#dialog-language' ).text( $( '#lang_list' ).children( ':selected' ).first().text() );
+ // language properties come from the select dropdown #lang_list which is built server side and well escaped.
+ // see template view-wizard-step-languages.php.
+ $( this ).find( '#dialog-language-flag' ).empty().prepend( $( '#lang_list' ).children( ':selected' ).data( 'flag-html' ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ },
+ buttons: [
+ {
+ text: pll_wizard_params.i18n_dialog_yes_button,
+ click: function ( event ) {
+ confirmDialog( 'yes' );
+ }
+ },
+ {
+ text: pll_wizard_params.i18n_dialog_no_button,
+ click: function ( event ) {
+ confirmDialog( 'no' );
+ }
+ },
+ {
+ text: pll_wizard_params.i18n_dialog_ignore_button,
+ click: function ( event ) {
+ confirmDialog( 'ignore' );
+ }
+ }
+ ]
+ }
+ )
+ }
+);
+
diff --git a/wp-content/plugins/polylang/js/build/languages-step.min.js b/wp-content/plugins/polylang/js/build/languages-step.min.js
new file mode 100644
index 0000000000..64ce94e2b3
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/languages-step.min.js
@@ -0,0 +1 @@
+jQuery((function(a){var e=a(".languages-step"),n=a("#language-fields"),t=a("#languages"),l=a("#languages tbody"),i=a("#defined-languages tbody"),r=a("#lang_list"),d=a('[name="save_step"]'),s=a("#messages"),o=new Map,g=a("#dialog");function u(e){var i=a("
").text(e.text).prepend(e.flagUrl),d=a("
").append(a("
").addClass("dashicons dashicons-trash").attr("data-language",e.locale).append(a("
").addClass("screen-reader-text").text(pll_wizard_params.i18n_remove_language_icon))),s=a("
").prepend(d).prepend(i),g=a("
").attr({type:"hidden",name:"languages[]"}).val(e.locale);r.val(""),r.selectmenu("refresh"),o.set(e.locale,e),l.append(s),l.on("click","span[data-language="+e.locale+"]",(function(e){e.preventDefault(),a(this).parents("tr").remove();n.children("input[value="+a(this).data("language")+"]").remove();l.children().length<=0&&t.hide(),o.delete(a(this).data("language")),c()})),n.append(g)}function p(e){s.empty(),s.prepend(a("
").addClass("error").text(e))}function c(){s.empty(),e.find(".error").removeClass("error field-in-error")}function _(a){a.addClass("error field-in-error")}function m(a){a.trigger("focus")}r.on("selectmenuchange",(function(){c()})),a("#add-language").on("click",(function(e){c();var n=e.currentTarget.form.lang_list.options[e.currentTarget.form.lang_list.selectedIndex];if(""===n.value||o.has(n.value)){var l=pll_wizard_params.i18n_no_language_selected;o.has(n.value)&&(l=pll_wizard_params.i18n_language_already_added),p(l),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))}else u({locale:n.value,text:n.innerText,name:a(n).data("language-name"),flagUrl:a(n).data("flag-html")}),t.show(),m(a("#lang_list-button"))})),e.on("submit",(function(n){var t,l=i.children().length>0,s=a("#lang_list").val();return o.size<=0&&!l?(""===s?(p(pll_wizard_params.i18n_no_language_added),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))):(p(pll_wizard_params.i18n_add_language_needed),_(r.next("span.ui-selectmenu-button")),m(a("#add-language"))),!1):""!==s?(o.has(s)?(p(pll_wizard_params.i18n_language_already_added),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))):g.dialog("open"),!1):((t=d).prop("disabled",!0),void e.append(a("
").prop({type:"hidden",name:t.prop("name"),value:t.prop("value")})))}));var f=new URLSearchParams(document.location.search);function h(n){switch(n){case"yes":var t=a("#lang_list").children(":selected");u({locale:t[0].value,text:t[0].innerText,name:a(t).data("language-name"),flagUrl:a(t).data("flag-html")});break;case"no":r.val("")}g.dialog("close"),"ignore"===n?m(a("#lang_list-button")):e.submit()}f.has("activate_error")&&void 0!==pll_wizard_params[f.get("activate_error")]&&p(pll_wizard_params[f.get("activate_error")]),g.dialog({autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:pll_wizard_params.i18n_dialog_title,minWidth:600,maxWidth:"100%",open:function(e,n){a("body").hasClass("rtl")&&a(this).parent().css({right:a(this).parent().css("left"),left:"auto"}),a(this).find("#dialog-language").text(a("#lang_list").children(":selected").first().text()),a(this).find("#dialog-language-flag").empty().prepend(a("#lang_list").children(":selected").data("flag-html"))},buttons:[{text:pll_wizard_params.i18n_dialog_yes_button,click:function(a){h("yes")}},{text:pll_wizard_params.i18n_dialog_no_button,click:function(a){h("no")}},{text:pll_wizard_params.i18n_dialog_ignore_button,click:function(a){h("ignore")}}]})}));
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/js/build/nav-menu.js b/wp-content/plugins/polylang/js/build/nav-menu.js
new file mode 100644
index 0000000000..723ebe01e7
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/nav-menu.js
@@ -0,0 +1,105 @@
+/**
+ * Handles the options in the language switcher nav menu metabox.
+ *
+ * @package Polylang
+ */
+
+jQuery(
+ function ( $ ) {
+ $( '#update-nav-menu' ).on(
+ 'click',
+ function ( e ) {
+ if ( e.target && e.target.className && -1 != e.target.className.indexOf( 'item-edit' ) ) {
+ $( "input[value='#pll_switcher'][type=text]" ).parent().parent().parent().each(
+ function () {
+ var item = $( this ).attr( 'id' ).substring( 19 );
+ $( this ).children( 'p:not( .field-move )' ).remove(); // remove default fields we don't need
+
+ // item is a number part of id of parent menu item built by WordPress
+ // pll_data is built server side with i18n strings without HTML and data retrieved from post meta
+ // the usage of attr method is safe before append call.
+ h = $( '
' ).attr(
+ {
+ type: 'hidden',
+ id: 'edit-menu-item-title-' + item,
+ name: 'menu-item-title[' + item + ']',
+ value: pll_data.title
+ }
+ );
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+
+ h = $( '
' ).attr(
+ {
+ type: 'hidden',
+ id: 'edit-menu-item-url-' + item,
+ name: 'menu-item-url[' + item + ']',
+ value: '#pll_switcher'
+ }
+ );
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+
+ // a hidden field which exits only if our jQuery code has been executed
+ h = $( '
' ).attr(
+ {
+ type: 'hidden',
+ id: 'edit-menu-item-pll-detect-' + item,
+ name: 'menu-item-pll-detect[' + item + ']',
+ value: 1
+ }
+ );
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+
+ ids = Array( 'hide_if_no_translation', 'hide_current', 'force_home', 'show_flags', 'show_names', 'dropdown' ); // reverse order
+
+ // add the fields
+ for ( var i = 0, idsLength = ids.length; i < idsLength; i++ ) {
+ p = $( '
' ).attr( 'class', 'description' );
+ // p is hardcoded just above by using attr method which is safe.
+ $( this ).prepend( p ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ // item is a number part of id of parent menu item built by WordPress
+ // pll_data is built server side with i18n strings without HTML
+ label = $( '' ).attr( 'for', 'edit-menu-item-' + ids[ i ] + '-' + item ).text( ' ' + pll_data.strings[ ids[ i ] ] );
+ p.append( label ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ cb = $( ' ' ).attr(
+ {
+ type: 'checkbox',
+ id: 'edit-menu-item-' + ids[ i ] + '-' + item,
+ name: 'menu-item-' + ids[ i ] + '[' + item + ']',
+ value: 1
+ }
+ );
+ if ( ( typeof( pll_data.val[ item ] ) != 'undefined' && pll_data.val[ item ][ ids[ i ] ] == 1 ) || ( typeof( pll_data.val[ item ] ) == 'undefined' && ids[ i ] == 'show_names' ) ) { // show_names as default value
+ cb.prop( 'checked', true );
+ }
+ // See reasons above. Checkbox are totally hardcoded here with safe value
+ label.prepend( cb ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ }
+ }
+ );
+
+ // disallow unchecking both show names and show flags
+ $( '.menu-item-data-object-id' ).each(
+ function () {
+ var id = $( this ).val();
+ var options = ['names-', 'flags-'];
+ $.each(
+ options,
+ function ( i, v ) {
+ $( '#edit-menu-item-show_' + v + id ).on(
+ 'change',
+ function () {
+ if ( true != $( this ).prop( 'checked' ) ) {
+ $( '#edit-menu-item-show_' + options[ 1 - i ] + id ).prop( 'checked', true );
+ }
+ }
+ );
+ }
+ );
+ }
+ );
+ }
+ }
+ );
+ }
+);
+
diff --git a/wp-content/plugins/polylang/js/build/nav-menu.min.js b/wp-content/plugins/polylang/js/build/nav-menu.min.js
new file mode 100644
index 0000000000..47d9d0d857
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/nav-menu.min.js
@@ -0,0 +1 @@
+jQuery((function(e){e("#update-nav-menu").on("click",(function(t){t.target&&t.target.className&&-1!=t.target.className.indexOf("item-edit")&&(e("input[value='#pll_switcher'][type=text]").parent().parent().parent().each((function(){var t=e(this).attr("id").substring(19);e(this).children("p:not( .field-move )").remove(),h=e(" ").attr({type:"hidden",id:"edit-menu-item-title-"+t,name:"menu-item-title["+t+"]",value:pll_data.title}),e(this).append(h),h=e(" ").attr({type:"hidden",id:"edit-menu-item-url-"+t,name:"menu-item-url["+t+"]",value:"#pll_switcher"}),e(this).append(h),h=e(" ").attr({type:"hidden",id:"edit-menu-item-pll-detect-"+t,name:"menu-item-pll-detect["+t+"]",value:1}),e(this).append(h),ids=Array("hide_if_no_translation","hide_current","force_home","show_flags","show_names","dropdown");for(var i=0,a=ids.length;i").attr("class","description"),e(this).prepend(p),label=e("").attr("for","edit-menu-item-"+ids[i]+"-"+t).text(" "+pll_data.strings[ids[i]]),p.append(label),cb=e(" ").attr({type:"checkbox",id:"edit-menu-item-"+ids[i]+"-"+t,name:"menu-item-"+ids[i]+"["+t+"]",value:1}),(void 0!==pll_data.val[t]&&1==pll_data.val[t][ids[i]]||void 0===pll_data.val[t]&&"show_names"==ids[i])&&cb.prop("checked",!0),label.prepend(cb)})),e(".menu-item-data-object-id").each((function(){var t=e(this).val(),i=["names-","flags-"];e.each(i,(function(a,n){e("#edit-menu-item-show_"+n+t).on("change",(function(){1!=e(this).prop("checked")&&e("#edit-menu-item-show_"+i[1-a]+t).prop("checked",!0)}))}))})))}))}));
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/js/build/post.js b/wp-content/plugins/polylang/js/build/post.js
new file mode 100644
index 0000000000..14c3cd4b08
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/post.js
@@ -0,0 +1,184 @@
+/**
+ * @package Polylang
+ */
+
+/**
+ * Tag suggest in quick edit
+ */
+jQuery(
+ function ( $ ) {
+ $.ajaxPrefilter(
+ function ( options, originalOptions, jqXHR ) {
+ if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=ajax-tag-search' ) && ( lang = $( ':input[name="inline_lang_choice"]' ).val() ) ) {
+ options.data = 'lang=' + lang + '&' + options.data;
+ }
+ }
+ );
+ }
+);
+
+/**
+ * Quick edit
+ */
+jQuery(
+ function ( $ ) {
+ const handleQuickEditInsertion = ( mutationsList ) => {
+ for ( const mutation of mutationsList ) {
+ const addedNodes = Array.from( mutation.addedNodes ).filter( el => el.nodeType === Node.ELEMENT_NODE )
+ const form = addedNodes[0];
+ if ( 0 < mutation.addedNodes.length && form.classList.contains( 'inline-editor' ) ) {
+ // WordPress has inserted the quick edit form.
+ const post_id = Number( form.id.substring( 5 ) );
+
+ if ( post_id > 0 ) {
+ // Get the language dropdown.
+ const select = form.querySelector( 'select[name="inline_lang_choice"]' );
+ const lang = document.querySelector( '#lang_' + String( post_id ) ).innerHTML;
+ select.value = lang; // Populates the dropdown with the post language.
+
+ filter_terms( lang ); // Initial filter for category checklist.
+ filter_pages( lang ); // Initial filter for parent dropdown.
+
+ // Modify category checklist and parent dropdown on language change.
+ select.addEventListener(
+ 'change',
+ function ( event ) {
+ const newLang = event.target.value;
+ filter_terms( newLang );
+ filter_pages( newLang );
+ }
+ );
+ }
+ }
+ /**
+ * Filters the category checklist.
+ */
+ function filter_terms( lang ) {
+ if ( "undefined" != typeof( pll_term_languages ) ) {
+ $.each(
+ pll_term_languages,
+ function ( lg, term_tax ) {
+ $.each(
+ term_tax,
+ function ( tax, terms ) {
+ $.each(
+ terms,
+ function ( i ) {
+ id = '#' + tax + '-' + pll_term_languages[ lg ][ tax ][ i ];
+ lang == lg ? $( id ).show() : $( id ).hide();
+ }
+ );
+ }
+ );
+ }
+ );
+ }
+ }
+
+ /**
+ * Filters the parent page dropdown list.
+ */
+ function filter_pages( lang ) {
+ if ( "undefined" != typeof( pll_page_languages ) ) {
+ $.each(
+ pll_page_languages,
+ function ( lg, pages ) {
+ $.each(
+ pages,
+ function ( i ) {
+ v = $( '#post_parent option[value="' + pll_page_languages[ lg ][ i ] + '"]' );
+ lang == lg ? v.show() : v.hide();
+ }
+ );
+ }
+ );
+ }
+ }
+ }
+ }
+ const table = document.getElementById( 'the-list' );
+ const config = { childList: true, subtree: true };
+ const observer = new MutationObserver( handleQuickEditInsertion );
+
+ observer.observe( table, config);
+ }
+);
+
+/**
+ * Update rows of translated posts when the language is modified in quick edit
+ * Acts on ajaxSuccess event
+ */
+jQuery(
+ function ( $ ) {
+ $( document ).ajaxSuccess(
+ function ( event, xhr, settings ) {
+ function update_rows( post_id ) {
+ // collect old translations
+ var translations = new Array();
+ $( '.translation_' + post_id ).each(
+ function () {
+ translations.push( $( this ).parent().parent().attr( 'id' ).substring( 5 ) );
+ }
+ );
+
+ var data = {
+ action: 'pll_update_post_rows',
+ post_id: post_id,
+ translations: translations.join( ',' ),
+ post_type: $( "input[name='post_type']" ).val(),
+ screen: $( "input[name='screen']" ).val(),
+ _pll_nonce: $( "input[name='_inline_edit']" ).val() // reuse quick edit nonce
+ };
+
+ // get the modified rows in ajax and update them
+ $.post(
+ ajaxurl,
+ data,
+ function ( response ) {
+ if ( response ) {
+ // Since WP changeset #52710 parseAjaxResponse() return content to notice the user in a HTML tag with ajax-response id.
+ // Not to disturb this behaviour by executing another ajax request in the ajaxSuccess event, we need to target another unexisting id.
+ var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
+ $.each(
+ res.responses,
+ function () {
+ if ( 'row' == this.what ) {
+ // data is built with a call to WP_Posts_List_Table::single_row method
+ // which uses internally other WordPress methods which escape correctly values.
+ // For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::post_column method.
+ $( "#post-" + this.supplemental.post_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
+ }
+ }
+ );
+ }
+ }
+ );
+ }
+
+ if ( 'string' == typeof( settings.data ) ) { // Need to check the type due to block editor sometime sending FormData objects
+ var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
+ if ( 'undefined' != typeof( data['action'] ) && 'inline-save' == data['action'] ) {
+ update_rows( data['post_ID'] );
+ }
+ }
+ }
+ );
+ }
+);
+
+/**
+ * Media list table
+ * When clicking on attach link, filters find post list per media language
+ */
+jQuery(
+ function ( $ ) {
+ $.ajaxPrefilter(
+ function ( options, originalOptions, jqXHR ) {
+ if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=find_posts' ) ) {
+ options.data = 'pll_post_id=' + $( '#affected' ).val() + '&' + options.data;
+ }
+ }
+ );
+ }
+);
+
diff --git a/wp-content/plugins/polylang/js/build/post.min.js b/wp-content/plugins/polylang/js/build/post.min.js
new file mode 100644
index 0000000000..0e9e57145f
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/post.min.js
@@ -0,0 +1 @@
+jQuery((function(n){n.ajaxPrefilter((function(e,t,a){"string"==typeof e.data&&-1!==e.data.indexOf("action=ajax-tag-search")&&(lang=n(':input[name="inline_lang_choice"]').val())&&(e.data="lang="+lang+"&"+e.data)}))})),jQuery((function(n){const e=document.getElementById("the-list");new MutationObserver((e=>{for(const i of e){const o=Array.from(i.addedNodes).filter((n=>n.nodeType===Node.ELEMENT_NODE))[0];if(00){const l=o.querySelector('select[name="inline_lang_choice"]'),c=document.querySelector("#lang_"+String(s)).innerHTML;l.value=c,t(c),a(c),l.addEventListener("change",(function(n){const e=n.target.value;t(e),a(e)}))}}function t(e){"undefined"!=typeof pll_term_languages&&n.each(pll_term_languages,(function(t,a){n.each(a,(function(a,i){n.each(i,(function(i){id="#"+a+"-"+pll_term_languages[t][a][i],e==t?n(id).show():n(id).hide()}))}))}))}function a(e){"undefined"!=typeof pll_page_languages&&n.each(pll_page_languages,(function(t,a){n.each(a,(function(a){v=n('#post_parent option[value="'+pll_page_languages[t][a]+'"]'),e==t?v.show():v.hide()}))}))}}})).observe(e,{childList:!0,subtree:!0})})),jQuery((function(n){n(document).ajaxSuccess((function(e,t,a){if("string"==typeof a.data){var i=wpAjax.unserialize(a.data);void 0!==i.action&&"inline-save"==i.action&&function(e){var t=new Array;n(".translation_"+e).each((function(){t.push(n(this).parent().parent().attr("id").substring(5))}));var a={action:"pll_update_post_rows",post_id:e,translations:t.join(","),post_type:n("input[name='post_type']").val(),screen:n("input[name='screen']").val(),_pll_nonce:n("input[name='_inline_edit']").val()};n.post(ajaxurl,a,(function(e){if(e){var t=wpAjax.parseAjaxResponse(e,"pll-ajax-response");n.each(t.responses,(function(){"row"==this.what&&n("#post-"+this.supplemental.post_id).replaceWith(this.data)}))}}))}(i.post_ID)}}))})),jQuery((function(n){n.ajaxPrefilter((function(e,t,a){"string"==typeof e.data&&-1!==e.data.indexOf("action=find_posts")&&(e.data="pll_post_id="+n("#affected").val()+"&"+e.data)}))}));
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/js/build/term.js b/wp-content/plugins/polylang/js/build/term.js
new file mode 100644
index 0000000000..88fe5c054f
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/term.js
@@ -0,0 +1,232 @@
+/**
+ * @package Polylang
+ */
+
+/**
+ * Quick edit
+ */
+jQuery(
+ function ( $ ) {
+ const handleQuickEditInsertion = ( mutationsList ) => {
+ for ( const mutation of mutationsList ) {
+ const addedNodes = Array.from( mutation.addedNodes ).filter( el => el.nodeType === Node.ELEMENT_NODE )
+ const form = addedNodes[0];
+ if ( 0 < mutation.addedNodes.length && form.classList.contains( 'inline-edit-row' ) ) {
+ // WordPress has inserted the quick edit form.
+ const term_id = Number( form.id.substring( 5 ) );
+
+ if ( term_id > 0 ) {
+ // Get the language dropdown.
+ const select = form.querySelector( 'select[name="inline_lang_choice"]' );
+ const lang = document.querySelector( '#lang_' + String( term_id ) ).innerHTML;
+ select.value = lang; // Populates the dropdown with the post language.
+
+ // Disable the language dropdown for default categories.
+ const default_cat = document.querySelector( `#default_cat_${term_id}` )?.innerHTML;
+ if ( term_id == default_cat ) {
+ select.disabled = true;
+ }
+ }
+ }
+ }
+ }
+ const table = document.getElementById( 'the-list' );
+ if ( null !== table ) {
+ // Ensure the table is displayed before listening to any change.
+ const config = { childList: true, subtree: true };
+ const observer = new MutationObserver( handleQuickEditInsertion );
+
+ observer.observe( table, config);
+ }
+ }
+);
+
+/**
+ * Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit.
+ * Acts on ajaxSuccess event.
+ */
+jQuery(
+ function ( $ ) {
+ $( document ).ajaxSuccess(
+ function ( event, xhr, settings ) {
+ function update_rows( term_id ) {
+ // collect old translations
+ var translations = new Array();
+ $( '.translation_' + term_id ).each(
+ function () {
+ translations.push( $( this ).parent().parent().attr( 'id' ).substring( 4 ) );
+ }
+ );
+
+ var data = {
+ action: 'pll_update_term_rows',
+ term_id: term_id,
+ translations: translations.join( ',' ),
+ taxonomy: $( "input[name='taxonomy']" ).val(),
+ post_type: $( "input[name='post_type']" ).val(),
+ screen: $( "input[name='screen']" ).val(),
+ _pll_nonce: $( '#_pll_nonce' ).val()
+ };
+
+ // get the modified rows in ajax and update them
+ $.post(
+ ajaxurl,
+ data,
+ function ( response ) {
+ if ( response ) {
+ // Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
+ var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
+ $.each(
+ res.responses,
+ function () {
+ if ( 'row' == this.what ) {
+ // data is built with a call to WP_Terms_List_Table::single_row method
+ // which uses internally other WordPress methods which escape correctly values.
+ // For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::term_column method.
+ $( "#tag-" + this.supplemental.term_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
+ }
+ }
+ );
+ }
+ }
+ );
+ }
+
+ var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
+ if ( 'undefined' != typeof( data['action'] ) ) {
+ switch ( data['action'] ) {
+ // when adding a term, the new term_id is in the ajax response
+ case 'add-tag':
+ // Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
+ res = wpAjax.parseAjaxResponse( xhr.responseXML, 'pll-ajax-response' );
+ $.each(
+ res.responses,
+ function () {
+ if ( 'term' == this.what ) {
+ update_rows( this.supplemental.term_id );
+ }
+ }
+ );
+
+ // and also reset translations hidden input fields
+ $( '.htr_lang' ).val( 0 );
+ break;
+
+ // when deleting a term
+ case 'delete-tag':
+ update_rows( data['tag_ID'] );
+ break;
+
+ // in case the language is modified in quick edit and breaks translations
+ case 'inline-save-tax':
+ update_rows( data['tax_ID'] );
+ break;
+ }
+ }
+ }
+ );
+ }
+);
+
+jQuery(
+ function ( $ ) {
+ // translations autocomplete input box
+ function init_translations() {
+ $( '.tr_lang' ).each(
+ function () {
+ var tr_lang = $( this ).attr( 'id' ).substring( 8 );
+ var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
+
+ $( this ).autocomplete(
+ {
+ minLength: 0,
+ source: ajaxurl + '?action=pll_terms_not_translated' +
+ '&term_language=' + $( '#term_lang_choice' ).val() +
+ '&term_id=' + $( "input[name='tag_ID']" ).val() +
+ '&taxonomy=' + $( "input[name='taxonomy']" ).val() +
+ '&translation_language=' + tr_lang +
+ '&post_type=' + typenow +
+ '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
+ select: function ( event, ui ) {
+ $( '#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
+ $( this ).on(
+ 'blur',
+ function () {
+ if ( ! $( this ).val() ) {
+ $( '#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
+ }
+ }
+ );
+ }
+ );
+ }
+
+ init_translations();
+
+ // ajax for changing the term's language
+ $( '#term_lang_choice' ).on(
+ 'change',
+ function () {
+ var value = $( this ).val();
+ var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
+ var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' );
+
+ var data = {
+ action: 'term_lang_choice',
+ lang: value,
+ from_tag: $( "input[name='from_tag']" ).val(),
+ term_id: $( "input[name='tag_ID']" ).val(),
+ taxonomy: $( "input[name='taxonomy']" ).val(),
+ post_type: typenow,
+ _pll_nonce: $( '#_pll_nonce' ).val()
+ };
+
+ $.post(
+ ajaxurl,
+ data,
+ function ( response ) {
+ // Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
+ var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
+ $.each(
+ res.responses,
+ function () {
+ switch ( this.what ) {
+ case 'translations': // translations fields
+ // Data is built and come from server side and is well escaped when necessary
+ $( "#term-translations" ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ init_translations();
+ break;
+ case 'parent': // parent dropdown list for hierarchical taxonomies
+ // data correctly escaped in PLL_Admin_Filters_Term::term_lang_choice method which uses wp_dropdown_categories function.
+ $( '#parent' ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
+ break;
+ case 'tag_cloud': // popular items
+ // data correctly escaped in PLL_Admin_Filters_Term::term_lang_choice method which uses wp_tag_cloud and wp_generate_tag_cloud functions.
+ $( '.tagcloud' ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
+ break;
+ case 'flag': // flag in front of the select dropdown
+ // Data is built and come from server side and is well escaped when necessary
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ break;
+ }
+ }
+ );
+
+ // Modifies the text direction
+ $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
+ }
+ );
+ }
+ );
+ }
+);
+
diff --git a/wp-content/plugins/polylang/js/build/term.min.js b/wp-content/plugins/polylang/js/build/term.min.js
new file mode 100644
index 0000000000..4a5fc94036
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/term.min.js
@@ -0,0 +1 @@
+jQuery((function(t){const a=t=>{for(const a of t){const t=Array.from(a.addedNodes).filter((t=>t.nodeType===Node.ELEMENT_NODE))[0];if(00){const e=t.querySelector('select[name="inline_lang_choice"]'),n=document.querySelector("#lang_"+String(a)).innerHTML;e.value=n;const l=document.querySelector(`#default_cat_${a}`)?.innerHTML;a==l&&(e.disabled=!0)}}}},e=document.getElementById("the-list");if(null!==e){const t={childList:!0,subtree:!0};new MutationObserver(a).observe(e,t)}})),jQuery((function(t){t(document).ajaxSuccess((function(a,e,n){function l(a){var e=new Array;t(".translation_"+a).each((function(){e.push(t(this).parent().parent().attr("id").substring(4))}));var n={action:"pll_update_term_rows",term_id:a,translations:e.join(","),taxonomy:t("input[name='taxonomy']").val(),post_type:t("input[name='post_type']").val(),screen:t("input[name='screen']").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,n,(function(a){if(a){var e=wpAjax.parseAjaxResponse(a,"pll-ajax-response");t.each(e.responses,(function(){"row"==this.what&&t("#tag-"+this.supplemental.term_id).replaceWith(this.data)}))}}))}var s=wpAjax.unserialize(n.data);if(void 0!==s.action)switch(s.action){case"add-tag":res=wpAjax.parseAjaxResponse(e.responseXML,"pll-ajax-response"),t.each(res.responses,(function(){"term"==this.what&&l(this.supplemental.term_id)})),t(".htr_lang").val(0);break;case"delete-tag":l(s.tag_ID);break;case"inline-save-tax":l(s.tax_ID)}}))})),jQuery((function(t){function a(){t(".tr_lang").each((function(){var a=t(this).attr("id").substring(8),e=t(this).parent().parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_terms_not_translated&term_language="+t("#term_lang_choice").val()+"&term_id="+t("input[name='tag_ID']").val()+"&taxonomy="+t("input[name='taxonomy']").val()+"&translation_language="+a+"&post_type="+typenow+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(n,l){t("#htr_lang_"+a).val(l.item.id),e.html(l.item.link)}}),t(this).on("blur",(function(){t(this).val()||(t("#htr_lang_"+a).val(0),e.html(e.siblings(".hidden").children().clone()))}))}))}a(),t("#term_lang_choice").on("change",(function(){var e=t(this).val(),n=t(this).children('option[value="'+e+'"]').attr("lang"),l=t('.pll-translation-column > span[lang="'+n+'"]').attr("dir"),s={action:"term_lang_choice",lang:e,from_tag:t("input[name='from_tag']").val(),term_id:t("input[name='tag_ID']").val(),taxonomy:t("input[name='taxonomy']").val(),post_type:typenow,_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,s,(function(e){var n=wpAjax.parseAjaxResponse(e,"pll-ajax-response");t.each(n.responses,(function(){switch(this.what){case"translations":t("#term-translations").html(this.data),a();break;case"parent":t("#parent").replaceWith(this.data);break;case"tag_cloud":t(".tagcloud").replaceWith(this.data);break;case"flag":t(".pll-select-flag").html(this.data)}})),t("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+l)}))}))}));
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/js/build/user.js b/wp-content/plugins/polylang/js/build/user.js
new file mode 100644
index 0000000000..03e6714a04
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/user.js
@@ -0,0 +1,34 @@
+/**
+ * Adds one biography input field per language in the user profile.
+ *
+ * @package Polylang
+ */
+
+jQuery(
+ function ( $ ) {
+ // biography
+ // FIXME there is probably a more efficient way to do this
+ var td = $( '#description' ).parent();
+ var d = $( '#description' ).clone();
+ var span = td.children( '.description' ).clone();
+ td.children().remove();
+
+ $( '.biography' ).each(
+ function () {
+ lang = $( this ).attr( 'name' ).split( '___' );
+ desc = d.clone();
+ desc.attr( 'name', 'description_' + lang[0] );
+ desc.attr( 'id', 'description_' + lang[0] );
+ // Whitelist because description and lang value is already escaped by the side of PHP
+ desc.html( $( this ).val() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ td.append( $( '
' ).text( lang[1] ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ td.append( desc ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ }
+ );
+
+ td.append( ' ' );
+ // Whitelist because description come from html code generated by WordPress
+ td.append( span ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ }
+);
+
diff --git a/wp-content/plugins/polylang/js/build/user.min.js b/wp-content/plugins/polylang/js/build/user.min.js
new file mode 100644
index 0000000000..5ffd380a5d
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/user.min.js
@@ -0,0 +1 @@
+jQuery((function(e){var n=e("#description").parent(),i=e("#description").clone(),t=n.children(".description").clone();n.children().remove(),e(".biography").each((function(){lang=e(this).attr("name").split("___"),desc=i.clone(),desc.attr("name","description_"+lang[0]),desc.attr("id","description_"+lang[0]),desc.html(e(this).val()),n.append(e("
").text(lang[1])),n.append(desc)})),n.append(" "),n.append(t)}));
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/js/build/widgets.js b/wp-content/plugins/polylang/js/build/widgets.js
new file mode 100644
index 0000000000..3d2ec2c1b4
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/widgets.js
@@ -0,0 +1,152 @@
+/**
+ * Adds a flag to the widgets filtered by a language.
+ *
+ * @package Polylang
+ */
+
+jQuery(
+ function ( $ ) {
+ var widgets_container,
+ widgets_selector,
+ flags,
+ isBlockEditor = 'undefined' !== typeof wp.blockEditor;
+
+ if ( 'undefined' !== typeof pll_widgets && pll_widgets.hasOwnProperty( 'flags' ) ) {
+ flags = pll_widgets.flags;
+ }
+
+ /**
+ * Prepend widget titles with a flag once a language is selected.
+ *
+ * @param {object} widget The widget element.
+ * @return {void} Nothing.
+ */
+ function add_flag( widget ) {
+ if ( ! flags ) {
+ return;
+ }
+ widget = $( widget );
+ var title = isBlockEditor ? widget.prev('h3') : $( '.widget-top .widget-title h3', widget ),
+ locale = $( '.pll-lang-choice option:selected', widget ).val(),
+ // Icon is HTML built and come from server side and is well escaped when necessary
+ icon = ( locale && flags.hasOwnProperty( locale ) ) ? flags[ locale ] : null;
+
+ if ( icon ) {
+ icon += ' ';
+ var current = $( '.pll-lang', title );
+ if ( current.length ) {
+ current.html( icon ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ } else {
+ flag = $( ' ' ).addClass( 'pll-lang' ).html( icon ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
+ // See the comment above about the icon which is safe. So it is also safe to prepend flag which uses icon.
+ title.prepend( flag ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ }
+ } else {
+ $( '.pll-lang', title ).remove();
+ }
+ }
+
+ if ( isBlockEditor ) {
+
+ widgets_container = $( '.edit-widgets-main-block-list' );
+ widgets_selector = '.widget';
+
+ // Update flags when we click on the legacy widget to display its form.
+ widgets_container.on(
+ 'click',
+ '.wp-block-legacy-widget',
+ function () {
+ add_flag( $( this ).find( '.widget' ) );
+ }
+ );
+
+ } else {
+ if ( 'undefined' !== typeof wp.customize ) {
+
+ widgets_container = $( '#customize-controls' );
+ widgets_selector = '.customize-control .widget';
+
+ /**
+ * WP Customizer add control listener.
+ *
+ * @link https://wordpress.stackexchange.com/questions/256536/callback-after-wordpress-customizer-complete-loading
+ *
+ * @param {object} control The control type.
+ * @return {void} Nothing.
+ */
+ function customize_add_flag( control ) {
+ if ( ! control.extended( wp.customize.Widgets.WidgetControl ) ) {
+ return;
+ }
+
+ /*
+ * Make sure the widget's contents are embedded; normally this is done
+ * when the control is expanded, for DOM performance reasons.
+ */
+ control.embedWidgetContent();
+
+ // Now we know for sure the widget is fully embedded.
+ add_flag( control.container.find( '.widget' ) );
+ }
+ wp.customize.control.each( customize_add_flag );
+ wp.customize.control.bind( 'add', customize_add_flag );
+
+ } else {
+
+ widgets_container = $( '#widgets-right' );
+ widgets_selector = '.widget';
+
+ }
+
+ // Add flags on load.
+ $( widgets_selector, widgets_container ).each(
+ function () {
+ add_flag( this );
+ }
+ );
+ }
+
+ // Update flags.
+ widgets_container.on(
+ 'change',
+ '.pll-lang-choice',
+ function () {
+ add_flag( $( this ).parents( '.widget' ) );
+ }
+ );
+
+ function pll_toggle( a, test ) {
+ test ? a.show() : a.hide();
+ }
+
+ // Remove all options if dropdown is checked.
+ $( '.widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list' ).on(
+ 'change',
+ '.pll-dropdown',
+ function () {
+ var this_id = $( this ).parent().parent().parent().children( '.widget-id' ).attr( 'value' );
+ pll_toggle( $( '.no-dropdown-' + this_id ), true != $( this ).prop( 'checked' ) );
+ }
+ );
+
+ // Disallow unchecking both show names and show flags.
+ var options = ['-show_flags', '-show_names'];
+ $.each(
+ options,
+ function ( i, v ) {
+ $( '.widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list' ).on(
+ 'change',
+ '.pll' + v,
+ function () {
+ var this_id = $( this ).parent().parent().parent().children( '.widget-id' ).attr( 'value' );
+ if ( true != $( this ).prop( 'checked' ) ) {
+ $( '#widget-' + this_id + options[ 1 - i ] ).prop( 'checked', true );
+ }
+ }
+ );
+ }
+ );
+
+ }
+);
+
diff --git a/wp-content/plugins/polylang/js/build/widgets.min.js b/wp-content/plugins/polylang/js/build/widgets.min.js
new file mode 100644
index 0000000000..50d281224c
--- /dev/null
+++ b/wp-content/plugins/polylang/js/build/widgets.min.js
@@ -0,0 +1 @@
+jQuery((function(e){var t,i,n,o=void 0!==wp.blockEditor;function l(t){if(n){t=e(t);var i=o?t.prev("h3"):e(".widget-top .widget-title h3",t),l=e(".pll-lang-choice option:selected",t).val(),d=l&&n.hasOwnProperty(l)?n[l]:null;if(d){d+=" ";var s=e(".pll-lang",i);s.length?s.html(d):(flag=e(" ").addClass("pll-lang").html(d),i.prepend(flag))}else e(".pll-lang",i).remove()}}if("undefined"!=typeof pll_widgets&&pll_widgets.hasOwnProperty("flags")&&(n=pll_widgets.flags),o)i=".widget",(t=e(".edit-widgets-main-block-list")).on("click",".wp-block-legacy-widget",(function(){l(e(this).find(".widget"))}));else{if(void 0!==wp.customize){function d(e){e.extended(wp.customize.Widgets.WidgetControl)&&(e.embedWidgetContent(),l(e.container.find(".widget")))}t=e("#customize-controls"),i=".customize-control .widget",wp.customize.control.each(d),wp.customize.control.bind("add",d)}else t=e("#widgets-right"),i=".widget";e(i,t).each((function(){l(this)}))}t.on("change",".pll-lang-choice",(function(){l(e(this).parents(".widget"))})),e(".widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list").on("change",".pll-dropdown",(function(){var t,i=e(this).parent().parent().parent().children(".widget-id").attr("value");t=e(".no-dropdown-"+i),1!=e(this).prop("checked")?t.show():t.hide()}));var s=["-show_flags","-show_names"];e.each(s,(function(t,i){e(".widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list").on("change",".pll"+i,(function(){var i=e(this).parent().parent().parent().children(".widget-id").attr("value");1!=e(this).prop("checked")&&e("#widget-"+i+s[1-t]).prop("checked",!0)}))}))}));
\ No newline at end of file
diff --git a/wp-content/plugins/polylang/modules/machine-translation/load.php b/wp-content/plugins/polylang/modules/machine-translation/load.php
new file mode 100644
index 0000000000..ea9e04fbf0
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/machine-translation/load.php
@@ -0,0 +1,18 @@
+model->has_languages() ) {
+ add_filter(
+ 'pll_settings_modules',
+ function ( $modules ) {
+ $modules[] = 'PLL_Settings_Preview_Machine_Translation';
+ return $modules;
+ }
+ );
+}
diff --git a/wp-content/plugins/polylang/modules/machine-translation/settings-preview-machine-translation.php b/wp-content/plugins/polylang/modules/machine-translation/settings-preview-machine-translation.php
new file mode 100644
index 0000000000..96514330da
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/machine-translation/settings-preview-machine-translation.php
@@ -0,0 +1,46 @@
+ 'machine_translation',
+ 'title' => __( 'Machine Translation', 'polylang' ),
+ 'description' => __( 'Allows linkage to DeepL Translate.', 'polylang' ),
+ 'active_option' => 'preview',
+ );
+
+ parent::__construct( $polylang, array_merge( $default, $args ) );
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/share-slug/load.php b/wp-content/plugins/polylang/modules/share-slug/load.php
new file mode 100644
index 0000000000..771939c6fd
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/share-slug/load.php
@@ -0,0 +1,20 @@
+model->has_languages() ) {
+ add_filter(
+ 'pll_settings_modules',
+ function ( $modules ) {
+ $modules[] = 'PLL_Settings_Preview_Share_Slug';
+ return $modules;
+ }
+ );
+}
diff --git a/wp-content/plugins/polylang/modules/share-slug/settings-preview-share-slug.php b/wp-content/plugins/polylang/modules/share-slug/settings-preview-share-slug.php
new file mode 100644
index 0000000000..17f6913779
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/share-slug/settings-preview-share-slug.php
@@ -0,0 +1,56 @@
+ 'share-slugs',
+ 'title' => __( 'Share slugs', 'polylang' ),
+ 'description' => $this->get_description(),
+ 'active_option' => 'preview',
+ );
+
+ parent::__construct( $polylang, array_merge( $default, $args ) );
+ }
+
+ /**
+ * Returns the module description.
+ *
+ * @since 3.1
+ *
+ * @return string
+ */
+ protected function get_description() {
+ return __( 'Allows to share the same URL slug across languages for posts and terms.', 'polylang' );
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/site-health/admin-site-health.php b/wp-content/plugins/polylang/modules/site-health/admin-site-health.php
new file mode 100644
index 0000000000..c71d93f67c
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/site-health/admin-site-health.php
@@ -0,0 +1,520 @@
+model = &$polylang->model;
+ $this->static_pages = &$polylang->static_pages;
+
+ // Information tab.
+ add_filter( 'debug_information', array( $this, 'info_options' ), 15 );
+ add_filter( 'debug_information', array( $this, 'info_languages' ), 15 );
+ add_filter( 'debug_information', array( $this, 'info' ), 15 );
+
+ // Tests Tab.
+ add_filter( 'site_status_tests', array( $this, 'status_tests' ) );
+ add_filter( 'site_status_test_php_modules', array( $this, 'site_status_test_php_modules' ) ); // Require simplexml in Site health.
+ }
+
+ /**
+ * Returns a list of keys to exclude from the site health information.
+ *
+ * @since 2.8
+ *
+ * @return string[] List of option keys to ignore.
+ */
+ protected function exclude_options_keys() {
+ return array(
+ 'uninstall',
+ 'first_activation',
+ );
+ }
+
+ /**
+ * Returns a list of keys to exclude from the site health information.
+ *
+ * @since 2.8
+ *
+ * @return string[] List of language keys to ignore.
+ */
+ protected function exclude_language_keys() {
+ return array(
+ 'flag',
+ 'host',
+ 'taxonomy',
+ 'description',
+ 'parent',
+ 'filter',
+ 'custom_flag',
+ );
+ }
+
+ /**
+ * Formats an array to display in options information.
+ *
+ * @since 2.8
+ *
+ * @param array $array An array of formatted data.
+ * @return string
+ */
+ protected function format_array( $array ) {
+ array_walk(
+ $array,
+ function ( &$value, $key ) {
+ if ( is_array( $value ) ) {
+ $ids = implode( ' , ', $value );
+ $value = "$key => $ids";
+ } else {
+ $value = "$key => $value";
+ }
+ }
+ );
+ return implode( ' | ', $array );
+ }
+
+ /**
+ * Transforms the option value to readable human sentence.
+ *
+ * @since 3.3
+ *
+ * @param string $key Option name.
+ * @param mixed $value Option value.
+ * @return mixed Option value.
+ */
+ public function format_value( $key, $value ) {
+ switch ( $key ) {
+ case 'browser':
+ if ( ! $value ) {
+ $value = '0: ' . esc_html__( 'Detect browser language deactivated', 'polylang' );
+ break;
+ }
+ $value = '1: ' . esc_html__( 'Detect browser language activated', 'polylang' );
+ break;
+ case 'rewrite':
+ if ( $value ) {
+ $value = '1: ' . esc_html__( 'Remove /language/ in pretty permalinks', 'polylang' );
+ break;
+ }
+ $value = '0: ' . esc_html__( 'Keep /language/ in pretty permalinks', 'polylang' );
+ break;
+ case 'hide_default':
+ if ( $value ) {
+ $value = '1: ' . esc_html__( 'Hide URL language information for default language', 'polylang' );
+ break;
+ }
+ $value = '0: ' . esc_html__( 'Display URL language information for default language', 'polylang' );
+ break;
+ case 'force_lang':
+ switch ( $value ) {
+ case '0':
+ $value = '0: ' . esc_html__( 'The language is set from content', 'polylang' );
+ break;
+ case '1':
+ $value = '1: ' . esc_html__( 'The language is set from the directory name in pretty permalinks', 'polylang' );
+ break;
+ case '2':
+ $value = '2: ' . esc_html__( 'The language is set from the subdomain name in pretty permalinks', 'polylang' );
+ break;
+ case '3':
+ $value = '3: ' . esc_html__( 'The language is set from different domains', 'polylang' );
+ break;
+ }
+ break;
+ case 'redirect_lang':
+ if ( $value ) {
+ $value = '1: ' . esc_html__( 'The front page URL contains the language code instead of the page name or page id', 'polylang' );
+ break;
+ }
+ $value = '0: ' . esc_html__( 'The front page URL contains the page name or page id instead of the language code', 'polylang' );
+
+ break;
+ case 'media_support':
+ if ( ! $value ) {
+ $value = '0: ' . esc_html__( 'The media are not translated', 'polylang' );
+ break;
+ }
+ $value = '1: ' . esc_html__( 'The media are translated', 'polylang' );
+ break;
+
+ case 'sync':
+ if ( empty( $value ) ) {
+ $value = '0: ' . esc_html__( 'Synchronization disabled', 'polylang' );
+ }
+ break;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Add Polylang Options to Site Health Information tab.
+ *
+ * @since 2.8
+ *
+ * @param array $debug_info The debug information to be added to the core information page.
+ * @return array
+ */
+ public function info_options( $debug_info ) {
+ $fields = array();
+
+ foreach ( $this->model->options as $key => $value ) {
+ if ( in_array( $key, $this->exclude_options_keys() ) ) {
+ continue;
+ }
+
+ $value = $this->format_value( $key, $value );
+
+ switch ( $key ) {
+ case 'domains':
+ if ( 3 === $this->model->options['force_lang'] ) {
+ $value = is_array( $value ) ? $value : array();
+ $value = $this->format_array( $value );
+
+ $fields[ $key ]['label'] = $key;
+ $fields[ $key ]['value'] = $value;
+ }
+ break;
+
+ case 'nav_menus':
+ $current_theme = get_stylesheet();
+ if ( is_array( $value ) && isset( $value[ $current_theme ] ) ) {
+ foreach ( $value[ $current_theme ] as $location => $lang ) {
+ $lang = is_array( $lang ) ? $lang : array();
+
+ $fields[ $location ]['label'] = sprintf( 'menu: %s', $location );
+ $fields[ $location ]['value'] = $this->format_array( $lang );
+ }
+ }
+ break;
+
+ case 'media':
+ $value = is_array( $value ) ? $value : array();
+ foreach ( $value as $sub_key => $sub_value ) {
+ $fields[ "$key-$sub_key" ]['label'] = "$key $sub_key";
+ $fields[ "$key-$sub_key" ]['value'] = $sub_value;
+ }
+ break;
+
+ case 'post_types':
+ $fields[ $key ]['label'] = $key;
+ $fields[ $key ]['value'] = implode( ', ', $this->model->get_translated_post_types() );
+ break;
+
+ case 'taxonomies':
+ $fields[ $key ]['label'] = $key;
+ $fields[ $key ]['value'] = implode( ', ', $this->model->get_translated_taxonomies() );
+ break;
+
+ default:
+ $fields[ $key ]['label'] = $key;
+ $fields[ $key ]['value'] = empty( $value ) ? '0' : $value;
+ break;
+ }
+ }
+
+ $debug_info['pll_options'] = array(
+ /* translators: placeholder is the plugin name */
+ 'label' => sprintf( __( '%s options', 'polylang' ), POLYLANG ),
+ 'fields' => $fields,
+ );
+
+ return $debug_info;
+ }
+
+ /**
+ * Adds Polylang Languages settings to Site Health Information tab.
+ *
+ * @since 2.8
+ *
+ * @param array $debug_info The debug information to be added to the core information page.
+ * @return array
+ */
+ public function info_languages( $debug_info ) {
+ foreach ( $this->model->get_languages_list() as $language ) {
+ $fields = array();
+
+ foreach ( $language->to_array() as $key => $value ) {
+ if ( in_array( $key, $this->exclude_language_keys(), true ) ) {
+ continue;
+ }
+
+ if ( empty( $value ) ) {
+ $value = '0';
+ }
+
+ $fields[ $key ]['label'] = $key;
+
+ if ( 'term_props' === $key && is_array( $value ) ) {
+ $fields[ $key ]['value'] = $this->get_info_term_props( $value );
+ } else {
+ $fields[ $key ]['value'] = $value;
+ }
+
+ if ( 'term_group' === $key ) {
+ $fields[ $key ]['label'] = 'order'; // Changed for readability but not translated as other keys are not.
+ }
+ }
+
+ $debug_info[ 'pll_language_' . $language->slug ] = array(
+ /* translators: placeholder is the language name */
+ 'label' => sprintf( __( 'Language: %s', 'polylang' ), $language->name ),
+ /* translators: placeholder is the flag image */
+ 'description' => sprintf( esc_html__( 'Flag used in the language switcher: %s', 'polylang' ), $this->get_flag( $language ) ),
+ 'fields' => $fields,
+ );
+ }
+
+ return $debug_info;
+ }
+
+ /**
+ * Adds term props data to the info languages array.
+ *
+ * @since 3.4
+ *
+ * @param array $value The term props data.
+ * @return array The term props data formatted for the info languages tab.
+ */
+ protected function get_info_term_props( $value ) {
+ $return_value = array();
+
+ foreach ( $value as $language_taxonomy => $item ) {
+ $language_taxonomy_array = array_fill( 0, count( $item ), $language_taxonomy );
+
+ $keys_with_language_taxonomy = array_map(
+ function ( $key, $language_taxonomy ) {
+ return "{$language_taxonomy}/{$key}";
+ },
+ array_keys( $item ),
+ $language_taxonomy_array
+ );
+
+ $value = array_combine( $keys_with_language_taxonomy, $item );
+ if ( is_array( $value ) ) {
+ $return_value = array_merge( $return_value, $value );
+ }
+ }
+ return $return_value;
+ }
+
+ /**
+ * Returns the flag used in the language switcher.
+ *
+ * @since 2.8
+ *
+ * @param PLL_Language $language Language object.
+ * @return string
+ */
+ protected function get_flag( $language ) {
+ $flag = $language->get_display_flag();
+ return empty( $flag ) ? '' . esc_html__( 'Undefined', 'polylang' ) . ' ' : $flag;
+ }
+
+ /**
+ * Add a Site Health test on homepage translation.
+ *
+ * @since 2.8
+ *
+ * @param array $tests Array with tests declaration data.
+ * @return array
+ */
+ public function status_tests( $tests ) {
+ // Add the test only if the homepage displays static page.
+ if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
+ $tests['direct']['pll_homepage'] = array(
+ 'label' => esc_html__( 'Homepage translated', 'polylang' ),
+ 'test' => array( $this, 'homepage_test' ),
+ );
+ }
+ return $tests;
+ }
+
+ /**
+ * Test if the home page is translated or not.
+ *
+ * @since 2.8
+ *
+ * @return array $result Array with test results.
+ */
+ public function homepage_test() {
+ $result = array(
+ 'label' => esc_html__( 'All languages have a translated homepage', 'polylang' ),
+ 'status' => 'good',
+ 'badge' => array(
+ 'label' => POLYLANG,
+ 'color' => 'blue',
+ ),
+ 'description' => sprintf(
+ '%s
',
+ esc_html__( 'It is mandatory to translate the static front page in all languages.', 'polylang' )
+ ),
+ 'actions' => '',
+ 'test' => 'pll_homepage',
+ );
+
+ $message = $this->static_pages->get_must_translate_message();
+
+ if ( ! empty( $message ) ) {
+ $result['status'] = 'critical';
+ $result['label'] = esc_html__( 'The homepage is not translated in all languages', 'polylang' );
+ $result['description'] = sprintf( '%s
', $message );
+ }
+ return $result;
+ }
+
+ /**
+ * Add Polylang Warnings to Site Health Information tab.
+ *
+ * @since 3.1
+ *
+ * @param array $debug_info The debug information to be added to the core information page.
+ * @return array
+ */
+ public function info( $debug_info ) {
+ $fields = array();
+
+ // Add Post Types without languages.
+ $posts_no_lang = $this->get_post_ids_without_lang();
+
+ if ( ! empty( $posts_no_lang ) ) {
+ $fields['post-no-lang']['label'] = __( 'Posts without language', 'polylang' );
+ $fields['post-no-lang']['value'] = $this->format_array( $posts_no_lang );
+ }
+
+ $terms_no_lang = $this->get_term_ids_without_lang();
+
+ if ( ! empty( $terms_no_lang ) ) {
+ $fields['term-no-lang']['label'] = __( 'Terms without language', 'polylang' );
+ $fields['term-no-lang']['value'] = $this->format_array( $terms_no_lang );
+ }
+
+ // Add WPML files.
+ $wpml_files = PLL_WPML_Config::instance()->get_files();
+ if ( ! empty( $wpml_files ) ) {
+ $fields['wpml']['label'] = 'wpml-config.xml files';
+ $fields['wpml']['value'] = $wpml_files;
+
+ if ( ! extension_loaded( 'simplexml' ) ) {
+ $fields['simplexml']['label'] = __( 'PHP SimpleXML extension', 'polylang' );
+ $fields['simplexml']['value'] = __( 'Not loaded. Contact your host provider.', 'polylang' );
+ }
+ }
+
+ // Create the section.
+ if ( ! empty( $fields ) ) {
+ $debug_info['pll_warnings'] = array(
+ /* translators: placeholder is the plugin name */
+ 'label' => sprintf( __( '%s information', 'polylang' ), POLYLANG ),
+ 'fields' => $fields,
+ );
+ }
+
+ return $debug_info;
+ }
+
+ /**
+ * Get an array with post_type as key and post ids as value.
+ *
+ * @since 3.1
+ *
+ * @param int $limit Max number of posts to show per post type. `-1` to return all of them. Default is 5.
+ * @return int[][] Array containing an array of post ids.
+ *
+ * @phpstan-param -1|positive-int $limit
+ */
+ public function get_post_ids_without_lang( $limit = 5 ) {
+ $posts = array();
+
+ foreach ( $this->model->get_translated_post_types() as $post_type ) {
+ $post_ids_with_no_language = $this->model->get_posts_with_no_lang( $post_type, $limit );
+
+ if ( ! empty( $post_ids_with_no_language ) ) {
+ foreach ( $post_ids_with_no_language as $id ) {
+ $posts[ $post_type ][] = $id;
+ }
+ }
+ }
+
+ return $posts;
+ }
+
+ /**
+ * Get an array with taxonomy as key and term ids as value.
+ *
+ * @since 3.1
+ *
+ * @param int $limit Max number of terms to show per post type. `-1` to return all of them. Default is 5.
+ * @return int[][] Array containing an array of term ids.
+ *
+ * @phpstan-param -1|positive-int $limit
+ */
+ public function get_term_ids_without_lang( $limit = 5 ) {
+ $terms = array();
+
+ foreach ( $this->model->get_translated_taxonomies() as $taxonomy ) {
+ $term_ids_with_no_language = $this->model->get_terms_with_no_lang( $taxonomy, $limit );
+
+ if ( ! empty( $term_ids_with_no_language ) ) {
+ foreach ( $term_ids_with_no_language as $id ) {
+ $terms[ $taxonomy ][] = $id;
+ }
+ }
+ }
+
+ return $terms;
+ }
+
+ /**
+ * Requires the simplexml PHP module when a wpml-config.xml has been found.
+ *
+ * @since 3.1
+ * @since 3.2 Moved from PLL_WPML_Config
+ *
+ * @param array $modules An associative array of modules to test for.
+ * @return array
+ */
+ public function site_status_test_php_modules( $modules ) {
+ $files = PLL_WPML_Config::instance()->get_files();
+ if ( ! empty( $files ) ) {
+ $modules['simplexml'] = array(
+ 'extension' => 'simplexml',
+ 'required' => true,
+ );
+ }
+ return $modules;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/site-health/load.php b/wp-content/plugins/polylang/modules/site-health/load.php
new file mode 100644
index 0000000000..09edf1f2a7
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/site-health/load.php
@@ -0,0 +1,14 @@
+model->has_languages() ) {
+ $polylang->site_health = new PLL_Admin_Site_Health( $polylang );
+}
diff --git a/wp-content/plugins/polylang/modules/sitemaps/abstract-sitemaps.php b/wp-content/plugins/polylang/modules/sitemaps/abstract-sitemaps.php
new file mode 100644
index 0000000000..a74e60e38b
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sitemaps/abstract-sitemaps.php
@@ -0,0 +1,37 @@
+ 'class-wp-sitemaps-posts' );
+ return $whitelist;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sitemaps/load.php b/wp-content/plugins/polylang/modules/sitemaps/load.php
new file mode 100644
index 0000000000..9185676aa6
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sitemaps/load.php
@@ -0,0 +1,17 @@
+model->has_languages() ) {
+ if ( $polylang->links_model instanceof PLL_Links_Abstract_Domain ) {
+ $polylang->sitemaps = new PLL_Sitemaps_Domain( $polylang );
+ } else {
+ $polylang->sitemaps = new PLL_Sitemaps( $polylang );
+ }
+ $polylang->sitemaps->init();
+}
diff --git a/wp-content/plugins/polylang/modules/sitemaps/multilingual-sitemaps-provider.php b/wp-content/plugins/polylang/modules/sitemaps/multilingual-sitemaps-provider.php
new file mode 100644
index 0000000000..535adc79c5
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sitemaps/multilingual-sitemaps-provider.php
@@ -0,0 +1,218 @@
+name = $provider->name;
+ $this->object_type = $provider->object_type;
+
+ $this->provider = $provider;
+ $this->links_model = &$links_model;
+ $this->model = &$links_model->model;
+ }
+
+ /**
+ * Gets a URL list for a sitemap.
+ *
+ * @since 2.8
+ *
+ * @param int $page_num Page of results.
+ * @param string $object_subtype Optional. Object subtype name. Default empty.
+ * @return array Array of URLs for a sitemap.
+ */
+ public function get_url_list( $page_num, $object_subtype = '' ) {
+ return $this->provider->get_url_list( $page_num, $object_subtype );
+ }
+
+ /**
+ * Gets the max number of pages available for the object type.
+ *
+ * @since 2.8
+ *
+ * @param string $object_subtype Optional. Object subtype. Default empty.
+ * @return int Total number of pages.
+ */
+ public function get_max_num_pages( $object_subtype = '' ) {
+ return $this->provider->get_max_num_pages( $object_subtype );
+ }
+
+ /**
+ * Filters the query arguments to add the language.
+ *
+ * @since 2.8
+ *
+ * @param array $args Sitemap provider WP_Query or WP_Term_Query arguments.
+ * @return array
+ */
+ public static function query_args( $args ) {
+ if ( ! empty( self::$filter_lang ) ) {
+ $args['lang'] = self::$filter_lang;
+ }
+ return $args;
+ }
+
+ /**
+ * Gets data for a given sitemap type.
+ *
+ * @since 2.8
+ *
+ * @param string $object_subtype_name Object subtype name if any.
+ * @param string $lang Optional language name.
+ * @return array
+ */
+ protected function get_sitemap_data( $object_subtype_name, $lang = '' ) {
+ $object_subtype_name = (string) $object_subtype_name;
+
+ if ( ! empty( $lang ) ) {
+ self::$filter_lang = $lang;
+ }
+
+ $return = array(
+ 'name' => implode( '-', array_filter( array( $object_subtype_name, $lang ) ) ),
+ 'pages' => $this->get_max_num_pages( $object_subtype_name ),
+ );
+
+ self::$filter_lang = '';
+ return $return;
+ }
+
+ /**
+ * Gets data about each sitemap type.
+ *
+ * @since 2.8
+ *
+ * @return array[] Array of sitemap types including object subtype name and number of pages.
+ */
+ public function get_sitemap_type_data() {
+ $sitemap_data = array();
+
+ add_filter( 'wp_sitemaps_posts_query_args', array( __CLASS__, 'query_args' ) );
+ add_filter( 'wp_sitemaps_taxonomies_query_args', array( __CLASS__, 'query_args' ) );
+
+ $object_subtypes = $this->get_object_subtypes();
+
+ if ( empty( $object_subtypes ) ) {
+ foreach ( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) as $language ) {
+ $sitemap_data[] = $this->get_sitemap_data( '', $language );
+ }
+ }
+
+ switch ( $this->provider->name ) {
+ case 'posts':
+ $func = array( $this->model, 'is_translated_post_type' );
+ break;
+ case 'taxonomies':
+ $func = array( $this->model, 'is_translated_taxonomy' );
+ break;
+ default:
+ return $sitemap_data;
+ }
+
+ foreach ( array_keys( $object_subtypes ) as $object_subtype_name ) {
+ if ( call_user_func( $func, $object_subtype_name ) ) {
+ foreach ( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) as $language ) {
+ $sitemap_data[] = $this->get_sitemap_data( $object_subtype_name, $language );
+ }
+ } else {
+ $sitemap_data[] = $this->get_sitemap_data( $object_subtype_name );
+ }
+ }
+
+ return $sitemap_data;
+ }
+
+ /**
+ * Gets the URL of a sitemap entry.
+ *
+ * @since 2.8
+ *
+ * @param string $name The name of the sitemap.
+ * @param int $page The page of the sitemap.
+ * @return string The composed URL for a sitemap entry.
+ */
+ public function get_sitemap_url( $name, $page ) {
+ // Check if a language was added in $name.
+ $pattern = '#(' . implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')$#';
+ if ( preg_match( $pattern, $name, $matches ) ) {
+ $lang = $this->model->get_language( $matches[1] );
+
+ if ( ! empty( $lang ) ) {
+ $name = preg_replace( '#(-?' . $lang->slug . ')$#', '', $name );
+ $url = $this->provider->get_sitemap_url( $name, $page );
+ return $this->links_model->add_language_to_link( $url, $lang );
+ }
+ }
+
+ // If no language is present in $name, we may attempt to get the current sitemap url (e.g. in redirect_canonical() ).
+ if ( get_query_var( 'lang' ) ) {
+ $lang = $this->model->get_language( get_query_var( 'lang' ) );
+ $url = $this->provider->get_sitemap_url( $name, $page );
+ return $this->links_model->add_language_to_link( $url, $lang );
+ }
+
+ return $this->provider->get_sitemap_url( $name, $page );
+ }
+
+ /**
+ * Returns the list of supported object subtypes exposed by the provider.
+ *
+ * @since 2.8
+ *
+ * @return array List of object subtypes objects keyed by their name.
+ */
+ public function get_object_subtypes() {
+ return $this->provider->get_object_subtypes();
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sitemaps/sitemaps-domain.php b/wp-content/plugins/polylang/modules/sitemaps/sitemaps-domain.php
new file mode 100644
index 0000000000..62945471d0
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sitemaps/sitemaps-domain.php
@@ -0,0 +1,71 @@
+links_model = &$polylang->links_model;
+ }
+
+ /**
+ * Setups actions and filters.
+ *
+ * @since 3.0
+ *
+ * @return void
+ */
+ public function init() {
+ parent::init();
+
+ add_filter( 'wp_sitemaps_index_entry', array( $this, 'index_entry' ) );
+ add_filter( 'wp_sitemaps_stylesheet_url', array( $this->links_model, 'site_url' ) );
+ add_filter( 'wp_sitemaps_stylesheet_index_url', array( $this->links_model, 'site_url' ) );
+ add_filter( 'home_url', array( $this, 'sitemap_url' ) );
+ }
+
+ /**
+ * Filters the sitemap index entries for subdomains and multiple domains.
+ *
+ * @since 2.8
+ *
+ * @param array $sitemap_entry Sitemap entry for the post.
+ * @return array
+ */
+ public function index_entry( $sitemap_entry ) {
+ $sitemap_entry['loc'] = $this->links_model->site_url( $sitemap_entry['loc'] );
+ return $sitemap_entry;
+ }
+
+ /**
+ * Makes sure that the sitemap urls are always evaluated on the current domain.
+ *
+ * @since 2.8.4
+ *
+ * @param string $url A sitemap url.
+ * @return string
+ */
+ public function sitemap_url( $url ) {
+ if ( false !== strpos( $url, '/wp-sitemap' ) ) {
+ $url = $this->links_model->site_url( $url );
+ }
+ return $url;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sitemaps/sitemaps.php b/wp-content/plugins/polylang/modules/sitemaps/sitemaps.php
new file mode 100644
index 0000000000..799f614979
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sitemaps/sitemaps.php
@@ -0,0 +1,128 @@
+links_model = &$polylang->links_model;
+ $this->model = &$polylang->model;
+ $this->options = &$polylang->options;
+ }
+
+ /**
+ * Setups actions and filters.
+ *
+ * @since 2.8
+ *
+ * @return void
+ */
+ public function init() {
+ parent::init();
+
+ add_filter( 'pll_set_language_from_query', array( $this, 'set_language_from_query' ), 10, 2 );
+ add_filter( 'rewrite_rules_array', array( $this, 'rewrite_rules' ) );
+ add_filter( 'wp_sitemaps_add_provider', array( $this, 'replace_provider' ) );
+ }
+
+ /**
+ * Assigns the current language to the default language when the sitemap url
+ * doesn't include any language.
+ *
+ * @since 2.8
+ *
+ * @param string|bool $lang Current language code, false if not set yet.
+ * @param WP_Query $query Main WP query object.
+ * @return string|bool
+ */
+ public function set_language_from_query( $lang, $query ) {
+ if ( isset( $query->query['sitemap'] ) && empty( $query->query['lang'] ) ) {
+ $lang = $this->options['default_lang'];
+ }
+ return $lang;
+ }
+
+ /**
+ * Filters the sitemaps rewrite rules to take the languages into account.
+ *
+ * @since 2.8
+ *
+ * @param string[] $rules Rewrite rules.
+ * @return string[] Modified rewrite rules.
+ */
+ public function rewrite_rules( $rules ) {
+ global $wp_rewrite;
+
+ $languages = $this->model->get_languages_list(
+ array(
+ 'fields' => 'slug',
+ 'hide_default' => $this->options['hide_default'],
+ )
+ );
+
+ if ( empty( $languages ) ) {
+ return $rules;
+ }
+
+ $slug = $wp_rewrite->root . ( $this->options['rewrite'] ? '^' : '^language/' ) . '(' . implode( '|', $languages ) . ')/';
+
+ $newrules = array();
+
+ foreach ( $rules as $key => $rule ) {
+ if ( false !== strpos( $rule, 'sitemap=$matches[1]' ) ) {
+ $newrules[ str_replace( '^wp-sitemap', $slug . 'wp-sitemap', $key ) ] = str_replace(
+ array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
+ array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
+ $rule
+ ); // Should be enough!
+ }
+
+ $newrules[ $key ] = $rule;
+ }
+ return $newrules;
+ }
+
+ /**
+ * Replaces a sitemap provider by our decorator.
+ *
+ * @since 2.8
+ *
+ * @param WP_Sitemaps_Provider $provider Instance of a WP_Sitemaps_Provider.
+ * @return WP_Sitemaps_Provider
+ */
+ public function replace_provider( $provider ) {
+ if ( $provider instanceof WP_Sitemaps_Provider ) {
+ $provider = new PLL_Multilingual_Sitemaps_Provider( $provider, $this->links_model );
+ }
+ return $provider;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sync/admin-sync.php b/wp-content/plugins/polylang/modules/sync/admin-sync.php
new file mode 100644
index 0000000000..5d55e3284b
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sync/admin-sync.php
@@ -0,0 +1,233 @@
+model->post->get_translation( $id, sanitize_key( $_GET['new_lang'] ) ) ) {
+ $post_parent = $parent;
+ }
+ }
+ return $post_parent;
+ }
+
+ /**
+ * Copy menu order, comment, ping status and optionally the date when creating a new translation
+ *
+ * @since 2.5
+ *
+ * @param array $data An array of slashed post data.
+ * @return array
+ */
+ public function wp_insert_post_data( $data ) {
+ if ( isset( $GLOBALS['pagenow'], $_GET['from_post'], $_GET['new_lang'] ) && 'post-new.php' === $GLOBALS['pagenow'] && $this->model->is_translated_post_type( $data['post_type'] ) ) {
+ check_admin_referer( 'new-post-translation' );
+
+ $from_post_id = (int) $_GET['from_post'];
+ $from_post = get_post( $from_post_id );
+
+ if ( $from_post instanceof WP_Post ) {
+ foreach ( array( 'menu_order', 'comment_status', 'ping_status' ) as $property ) {
+ $data[ $property ] = $from_post->$property;
+ }
+
+ // Copy the date only if the synchronization is activated
+ if ( in_array( 'post_date', $this->options['sync'] ) ) {
+ $data['post_date'] = $from_post->post_date;
+ $data['post_date_gmt'] = $from_post->post_date_gmt;
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Copy post metas, and taxonomies when using "Add new" ( translation )
+ *
+ * @since 2.5
+ * @since 3.1 Use of use_block_editor_for_post filter instead of rest_api_init which is triggered too early in WP 5.8.
+ *
+ * @param bool $is_block_editor Whether the post can be edited or not.
+ * @return bool
+ */
+ public function new_post_translation( $is_block_editor ) {
+ global $post;
+ static $done = array();
+
+ if ( ! empty( $post ) && isset( $GLOBALS['pagenow'], $_GET['from_post'], $_GET['new_lang'] ) && 'post-new.php' === $GLOBALS['pagenow'] && $this->model->is_translated_post_type( $post->post_type ) ) {
+ check_admin_referer( 'new-post-translation' );
+
+ // Capability check already done in post-new.php
+ $from_post_id = (int) $_GET['from_post'];
+ $lang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) );
+
+ if ( ! $from_post_id || ! $lang || ! empty( $done[ $from_post_id ] ) ) {
+ return $is_block_editor;
+ }
+
+ $done[ $from_post_id ] = true; // Avoid a second duplication in the block editor. Using an array only to allow multiple phpunit tests.
+
+ $this->taxonomies->copy( $from_post_id, $post->ID, $lang->slug );
+ $this->post_metas->copy( $from_post_id, $post->ID, $lang->slug );
+
+ if ( is_sticky( $from_post_id ) ) {
+ stick_post( $post->ID );
+ }
+ }
+
+ return $is_block_editor;
+ }
+
+ /**
+ * Get post fields to synchronize.
+ *
+ * @since 2.4
+ *
+ * @param WP_Post $post Post object.
+ * @return array Fields to synchronize.
+ */
+ protected function get_fields_to_sync( $post ) {
+ global $wpdb;
+
+ $postarr = parent::get_fields_to_sync( $post );
+
+ // For new drafts, save the date now otherwise it is overridden by WP. Thanks to JoryHogeveen. See #32.
+ if ( in_array( 'post_date', $this->options['sync'] ) && isset( $GLOBALS['pagenow'], $_GET['from_post'], $_GET['new_lang'] ) && 'post-new.php' === $GLOBALS['pagenow'] ) {
+ check_admin_referer( 'new-post-translation' );
+
+ unset( $postarr['post_date'] );
+ unset( $postarr['post_date_gmt'] );
+
+ $original = get_post( (int) $_GET['from_post'] );
+
+ if ( $original instanceof WP_Post ) {
+ $wpdb->update(
+ $wpdb->posts,
+ array(
+ 'post_date' => $original->post_date,
+ 'post_date_gmt' => $original->post_date_gmt,
+ ),
+ array( 'ID' => $post->ID )
+ );
+ }
+ }
+
+ if ( isset( $GLOBALS['post_type'] ) ) {
+ $post_type = $GLOBALS['post_type'];
+ } elseif ( isset( $_REQUEST['post_type'] ) ) {
+ $post_type = sanitize_key( $_REQUEST['post_type'] ); // 2nd case for quick edit
+ }
+
+ // Make sure not to impact media translations when creating them at the same time as post
+ if ( in_array( 'post_parent', $this->options['sync'] ) && ( ! isset( $post_type ) || $post_type !== $post->post_type ) ) {
+ unset( $postarr['post_parent'] );
+ }
+
+ return $postarr;
+ }
+
+ /**
+ * Synchronizes post fields in translations.
+ *
+ * @since 1.2
+ *
+ * @param int $post_id Post id.
+ * @param WP_Post $post Post object.
+ * @param int[] $translations Post translations.
+ */
+ public function pll_save_post( $post_id, $post, $translations ) {
+ parent::pll_save_post( $post_id, $post, $translations );
+
+ // Sticky posts
+ if ( in_array( 'sticky_posts', $this->options['sync'] ) ) {
+ $stickies = get_option( 'sticky_posts' );
+ if ( isset( $_REQUEST['sticky'] ) && 'sticky' === $_REQUEST['sticky'] ) { // phpcs:ignore WordPress.Security.NonceVerification
+ $stickies = array_merge( $stickies, array_values( $translations ) );
+ } else {
+ $stickies = array_diff( $stickies, array_values( $translations ) );
+ }
+ update_option( 'sticky_posts', array_unique( $stickies ) );
+ }
+ }
+
+ /**
+ * Some backward compatibility with Polylang < 2.3
+ * allows to call PLL()->sync->copy_post_metas() and PLL()->sync->copy_taxonomies()
+ * used for example in Polylang for WooCommerce
+ * the compatibility is however only partial as the 4th argument $sync is lost
+ *
+ * @since 2.3
+ *
+ * @param string $func Function name
+ * @param array $args Function arguments
+ * @return mixed|void
+ */
+ public function __call( $func, $args ) {
+ $obj = substr( $func, 5 );
+
+ if ( is_object( $this->$obj ) && method_exists( $this->$obj, 'copy' ) ) {
+ if ( WP_DEBUG ) {
+ $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
+ $i = 1 + empty( $debug[1]['line'] ); // The file and line are in $debug[2] if the function was called using call_user_func
+
+ trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
+ sprintf(
+ '%1$s was called incorrectly in %3$s on line %4$s: the call to PLL()->sync->%1$s() has been deprecated in Polylang 2.3, use PLL()->sync->%2$s->copy() instead.' . "\nError handler",
+ esc_html( $func ),
+ esc_html( $obj ),
+ esc_html( $debug[ $i ]['file'] ),
+ absint( $debug[ $i ]['line'] )
+ )
+ );
+ }
+ return call_user_func_array( array( $this->$obj, 'copy' ), $args );
+ }
+
+ $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
+ trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
+ sprintf(
+ 'Call to undefined function PLL()->sync->%1$s() in %2$s on line %3$s' . "\nError handler",
+ esc_html( $func ),
+ esc_html( $debug[0]['file'] ),
+ absint( $debug[0]['line'] )
+ ),
+ E_USER_ERROR
+ );
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sync/load.php b/wp-content/plugins/polylang/modules/sync/load.php
new file mode 100644
index 0000000000..6978cd6f8c
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sync/load.php
@@ -0,0 +1,26 @@
+model->has_languages() ) {
+ if ( $polylang instanceof PLL_Admin_Base ) {
+ $polylang->sync = new PLL_Admin_Sync( $polylang );
+ } else {
+ $polylang->sync = new PLL_Sync( $polylang );
+ }
+
+ add_filter(
+ 'pll_settings_modules',
+ function ( $modules ) {
+ $modules[] = 'PLL_Settings_Sync';
+ return $modules;
+ }
+ );
+}
diff --git a/wp-content/plugins/polylang/modules/sync/settings-sync.php b/wp-content/plugins/polylang/modules/sync/settings-sync.php
new file mode 100644
index 0000000000..4f00285695
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sync/settings-sync.php
@@ -0,0 +1,115 @@
+ 'sync',
+ 'title' => __( 'Synchronization', 'polylang' ),
+ 'description' => __( 'The synchronization options allow to maintain exact same values (or translations in the case of taxonomies and page parent) of meta content between the translations of a post or page.', 'polylang' ),
+ )
+ );
+ }
+
+ /**
+ * Deactivates the module
+ *
+ * @since 1.8
+ */
+ public function deactivate() {
+ $this->options['sync'] = array();
+ update_option( 'polylang', $this->options );
+ }
+
+ /**
+ * Displays the settings form
+ *
+ * @since 1.8
+ */
+ protected function form() {
+ ?>
+
+ empty( $options['sync'] ) ? array() : array_keys( $options['sync'], 1 ) );
+ return $newoptions; // Take care to return only validated options.
+ }
+
+ /**
+ * Get the row actions.
+ *
+ * @since 1.8
+ *
+ * @return string[] Row actions.
+ */
+ protected function get_actions() {
+ return empty( $this->options['sync'] ) ? array( 'configure' ) : array( 'configure', 'deactivate' );
+ }
+
+ /**
+ * Get the list of synchronization settings.
+ *
+ * @since 1.0
+ *
+ * @return string[] Array synchronization options.
+ */
+ public static function list_metas_to_sync() {
+ return array(
+ 'taxonomies' => __( 'Taxonomies', 'polylang' ),
+ 'post_meta' => __( 'Custom fields', 'polylang' ),
+ 'comment_status' => __( 'Comment status', 'polylang' ),
+ 'ping_status' => __( 'Ping status', 'polylang' ),
+ 'sticky_posts' => __( 'Sticky posts', 'polylang' ),
+ 'post_date' => __( 'Published date', 'polylang' ),
+ 'post_format' => __( 'Post format', 'polylang' ),
+ 'post_parent' => __( 'Page parent', 'polylang' ),
+ '_wp_page_template' => __( 'Page template', 'polylang' ),
+ 'menu_order' => __( 'Page order', 'polylang' ),
+ '_thumbnail_id' => __( 'Featured image', 'polylang' ),
+ );
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sync/sync-metas.php b/wp-content/plugins/polylang/modules/sync/sync-metas.php
new file mode 100644
index 0000000000..5a77213a43
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sync/sync-metas.php
@@ -0,0 +1,406 @@
+model = &$polylang->model;
+
+ add_filter( "add_{$this->meta_type}_metadata", array( $this, 'can_synchronize_metadata' ), 1, 3 );
+ add_filter( "update_{$this->meta_type}_metadata", array( $this, 'can_synchronize_metadata' ), 1, 3 );
+ add_filter( "delete_{$this->meta_type}_metadata", array( $this, 'can_synchronize_metadata' ), 1, 3 );
+
+ $this->add_all_meta_actions();
+
+ add_action( "pll_save_{$this->meta_type}", array( $this, 'save_object' ), 10, 3 );
+ }
+
+ /**
+ * Removes "added_{$this->meta_type}_meta" action
+ *
+ * @since 2.3
+ *
+ * @return void
+ */
+ protected function remove_add_meta_action() {
+ remove_action( "added_{$this->meta_type}_meta", array( $this, 'add_meta' ) );
+ }
+
+ /**
+ * Removes all meta synchronization actions and filters
+ *
+ * @since 2.3
+ *
+ * @return void
+ */
+ public function remove_all_meta_actions() {
+ $this->remove_add_meta_action();
+
+ remove_filter( "update_{$this->meta_type}_metadata", array( $this, 'update_metadata' ), 999 );
+ remove_action( "update_{$this->meta_type}_meta", array( $this, 'update_meta' ) );
+
+ remove_action( "delete_{$this->meta_type}_meta", array( $this, 'store_metas_to_sync' ) );
+ remove_action( "deleted_{$this->meta_type}_meta", array( $this, 'delete_meta' ) );
+ }
+
+ /**
+ * Adds "added_{$this->meta_type}_meta" action
+ *
+ * @since 2.3
+ *
+ * @return void
+ */
+ protected function restore_add_meta_action() {
+ add_action( "added_{$this->meta_type}_meta", array( $this, 'add_meta' ), 10, 4 );
+ }
+
+ /**
+ * Adds meta synchronization actions and filters
+ *
+ * @since 2.3
+ *
+ * @return void
+ */
+ public function add_all_meta_actions() {
+ $this->restore_add_meta_action();
+
+ add_filter( "update_{$this->meta_type}_metadata", array( $this, 'update_metadata' ), 999, 5 ); // Very late in case a filter prevents the meta to be updated
+ add_action( "update_{$this->meta_type}_meta", array( $this, 'update_meta' ), 10, 4 );
+
+ add_action( "delete_{$this->meta_type}_meta", array( $this, 'store_metas_to_sync' ), 10, 2 );
+ add_action( "deleted_{$this->meta_type}_meta", array( $this, 'delete_meta' ), 10, 4 );
+ }
+
+ /**
+ * Maybe modify ("translate") a meta value when it is copied or synchronized
+ *
+ * @since 2.3
+ *
+ * @param mixed $value Meta value
+ * @param string $key Meta key
+ * @param int $from Id of the source
+ * @param int $to Id of the target
+ * @param string $lang Language of target
+ * @return mixed
+ */
+ protected function maybe_translate_value( $value, $key, $from, $to, $lang ) {
+ /**
+ * Filter a meta value before is copied or synchronized
+ *
+ * @since 2.3
+ *
+ * @param mixed $value Meta value
+ * @param string $key Meta key
+ * @param string $lang Language of target
+ * @param int $from Id of the source
+ * @param int $to Id of the target
+ */
+ return apply_filters( "pll_translate_{$this->meta_type}_meta", maybe_unserialize( $value ), $key, $lang, $from, $to );
+ }
+
+ /**
+ * Get the custom fields to copy or synchronize.
+ *
+ * @since 2.3
+ *
+ * @param int $from Id of the post from which we copy information.
+ * @param int $to Id of the post to which we paste information.
+ * @param string $lang Language slug.
+ * @param bool $sync True if it is synchronization, false if it is a copy.
+ * @return string[] List of meta keys.
+ */
+ protected function get_metas_to_copy( $from, $to, $lang, $sync = false ) {
+ /**
+ * Filters the custom fields to copy or synchronize.
+ *
+ * @since 0.6
+ * @since 1.9.2 The `$from`, `$to`, `$lang` parameters were added.
+ *
+ * @param string[] $keys List of custom fields names.
+ * @param bool $sync True if it is synchronization, false if it is a copy.
+ * @param int $from Id of the post from which we copy information.
+ * @param int $to Id of the post to which we paste information.
+ * @param string $lang Language slug.
+ */
+ return array_unique( apply_filters( "pll_copy_{$this->meta_type}_metas", array(), $sync, $from, $to, $lang ) );
+ }
+
+ /**
+ * Disallow modifying synchronized meta if the current user can not modify translations
+ *
+ * @since 2.6
+ *
+ * @param null|bool $check Whether to allow adding/updating/deleting metadata.
+ * @param int $id Object ID.
+ * @param string $meta_key Meta key.
+ * @return null|bool
+ */
+ public function can_synchronize_metadata( $check, $id, $meta_key ) {
+ if ( ! $this->model->{$this->meta_type}->current_user_can_synchronize( $id ) ) {
+ $tr_ids = $this->model->{$this->meta_type}->get_translations( $id );
+
+ foreach ( $tr_ids as $lang => $tr_id ) {
+ if ( $tr_id != $id ) {
+ $to_copy = $this->get_metas_to_copy( $id, $tr_id, $lang, true );
+ if ( in_array( $meta_key, $to_copy ) ) {
+ return false;
+ }
+ }
+ }
+ }
+ return $check;
+ }
+
+ /**
+ * Synchronize added metas across translations
+ *
+ * @since 2.3
+ *
+ * @param int $mid Meta id.
+ * @param int $id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
+ * @return void
+ */
+ public function add_meta( $mid, $id, $meta_key, $meta_value ) {
+ static $avoid_recursion = false;
+
+ if ( ! $avoid_recursion ) {
+ $avoid_recursion = true;
+ $tr_ids = $this->model->{$this->meta_type}->get_translations( $id );
+
+ foreach ( $tr_ids as $lang => $tr_id ) {
+ if ( $tr_id != $id ) {
+ $to_copy = $this->get_metas_to_copy( $id, $tr_id, $lang, true );
+ if ( in_array( $meta_key, $to_copy ) ) {
+ $meta_value = $this->maybe_translate_value( $meta_value, $meta_key, $id, $tr_id, $lang );
+ add_metadata( $this->meta_type, $tr_id, wp_slash( $meta_key ), is_object( $meta_value ) ? $meta_value : wp_slash( $meta_value ) );
+ }
+ }
+ }
+
+ $avoid_recursion = false;
+ }
+ }
+
+ /**
+ * Stores the previous value when updating metas
+ *
+ * @since 2.3
+ *
+ * @param null|bool $r Not used
+ * @param int $id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
+ * @param mixed $prev_value If specified, only update existing metadata entries with the specified value.
+ * @return null|bool Unchanged
+ */
+ public function update_metadata( $r, $id, $meta_key, $meta_value, $prev_value ) {
+ if ( null === $r ) {
+ $hash = md5( "$id|$meta_key|" . maybe_serialize( $meta_value ) );
+ $this->prev_value[ $hash ] = $prev_value;
+ }
+ return $r;
+ }
+
+ /**
+ * Synchronize updated metas across translations
+ *
+ * @since 2.3
+ *
+ * @param int $mid Meta id.
+ * @param int $id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
+ * @return void
+ */
+ public function update_meta( $mid, $id, $meta_key, $meta_value ) {
+ static $avoid_recursion = false;
+ $id = (int) $id;
+
+ if ( ! $avoid_recursion ) {
+ $avoid_recursion = true;
+ $hash = md5( "$id|$meta_key|" . maybe_serialize( $meta_value ) );
+
+ $prev_meta = get_metadata_by_mid( $this->meta_type, $mid );
+
+ if ( $prev_meta ) {
+ $this->remove_add_meta_action(); // We don't want to sync back the new metas
+ $tr_ids = $this->model->{$this->meta_type}->get_translations( $id );
+
+ foreach ( $tr_ids as $lang => $tr_id ) {
+ if ( $tr_id != $id && in_array( $meta_key, $this->get_metas_to_copy( $id, $tr_id, $lang, true ) ) ) {
+ if ( empty( $this->prev_value[ $hash ] ) || $this->prev_value[ $hash ] === $prev_meta->meta_value ) {
+ $prev_value = $this->maybe_translate_value( $prev_meta->meta_value, $meta_key, $id, $tr_id, $lang );
+ $meta_value = $this->maybe_translate_value( $meta_value, $meta_key, $id, $tr_id, $lang );
+ update_metadata( $this->meta_type, $tr_id, wp_slash( $meta_key ), is_object( $meta_value ) ? $meta_value : wp_slash( $meta_value ), $prev_value );
+ }
+ }
+ }
+ $this->restore_add_meta_action();
+ }
+
+ unset( $this->prev_value[ $hash ] );
+ $avoid_recursion = false;
+ }
+ }
+
+ /**
+ * Store metas to synchronize before deleting them.
+ *
+ * @since 2.3
+ *
+ * @param int[] $mids Not used.
+ * @param int $id Object ID.
+ * @return void
+ */
+ public function store_metas_to_sync( $mids, $id ) {
+ $tr_ids = $this->model->{$this->meta_type}->get_translations( $id );
+
+ foreach ( $tr_ids as $lang => $tr_id ) {
+ $this->to_copy[ $id ][ $tr_id ] = $this->get_metas_to_copy( $id, $tr_id, $lang, true );
+ }
+ }
+
+ /**
+ * Synchronizes deleted meta across translations.
+ *
+ * @since 2.3
+ *
+ * @param int[] $mids Not used.
+ * @param int $id Object ID.
+ * @param string $key Meta key.
+ * @param mixed $value Meta value.
+ * @return void
+ */
+ public function delete_meta( $mids, $id, $key, $value ) {
+ static $avoid_recursion = false;
+
+ if ( ! $avoid_recursion ) {
+ $avoid_recursion = true;
+
+ $tr_ids = $this->model->{$this->meta_type}->get_translations( $id );
+
+ foreach ( $tr_ids as $lang => $tr_id ) {
+ if ( $tr_id != $id ) {
+ if ( in_array( $key, $this->to_copy[ $id ][ $tr_id ] ) ) {
+ if ( '' !== $value && null !== $value && false !== $value ) { // Same test as WP
+ $value = $this->maybe_translate_value( $value, $key, $id, $tr_id, $lang );
+ }
+ delete_metadata( $this->meta_type, $tr_id, wp_slash( $key ), is_object( $value ) ? $value : wp_slash( $value ) );
+ }
+ }
+ }
+ }
+
+ $avoid_recursion = false;
+ }
+
+ /**
+ * Copy or synchronize metas
+ *
+ * @since 2.3
+ *
+ * @param int $from Id of the source object
+ * @param int $to Id of the target object
+ * @param string $lang Language code of the target object
+ * @param bool $sync Optional, defaults to true. True if it is synchronization, false if it is a copy
+ * @return void
+ */
+ public function copy( $from, $to, $lang, $sync = false ) {
+ $this->remove_all_meta_actions();
+
+ $to_copy = $this->get_metas_to_copy( $from, $to, $lang, $sync );
+ $metas = get_metadata( $this->meta_type, $from );
+ $metas = is_array( $metas ) ? $metas : array();
+ $tr_metas = get_metadata( $this->meta_type, $to );
+ $tr_metas = is_array( $tr_metas ) ? $tr_metas : array();
+
+ foreach ( $to_copy as $key ) {
+ if ( empty( $metas[ $key ] ) ) {
+ if ( ! empty( $tr_metas[ $key ] ) ) {
+ // If the meta key is not present in the source object, delete all values
+ delete_metadata( $this->meta_type, $to, wp_slash( $key ) );
+ }
+ } elseif ( ! empty( $tr_metas[ $key ] ) && 1 === count( $metas[ $key ] ) && 1 === count( $tr_metas[ $key ] ) ) {
+ // One custom field to update
+ $value = reset( $metas[ $key ] );
+ $value = maybe_unserialize( $value );
+ $to_value = $this->maybe_translate_value( $value, $key, $from, $to, $lang );
+ update_metadata( $this->meta_type, $to, wp_slash( $key ), is_object( $to_value ) ? $to_value : wp_slash( $to_value ) );
+ } else {
+ // Multiple custom fields, either in the source or the target
+ if ( ! empty( $tr_metas[ $key ] ) ) {
+ // The synchronization of multiple values custom fields is easier if we delete all metas first
+ delete_metadata( $this->meta_type, $to, wp_slash( $key ) );
+ }
+
+ foreach ( $metas[ $key ] as $value ) {
+ $value = maybe_unserialize( $value );
+ $to_value = $this->maybe_translate_value( $value, $key, $from, $to, $lang );
+ add_metadata( $this->meta_type, $to, wp_slash( $key ), is_object( $to_value ) ? $to_value : wp_slash( $to_value ) );
+ }
+ }
+ }
+
+ $this->add_all_meta_actions();
+ }
+
+ /**
+ * If synchronized custom fields were previously not synchronized, it is expected
+ * that saving a post (or term) will synchronize them.
+ *
+ * @since 2.3
+ *
+ * @param int $object_id Id of the object being saved.
+ * @param object $obj Not used.
+ * @param int[] $translations The list of translations object ids.
+ * @return void
+ */
+ public function save_object( $object_id, $obj, $translations ) {
+ foreach ( $translations as $tr_lang => $tr_id ) {
+ if ( $tr_id != $object_id ) {
+ $this->copy( $object_id, $tr_id, $tr_lang, true );
+ }
+ }
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sync/sync-post-metas.php b/wp-content/plugins/polylang/modules/sync/sync-post-metas.php
new file mode 100644
index 0000000000..47c66cc97e
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sync/sync-post-metas.php
@@ -0,0 +1,94 @@
+meta_type = 'post';
+
+ parent::__construct( $polylang );
+
+ $this->options = &$polylang->options;
+
+ add_filter( 'pll_translate_post_meta', array( $this, 'translate_thumbnail_id' ), 10, 3 );
+ }
+
+ /**
+ * Get the custom fields to copy or synchronize.
+ *
+ * @since 2.3
+ *
+ * @param int $from Id of the post from which we copy information.
+ * @param int $to Id of the post to which we paste information.
+ * @param string $lang Language slug.
+ * @param bool $sync True if it is synchronization, false if it is a copy.
+ * @return string[] List of meta keys.
+ */
+ protected function get_metas_to_copy( $from, $to, $lang, $sync = false ) {
+ $keys = array();
+
+ // Get public meta keys ( including from translated post in case we just deleted a custom field ).
+ if ( ! $sync || in_array( 'post_meta', $this->options['sync'] ) ) {
+ $from_keys = (array) get_post_custom_keys( $from );
+ $to_keys = (array) get_post_custom_keys( $to );
+
+ $keys = array_unique( array_merge( $from_keys, $to_keys ) );
+ foreach ( $keys as $k => $meta_key ) {
+ if ( is_protected_meta( $meta_key ) ) {
+ unset( $keys[ $k ] );
+ }
+ }
+ }
+
+ // Add page template and featured image.
+ foreach ( array( '_wp_page_template', '_thumbnail_id' ) as $meta ) {
+ if ( ! $sync || in_array( $meta, $this->options['sync'] ) ) {
+ $keys[] = $meta;
+ }
+ }
+
+ if ( $this->options['media_support'] ) {
+ $keys[] = '_wp_attached_file';
+ $keys[] = '_wp_attachment_metadata';
+ $keys[] = '_wp_attachment_backup_sizes';
+ $keys[] = '_wp_attachment_is_custom_header'; // Random header image.
+ }
+
+ /** This filter is documented in modules/sync/sync-metas.php */
+ return array_unique( apply_filters( 'pll_copy_post_metas', $keys, $sync, $from, $to, $lang ) );
+ }
+
+ /**
+ * Translates the thumbnail id.
+ *
+ * @since 2.3
+ *
+ * @param int $value Thumbnail id.
+ * @param string $key Meta key.
+ * @param string $lang Language code.
+ * @return int
+ */
+ public function translate_thumbnail_id( $value, $key, $lang ) {
+ return ( $this->options['media_support'] && '_thumbnail_id' === $key && $to_value = $this->model->post->get_translation( $value, $lang ) ) ? $to_value : $value;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sync/sync-tax.php b/wp-content/plugins/polylang/modules/sync/sync-tax.php
new file mode 100644
index 0000000000..4d969eb6a7
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sync/sync-tax.php
@@ -0,0 +1,310 @@
+model = &$polylang->model;
+ $this->options = &$polylang->options;
+
+ add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
+ add_action( 'pll_save_term', array( $this, 'create_term' ), 10, 3 );
+ add_action( 'pre_delete_term', array( $this, 'pre_delete_term' ) );
+ add_action( 'delete_term', array( $this, 'delete_term' ) );
+ }
+
+ /**
+ * Get the list of taxonomies to copy or to synchronize.
+ *
+ * @since 1.7
+ * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
+ * @since 3.2 Changed visibility from protected to public.
+ *
+ * @param bool $sync True if it is synchronization, false if it is a copy.
+ * @param int $from Id of the post from which we copy information, optional, defaults to null.
+ * @param int $to Id of the post to which we paste information, optional, defaults to null.
+ * @param string $lang Language slug, optional, defaults to null.
+ * @return string[] List of taxonomy names.
+ */
+ public function get_taxonomies_to_copy( $sync, $from = null, $to = null, $lang = null ) {
+ $taxonomies = ! $sync || in_array( 'taxonomies', $this->options['sync'] ) ? $this->model->get_translated_taxonomies() : array();
+ if ( ! $sync || in_array( 'post_format', $this->options['sync'] ) ) {
+ $taxonomies[] = 'post_format';
+ }
+
+ /**
+ * Filters the taxonomies to copy or synchronize.
+ *
+ * @since 1.7
+ * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
+ *
+ * @param string[] $taxonomies List of taxonomy names.
+ * @param bool $sync True if it is synchronization, false if it is a copy.
+ * @param int $from Id of the post from which we copy information.
+ * @param int $to Id of the post to which we paste information.
+ * @param string $lang Language slug.
+ */
+ return array_unique( apply_filters( 'pll_copy_taxonomies', $taxonomies, $sync, $from, $to, $lang ) );
+ }
+
+ /**
+ * When copying or synchronizing terms, translate terms in translatable taxonomies
+ *
+ * @since 2.3
+ *
+ * @param int $object_id Object ID.
+ * @param int[] $terms List of terms ids assigned to the source post.
+ * @param string $taxonomy Taxonomy name.
+ * @param string $lang Language slug.
+ * @return int[] List of terms ids to assign to the target post.
+ */
+ protected function maybe_translate_terms( $object_id, $terms, $taxonomy, $lang ) {
+ if ( is_array( $terms ) && $this->model->is_translated_taxonomy( $taxonomy ) ) {
+ $newterms = array();
+
+ // Convert to term ids if we got tag names
+ $strings = array_map( 'is_string', $terms );
+ if ( in_array( true, $strings, true ) ) {
+ $terms = get_the_terms( $object_id, $taxonomy );
+ $terms = wp_list_pluck( $terms, 'term_id' );
+ }
+
+ foreach ( $terms as $term ) {
+ /**
+ * Filter the translated term when a post translation is created or synchronized
+ *
+ * @since 2.3
+ *
+ * @param int $tr_term Translated term id
+ * @param int $term Source term id
+ * @param string $lang Language slug
+ */
+ if ( $term_id = apply_filters( 'pll_maybe_translate_term', (int) $this->model->term->get_translation( $term, $lang ), $term, $lang ) ) {
+ $newterms[] = (int) $term_id; // Cast is important otherwise we get 'numeric' tags
+ }
+ }
+
+ return $newterms;
+ }
+
+ return $terms; // Empty $terms or untranslated taxonomy
+ }
+
+ /**
+ * Maybe copy taxonomy terms from one post to the other.
+ *
+ * @since 2.6
+ *
+ * @param int $object_id Source object ID.
+ * @param int $tr_id Target object ID.
+ * @param string $lang Target language.
+ * @param array $terms An array of object terms.
+ * @param string $taxonomy Taxonomy slug.
+ * @param bool $append Whether to append new terms to the old terms.
+ * @return void
+ */
+ protected function copy_object_terms( $object_id, $tr_id, $lang, $terms, $taxonomy, $append ) {
+ $to_copy = $this->get_taxonomies_to_copy( true, $object_id, $tr_id, $lang );
+
+ if ( in_array( $taxonomy, $to_copy ) ) {
+ $newterms = $this->maybe_translate_terms( $object_id, $terms, $taxonomy, $lang );
+
+ // For some reasons, the user may have untranslated terms in the translation. Don't forget them.
+ if ( $this->model->is_translated_taxonomy( $taxonomy ) ) {
+ $tr_terms = get_the_terms( $tr_id, $taxonomy );
+ if ( is_array( $tr_terms ) ) {
+ foreach ( $tr_terms as $term ) {
+ if ( ! $this->model->term->get_translation( $term->term_id, $this->model->post->get_language( $object_id ) ) ) {
+ $newterms[] = (int) $term->term_id;
+ }
+ }
+ }
+ }
+
+ wp_set_object_terms( $tr_id, $newterms, $taxonomy, $append );
+ }
+ }
+
+ /**
+ * When assigning terms to a post, assign translated terms to the translated posts (synchronisation).
+ *
+ * @since 2.3
+ *
+ * @param int $object_id Object ID.
+ * @param array $terms An array of object terms.
+ * @param int[] $tt_ids An array of term taxonomy IDs.
+ * @param string $taxonomy Taxonomy slug.
+ * @param bool $append Whether to append new terms to the old terms.
+ * @return void
+ */
+ public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append ) {
+ static $avoid_recursion = false;
+ $taxonomy_object = get_taxonomy( $taxonomy );
+
+ // Make sure that the taxonomy is registered for a post type
+ if ( ! $avoid_recursion && ! empty( $taxonomy_object ) && array_filter( $taxonomy_object->object_type, 'post_type_exists' ) ) {
+ $avoid_recursion = true;
+
+ $tr_ids = $this->model->post->get_translations( $object_id );
+
+ foreach ( $tr_ids as $lang => $tr_id ) {
+ if ( $tr_id !== $object_id ) {
+ if ( $this->model->post->current_user_can_synchronize( $object_id ) ) {
+ $this->copy_object_terms( $object_id, $tr_id, $lang, $terms, $taxonomy, $append );
+ } else {
+ // No permission to synchronize, so let's synchronize in reverse order
+ $orig_lang = array_search( $object_id, $tr_ids );
+ $tr_terms = get_the_terms( $tr_id, $taxonomy );
+
+ if ( false === $tr_terms ) {
+ $tr_terms = array();
+ }
+
+ if ( is_string( $orig_lang ) && is_array( $tr_terms ) ) {
+ $tr_terms = wp_list_pluck( $tr_terms, 'term_id' );
+ $this->copy_object_terms( $tr_id, $object_id, $orig_lang, $tr_terms, $taxonomy, $append );
+ }
+ break;
+ }
+ }
+ }
+
+ $avoid_recursion = false;
+ }
+ }
+
+ /**
+ * Copy terms from one post to a translation, does not sync
+ *
+ * @since 2.3
+ *
+ * @param int $from Id of the source post
+ * @param int $to Id of the target post
+ * @param string $lang Language slug
+ * @return void
+ */
+ public function copy( $from, $to, $lang ) {
+ remove_action( 'set_object_terms', array( $this, 'set_object_terms' ) );
+
+ // Get taxonomies to sync for this post type
+ $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( false, $from, $to, $lang ) );
+
+ // Update the term cache to reduce the number of queries in the loop
+ update_object_term_cache( array( $from ), get_post_type( $from ) );
+
+ // Copy
+ foreach ( $taxonomies as $tax ) {
+ if ( $terms = get_the_terms( $from, $tax ) ) {
+ $terms = array_map( 'intval', wp_list_pluck( $terms, 'term_id' ) );
+ $newterms = $this->maybe_translate_terms( $from, $terms, $tax, $lang );
+
+ if ( ! empty( $newterms ) ) {
+ wp_set_object_terms( $to, $newterms, $tax );
+ }
+ }
+ }
+
+ add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
+ }
+
+ /**
+ * When creating a new term, associate it to posts having translations associated to the translated terms.
+ *
+ * @since 2.3
+ *
+ * @param int $term_id Id of the created term.
+ * @param string $taxonomy Taxonomy.
+ * @param int[] $translations Ids of the translations of the created term.
+ * @return void
+ */
+ public function create_term( $term_id, $taxonomy, $translations ) {
+ if ( doing_action( 'create_term' ) && in_array( $taxonomy, $this->get_taxonomies_to_copy( true ) ) ) {
+ // Get all posts associated to the translated terms
+ $tr_posts = get_posts(
+ array(
+ 'numberposts' => -1,
+ 'nopaging' => true,
+ 'post_type' => 'any',
+ 'post_status' => 'any',
+ 'fields' => 'ids',
+ 'tax_query' => array(
+ array(
+ 'taxonomy' => $taxonomy,
+ 'field' => 'id',
+ 'terms' => array_merge( array( $term_id ), array_values( $translations ) ),
+ 'include_children' => false,
+ ),
+ ),
+ )
+ );
+
+ $lang = $this->model->term->get_language( $term_id ); // Language of the created term
+ $posts = array();
+
+ foreach ( $tr_posts as $post_id ) {
+ $post = $this->model->post->get_translation( $post_id, $lang );
+
+ if ( $post ) {
+ $posts[] = $post;
+ }
+ }
+
+ $posts = array_unique( $posts );
+
+ foreach ( $posts as $post_id ) {
+ if ( current_user_can( 'assign_term', $term_id ) ) {
+ wp_set_object_terms( $post_id, $term_id, $taxonomy, true );
+ }
+ }
+ }
+ }
+
+ /**
+ * Deactivate the synchronization of terms before deleting a term
+ * to avoid translated terms to be removed from translated posts
+ *
+ * @since 2.3.2
+ *
+ * @return void
+ */
+ public function pre_delete_term() {
+ remove_action( 'set_object_terms', array( $this, 'set_object_terms' ) );
+ }
+
+ /**
+ * Re-activate the synchronization of terms after a term is deleted
+ *
+ * @since 2.3.2
+ *
+ * @return void
+ */
+ public function delete_term() {
+ add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sync/sync-term-metas.php b/wp-content/plugins/polylang/modules/sync/sync-term-metas.php
new file mode 100644
index 0000000000..e26088fad9
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sync/sync-term-metas.php
@@ -0,0 +1,25 @@
+meta_type = 'term';
+
+ parent::__construct( $polylang );
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/sync/sync.php b/wp-content/plugins/polylang/modules/sync/sync.php
new file mode 100644
index 0000000000..6fe7a65ff0
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/sync/sync.php
@@ -0,0 +1,268 @@
+model = &$polylang->model;
+ $this->options = &$polylang->options;
+
+ $this->taxonomies = new PLL_Sync_Tax( $polylang );
+ $this->post_metas = new PLL_Sync_Post_Metas( $polylang );
+ $this->term_metas = new PLL_Sync_Term_Metas( $polylang );
+
+ add_filter( 'wp_insert_post_parent', array( $this, 'can_sync_post_parent' ), 10, 3 );
+ add_filter( 'wp_insert_post_data', array( $this, 'can_sync_post_data' ), 10, 2 );
+
+ add_action( 'pll_save_post', array( $this, 'pll_save_post' ), 10, 3 );
+ add_action( 'created_term', array( $this, 'sync_term_parent' ), 10, 3 );
+ add_action( 'edited_term', array( $this, 'sync_term_parent' ), 10, 3 );
+
+ add_action( 'pll_duplicate_term', array( $this->term_metas, 'copy' ), 10, 3 );
+
+ if ( $this->options['media_support'] ) {
+ add_action( 'pll_translate_media', array( $this->taxonomies, 'copy' ), 10, 3 );
+ add_action( 'pll_translate_media', array( $this->post_metas, 'copy' ), 10, 3 );
+ add_action( 'edit_attachment', array( $this, 'edit_attachment' ) );
+ }
+
+ add_filter( 'pre_update_option_sticky_posts', array( $this, 'sync_sticky_posts' ), 10, 2 );
+ }
+
+ /**
+ * Get post fields to synchronize.
+ *
+ * @since 2.4
+ *
+ * @param WP_Post $post Post object.
+ * @return array
+ */
+ protected function get_fields_to_sync( $post ) {
+ $postarr = array();
+
+ foreach ( array( 'comment_status', 'ping_status', 'menu_order' ) as $property ) {
+ if ( in_array( $property, $this->options['sync'] ) ) {
+ $postarr[ $property ] = $post->$property;
+ }
+ }
+
+ if ( in_array( 'post_date', $this->options['sync'] ) ) {
+ $postarr['post_date'] = $post->post_date;
+ $postarr['post_date_gmt'] = $post->post_date_gmt;
+ }
+
+ if ( in_array( 'post_parent', $this->options['sync'] ) ) {
+ $postarr['post_parent'] = wp_get_post_parent_id( $post->ID );
+ }
+
+ return $postarr;
+ }
+
+ /**
+ * Prevents synchronized post parent modification if the current user hasn't enough rights
+ *
+ * @since 2.6
+ *
+ * @param int $post_parent Post parent ID
+ * @param int $post_id Post ID, unused
+ * @param array $postarr Array of parsed post data
+ * @return int
+ */
+ public function can_sync_post_parent( $post_parent, $post_id, $postarr ) {
+ if ( ! empty( $postarr['ID'] ) && ! $this->model->post->current_user_can_synchronize( $postarr['ID'] ) ) {
+ $tr_ids = $this->model->post->get_translations( $postarr['ID'] );
+ foreach ( $tr_ids as $tr_id ) {
+ if ( $tr_id !== $postarr['ID'] && $post = get_post( $tr_id ) ) {
+ $post_parent = $post->post_parent;
+ break;
+ }
+ }
+ }
+ return $post_parent;
+ }
+
+ /**
+ * Prevents synchronized post data modification if the current user hasn't enough rights
+ *
+ * @since 2.6
+ *
+ * @param array $data An array of slashed post data.
+ * @param array $postarr An array of sanitized, but otherwise unmodified post data.
+ * @return array
+ */
+ public function can_sync_post_data( $data, $postarr ) {
+ if ( ! empty( $postarr['ID'] ) && ! $this->model->post->current_user_can_synchronize( $postarr['ID'] ) ) {
+ foreach ( $this->model->post->get_translations( $postarr['ID'] ) as $tr_id ) {
+ if ( $tr_id !== $postarr['ID'] && $post = get_post( $tr_id ) ) {
+ $to_sync = $this->get_fields_to_sync( $post );
+ $data = array_merge( $data, $to_sync );
+ break;
+ }
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * Synchronizes post fields in translations.
+ *
+ * @since 2.4
+ *
+ * @param int $post_id Post id.
+ * @param WP_Post $post Post object.
+ * @param int[] $translations Post translations.
+ * @return void
+ */
+ public function pll_save_post( $post_id, $post, $translations ) {
+ global $wpdb;
+
+ if ( $this->model->post->current_user_can_synchronize( $post_id ) ) {
+ $postarr = $this->get_fields_to_sync( $post );
+
+ if ( ! empty( $postarr ) ) {
+ foreach ( $translations as $lang => $tr_id ) {
+ if ( ! $tr_id || $tr_id === $post_id ) {
+ continue;
+ }
+
+ $tr_arr = $postarr;
+ unset( $tr_arr['post_parent'] );
+
+ // Do not update the translation parent if the user set a parent with no translation.
+ if ( isset( $postarr['post_parent'] ) ) {
+ $post_parent = $postarr['post_parent'] ? $this->model->post->get_translation( $postarr['post_parent'], $lang ) : 0;
+ if ( ! ( $postarr['post_parent'] && ! $post_parent ) ) {
+ $tr_arr['post_parent'] = $post_parent;
+ }
+ }
+
+ // Update all the rows at once.
+ if ( ! empty( $tr_arr ) ) {
+ // Don't use wp_update_post to avoid infinite loop.
+ $wpdb->update( $wpdb->posts, $tr_arr, array( 'ID' => $tr_id ) );
+ clean_post_cache( $tr_id );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Synchronize term parent in translations
+ * Calling clean_term_cache *after* this is mandatory otherwise the $taxonomy_children option is not correctly updated
+ *
+ * @since 2.3
+ *
+ * @param int $term_id Term id.
+ * @param int $tt_id Term taxonomy id, not used.
+ * @param string $taxonomy Taxonomy name.
+ * @return void
+ */
+ public function sync_term_parent( $term_id, $tt_id, $taxonomy ) {
+ global $wpdb;
+
+ if ( is_taxonomy_hierarchical( $taxonomy ) && $this->model->is_translated_taxonomy( $taxonomy ) ) {
+ $term = get_term( $term_id );
+
+ if ( $term instanceof WP_Term ) {
+ $translations = $this->model->term->get_translations( $term_id );
+
+ foreach ( $translations as $lang => $tr_id ) {
+ if ( $tr_id !== $term_id ) {
+ $tr_parent = $this->model->term->get_translation( $term->parent, $lang );
+ $tr_term = get_term( (int) $tr_id, $taxonomy );
+
+ if ( $tr_term instanceof WP_Term && ! ( $term->parent && empty( $tr_parent ) ) ) {
+ $wpdb->update(
+ $wpdb->term_taxonomy,
+ array( 'parent' => $tr_parent ? $tr_parent : 0 ),
+ array( 'term_taxonomy_id' => $tr_term->term_taxonomy_id )
+ );
+
+ clean_term_cache( $tr_id, $taxonomy ); // OK since WP 3.9.
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Synchronizes terms and metas in translations for media
+ *
+ * @since 1.8
+ *
+ * @param int $post_id post id
+ * @return void
+ */
+ public function edit_attachment( $post_id ) {
+ $this->pll_save_post( $post_id, get_post( $post_id ), $this->model->post->get_translations( $post_id ) );
+ }
+
+ /**
+ * Synchronize sticky posts.
+ *
+ * @since 2.3
+ *
+ * @param int[] $value New option value.
+ * @param int[] $old_value Old option value.
+ * @return int[]
+ */
+ public function sync_sticky_posts( $value, $old_value ) {
+ if ( in_array( 'sticky_posts', $this->options['sync'] ) ) {
+ // Stick post
+ if ( $sticked = array_diff( $value, $old_value ) ) {
+ $translations = $this->model->post->get_translations( reset( $sticked ) );
+ $value = array_unique( array_merge( $value, array_values( $translations ) ) );
+ }
+
+ // Unstick post
+ if ( $unsticked = array_diff( $old_value, $value ) ) {
+ $translations = $this->model->post->get_translations( reset( $unsticked ) );
+ $value = array_unique( array_diff( $value, array_values( $translations ) ) );
+ }
+ }
+
+ return $value;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/translate-slugs/load.php b/wp-content/plugins/polylang/modules/translate-slugs/load.php
new file mode 100644
index 0000000000..0a14423365
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/translate-slugs/load.php
@@ -0,0 +1,20 @@
+model->has_languages() ) {
+ add_filter(
+ 'pll_settings_modules',
+ function ( $modules ) {
+ $modules[] = 'PLL_Settings_Preview_Translate_Slugs';
+ return $modules;
+ }
+ );
+}
diff --git a/wp-content/plugins/polylang/modules/translate-slugs/settings-preview-translate-slugs.php b/wp-content/plugins/polylang/modules/translate-slugs/settings-preview-translate-slugs.php
new file mode 100644
index 0000000000..a17d7415a9
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/translate-slugs/settings-preview-translate-slugs.php
@@ -0,0 +1,56 @@
+ 'translate-slugs',
+ 'title' => __( 'Translate slugs', 'polylang' ),
+ 'description' => $this->get_description(),
+ 'active_option' => 'preview',
+ );
+
+ parent::__construct( $polylang, array_merge( $default, $args ) );
+ }
+
+ /**
+ * Returns the module description.
+ *
+ * @since 3.1
+ *
+ * @return string
+ */
+ protected function get_description() {
+ return __( 'Allows to translate custom post types and taxonomies slugs in URLs.', 'polylang' );
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/wizard/css/wizard.css b/wp-content/plugins/polylang/modules/wizard/css/wizard.css
new file mode 100644
index 0000000000..b6c6a62a9c
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/css/wizard.css
@@ -0,0 +1,951 @@
+@charset "UTF-8";
+body {
+ margin: 65px auto 24px;
+ box-shadow: none;
+ background: #f1f1f1;
+ padding: 0;
+ border: 0; /* fix-pro #856 override WP install.css */
+}
+
+#pll-logo {
+ border: 0;
+ margin: 0 0 24px;
+ padding: 0;
+ text-align: center;
+ font-family: sans-serif;
+ font-size: 64px;
+ text-transform: uppercase;
+ color: #000;
+ line-height: normal;
+}
+#pll-logo a {
+ display: flex;
+ justify-content: center;
+ color: #000;
+ text-decoration: none;
+}
+
+#pll-logo img {
+ max-width: 100%;
+ margin-right: 16px;
+}
+.rtl #pll-logo img {
+ margin-right: 0;
+ margin-left: 16px;
+}
+
+.pll-wizard-footer {
+ text-align: center
+}
+
+.pll-wizard .select2-container {
+ text-align: left;
+ width: auto
+}
+
+.pll-wizard .hidden {
+ display: none
+}
+
+.pll-wizard-content {
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .13);
+ padding: 2em;
+ margin: 0 0 20px;
+ background: #fff;
+ overflow: hidden;
+ zoom: 1;
+ text-align: left;
+}
+.rtl .pll-wizard-content{
+ text-align: right;
+}
+
+.pll-wizard-content h1,
+.pll-wizard-content h2,
+.pll-wizard-content h3,
+.pll-wizard-content table {
+ margin: 0 0 20px;
+ border: 0;
+ padding: 0;
+ color: #666;
+ clear: none;
+ font-weight: 500
+}
+
+.pll-wizard-content p {
+ margin: 20px 0;
+ font-size: 1em;
+ line-height: 1.75em;
+ color: #666
+}
+
+.pll-wizard-content table {
+ font-size: 1em;
+ line-height: 1.75em;
+ color: #666;
+ width: 100%;
+ margin-top: 20px;
+}
+.pll-wizard-content table td span{
+ display: inline-block;
+}
+
+.pll-wizard-content table caption {
+ caption-side: bottom;
+ font-style: italic;
+ text-align: right;
+}
+.rtl .pll-wizard-content table caption {
+ text-align: left;
+}
+
+.pll-wizard-content table caption .icon-default-lang{
+ font-style: normal;
+}
+
+.pll-wizard-content a {
+ color: #a03f3f;
+}
+
+.pll-wizard-content a:focus,
+.pll-wizard-content a:hover,
+.pll-wizard-footer-links:hover {
+ color: #dd5454
+}
+
+.pll-wizard-content .pll-wizard-next-steps {
+ overflow: hidden;
+ margin: 0 0 24px;
+ padding-bottom: 2px
+}
+
+.pll-wizard-content .pll-wizard-next-steps h2 {
+ margin-bottom: 12px
+}
+
+.pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-first {
+ float: left;
+ width: 50%;
+ box-sizing: border-box
+}
+
+.pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-last {
+ float: right;
+ width: 50%;
+ box-sizing: border-box
+}
+
+.pll-wizard-content .pll-wizard-next-steps ul {
+ padding: 0 2em 0 0;
+ list-style: none outside;
+ margin: 0
+}
+
+.pll-wizard-content .pll-wizard-next-steps ul li a {
+ display: block;
+ padding: 0 0 .75em
+}
+
+.pll-wizard-content .pll-wizard-next-steps ul li a::before {
+ color: #82878c;
+ font: normal 20px/1 dashicons;
+ speak: none;
+ display: inline-block;
+ padding: 0 10px 0 0;
+ top: 1px;
+ position: relative;
+ text-decoration: none!important;
+ vertical-align: top
+}
+
+.pll-wizard-steps {
+ padding: 0 0 24px;
+ margin: 0;
+ list-style: none outside;
+ overflow: hidden;
+ color: #ccc;
+ width: 100%;
+ display: -webkit-inline-box;
+ display: -webkit-inline-flex;
+ display: inline-flex
+}
+
+.pll-wizard-steps li {
+ width: 100%;
+ float: left;
+ padding: 0 0 .8em;
+ margin: 0;
+ text-align: center;
+ position: relative;
+ border-bottom: 4px solid #ccc;
+ line-height: 1.4em
+}
+
+.pll-wizard-steps li a {
+ color: #a03f3f;
+ text-decoration: none;
+ padding: 1.5em;
+ margin: -1.5em;
+ position: relative;
+ z-index: 1
+}
+
+.pll-wizard-steps li a:focus,
+.pll-wizard-steps li a:hover {
+ color: #dd5454;
+ text-decoration: underline
+}
+
+.pll-wizard-steps li::before {
+ content: "";
+ border: 4px solid #ccc;
+ border-radius: 100%;
+ width: 4px;
+ height: 4px;
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ margin-left: -6px;
+ margin-bottom: -8px;
+ background: #fff
+}
+
+.pll-wizard-steps li.active {
+ border-color: #a03f3f;
+ color: #a03f3f;
+ font-weight: 700
+}
+
+.pll-wizard-steps li.active::before {
+ border-color: #a03f3f
+}
+
+.pll-wizard-steps li.done {
+ border-color: #a03f3f;
+ color: #a03f3f
+}
+
+.pll-wizard-steps li.done::before {
+ border-color: #a03f3f;
+ background: #a03f3f
+}
+
+.pll-wizard .pll-wizard-actions {
+ overflow: hidden;
+ margin: 20px 0 0;
+ position: relative
+}
+
+.pll-wizard .pll-wizard-actions .button {
+ font-size: 16px;
+ font-weight: 300;
+ padding: 1em 2em;
+ line-height: 1em;
+ margin-right: .5em;
+ margin-bottom: 2px;
+ margin-top: 10px;
+ height: auto;
+ border-radius: 4px;
+ box-shadow: none;
+ min-width: auto;
+ border-color: #a03f3f;
+ color: #a03f3f;
+}
+
+.pll-wizard .pll-wizard-content .button {
+ border-color: #a03f3f;
+ color: #a03f3f;
+}
+
+.pll-wizard .pll-wizard-content .button-primary,
+.pll-wizard .pll-wizard-actions .button-primary {
+ background-color: #a03f3f;
+ border-color: #a03f3f;
+ color: #fff;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #a03f3f;
+ text-shadow: 0 -1px 1px #a03f3f, 1px 0 1px #a03f3f, 0 1px 1px #a03f3f, -1px 0 1px #a03f3f;
+ margin: 0;
+ opacity: 1
+}
+
+.pll-wizard .pll-wizard-content .button-small .dashicons {
+ font-size: 15px;
+ height: auto;
+ vertical-align: middle;
+}
+
+.pll-wizard .button-primary:active,
+.pll-wizard .button-primary:focus,
+.pll-wizard input[type="checkbox"]:focus + label.button-primary,
+.pll-wizard .button-primary:hover {
+ background: #dd5454;
+ border-color: #dd5454;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #dd5454
+}
+.pll-wizard .pll-wizard-actions .button-primary[disabled],
+.pll-wizard .pll-wizard-actions .button-primary:disabled,
+.pll-wizard .pll-wizard-actions .button-primary.disabled {
+ cursor: wait;
+ background-color: #bb5454 !important;
+ border-color: #bb5454 !important;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #bb5454 !important;
+ text-shadow: 0 -1px 1px #bb5454, 1px 0 1px #bb5454, 0 1px 1px #bb5454, -1px 0 1px #bb5454 !important;
+ color: #ffa3a3 !important;
+}
+.pll-wizard-content p:last-child {
+ margin-bottom: 0
+}
+
+.pll-wizard-footer-links {
+ font-size: .85em;
+ color: #7b7b7b;
+ margin: 1.18em auto;
+ display: inline-block;
+ text-align: center
+}
+
+.pll-wizard-services {
+ border: 1px solid #eee;
+ padding: 0;
+ margin: 0 0 1em;
+ list-style: none outside;
+ border-radius: 4px;
+ overflow: hidden
+}
+
+.pll-wizard-services p {
+ margin: 0 0 1em 0;
+ padding: 0;
+ font-size: 1em;
+ line-height: 1.5em
+}
+
+.pll-wizard-service-item {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ -webkit-box-pack: justify;
+ -webkit-justify-content: space-between;
+ justify-content: space-between;
+ padding: 0;
+ border-bottom: 1px solid #eee;
+ color: #666;
+ -webkit-box-align: center;
+ -webkit-align-items: center;
+ align-items: center
+}
+
+.media-step .pll-wizard-service-item{
+ border: 0;
+}
+
+.media-step .pll-wizard-service-item:last-child{
+ display: block;
+}
+
+.media-step .pll-wizard-service-item .pll-wizard-service-enable{
+ padding-bottom: 0;
+}
+
+.pll-wizard-service-item:last-child {
+ border-bottom: 0
+}
+
+.pll-wizard-service-item .pll-wizard-service-name {
+ -webkit-flex-basis: 0;
+ flex-basis: 0;
+ min-width: 160px;
+ text-align: center;
+ font-weight: 700;
+ padding: 2em 0;
+ -webkit-align-self: stretch;
+ align-self: stretch;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-align: baseline;
+ -webkit-align-items: baseline;
+ align-items: baseline
+}
+
+.pll-wizard-service-item .pll-wizard-service-name img {
+ max-width: 75px
+}
+
+.pll-wizard-service-item .pll-wizard-service-description {
+ -webkit-box-flex: 1;
+ -webkit-flex-grow: 1;
+ flex-grow: 1;
+ padding: 20px
+}
+
+.pll-wizard-service-item .pll-wizard-service-example {
+ padding: 0 20px 20px
+}
+
+.pll-wizard-service-item .pll-wizard-service-example p{
+ text-align: right;
+}
+.rtl .pll-wizard-service-item .pll-wizard-service-example p{
+ text-align: left;
+}
+
+.pll-wizard-service-item .pll-wizard-service-description p {
+ margin-bottom: 1em
+}
+
+.pll-wizard-service-item .pll-wizard-service-description p:last-child {
+ margin-bottom: 0
+}
+
+.pll-wizard-service-item .pll-wizard-service-description .pll-wizard-service-settings-description {
+ display: block;
+ font-style: italic;
+ color: #999
+}
+
+.pll-wizard-service-item .pll-wizard-service-enable {
+ -webkit-flex-basis: 0;
+ flex-basis: 0;
+ min-width: 75px;
+ text-align: center;
+ cursor: pointer;
+ padding: 2em 0;
+ position: relative;
+ max-height: 1.5em;
+ -webkit-align-self: flex-start;
+ align-self: flex-start;
+ -webkit-box-ordinal-group: 4;
+ -webkit-order: 3;
+ order: 3
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle {
+ position: relative
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] {
+ position:absolute;
+ opacity: 0;
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label {
+ position: relative;
+ display: inline-block;
+ width: 44px;
+ height: 20px;
+ border-radius: 10em;
+ cursor: pointer;
+ text-indent: -9999px;
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:focus + label {
+ border:1px dashed #777;
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::before,
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::after {
+ content: '';
+ position: absolute;
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::before {
+ left: 0;
+ top: 0;
+ width: 44px;
+ height: 20px;
+ background: #ddd;
+ border-radius: 10em;
+ transition: background-color .2s;
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::after {
+ width: 16px;
+ height: 16px;
+ transition: all .2s;
+ border-radius: 50%;
+ background: #fff;
+ margin: 2px;
+ top: 0;
+ left: 0;
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked + label::before {
+ background:#a03f3f;
+}
+
+.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked + label::after {
+ right: 0;
+ left:auto;
+}
+
+.pll-wizard-service-item .pll-wizard-service-settings {
+ display: none;
+ margin-top: .75em;
+ margin-bottom: 0;
+ cursor: default
+}
+
+.pll-wizard-service-item .pll-wizard-service-settings.hide {
+ display: none
+}
+
+.pll-wizard-service-item.checked .pll-wizard-service-settings {
+ display: inline-block
+}
+
+.pll-wizard-service-item.checked .pll-wizard-service-settings.hide {
+ display: none
+}
+
+.pll-wizard-service-item.closed {
+ border-bottom: 0
+}
+
+.step {
+ text-align: center
+}
+
+.pll-wizard .button .dashicons{
+ vertical-align: middle;
+}
+.rtl .dashicons-arrow-right-alt2:before {
+ content: "\f341";
+}
+.pll-wizard .pll-wizard-actions .button:active,
+.pll-wizard .pll-wizard-actions .button:focus,
+.pll-wizard .pll-wizard-actions .button:hover {
+ box-shadow: none
+}
+
+.pll-wizard-next-steps {
+ border: 1px solid #eee;
+ border-radius: 4px;
+ list-style: none;
+ padding: 0
+}
+
+.pll-wizard-next-steps li {
+ padding: 0
+}
+
+.pll-wizard-next-steps .pll-wizard-next-step-item {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ border-top: 1px solid #eee
+}
+
+.pll-wizard-next-steps .pll-wizard-next-step-item.no-border,
+.pll-wizard-next-steps .pll-wizard-next-step-item:first-child {
+ border-top: 0
+}
+
+.pll-wizard-next-steps .pll-wizard-next-step-description {
+ -webkit-box-flex: 1;
+ -webkit-flex-grow: 1;
+ flex-grow: 1;
+ margin: 1.5em
+}
+
+.pll-wizard-next-steps .pll-wizard-next-step-action {
+ -webkit-box-flex: 0;
+ -webkit-flex-grow: 0;
+ flex-grow: 0;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-align: center;
+ -webkit-align-items: center;
+ align-items: center
+}
+
+.pll-wizard-next-steps .pll-wizard-next-step-action .button {
+ margin: 1em 1.5em
+}
+
+.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-description,
+.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-actions,
+.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-action .button{
+ margin-top: 0;
+}
+
+
+.pll-wizard-next-steps p.next-step-heading {
+ margin: 0;
+ font-size: .95em;
+ font-weight: 400;
+ font-variant: all-petite-caps
+}
+
+.pll-wizard-next-steps p.next-step-extra-info {
+ margin: 0
+}
+
+.pll-wizard-next-steps h3.next-step-description {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 600;
+}
+
+.pll-wizard-next-steps .pll-wizard-additional-steps {
+ border-top: 1px solid #eee;
+}
+
+.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-next-step-description {
+ margin-bottom: 0
+}
+
+.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions {
+ margin: 0 0 1.5em 0;
+}
+
+.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button {
+ font-size: 15px;
+ margin: 1em 0 1em 1.5em;
+}
+.rtl .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button {
+ margin: 1em 1.5em 1em 0;
+}
+
+.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button::last-child {
+ margin-right: 1.5em;
+}
+
+.pll-wizard-content img{
+ max-width: 100%;
+ margin-right: 0.5em;
+}
+.rtl .pll-wizard-content img{
+ margin-left: 0.5em;
+}
+
+.pll-wizard-content .form-field label{
+ margin-bottom: 5px;
+ display: block;
+}
+
+.pll-wizard-content .form-field select{
+ padding: 3px;
+}
+
+.pll-wizard-content .languages-step select,
+.pll-wizard-content .untranslated-contents-step select{
+ width: 100%;
+}
+
+.languages-step .form-field .button{
+ margin-left: 15px;
+}
+.languages-step .form-field .button > span{
+ margin-right: 0.3em;
+}
+.rtl .languages-step .form-field .button{
+ margin-left: 0;
+ margin-right: 15px;
+}
+.rtl .languages-step .form-field .button > span{
+ margin-left: 0.3em;
+ margin-right: 0;
+}
+
+.pll-wizard-content .languages-step .select-language-field{
+ display: flex;
+}
+
+.pll-wizard-content #languages{
+ display: none;
+}
+.pll-wizard-content #languages tr th:first-child{
+ width: 80%;
+}
+.pll-wizard-content #languages .dashicons{
+ color: #a03f3f;
+}
+.pll-wizard-content #languages img{
+ margin-right: 5px;
+}
+.pll-wizard-content .error{
+ color: #a03f3f;
+ font-weight: bold;
+}
+.pll-wizard-content #messages .error{
+ background: #fccfcf;
+ padding: 0.5rem;
+ border: 1px solid #a03f3f;
+ margin-bottom: 0.5rem;
+}
+
+.pll-wizard-content #slide-toggle{
+ position:absolute;
+ opacity: 0;
+}
+
+.pll-wizard-content #slide-toggle + label{
+ position:relative;
+}
+.pll-wizard-content #slide-toggle + label + span{
+ display: block;
+}
+
+.pll-wizard-content #slide-toggle + label .dashicons{
+ margin-right: 0.3em;
+}
+.rtl .pll-wizard-content #slide-toggle + label .dashicons{
+ margin-left: 0.3em;
+ margin-right: 0;
+}
+.pll-wizard-content #slide-toggle ~ #screenshot > img {
+ max-height: 500px;
+ margin-top: 10px;
+ -webkit-transition: all .5s cubic-bezier(0, 1, 0.5, 1);
+ transition: all .5s cubic-bezier(0, 1, 0.5, 1);
+}
+.pll-wizard-content #slide-toggle:checked ~ #screenshot > img {
+ max-height: 0;
+}
+.hide {
+ display: none;
+}
+
+input[type="text"].field-in-error,
+input[type="password"].field-in-error,
+input[type="checkbox"].field-in-error,
+input[type="color"].field-in-error,
+input[type="date"].field-in-error,
+input[type="datetime"].field-in-error,
+input[type="datetime-local"].field-in-error,
+input[type="email"].field-in-error,
+input[type="month"].field-in-error,
+input[type="number"].field-in-error,
+input[type="search"].field-in-error,
+input[type="radio"].field-in-error,
+input[type="tel"].field-in-error,
+input[type="text"].field-in-error,
+input[type="time"].field-in-error,
+input[type="url"].field-in-error,
+input[type="week"].field-in-error,
+select.field-in-error,
+textarea.field-in-error,
+span.field-in-error,
+.field-in-error{
+ border-color: #a03f3f;
+}
+
+input[type="text"].field-in-error:focus,
+input[type="password"].field-in-error:focus,
+input[type="checkbox"].field-in-error:focus,
+input[type="color"].field-in-error:focus,
+input[type="date"].field-in-error:focus,
+input[type="datetime"].field-in-error:focus,
+input[type="datetime-local"].field-in-error:focus,
+input[type="email"].field-in-error:focus,
+input[type="month"].field-in-error:focus,
+input[type="number"].field-in-error:focus,
+input[type="search"].field-in-error:focus,
+input[type="radio"].field-in-error:focus,
+input[type="tel"].field-in-error:focus,
+input[type="text"].field-in-error:focus,
+input[type="time"].field-in-error:focus,
+input[type="url"].field-in-error:focus,
+input[type="week"].field-in-error:focus,
+select.field-in-error:focus,
+textarea.field-in-error:focus,
+span.field-in-error:focus,
+.field-in-error:focus{
+ border: 1px solid #a03f3f;
+ box-shadow: 0 0 2px rgba(160, 63, 63, 0.8);
+ outline-color: #a03f3f;
+ outline-style: auto;
+ outline-width: thin;
+}
+
+/* override install styles by returning back to forms styles */
+.form-table input.regular-text{
+ width: 25em;
+}
+.form-table input.field-in-error{
+ border-color: #a03f3f;
+}
+#pll-licenses-table td{
+ padding: 10px 9px;
+}
+#pll-licenses-table .license-valid td p{
+ min-width: 35em;
+}
+#pll-licenses-table .pll-deactivate-license{
+ margin: 0 0 0 20px;
+}
+.rtl #pll-licenses-table .pll-deactivate-license{
+ margin: 0 10px 0 0;
+}
+.pll-wizard-content .documentation {
+ padding: 24px 24px 0;
+ margin: 0 0 24px;
+ overflow: hidden;
+ background: #f5f5f5
+}
+
+.pll-wizard-content .documentation p {
+ padding: 0;
+ margin: 0 0 12px;
+}
+.documentation-container {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.documentation-container .documentation-button-container {
+ -webkit-box-flex: 0;
+ -webkit-flex-grow: 0;
+ flex-grow: 0;
+}
+
+.wc-setup .wc-setup-actions .button.documentation-button {
+ height: 42px;
+ padding: 0 1em;
+ margin: 0;
+}
+#dialog{
+ display: none;
+}
+.pll-wizard .ui-dialog.ui-widget-content{
+ max-height: none;
+}
+.pll-wizard .ui-dialog-title::before{
+ content: "\f534";
+ font-family: dashicons;
+ display: inline-block;
+ line-height: 1;
+ font-weight: 400;
+ font-style: normal;
+ speak: none;
+ text-decoration: inherit;
+ text-transform: none;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ width: 20px;
+ height: 20px;
+ font-size: 20px;
+ vertical-align: middle;
+ text-align: center;
+ margin: 0 5px 5px 0;
+ transition: color 0.1s ease-in;
+}
+.rtl.pll-wizard .ui-dialog-title::before{
+ margin-right: 0;
+ margin-left: 5px;
+}
+.pll-wizard .ui-dialog ul{
+ list-style: disc;
+ padding-left: 20px;
+}
+.rtl.pll-wizard .ui-dialog ul{
+ padding-left: 0;
+ padding-right: 20px;
+}
+.pll-wizard li{
+ margin-bottom: 0;
+}
+#translations{
+ border-collapse: collapse;
+}
+#translations tbody:nth-child(odd){
+ background-color: #f9f9f9;
+}
+#translations.striped > tbody > :nth-child(odd) {
+ background-color: transparent; /* Override common WordPress style */
+}
+.pll-wizard-content mark{
+ background: transparent none;
+}
+.pll-wizard-content mark{
+ color: #7ad03a;
+}
+@media screen and (max-width: 782px) {
+ /* Override WordPress button css rules */
+ .languages-step .form-field .button{
+ font-size: 13px;
+ line-height: 26px;
+ height: 28px;
+ padding: 0 10px 1px;
+ vertical-align: top;
+ }
+
+ #pll-licenses-table .pll-deactivate-license{
+ margin: 10px 0 5px;
+ }
+}
+@media only screen and (max-width:620px) {
+ /* Override dialog width rule */
+ .ui-dialog{
+ width: 100% !important;
+ }
+
+}
+@media only screen and (max-width:500px) {
+ #pll-logo a,
+ .select-language-field{
+ flex-direction: column;
+ }
+ .select-language-field .action-buttons{
+ display: flex;
+ justify-content: flex-end;
+ }
+ .languages-step .form-field .button{
+ margin: 5px 0 0;
+ }
+}
+@media only screen and (max-width:400px) {
+ #pll-logo {
+ font-size: 56px;
+ }
+ .pll-wizard-steps {
+ display: none
+ }
+ .pll-wizard-service-item {
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap
+ }
+ .pll-wizard-service-item .pll-wizard-service-enable {
+ -webkit-box-ordinal-group: 3;
+ -webkit-order: 2;
+ order: 2;
+ padding: 20px 0 0
+ }
+ .pll-wizard-service-item .pll-wizard-service-description {
+ -webkit-box-ordinal-group: 4;
+ -webkit-order: 3;
+ order: 3
+ }
+ .pll-wizard-service-item .pll-wizard-service-name {
+ padding: 20px 20px 0;
+ text-align: left;
+ -webkit-box-pack: justify!important;
+ -webkit-justify-content: space-between!important;
+ justify-content: space-between!important
+ }
+ .pll-wizard-service-item .pll-wizard-service-name img {
+ margin: 0
+ }
+ .pll-wizard-next-steps .pll-wizard-next-step-item {
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap
+ }
+ .pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-description {
+ margin-bottom: 0
+ }
+ .pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-action p {
+ margin: 0
+ }
+}
+@media only screen and (max-width:360px) {
+ #pll-logo {
+ font-size: 48px;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/wizard/html-wizard-notice.php b/wp-content/plugins/polylang/modules/wizard/html-wizard-notice.php
new file mode 100644
index 0000000000..d2ef5a948b
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/html-wizard-notice.php
@@ -0,0 +1,49 @@
+ 'mlang_wizard',
+ ),
+ admin_url( 'admin.php' )
+);
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wp-content/plugins/polylang/modules/wizard/images/media-screen-rtl.png b/wp-content/plugins/polylang/modules/wizard/images/media-screen-rtl.png
new file mode 100644
index 0000000000..293e18f4f9
Binary files /dev/null and b/wp-content/plugins/polylang/modules/wizard/images/media-screen-rtl.png differ
diff --git a/wp-content/plugins/polylang/modules/wizard/images/media-screen.png b/wp-content/plugins/polylang/modules/wizard/images/media-screen.png
new file mode 100644
index 0000000000..a90bf5c565
Binary files /dev/null and b/wp-content/plugins/polylang/modules/wizard/images/media-screen.png differ
diff --git a/wp-content/plugins/polylang/modules/wizard/images/polylang-logo.png b/wp-content/plugins/polylang/modules/wizard/images/polylang-logo.png
new file mode 100644
index 0000000000..55f67c2e43
Binary files /dev/null and b/wp-content/plugins/polylang/modules/wizard/images/polylang-logo.png differ
diff --git a/wp-content/plugins/polylang/modules/wizard/js/languages-step.js b/wp-content/plugins/polylang/modules/wizard/js/languages-step.js
new file mode 100644
index 0000000000..0ad845ebf5
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/js/languages-step.js
@@ -0,0 +1,302 @@
+/**
+ * @package Polylang
+ */
+
+jQuery(
+ function ( $ ) {
+ var addLanguageForm = $( '.languages-step' ); // Form element.
+ var languageFields = $( '#language-fields' ); // Element where to append hidden fields for creating language.
+ var languagesTable = $( '#languages' ); // Table element contains languages list to create.
+ var languagesListTable = $( '#languages tbody' ); // Table rows with languages list to create.
+ var definedLanguagesListTable = $( '#defined-languages tbody' ); // Table rows with already defined languages list.
+ var languagesList = $( '#lang_list' ); // Select form element with predefined languages without already created languages.
+ var nextStepButton = $( '[name="save_step"]' ); // The button for continuing to the next step.
+ var messagesContainer = $( '#messages' ); // Element where to display error messages.
+ var languagesMap = new Map(); // Languages map object for managing the languages to create.
+ var dialog = $( '#dialog' ); // Dialog box for alerting the language selected has not been added to the list.
+
+ /**
+ * Add a language in the list to create it in Polylang settings
+ *
+ * @param {object} language The language object
+ */
+ function addLanguage( language ) {
+ // language properties come from the select dropdown which is built server side and well escaped.
+ // see template view-wizard-step-languages.php.
+ var languageValueHtml = $( ' ' ).text( language.text ).prepend( language.flagUrl ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ var languageTrashIconHtml = $( ' ' )
+ .append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ $( ' ' )
+ .addClass( 'dashicons dashicons-trash' )
+ .attr( 'data-language', language.locale )
+ .append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ $( ' ' )
+ .addClass( 'screen-reader-text' )
+ .text( pll_wizard_params.i18n_remove_language_icon )
+ )
+ );
+ // see the comment and the hardcoded code above. languageTrashIconHtml and languageValueHtml are safe.
+ var languageLineHtml = $( ' ' ).prepend( languageTrashIconHtml ).prepend( languageValueHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ var languageFieldHtml = $( ' ' ).attr(
+ {
+ type: 'hidden',
+ name: 'languages[]'
+ }
+ ).val( language.locale );
+
+ languagesList.val( '' );
+ languagesList.selectmenu( 'refresh' ); // Refresh jQuery selectmenu widget after changing the value.
+
+ languagesMap.set( language.locale, language );
+
+ // see above how languageLineHtml is built.
+ languagesListTable.append( languageLineHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ // Bind click event on trash icon.
+ languagesListTable.on(
+ 'click',
+ 'span[data-language=' + language.locale + ']',
+ function ( event ) {
+ event.preventDefault();
+ // Remove line in languages table.
+ $( this ).parents( 'tr' ).remove();
+ // Remove input field.
+ var languageField = languageFields.children( 'input[value=' + $( this ).data( 'language' ) + ']' ).remove();
+ // If there is no more languages hide languages table.
+ if ( languagesListTable.children().length <= 0 ) {
+ languagesTable.hide();
+ }
+ // Remove language from the Map.
+ languagesMap.delete( $( this ).data( 'language' ) );
+ // Hide error message.
+ hideError();
+ }
+ );
+ // see above how languageFieldHtml is built.
+ // Add hidden input field for posting the form.
+ languageFields.append( languageFieldHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+
+ }
+
+ /**
+ * Display an error message
+ *
+ * @param {string} message The message to display
+ */
+ function showError( message ) {
+ messagesContainer.empty();
+ // html is hardcoded and use of jQuery text method which is safe to add message value.
+ // In addition message is i18n value which is initialized server side in PLL_Wizard::add_step_languages and correctly escaped.
+ messagesContainer.prepend( $( '
' ).addClass( 'error' ).text( message ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ }
+
+ /**
+ * Hide all error messages and fields in error
+ */
+ function hideError() {
+ messagesContainer.empty();
+ addLanguageForm.find( '.error' ).removeClass( 'error field-in-error' );
+ }
+
+ /**
+ * Style the field to indicate where the error is
+ *
+ * @param {object} field The jQuery element which is in error
+ */
+ function showFieldInError( field ) {
+ field.addClass( 'error field-in-error' );
+ }
+
+ /**
+ * Focus on a specific element
+ *
+ * @param {object} field The jQuery element which will be focused
+ */
+ function focusOnField( field ) {
+ field.trigger( 'focus' );
+ }
+
+ /**
+ * Disable a specific button
+ *
+ * @param {object} button
+ */
+ function disableButton( button ){
+ button.prop( 'disabled', true );
+ // Because the button is disabled we need to add the value of the button to ensure it will pass in the request.
+ addLanguageForm.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
+ $( ' ' ).prop(
+ {
+ type: 'hidden',
+ name: button.prop( 'name' ),
+ value: button.prop( 'value' )
+ }
+ )
+ );
+ }
+
+ /**
+ * Remove error when a new selection is done in languages list.
+ */
+ languagesList.on(
+ 'selectmenuchange',
+ function () {
+ hideError();;
+ }
+ );
+ /**
+ * Bind click event on "Add language" button
+ */
+ $( '#add-language' ).on(
+ 'click',
+ function ( event ) {
+ hideError();
+ var selectedOption = event.currentTarget.form.lang_list.options[event.currentTarget.form.lang_list.selectedIndex];
+ if ( '' !== selectedOption.value && ! languagesMap.has( selectedOption.value ) ) {
+ addLanguage(
+ {
+ locale: selectedOption.value,
+ text: selectedOption.innerText,
+ name: $( selectedOption ).data( 'language-name' ),
+ flagUrl: $( selectedOption ).data( 'flag-html' )
+ }
+ );
+ // Show table of languages.
+ languagesTable.show();
+ // Put back the focus on the select language field after clicking on "Add language button".
+ focusOnField( $( '#lang_list-button' ) );
+ } else {
+ var message = pll_wizard_params.i18n_no_language_selected;
+ if ( languagesMap.has( selectedOption.value ) ) {
+ message = pll_wizard_params.i18n_language_already_added;
+ }
+ showError( message );
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
+ focusOnField( $( '#lang_list-button' ) );
+
+ }
+ }
+ );
+
+ /**
+ * Bind submit event on "add_lang" form
+ */
+ addLanguageForm.on(
+ 'submit',
+ function ( event ) {
+ // Verify if there is at least one language.
+ var isLanguagesAlreadyDefined = definedLanguagesListTable.children().length > 0;
+ var selectedLanguage = $( '#lang_list' ).val();
+ if ( languagesMap.size <= 0 && ! isLanguagesAlreadyDefined ) {
+ if ( '' === selectedLanguage ) {
+ showError( pll_wizard_params.i18n_no_language_added );
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
+ focusOnField( $( '#lang_list-button' ) );
+ } else {
+ showError( pll_wizard_params.i18n_add_language_needed );
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
+ focusOnField( $( '#add-language' ) ); // Put the focus on the "Add language" button.
+ }
+ return false;
+ }
+ // Verify if the language has been added in the list otherwise display a dialog box to confirm what to do.
+ if ( '' !== selectedLanguage ) {
+ // Verify we don't add a duplicate language before opening the dialog box otherwise display an error message.
+ if ( ! languagesMap.has( selectedLanguage ) ) {
+ dialog.dialog( 'open' );
+ } else {
+ showError( pll_wizard_params.i18n_language_already_added );
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
+ focusOnField( $( '#lang_list-button' ) );
+ }
+ return false;
+ }
+ disableButton( nextStepButton );
+ }
+ );
+
+ // Is there an error return by PHP ?
+ var searchParams = new URLSearchParams( document.location.search );
+ if ( searchParams.has( 'activate_error' ) ) {
+ // If the error code exists, display it.
+ if ( undefined !== pll_wizard_params[ searchParams.get( 'activate_error' ) ] ) {
+ showError( pll_wizard_params[ searchParams.get( 'activate_error' ) ] );
+ }
+ }
+
+ function confirmDialog( what ) {
+ switch ( what ) {
+ case 'yes':
+ var selectedOption = $( '#lang_list' ).children( ':selected' );
+ addLanguage(
+ {
+ locale: selectedOption[0].value,
+ text: selectedOption[0].innerText,
+ name: $( selectedOption ).data( 'language-name' ),
+ flagUrl: $( selectedOption ).data( 'flag-html' )
+ }
+ );
+ break;
+ case 'no':
+ // Empty select form field and submit again the form.
+ languagesList.val( '' );
+ break;
+ case 'ignore':
+ }
+ dialog.dialog( 'close' );
+ if ( 'ignore' === what ) {
+ focusOnField( $( '#lang_list-button' ) );
+ } else {
+ addLanguageForm.submit();
+ }
+ }
+
+ // Initialize dialog box in the case a language is selected but not added in the list.
+ dialog.dialog(
+ {
+ autoOpen: false,
+ modal: true,
+ draggable: false,
+ resizable: false,
+ title: pll_wizard_params.i18n_dialog_title,
+ minWidth: 600,
+ maxWidth: '100%',
+ open: function ( event, ui ) {
+ // Change dialog box position for rtl language
+ if ( $( 'body' ).hasClass( 'rtl' ) ) {
+ $( this ).parent().css(
+ {
+ right: $( this ).parent().css( 'left' ),
+ left: 'auto'
+ }
+ );
+ }
+ // Display language name and flag information in dialog box.
+ $( this ).find( '#dialog-language' ).text( $( '#lang_list' ).children( ':selected' ).first().text() );
+ // language properties come from the select dropdown #lang_list which is built server side and well escaped.
+ // see template view-wizard-step-languages.php.
+ $( this ).find( '#dialog-language-flag' ).empty().prepend( $( '#lang_list' ).children( ':selected' ).data( 'flag-html' ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
+ },
+ buttons: [
+ {
+ text: pll_wizard_params.i18n_dialog_yes_button,
+ click: function ( event ) {
+ confirmDialog( 'yes' );
+ }
+ },
+ {
+ text: pll_wizard_params.i18n_dialog_no_button,
+ click: function ( event ) {
+ confirmDialog( 'no' );
+ }
+ },
+ {
+ text: pll_wizard_params.i18n_dialog_ignore_button,
+ click: function ( event ) {
+ confirmDialog( 'ignore' );
+ }
+ }
+ ]
+ }
+ )
+ }
+);
diff --git a/wp-content/plugins/polylang/modules/wizard/load.php b/wp-content/plugins/polylang/modules/wizard/load.php
new file mode 100644
index 0000000000..0febfd3403
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/load.php
@@ -0,0 +1,14 @@
+wizard = new PLL_Wizard( $polylang );
+}
diff --git a/wp-content/plugins/polylang/modules/wizard/view-wizard-page.php b/wp-content/plugins/polylang/modules/wizard/view-wizard-page.php
new file mode 100644
index 0000000000..30a7eec6a4
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/view-wizard-page.php
@@ -0,0 +1,108 @@
+
+
+>
+
+
+
+
+
+
+
+
+ steps[ $this->step ]['scripts'] ); ?>
+ styles, $this->steps[ $this->step ]['styles'] ) ); ?>
+
+
+
+
+
+ steps as $step_key => $step ) {
+ $is_completed = array_search( $this->step, array_keys( $this->steps ), true ) > array_search( $step_key, array_keys( $this->steps ), true );
+
+ if ( $step_key === $this->step ) {
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wp-content/plugins/polylang/modules/wizard/view-wizard-step-home-page.php b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-home-page.php
new file mode 100644
index 0000000000..16f7ac347d
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-home-page.php
@@ -0,0 +1,135 @@
+model->get_languages_list();
+$default_language = count( $languages ) > 0 ? $this->options['default_lang'] : null;
+$home_page_id = get_option( 'page_on_front' );
+$translations = $this->model->post->get_translations( $home_page_id );
+$untranslated_languages = array();
+$home_page = $home_page_id > 0 ? get_post( $home_page_id ) : null;
+$home_page_language = $this->model->post->get_language( $home_page_id );
+
+foreach ( $languages as $language ) {
+ if ( ! $this->model->post->get( $home_page_id, $language ) ) {
+ $untranslated_languages[] = $language;
+ }
+}
+?>
+
+
+
+
+
+
+
+ ' . esc_html( $home_page->post_title ) . ''
+ );
+ ?>
+
+ flag . ' ' . esc_html( $home_page_language->name ) . ' ' . esc_html( $home_page_language->locale ) . ' ' //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ )
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ model->get_language( $lang );
+ ?>
+
+
+ flag; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo ' ' . esc_html( $language->name ) . ' ' . esc_html( $language->locale ) . ' ';
+ ?>
+ is_default ) : ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = 1 ) : ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ flag; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo ' ' . esc_html( $lg->name ) . ' ' . esc_html( $lg->locale ) . ' ';
+ ?>
+ is_default ) : ?>
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wp-content/plugins/polylang/modules/wizard/view-wizard-step-languages.php b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-languages.php
new file mode 100644
index 0000000000..501fc182f3
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-languages.php
@@ -0,0 +1,136 @@
+model->get_languages_list();
+$default_language = count( $existing_languages ) > 0 ? $this->options['default_lang'] : null;
+$languages_list = array_diff_key(
+ PLL_Settings::get_predefined_languages(),
+ wp_list_pluck( $existing_languages, 'locale', 'locale' )
+);
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %3$s%2$s - %1$s %4$s ' . "\n",
+ esc_attr( $lg->locale ),
+ esc_html( $lg->name ),
+ $lg->flag, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ $lg->is_default ? ' ' . esc_html__( 'Default language', 'polylang' ) . ' ' : ''
+ );
+ }
+ ?>
+
+
+
+
+
+ ',
+ ' '
+ );
+ ?>
+
+
+
+
+
+
+ ' . esc_html__( 'Yes', 'polylang' ) . ''
+ );
+ ?>
+
+
+ ' . esc_html__( 'No', 'polylang' ) . ''
+ );
+ ?>
+
+
+ ' . esc_html__( 'Ignore', 'polylang' ) . ''
+ );
+ ?>
+
+
+
diff --git a/wp-content/plugins/polylang/modules/wizard/view-wizard-step-last.php b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-last.php
new file mode 100644
index 0000000000..497fe4756b
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-last.php
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wp-content/plugins/polylang/modules/wizard/view-wizard-step-licenses.php b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-licenses.php
new file mode 100644
index 0000000000..c7db239ff7
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-licenses.php
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
diff --git a/wp-content/plugins/polylang/modules/wizard/view-wizard-step-media.php b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-media.php
new file mode 100644
index 0000000000..cf221b46e9
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-media.php
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wp-content/plugins/polylang/modules/wizard/view-wizard-step-untranslated-contents.php b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-untranslated-contents.php
new file mode 100644
index 0000000000..eef8e8e7b4
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/view-wizard-step-untranslated-contents.php
@@ -0,0 +1,37 @@
+model->get_languages_list();
+?>
+
+
+
+
+
+
+
+
+
+ %2$s - %1$s' . "\n",
+ esc_attr( $lg->locale ),
+ esc_html( $lg->name ),
+ esc_html( $lg->flag ),
+ $lg->is_default ? ' selected="selected"' : ''
+ );
+ }
+ ?>
+
+
diff --git a/wp-content/plugins/polylang/modules/wizard/wizard.php b/wp-content/plugins/polylang/modules/wizard/wizard.php
new file mode 100644
index 0000000000..a900ca14c0
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wizard/wizard.php
@@ -0,0 +1,855 @@
+options = &$polylang->options;
+ $this->model = &$polylang->model;
+
+ // Display Wizard page before any other action to ensure displaying it outside the WordPress admin context.
+ // Hooked on admin_init with priority 40 to ensure PLL_Wizard_Pro is correctly initialized.
+ add_action( 'admin_init', array( $this, 'setup_wizard_page' ), 40 );
+ // Add Wizard submenu.
+ add_filter( 'pll_settings_tabs', array( $this, 'settings_tabs' ), 10, 1 );
+ // Add filter to select screens where to display the notice.
+ add_filter( 'pll_can_display_notice', array( $this, 'can_display_notice' ), 10, 2 );
+
+ // Default steps.
+ add_filter( 'pll_wizard_steps', array( $this, 'add_step_licenses' ), 100 );
+ add_filter( 'pll_wizard_steps', array( $this, 'add_step_languages' ), 200 );
+ add_filter( 'pll_wizard_steps', array( $this, 'add_step_media' ), 300 );
+ add_filter( 'pll_wizard_steps', array( $this, 'add_step_untranslated_contents' ), 400 );
+ add_filter( 'pll_wizard_steps', array( $this, 'add_step_home_page' ), 500 );
+ add_filter( 'pll_wizard_steps', array( $this, 'add_step_last' ), 999 );
+ }
+
+ /**
+ * Save an activation transient when Polylang is activating to redirect to the wizard
+ *
+ * @since 2.7
+ *
+ * @param bool $network_wide if activated for all sites in the network.
+ * @return void
+ */
+ public static function start_wizard( $network_wide ) {
+ $options = get_option( 'polylang' );
+
+ if ( wp_doing_ajax() || $network_wide || ! empty( $options ) ) {
+ return;
+ }
+ set_transient( 'pll_activation_redirect', 1, 30 );
+ }
+
+ /**
+ * Redirect to the wizard depending on the context
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function redirect_to_wizard() {
+ if ( get_transient( 'pll_activation_redirect' ) ) {
+ $do_redirect = true;
+ if ( ( isset( $_GET['page'] ) && 'mlang_wizard' === sanitize_key( $_GET['page'] ) || isset( $_GET['activate-multi'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ delete_transient( 'pll_activation_redirect' );
+ $do_redirect = false;
+ }
+
+ if ( $do_redirect ) {
+ wp_safe_redirect(
+ esc_url_raw(
+ add_query_arg(
+ array(
+ 'page' => 'mlang_wizard',
+ ),
+ admin_url( 'admin.php' )
+ )
+ )
+ );
+ exit;
+ }
+ }
+ }
+
+ /**
+ * Add an admin Polylang submenu to access the wizard
+ *
+ * @since 2.7
+ *
+ * @param string[] $tabs Submenus list.
+ * @return string[] Submenus list updated.
+ */
+ public function settings_tabs( $tabs ) {
+ $tabs['wizard'] = esc_html__( 'Setup', 'polylang' );
+ return $tabs;
+ }
+
+ /**
+ * Returns true if the media step is displayable, false otherwise.
+ *
+ * @since 2.7
+ *
+ * @param PLL_Language[] $languages List of language objects.
+ * @return bool
+ */
+ public function is_media_step_displayable( $languages ) {
+ $media = array();
+ // If there is no language or only one the media step is displayable.
+ if ( ! $languages || count( $languages ) < 2 ) {
+ return true;
+ }
+ foreach ( $languages as $language ) {
+ $media[ $language->slug ] = $this->model->count_posts(
+ $language,
+ array(
+ 'post_type' => array( 'attachment' ),
+ 'post_status' => 'inherit',
+ )
+ );
+ }
+ return count( array_filter( $media ) ) === 0;
+ }
+
+ /**
+ * Check if the licenses step is displayable
+ *
+ * @since 2.7
+ *
+ * @return bool
+ */
+ public function is_licenses_step_displayable() {
+ $licenses = apply_filters( 'pll_settings_licenses', array() );
+ return count( $licenses ) > 0;
+ }
+
+ /**
+ * Setup the wizard page
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function setup_wizard_page() {
+
+ PLL_Admin_Notices::add_notice( 'wizard', $this->wizard_notice() );
+
+ $this->redirect_to_wizard();
+ if ( ! Polylang::is_wizard() ) {
+ return;
+ }
+ if ( ! current_user_can( 'manage_options' ) ) {
+ wp_die( esc_html__( 'Sorry, you are not allowed to manage options for this site.', 'polylang' ) );
+ }
+
+ // Enqueue scripts and styles especially for the wizard.
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
+
+ $this->steps = apply_filters( 'pll_wizard_steps', $this->steps );
+ $step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification
+
+ $this->step = $step && array_key_exists( $step, $this->steps ) ? $step : current( array_keys( $this->steps ) );
+
+ $has_languages = $this->model->has_languages();
+
+ if ( ! $has_languages && ! in_array( $this->step, array( 'licenses', 'languages' ) ) ) {
+ wp_safe_redirect( esc_url_raw( $this->get_step_link( 'languages' ) ) );
+ exit;
+ }
+
+ if ( $has_languages && $this->model->get_objects_with_no_lang( 1 ) && ! in_array( $this->step, array( 'licenses', 'languages', 'media', 'untranslated-contents' ) ) ) {
+ wp_safe_redirect( esc_url_raw( $this->get_step_link( 'untranslated-contents' ) ) );
+ exit;
+ }
+
+ // Call the handler of the step for going to the next step.
+ // Be careful nonce verification with check_admin_referer must be done in each handler.
+ if ( ! empty( $_POST['save_step'] ) && isset( $this->steps[ $this->step ]['handler'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ call_user_func( $this->steps[ $this->step ]['handler'] );
+ }
+
+ $this->display_wizard_page();
+ // Ensure nothing is done after including the page.
+ exit;
+ }
+
+ /**
+ * Adds some admin screens where to display the wizard notice
+ *
+ * @since 2.7
+ *
+ * @param bool $can_display_notice Whether the notice can be displayed.
+ * @param string $notice The notice name.
+ * @return bool
+ */
+ public function can_display_notice( $can_display_notice, $notice ) {
+ if ( ! $can_display_notice && 'wizard' === $notice ) {
+ $screen = get_current_screen();
+ $can_display_notice = ! empty( $screen ) && in_array(
+ $screen->base,
+ array(
+ 'edit',
+ 'upload',
+ 'options-general',
+ )
+ );
+ }
+ return $can_display_notice;
+ }
+
+ /**
+ * Return html code of the wizard notice
+ *
+ * @since 2.7
+ *
+ * @return string
+ */
+ public function wizard_notice() {
+ ob_start();
+ include __DIR__ . '/html-wizard-notice.php';
+ return ob_get_clean();
+ }
+
+ /**
+ * Display the wizard page
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function display_wizard_page() {
+ set_current_screen( 'pll-wizard' );
+ include __DIR__ . '/view-wizard-page.php';
+ }
+
+ /**
+ * Enqueue scripts and styles for the wizard
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function enqueue_scripts() {
+ wp_enqueue_style( 'polylang_admin', plugins_url( '/css/build/admin' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
+ wp_enqueue_style( 'pll-wizard', plugins_url( '/css/build/wizard' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common', 'forms' ), POLYLANG_VERSION );
+
+ $this->styles = array( 'polylang_admin', 'pll-wizard' );
+ }
+
+ /**
+ * Get the suffix to enqueue non minified files in a Debug context
+ *
+ * @since 2.7
+ *
+ * @return string Empty when SCRIPT_DEBUG equal to true
+ * otherwise .min
+ */
+ public function get_suffix() {
+ return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
+ }
+
+ /**
+ * Get the URL for the step's screen.
+ *
+ * @since 2.7
+ *
+ * @param string $step slug (default: current step).
+ * @return string URL for the step if it exists.
+ * Empty string on failure.
+ */
+ public function get_step_link( $step = '' ) {
+ if ( ! $step ) {
+ $step = $this->step;
+ }
+
+ $keys = array_keys( $this->steps );
+
+ $step_index = array_search( $step, $keys, true );
+ if ( false === $step_index ) {
+ return '';
+ }
+
+ return add_query_arg( 'step', $keys[ $step_index ], remove_query_arg( 'activate_error' ) );
+ }
+
+ /**
+ * Get the URL for the next step's screen.
+ *
+ * @since 2.7
+ *
+ * @param string $step slug (default: current step).
+ * @return string URL for next step if a next step exists.
+ * Admin URL if it's the last step.
+ * Empty string on failure.
+ */
+ public function get_next_step_link( $step = '' ) {
+ if ( ! $step ) {
+ $step = $this->step;
+ }
+
+ $keys = array_keys( $this->steps );
+ if ( end( $keys ) === $step ) {
+ return admin_url();
+ }
+
+ $step_index = array_search( $step, $keys, true );
+ if ( false === $step_index ) {
+ return '';
+ }
+
+ return add_query_arg( 'step', $keys[ $step_index + 1 ], remove_query_arg( 'activate_error' ) );
+ }
+
+ /**
+ * Add licenses step to the wizard
+ *
+ * @since 2.7
+ *
+ * @param array $steps List of steps.
+ * @return array List of steps updated.
+ */
+ public function add_step_licenses( $steps ) {
+ // Add ajax action on deactivate button in licenses step.
+ add_action( 'wp_ajax_pll_deactivate_license', array( $this, 'deactivate_license' ) );
+
+ // Be careful pll_admin script is enqueued here without dependency except jquery because only code useful for deactivate license button is needed.
+ // To be really loaded the script need to be passed to the $steps['licenses']['scripts'] array below with the same handle than in wp_enqueue_script().
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery' ), POLYLANG_VERSION, true );
+ wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
+
+ if ( $this->is_licenses_step_displayable() ) {
+ $steps['licenses'] = array(
+ 'name' => esc_html__( 'Licenses', 'polylang' ),
+ 'view' => array( $this, 'display_step_licenses' ),
+ 'handler' => array( $this, 'save_step_licenses' ),
+ 'scripts' => array( 'pll_admin' ), // Polylang admin script used by deactivate license button.
+ 'styles' => array(),
+ );
+ }
+ return $steps;
+ }
+
+ /**
+ * Display the languages step form
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function display_step_licenses() {
+ include __DIR__ . '/view-wizard-step-licenses.php';
+ }
+
+ /**
+ * Execute the languages step
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function save_step_licenses() {
+ check_admin_referer( 'pll-wizard', '_pll_nonce' );
+
+ $redirect = $this->get_next_step_link();
+ $licenses = apply_filters( 'pll_settings_licenses', array() );
+
+ foreach ( $licenses as $license ) {
+ if ( ! empty( $_POST['licenses'][ $license->id ] ) ) {
+ $updated_license = $license->activate_license( sanitize_key( $_POST['licenses'][ $license->id ] ) );
+ if ( ! empty( $updated_license->license_data ) && false === $updated_license->license_data->success ) {
+ // Stay on this step with an error.
+ $redirect = add_query_arg(
+ array(
+ 'step' => $this->step,
+ 'activate_error' => 'i18n_license_key_error',
+ )
+ );
+ }
+ }
+ }
+
+ wp_safe_redirect( esc_url_raw( $redirect ) );
+ exit;
+ }
+
+ /**
+ * Ajax method to deactivate a license
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function deactivate_license() {
+ check_ajax_referer( 'pll-wizard', '_pll_nonce' );
+
+ if ( ! current_user_can( 'manage_options' ) ) {
+ wp_die( -1 );
+ }
+
+ if ( ! isset( $_POST['id'] ) ) {
+ wp_die( 0 );
+ }
+
+ $id = substr( sanitize_text_field( wp_unslash( $_POST['id'] ) ), 11 );
+ $licenses = apply_filters( 'pll_settings_licenses', array() );
+ $license = $licenses[ $id ];
+ $license->deactivate_license();
+
+ wp_send_json(
+ array(
+ 'id' => $id,
+ 'html' => $license->get_form_field(),
+ )
+ );
+ }
+
+ /**
+ * Add languages step to the wizard
+ *
+ * @since 2.7
+ *
+ * @param array $steps List of steps.
+ * @return array List of steps updated.
+ */
+ public function add_step_languages( $steps ) {
+ wp_deregister_script( 'pll_admin' ); // Deregister after the licenses step enqueue to update jquery-ui-selectmenu dependency.
+ // The wp-ajax-response and postbox dependencies is useless in wizard steps especially postbox which triggers a javascript error otherwise.
+ // To be really loaded the script need to be passed to the $steps['languages']['scripts'] array below with the same handle than in wp_enqueue_script().
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
+ wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
+ wp_register_script( 'pll-wizard-languages', plugins_url( '/js/build/languages-step' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-dialog' ), POLYLANG_VERSION, true );
+ wp_localize_script(
+ 'pll-wizard-languages',
+ 'pll_wizard_params',
+ array(
+ 'i18n_no_language_selected' => esc_html__( 'You need to select a language to be added.', 'polylang' ),
+ 'i18n_language_already_added' => esc_html__( 'You already added this language.', 'polylang' ),
+ 'i18n_no_language_added' => esc_html__( 'You need to add at least one language.', 'polylang' ),
+ 'i18n_add_language_needed' => esc_html__( 'You selected a language, however, to be able to continue, you need to add it.', 'polylang' ),
+ 'i18n_pll_add_language' => esc_html__( 'Impossible to add the language.', 'polylang' ),
+ 'i18n_pll_invalid_locale' => esc_html__( 'Enter a valid WordPress locale', 'polylang' ),
+ 'i18n_pll_invalid_slug' => esc_html__( 'The language code contains invalid characters', 'polylang' ),
+ 'i18n_pll_non_unique_slug' => esc_html__( 'The language code must be unique', 'polylang' ),
+ 'i18n_pll_invalid_name' => esc_html__( 'The language must have a name', 'polylang' ),
+ 'i18n_pll_invalid_flag' => esc_html__( 'The flag does not exist', 'polylang' ),
+ 'i18n_dialog_title' => esc_html__( "A language wasn't added.", 'polylang' ),
+ 'i18n_dialog_yes_button' => esc_html__( 'Yes', 'polylang' ),
+ 'i18n_dialog_no_button' => esc_html__( 'No', 'polylang' ),
+ 'i18n_dialog_ignore_button' => esc_html__( 'Ignore', 'polylang' ),
+ 'i18n_remove_language_icon' => esc_html__( 'Remove this language', 'polylang' ),
+ )
+ );
+ wp_enqueue_script( 'pll-wizard-languages' );
+ wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common', 'wp-jquery-ui-dialog' ), POLYLANG_VERSION );
+ $steps['languages'] = array(
+ 'name' => esc_html__( 'Languages', 'polylang' ),
+ 'view' => array( $this, 'display_step_languages' ),
+ 'handler' => array( $this, 'save_step_languages' ),
+ 'scripts' => array( 'pll-wizard-languages', 'pll_admin' ),
+ 'styles' => array( 'pll-wizard-selectmenu' ),
+ );
+ return $steps;
+ }
+
+ /**
+ * Display the languages step form
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function display_step_languages() {
+ include __DIR__ . '/view-wizard-step-languages.php';
+ }
+
+ /**
+ * Execute the languages step
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function save_step_languages() {
+ check_admin_referer( 'pll-wizard', '_pll_nonce' );
+
+ $all_languages = include POLYLANG_DIR . '/settings/languages.php';
+ $languages = isset( $_POST['languages'] ) && is_array( $_POST['languages'] ) ? array_map( 'sanitize_locale_name', $_POST['languages'] ) : false;
+ $saved_languages = array();
+
+ // If there is no language added or defined.
+ if ( empty( $languages ) && ! $this->model->has_languages() ) {
+ // Stay on this step with an error.
+ wp_safe_redirect(
+ esc_url_raw(
+ add_query_arg(
+ array(
+ 'step' => $this->step,
+ 'activate_error' => 'i18n_no_language_added',
+ )
+ )
+ )
+ );
+ exit;
+ }
+
+ // Otherwise process the languages to add or skip the step if no language has been added.
+ if ( ! empty( $languages ) ) {
+ require_once ABSPATH . 'wp-admin/includes/translation-install.php';
+ // Remove duplicate values.
+ $languages = array_unique( $languages );
+ // For each language add it in Polylang settings.
+ foreach ( $languages as $locale ) {
+ $saved_languages = $all_languages[ $locale ];
+
+ $saved_languages['slug'] = $saved_languages['code'];
+ $saved_languages['rtl'] = (int) ( 'rtl' === $saved_languages['dir'] );
+ $saved_languages['term_group'] = 0; // Default term_group.
+
+ $language_added = $this->model->add_language( $saved_languages );
+
+ if ( $language_added instanceof WP_Error && array_key_exists( 'pll_non_unique_slug', $language_added->errors ) ) {
+ // Get the slug from the locale : lowercase and dash instead of underscore.
+ $saved_languages['slug'] = strtolower( str_replace( '_', '-', $saved_languages['locale'] ) );
+ $language_added = $this->model->add_language( $saved_languages );
+ }
+
+ if ( $language_added instanceof WP_Error ) {
+ // Stay on this step with an error.
+ $error_keys = array_keys( $language_added->errors );
+ wp_safe_redirect(
+ esc_url_raw(
+ add_query_arg(
+ array(
+ 'step' => $this->step,
+ 'activate_error' => 'i18n_' . reset( $error_keys ),
+ )
+ )
+ )
+ );
+ exit;
+ }
+
+ if ( 'en_US' !== $locale && current_user_can( 'install_languages' ) ) {
+ wp_download_language_pack( $locale );
+ }
+ }
+ }
+ wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
+ exit;
+ }
+
+ /**
+ * Add the media step to the wizard.
+ *
+ * @since 2.7
+ *
+ * @param array $steps List of steps.
+ * @return array List of steps updated.
+ */
+ public function add_step_media( $steps ) {
+ $languages = $this->model->get_languages_list();
+
+ if ( $this->is_media_step_displayable( $languages ) ) {
+ $steps['media'] = array(
+ 'name' => esc_html__( 'Media', 'polylang' ),
+ 'view' => array( $this, 'display_step_media' ),
+ 'handler' => array( $this, 'save_step_media' ),
+ 'scripts' => array(),
+ 'styles' => array(),
+ );
+ }
+ return $steps;
+ }
+
+ /**
+ * Display the media step form
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function display_step_media() {
+ include __DIR__ . '/view-wizard-step-media.php';
+ }
+
+ /**
+ * Execute the media step
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function save_step_media() {
+ check_admin_referer( 'pll-wizard', '_pll_nonce' );
+
+ $media_support = isset( $_POST['media_support'] ) ? sanitize_key( $_POST['media_support'] ) === 'yes' : false;
+
+ $this->options['media_support'] = $media_support;
+
+ update_option( 'polylang', $this->options );
+
+ wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
+ exit;
+ }
+
+ /**
+ * Add untranslated contents step to the wizard
+ *
+ * @since 2.7
+ *
+ * @param array $steps List of steps.
+ * @return array List of steps updated.
+ */
+ public function add_step_untranslated_contents( $steps ) {
+ if ( ! $this->model->has_languages() || $this->model->get_objects_with_no_lang( 1 ) ) {
+ // Even if pll_admin is already enqueued with the same dependencies by the languages step, it is interesting to keep that it's also useful for the untranslated-contents step.
+ // To be really loaded the script need to be passed to the $steps['untranslated-contents']['scripts'] array below with the same handle than in wp_enqueue_script().
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
+ wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
+ wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common' ), POLYLANG_VERSION );
+ $steps['untranslated-contents'] = array(
+ 'name' => esc_html__( 'Content', 'polylang' ),
+ 'view' => array( $this, 'display_step_untranslated_contents' ),
+ 'handler' => array( $this, 'save_step_untranslated_contents' ),
+ 'scripts' => array( 'pll_admin' ),
+ 'styles' => array( 'pll-wizard-selectmenu' ),
+ );
+ }
+ return $steps;
+ }
+
+ /**
+ * Display the untranslated contents step form
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function display_step_untranslated_contents() {
+ include __DIR__ . '/view-wizard-step-untranslated-contents.php';
+ }
+
+ /**
+ * Execute the untranslated contents step
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function save_step_untranslated_contents() {
+ check_admin_referer( 'pll-wizard', '_pll_nonce' );
+
+ $lang = ! empty( $_POST['language'] ) && is_string( $_POST['language'] ) ? sanitize_locale_name( $_POST['language'] ) : false;
+
+ if ( empty( $lang ) ) {
+ $lang = $this->options['default_lang'];
+ }
+
+ $language = $this->model->get_language( $lang );
+
+ if ( $language instanceof PLL_Language ) {
+ $this->model->set_language_in_mass( $language );
+ }
+
+ wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
+ exit;
+ }
+
+ /**
+ * Add home page step to the wizard
+ *
+ * @since 2.7
+ *
+ * @param array $steps List of steps.
+ * @return array List of steps updated.
+ */
+ public function add_step_home_page( $steps ) {
+ $languages = $this->model->get_languages_list();
+ $home_page_id = get_option( 'page_on_front' );
+
+ $translations = $this->model->post->get_translations( $home_page_id );
+
+ if ( $home_page_id > 0 && ( ! $languages || count( $languages ) === 1 || count( $translations ) !== count( $languages ) ) ) {
+ $steps['home-page'] = array(
+ 'name' => esc_html__( 'Homepage', 'polylang' ),
+ 'view' => array( $this, 'display_step_home_page' ),
+ 'handler' => array( $this, 'save_step_home_page' ),
+ 'scripts' => array(),
+ 'styles' => array(),
+ );
+ }
+ return $steps;
+ }
+
+ /**
+ * Display the home page step form
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function display_step_home_page() {
+ include __DIR__ . '/view-wizard-step-home-page.php';
+ }
+
+ /**
+ * Execute the home page step
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function save_step_home_page() {
+ check_admin_referer( 'pll-wizard', '_pll_nonce' );
+
+ $default_language = $this->model->has_languages() ? $this->options['default_lang'] : null;
+ $home_page = isset( $_POST['home_page'] ) ? sanitize_key( $_POST['home_page'] ) : false;
+ $home_page_title = isset( $_POST['home_page_title'] ) ? sanitize_text_field( wp_unslash( $_POST['home_page_title'] ) ) : esc_html__( 'Homepage', 'polylang' );
+ $home_page_language = isset( $_POST['home_page_language'] ) ? sanitize_key( $_POST['home_page_language'] ) : false;
+
+ $untranslated_languages = isset( $_POST['untranslated_languages'] ) ? array_map( 'sanitize_key', $_POST['untranslated_languages'] ) : array();
+
+ call_user_func(
+ apply_filters( 'pll_wizard_create_home_page_translations', array( $this, 'create_home_page_translations' ) ),
+ $default_language,
+ $home_page,
+ $home_page_title,
+ $home_page_language,
+ $untranslated_languages
+ );
+
+ $this->model->clean_languages_cache();
+
+ wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
+ exit;
+ }
+
+ /**
+ * Create home page translations for each language defined.
+ *
+ * @since 2.7
+ *
+ * @param string $default_language Slug of the default language; null if no default language is defined.
+ * @param int $home_page Post ID of the home page if it's defined, false otherwise.
+ * @param string $home_page_title Home page title if it's defined, 'Homepage' otherwise.
+ * @param string $home_page_language Slug of the home page if it's defined, false otherwise.
+ * @param string[] $untranslated_languages Array of languages which needs to have a home page translated.
+ * @return void
+ */
+ public function create_home_page_translations( $default_language, $home_page, $home_page_title, $home_page_language, $untranslated_languages ) {
+ $translations = $this->model->post->get_translations( $home_page );
+
+ foreach ( $untranslated_languages as $language ) {
+ $language_properties = $this->model->get_language( $language );
+ $id = wp_insert_post(
+ array(
+ 'post_title' => $home_page_title . ' - ' . $language_properties->name,
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ )
+ );
+ $translations[ $language ] = $id;
+ pll_set_post_language( $id, $language );
+ }
+ pll_save_post_translations( $translations );
+ }
+
+ /**
+ * Add last step to the wizard
+ *
+ * @since 2.7
+ *
+ * @param array $steps List of steps.
+ * @return array List of steps updated.
+ */
+ public function add_step_last( $steps ) {
+ $steps['last'] = array(
+ 'name' => esc_html__( 'Ready!', 'polylang' ),
+ 'view' => array( $this, 'display_step_last' ),
+ 'handler' => array( $this, 'save_step_last' ),
+ 'scripts' => array(),
+ 'styles' => array(),
+ );
+ return $steps;
+ }
+
+ /**
+ * Display the last step form
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function display_step_last() {
+ // We ran the wizard once. So we can dismiss its notice.
+ PLL_Admin_Notices::dismiss( 'wizard' );
+ include __DIR__ . '/view-wizard-step-last.php';
+ }
+
+ /**
+ * Execute the last step
+ *
+ * @since 2.7
+ *
+ * @return void
+ */
+ public function save_step_last() {
+ check_admin_referer( 'pll-wizard', '_pll_nonce' );
+
+ wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
+ exit;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/wpml/load.php b/wp-content/plugins/polylang/modules/wpml/load.php
new file mode 100644
index 0000000000..0cd81b0a7e
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wpml/load.php
@@ -0,0 +1,17 @@
+model->has_languages() ) {
+ if ( ! defined( 'PLL_WPML_COMPAT' ) || PLL_WPML_COMPAT ) {
+ PLL_WPML_Compat::instance(); // WPML API.
+ PLL_WPML_Config::instance(); // wpml-config.xml.
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/wpml/wpml-api.php b/wp-content/plugins/polylang/modules/wpml/wpml-api.php
new file mode 100644
index 0000000000..ac827c620a
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wpml/wpml-api.php
@@ -0,0 +1,495 @@
+ not applicable.
+ add_filter( 'wpml_current_language', 'pll_current_language', 10, 0 );
+ add_filter( 'wpml_default_language', 'pll_default_language', 10, 0 );
+ // wpml_add_language_selector => not implemented.
+ // wpml_footer_language_selector => not applicable.
+ add_action( 'wpml_add_language_form_field', array( $this, 'wpml_add_language_form_field' ) );
+ add_filter( 'wpml_language_is_active', array( $this, 'wpml_language_is_active' ), 10, 2 );
+ add_filter( 'wpml_is_rtl', array( $this, 'wpml_is_rtl' ) );
+ // wpml_language_form_input_field => See wpml_add_language_form_field
+ // wpml_language_has_switched => See wpml_switch_language
+ add_filter( 'wpml_element_trid', array( $this, 'wpml_element_trid' ), 10, 3 );
+ add_filter( 'wpml_get_element_translations', array( $this, 'wpml_get_element_translations' ), 10, 3 );
+ // wpml_language_switcher => not implemented.
+ // wpml_browser_redirect_language_params => not implemented.
+ // wpml_enqueue_browser_redirect_language => not applicable.
+ // wpml_enqueued_browser_redirect_language => not applicable.
+ // wpml_encode_string => not applicable.
+ // wpml_decode_string => not applicable.
+
+ /*
+ * Retrieving Language Information for Content.
+ */
+ add_filter( 'wpml_post_language_details', 'wpml_get_language_information', 10, 2 );
+ add_action( 'wpml_switch_language', array( __CLASS__, 'wpml_switch_language' ), 10, 2 );
+ add_filter( 'wpml_element_language_code', array( $this, 'wpml_element_language_code' ), 10, 2 );
+ // wpml_element_language_details => not applicable.
+
+ /*
+ * Retrieving Localized Content.
+ */
+ add_filter( 'wpml_home_url', 'pll_home_url', 10, 0 );
+ add_filter( 'wpml_element_link', 'icl_link_to_element', 10, 7 );
+ add_filter( 'wpml_object_id', 'icl_object_id', 10, 4 );
+ add_filter( 'wpml_translate_single_string', array( $this, 'wpml_translate_single_string' ), 10, 4 );
+ // wpml_translate_string => not applicable.
+ // wpml_unfiltered_admin_string => not implemented.
+ add_filter( 'wpml_permalink', array( $this, 'wpml_permalink' ), 10, 2 );
+ // wpml_elements_without_translations => not implemented.
+ add_filter( 'wpml_get_translated_slug', array( $this, 'wpml_get_translated_slug' ), 10, 3 );
+
+ /*
+ * Finding the Translation State of Content.
+ */
+ // wpml_element_translation_type => not implemented.
+ add_filter( 'wpml_element_has_translations', array( $this, 'wpml_element_has_translations' ), 10, 3 );
+ // wpml_master_post_from_duplicate => not applicable.
+ // wpml_post_duplicates => not applicable.
+
+ /*
+ * Inserting Content.
+ */
+ // wpml_admin_make_post_duplicates => not applicable.
+ // wpml_make_post_duplicates => not applicable.
+ add_action( 'wpml_register_single_string', 'icl_register_string', 10, 3 );
+ // wpml_register_string => not applicable.
+ // wpml_register_string_packages => not applicable.
+ // wpml_delete_package_action => not applicable.
+ // wpml_show_package_language_ui => not applicable.
+ // wpml_set_element_language_details => not implemented.
+ // wpml_multilingual_options => not applicable.
+
+ /*
+ * Miscellaneous
+ */
+ // wpml_element_type => not applicable.
+ // wpml_setting => not applicable.
+ // wpml_sub_setting => not applicable.
+ // wpml_editor_cf_to_display => not applicable.
+ // wpml_tm_save_translation_cf => not implemented.
+ // wpml_tm_xliff_export_translated_cf => not applicable.
+ // wpml_tm_xliff_export_original_cf => not applicable.
+ // wpml_duplicate_generic_string => not applicable.
+ // wpml_translatable_user_meta_fields => not implemented.
+ // wpml_cross_domain_language_data => not applicable.
+ // wpml_get_cross_domain_language_data => not applicable.
+ // wpml_loaded => not applicable.
+ // wpml_st_loaded => not applicable.
+ // wpml_tm_loaded => not applicable.
+ // wpml_hide_management_column (3.4.1) => not applicable.
+ // wpml_ls_directories_to_scan => not applicable.
+ // wpml_ls_model_css_classes => not applicable.
+ // wpml_ls_model_language_css_classes => not applicable.
+ // wpml_tf_feedback_open_link => not applicable.
+ // wpml_sync_custom_field => not implemented.
+ // wpml_sync_all_custom_fields => not implemented.
+ // wpml_is_redirected => not implemented.
+
+ /*
+ * Updating Content
+ */
+ // wpml_set_translation_mode_for_post_type => not implemented.
+
+ /*
+ * Undocumented
+ */
+ add_filter( 'wpml_is_translated_post_type', array( $this, 'wpml_is_translated_post_type' ), 10, 2 );
+ add_filter( 'wpml_is_translated_taxonomy', array( $this, 'wpml_is_translated_taxonomy' ), 10, 2 );
+ }
+
+ /**
+ * Get a list of the languages enabled for a site.
+ *
+ * @since 2.0
+ *
+ * @param mixed $null Not used.
+ * @param array| string $args See arguments of icl_get_languages().
+ * @return array Array of arrays per language.
+ */
+ public function wpml_active_languages( $null, $args = '' ) {
+ return icl_get_languages( $args );
+ }
+
+ /**
+ * In WPML, get a language's native and translated name for display in a custom language switcher
+ * Since Polylang does not implement the translated name, always returns only the native name,
+ * so the 3rd, 4th and 5th parameters are not used.
+ *
+ * @since 2.2
+ *
+ * @param mixed $null Not used.
+ * @param string $native_name The language native name.
+ * @return string
+ */
+ public function wpml_display_language_names( $null, $native_name ) {
+ return $native_name;
+ }
+
+ /**
+ * Returns an HTML hidden input field with name=”lang” and as value the current language.
+ *
+ * @since 2.0
+ *
+ * @return void
+ */
+ public function wpml_add_language_form_field() {
+ $lang = pll_current_language();
+ $field = sprintf( ' ', esc_attr( $lang ) );
+ $field = apply_filters( 'wpml_language_form_input_field', $field, $lang );
+ echo $field; // phpcs:ignore WordPress.Security.EscapeOutput
+ }
+
+ /**
+ * Find out if a specific language is enabled for the site.
+ *
+ * @since 2.0
+ *
+ * @param mixed $null Not used.
+ * @param string $slug Language code.
+ * @return bool
+ */
+ public function wpml_language_is_active( $null, $slug ) {
+ $language = PLL()->model->get_language( $slug );
+ return ! empty( $language ) && $language->active;
+ }
+
+ /**
+ * Find out whether the current language text direction is RTL or not.
+ *
+ * @since 2.0
+ *
+ * @return bool
+ */
+ public function wpml_is_rtl() {
+ return pll_current_language( 'is_rtl' );
+ }
+
+ /**
+ * Returns the id of the translation group of a translated element.
+ *
+ * @since 3.4
+ *
+ * @param mixed $empty_value Not used.
+ * @param int $element_id The id of the item, post id for posts, term_taxonomy_id for terms.
+ * @param string $element_type Optional. The type of an element.
+ * @return int
+ */
+ public function wpml_element_trid( $empty_value, $element_id, $element_type = 'post_post' ) {
+ if ( 0 === strpos( $element_type, 'tax_' ) ) {
+ $element = get_term_by( 'term_taxonomy_id', $element_id );
+ if ( $element instanceof WP_Term ) {
+ $tr_term = PLL()->model->term->get_object_term( $element->term_id, 'term_translations' );
+ }
+ }
+
+ if ( 0 === strpos( $element_type, 'post_' ) ) {
+ $tr_term = PLL()->model->post->get_object_term( $element_id, 'post_translations' );
+ }
+
+ if ( isset( $tr_term ) && $tr_term instanceof WP_Term ) {
+ return $tr_term->term_id;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Returns the element translations info using the ID of the translation group.
+ *
+ * @since 3.4
+ *
+ * @param mixed $empty_value Not used.
+ * @param int $trid The ID of the translation group.
+ * @param string $element_type Optional. The type of an element.
+ * @return stdClass[]
+ */
+ public function wpml_get_element_translations( $empty_value, $trid, $element_type = 'post_post' ) {
+ $return = array();
+
+ if ( 0 === strpos( $element_type, 'tax_' ) ) {
+ $translations = PLL()->model->term->get_translations_from_term_id( $trid );
+ if ( empty( $translations ) ) {
+ return array();
+ }
+
+ $original = min( $translations ); // We suppose that the original is the first term created.
+ $source_lang = array_search( $original, $translations );
+
+ $args = array(
+ 'include' => $translations,
+ 'hide_empty' => false,
+ );
+ $_terms = get_terms( $args );
+
+ if ( ! is_array( $_terms ) ) {
+ return array();
+ }
+
+ $terms = array();
+ foreach ( $_terms as $term ) {
+ $terms[ $term->term_id ] = $term;
+ }
+
+ foreach ( $translations as $lang => $term_id ) {
+ if ( empty( $terms[ $term_id ] ) ) {
+ continue;
+ }
+
+ /*
+ * It seems that WPML fills the `instances` property with the total number of posts
+ * related to this term, while `WP_Term::$count` includes only *published* posts.
+ * We intentionnally accept this difference to avoid extra DB queries.
+ */
+ $return[ $lang ] = (object) array(
+ 'translation_id' => '0', // We have nothing equivalent.
+ 'language_code' => $lang,
+ 'element_id' => (string) $terms[ $term_id ]->term_taxonomy_id,
+ 'source_language_code' => $source_lang === $lang ? null : $source_lang,
+ 'element_type' => $element_type,
+ 'original' => $original === $term_id ? '1' : '0',
+ 'name' => $terms[ $term_id ]->name,
+ 'term_id' => (string) $term_id,
+ 'instances' => (string) $terms[ $term_id ]->count,
+ );
+ }
+ }
+
+ if ( 0 === strpos( $element_type, 'post_' ) ) {
+ $translations = PLL()->model->post->get_translations_from_term_id( $trid );
+ if ( empty( $translations ) ) {
+ return array();
+ }
+
+ $original = min( $translations ); // We suppose that the original is the first post created.
+ $source_lang = array_search( $original, $translations );
+
+ $args = array(
+ 'post__in' => $translations,
+ 'no_paging' => true,
+ 'posts_per_page' => -1,
+ 'update_post_meta_cache' => false,
+ 'update_post_term_cache' => false,
+ 'lang' => '',
+ );
+ $_posts = get_posts( $args );
+
+ $posts = array();
+ foreach ( $_posts as $post ) {
+ $posts[ $post->ID ] = $post;
+ }
+
+ foreach ( $translations as $lang => $post_id ) {
+ if ( empty( $posts[ $post_id ] ) ) {
+ continue;
+ }
+
+ $return[ $lang ] = (object) array(
+ 'translation_id' => '0', // We have nothing equivalent.
+ 'language_code' => $lang,
+ 'element_id' => (string) $post_id,
+ 'source_language_code' => $source_lang === $lang ? null : $source_lang,
+ 'element_type' => $element_type,
+ 'original' => $original === $post_id ? '1' : '0',
+ 'post_title' => $posts[ $post_id ]->post_title,
+ 'post_status' => $posts[ $post_id ]->post_status,
+ );
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Switches whole site to the given language or restores the language that was set when first calling this function.
+ * Unlike the WPML original action, it is not possible to set the current language and the cookie to different values.
+ *
+ * @since 2.7
+ *
+ * @param null|string $lang Language code to switch into, restores the original language if null.
+ * @param bool|string $cookie Optionally also switches the cookie.
+ * @return void
+ */
+ public static function wpml_switch_language( $lang = null, $cookie = false ) {
+ if ( null === self::$original_language ) {
+ self::$original_language = PLL()->curlang;
+ }
+
+ if ( empty( $lang ) ) {
+ PLL()->curlang = self::$original_language;
+ } elseif ( 'all' === $lang ) {
+ PLL()->curlang = null;
+ } elseif ( in_array( $lang, pll_languages_list() ) ) {
+ PLL()->curlang = PLL()->model->get_language( $lang );
+ }
+
+ if ( $cookie && isset( PLL()->choose_lang ) ) {
+ PLL()->choose_lang->maybe_setcookie();
+ }
+
+ do_action( 'wpml_language_has_switched', $lang, $cookie, self::$original_language );
+ }
+
+ /**
+ * Get the language code for a translatable element.
+ *
+ * @since 2.0
+ *
+ * @param mixed $language_code A 2-letter language code.
+ * @param array $args An array with two keys element_id => post_id or term_taxonomy_id, element_type => post type or taxonomy
+ * @return string|null
+ */
+ public function wpml_element_language_code( $language_code, $args ) {
+ $type = $args['element_type'];
+ $id = $args['element_id'];
+
+ if ( 'post' === $type || pll_is_translated_post_type( $type ) ) {
+ $language = pll_get_post_language( $id );
+ return is_string( $language ) ? $language : null;
+ }
+
+ if ( 'term' === $type || pll_is_translated_taxonomy( $type ) ) {
+ $term = get_term_by( 'term_taxonomy_id', $id );
+ if ( $term instanceof WP_Term ) {
+ $id = $term->term_id;
+ }
+ $language = pll_get_term_language( $id );
+ return is_string( $language ) ? $language : null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Translates a string.
+ *
+ * @since 2.0
+ *
+ * @param string $string The string's original value.
+ * @param string $context The string's registered context.
+ * @param string $name The string's registered name.
+ * @param null|string $lang Optional, return the translation in this language, defaults to current language.
+ * @return string The translated string.
+ */
+ public function wpml_translate_single_string( $string, $context, $name, $lang = null ) {
+ $has_translation = null; // Passed by reference.
+ return icl_translate( $context, $name, $string, false, $has_translation, $lang );
+ }
+
+ /**
+ * Converts a permalink to a language specific permalink.
+ *
+ * @since 2.2
+ *
+ * @param string $url The url to filter.
+ * @param null|string $lang Language code, optional, defaults to the current language.
+ * @return string
+ */
+ public function wpml_permalink( $url, $lang = '' ) {
+ $lang = PLL()->model->get_language( $lang );
+
+ if ( empty( $lang ) && ! empty( PLL()->curlang ) ) {
+ $lang = PLL()->curlang;
+ }
+
+ return empty( $lang ) ? $url : PLL()->links_model->switch_language_in_link( $url, $lang );
+ }
+
+ /**
+ * Translates a post type slug.
+ *
+ * @since 2.2
+ *
+ * @param string $slug Post type slug.
+ * @param string $post_type Post type name.
+ * @param string $lang Optional language code (defaults to current language).
+ * @return string
+ */
+ public function wpml_get_translated_slug( $slug, $post_type, $lang = null ) {
+ if ( isset( PLL()->translate_slugs ) ) {
+ if ( empty( $lang ) ) {
+ $lang = pll_current_language();
+ }
+
+ $slug = PLL()->translate_slugs->slugs_model->get_translated_slug( $post_type, $lang );
+ }
+ return $slug;
+ }
+
+ /**
+ * Find out whether a post type or a taxonomy term is translated.
+ *
+ * @since 2.0
+ *
+ * @param mixed $null Not used.
+ * @param int $id The post_id or term_id.
+ * @param string $type The post type or taxonomy.
+ * @return bool
+ */
+ public function wpml_element_has_translations( $null, $id, $type ) {
+ if ( 'post' === $type || pll_is_translated_post_type( $type ) ) {
+ return count( pll_get_post_translations( $id ) ) > 1;
+ } elseif ( 'term' === $type || pll_is_translated_taxonomy( $type ) ) {
+ return count( pll_get_term_translations( $id ) ) > 1;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if languages and translations are managed for this post type.
+ *
+ * @since 3.4
+ *
+ * @param mixed $value Not used.
+ * @param string $post_type The post type name.
+ * @return bool
+ */
+ public function wpml_is_translated_post_type( $value, $post_type ) {
+ return pll_is_translated_post_type( $post_type );
+ }
+
+ /**
+ * Returns true if languages and translations are managed for this taxonomy.
+ *
+ * @since 3.4
+ *
+ * @param mixed $value Not used.
+ * @param string $taxonomy The taxonomy name.
+ * @return bool
+ */
+ public function wpml_is_translated_taxonomy( $value, $taxonomy ) {
+ return pll_is_translated_taxonomy( $taxonomy );
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/wpml/wpml-compat.php b/wp-content/plugins/polylang/modules/wpml/wpml-compat.php
new file mode 100644
index 0000000000..a828d47547
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wpml/wpml-compat.php
@@ -0,0 +1,193 @@
+api = new PLL_WPML_API();
+
+ self::$strings = get_option( 'polylang_wpml_strings', array() );
+
+ if ( ! is_array( self::$strings ) ) {
+ self::$strings = array(); // In case the serialized option is corrupted.
+ }
+
+ add_action( 'pll_language_defined', array( $this, 'define_constants' ) );
+ add_action( 'pll_no_language_defined', array( $this, 'define_constants' ) );
+ add_filter( 'pll_get_strings', array( $this, 'get_strings' ) );
+ }
+
+ /**
+ * Access to the single instance of the class
+ *
+ * @since 1.7
+ *
+ * @return PLL_WPML_Compat
+ */
+ public static function instance() {
+ if ( empty( self::$instance ) ) {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Defines two WPML constants once the language has been defined
+ * The compatibility with WPML is not perfect on admin side as the constants are defined
+ * in 'setup_theme' by Polylang (based on user info) and 'plugins_loaded' by WPML (based on cookie).
+ *
+ * @since 0.9.5
+ *
+ * @return void
+ */
+ public function define_constants() {
+ if ( ! empty( PLL()->curlang ) ) {
+ if ( ! defined( 'ICL_LANGUAGE_CODE' ) ) {
+ define( 'ICL_LANGUAGE_CODE', PLL()->curlang->slug );
+ }
+
+ if ( ! defined( 'ICL_LANGUAGE_NAME' ) ) {
+ define( 'ICL_LANGUAGE_NAME', PLL()->curlang->name );
+ }
+ } elseif ( ! PLL() instanceof PLL_Frontend ) {
+ if ( ! defined( 'ICL_LANGUAGE_CODE' ) ) {
+ define( 'ICL_LANGUAGE_CODE', 'all' );
+ }
+
+ if ( ! defined( 'ICL_LANGUAGE_NAME' ) ) {
+ define( 'ICL_LANGUAGE_NAME', '' );
+ }
+ }
+ }
+
+ /**
+ * Unlike pll_register_string, icl_register_string stores the string in database
+ * so we need to do the same as some plugins or themes may expect this.
+ * We use a serialized option to store these strings.
+ *
+ * @since 1.0.2
+ *
+ * @param string|string[] $context The group in which the string is registered.
+ * @param string $name A unique name for the string.
+ * @param string $string The string to register.
+ * @return void
+ */
+ public function register_string( $context, $name, $string ) {
+ if ( ! $string || ! is_scalar( $string ) ) {
+ return;
+ }
+
+ /*
+ * WPML accepts arrays as context and internally converts them to strings.
+ * See WPML_Register_String_Filter::truncate_name_and_context().
+ * This possibility is used by Types.
+ */
+ if ( is_array( $context ) ) {
+ $name = isset( $context['context'] ) ? $name . $context['context'] : $name;
+ $context = $context['domain'] ?? '';
+ }
+
+ // If a string has already been registered with the same name and context, let's replace it.
+ $exist_string = $this->get_string_by_context_and_name( $context, $name );
+ if ( $exist_string && $exist_string !== $string ) {
+ $languages = PLL()->model->get_languages_list();
+
+ // Assign translations of the old string to the new string, except for the default language.
+ foreach ( $languages as $language ) {
+ if ( $language->is_default ) {
+ continue;
+ }
+ $mo = new PLL_MO();
+ $mo->import_from_db( $language );
+ $mo->add_entry( $mo->make_entry( $string, $mo->translate( $exist_string ) ) );
+ $mo->export_to_db( $language );
+ }
+ $this->unregister_string( $context, $name );
+ }
+
+ // Registers the string if it does not exist yet (multiline as in WPML).
+ $to_register = array( 'context' => $context, 'name' => $name, 'string' => $string, 'multiline' => true, 'icl' => true );
+ if ( ! in_array( $to_register, self::$strings ) ) {
+ $key = md5( "$context | $name" );
+ self::$strings[ $key ] = $to_register;
+ update_option( 'polylang_wpml_strings', self::$strings );
+ }
+ }
+
+ /**
+ * Removes a string from the registered strings list
+ *
+ * @since 1.0.2
+ *
+ * @param string $context The group in which the string is registered.
+ * @param string $name A unique name for the string.
+ * @return void
+ */
+ public function unregister_string( $context, $name ) {
+ $key = md5( "$context | $name" );
+ if ( isset( self::$strings[ $key ] ) ) {
+ unset( self::$strings[ $key ] );
+ update_option( 'polylang_wpml_strings', self::$strings );
+ }
+ }
+
+ /**
+ * Adds strings registered by icl_register_string to those registered by pll_register_string
+ *
+ * @since 1.0.2
+ *
+ * @param array $strings existing registered strings
+ * @return array registered strings with added strings through WPML API
+ */
+ public function get_strings( $strings ) {
+ return empty( self::$strings ) ? $strings : array_merge( $strings, self::$strings );
+ }
+
+ /**
+ * Get a registered string by its context and name
+ *
+ * @since 2.0
+ *
+ * @param string $context The group in which the string is registered.
+ * @param string $name A unique name for the string.
+ * @return bool|string The registered string, false if none was found.
+ */
+ public function get_string_by_context_and_name( $context, $name ) {
+ $key = md5( "$context | $name" );
+ return isset( self::$strings[ $key ] ) ? self::$strings[ $key ]['string'] : false;
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/wpml/wpml-config.php b/wp-content/plugins/polylang/modules/wpml/wpml-config.php
new file mode 100644
index 0000000000..9ba2662d14
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wpml/wpml-config.php
@@ -0,0 +1,1048 @@
+
+ */
+class PLL_WPML_Config {
+ /**
+ * Singleton instance
+ *
+ * @var PLL_WPML_Config|null
+ */
+ protected static $instance;
+
+ /**
+ * The content of all read xml files.
+ *
+ * @var SimpleXMLElement[]
+ */
+ protected $xmls = array();
+
+ /**
+ * The list of xml file paths.
+ *
+ * @var string[]|null
+ *
+ * @phpstan-var array|null
+ */
+ protected $files;
+
+ /**
+ * List of rules to extract strings to translate from blocks.
+ *
+ * @var array
+ *
+ * @phpstan-var array{
+ * xpath?: array>,
+ * key?: array>
+ * }|null
+ */
+ protected $parsing_rules = null;
+
+ /**
+ * Contains the list of path in `open_basedir`.
+ *
+ * @var string[]|null
+ */
+ private $open_basedir_paths;
+
+ /**
+ * Cache for parsed metas.
+ *
+ * @var array
+ *
+ * @phpstan-var array
+ */
+ private $parsed_metas = array();
+
+ /**
+ * Constructor
+ *
+ * @since 1.0
+ */
+ public function __construct() {
+ if ( extension_loaded( 'simplexml' ) ) {
+ $this->init();
+ }
+ }
+
+ /**
+ * Access to the single instance of the class
+ *
+ * @since 1.7
+ *
+ * @return PLL_WPML_Config
+ */
+ public static function instance() {
+ if ( empty( self::$instance ) ) {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Finds the wpml-config.xml files to parse and setup filters
+ *
+ * @since 1.0
+ *
+ * @return void
+ */
+ public function init() {
+ $this->xmls = array();
+ $files = $this->get_files();
+
+ if ( empty( $files ) ) {
+ return;
+ }
+
+ if ( ! extension_loaded( 'simplexml' ) ) {
+ return;
+ }
+
+ // Read all files.
+ foreach ( $files as $context => $file ) {
+ $xml = simplexml_load_file( $file );
+ if ( false !== $xml ) {
+ $this->xmls[ $context ] = $xml;
+ }
+ }
+
+ if ( empty( $this->xmls ) ) {
+ return;
+ }
+
+ add_filter( 'pll_copy_post_metas', array( $this, 'copy_post_metas' ), 20, 2 );
+ add_filter( 'pll_copy_term_metas', array( $this, 'copy_term_metas' ), 20, 2 );
+ add_filter( 'pll_get_post_types', array( $this, 'translate_types' ), 10, 2 );
+ add_filter( 'pll_get_taxonomies', array( $this, 'translate_taxonomies' ), 10, 2 );
+
+ // Export.
+ add_filter( 'pll_post_metas_to_export', array( $this, 'post_metas_to_export' ) );
+ add_filter( 'pll_term_metas_to_export', array( $this, 'term_metas_to_export' ) );
+ add_filter( 'pll_post_meta_encodings', array( $this, 'add_post_meta_encodings' ), 20 );
+ add_filter( 'pll_term_meta_encodings', array( $this, 'add_term_meta_encodings' ), 20 );
+ add_filter( 'pll_blocks_xpath_rules', array( $this, 'translate_blocks' ) );
+ add_filter( 'pll_blocks_rules_for_attributes', array( $this, 'translate_blocks_attributes' ) );
+
+ foreach ( $this->xmls as $context => $xml ) {
+ $keys = $xml->xpath( 'admin-texts/key' );
+
+ if ( ! is_array( $keys ) ) {
+ continue;
+ }
+
+ foreach ( $keys as $key ) {
+ $name = $this->get_field_attribute( $key, 'name' );
+
+ if ( false === strpos( $name, '*' ) ) {
+ $this->register_or_translate_option( $context, $name, $key );
+ continue;
+ }
+
+ $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
+ $names = preg_grep( $pattern, array_keys( wp_load_alloptions() ) );
+
+ if ( ! is_array( $names ) ) {
+ continue;
+ }
+
+ foreach ( $names as $_name ) {
+ $this->register_or_translate_option( $context, $_name, $key );
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns all wpml-config.xml files in MU plugins, plugins, theme, child theme, and Polylang custom directory.
+ *
+ * @since 3.1
+ *
+ * @return string[] A context identifier as array key, a file path as array value.
+ *
+ * @phpstan-return array
+ */
+ public function get_files() {
+ if ( is_array( $this->files ) ) {
+ return $this->files;
+ }
+
+ $this->files = array_merge(
+ // Plugins.
+ $this->get_plugin_files(),
+ // Theme and child theme.
+ $this->get_theme_files(),
+ // MU Plugins.
+ $this->get_mu_plugin_files(),
+ // Custom.
+ $this->get_custom_files()
+ );
+
+ return $this->files;
+ }
+
+ /**
+ * Adds post metas to the list of metas to copy when creating a new translation.
+ *
+ * @since 1.0
+ *
+ * @param string[] $metas The list of post metas to copy or synchronize.
+ * @param bool $sync True for sync, false for copy.
+ * @return string[] The list of post metas to copy or synchronize.
+ *
+ * @phpstan-param array $metas
+ */
+ public function copy_post_metas( $metas, $sync ) {
+ return $this->filter_metas_to_copy( (array) $metas, 'custom-fields/custom-field', (bool) $sync );
+ }
+
+ /**
+ * Adds term metas to the list of metas to copy when creating a new translation.
+ *
+ * @since 2.6
+ *
+ * @param string[] $metas The list of term metas to copy or synchronize.
+ * @param bool $sync True for sync, false for copy.
+ * @return string[] The list of term metas to copy or synchronize.
+ *
+ * @phpstan-param array $metas
+ */
+ public function copy_term_metas( $metas, $sync ) {
+ return $this->filter_metas_to_copy( (array) $metas, 'custom-term-fields/custom-term-field', (bool) $sync );
+ }
+
+ /**
+ * Adds post meta keys to export.
+ *
+ * @since 3.3
+ * @see PLL_Export_Metas
+ *
+ * @param array $keys {
+ * A recursive array containing nested meta sub-keys to translate.
+ * Ex: array(
+ * 'meta_to_translate_1' => 1,
+ * 'meta_to_translate_2' => 1,
+ * 'meta_to_translate_3' => array(
+ * 'sub_key_to_translate_1' => 1,
+ * 'sub_key_to_translate_2' => array(
+ * 'sub_sub_key_to_translate_1' => 1,
+ * ),
+ * ),
+ * )
+ * }
+ * @return array
+ *
+ * @phpstan-param array $keys
+ * @phpstan-return array
+ */
+ public function post_metas_to_export( $keys ) {
+ // Add keys that have the `action` attribute set to `translate`.
+ $keys = $this->add_metas_to_export( (array) $keys, 'custom-fields/custom-field' );
+
+ // Deal with sub-field translations.
+ foreach ( $this->xmls as $xml ) {
+ $fields = $xml->xpath( 'custom-fields-texts/key' );
+
+ if ( ! is_array( $fields ) ) {
+ // No 'custom-fields-texts' nodes.
+ continue;
+ }
+
+ foreach ( $fields as $field ) {
+ $name = $this->get_field_attribute( $field, 'name' );
+
+ if ( '' === $name ) {
+ // Wrong configuration: empty `name` attribute (meta name).
+ continue;
+ }
+
+ if ( ! array_key_exists( $name, $keys ) ) {
+ // Wrong configuration: the field is not in `custom-fields/custom-field`.
+ continue;
+ }
+
+ $keys = $this->xml_to_array( $field, $keys, 1 );
+ }
+ }
+
+ return $keys;
+ }
+
+ /**
+ * Adds term meta keys to export.
+ * Note: sub-key translations are not currently supported by WPML.
+ *
+ * @since 3.3
+ * @see PLL_Export_Metas
+ *
+ * @param array $keys {
+ * An array containing meta keys to translate.
+ * Ex: array(
+ * 'meta_to_translate_1' => 1,
+ * 'meta_to_translate_2' => 1,
+ * 'meta_to_translate_3' => 1,
+ * )
+ * }
+ * @return array
+ *
+ * @phpstan-param array $keys
+ * @phpstan-return array
+ */
+ public function term_metas_to_export( $keys ) {
+ // Add keys that have the `action` attribute set to `translate`.
+ return $this->add_metas_to_export( (array) $keys, 'custom-term-fields/custom-term-field' );
+ }
+
+ /**
+ * Specifies the encoding for post metas.
+ *
+ * @since 3.6
+ *
+ * @param string[] $metas An array containing meta names as array keys, and their encoding as array values.
+ * @return string[]
+ *
+ * @phpstan-param array $metas
+ */
+ public function add_post_meta_encodings( $metas ) {
+ return $this->add_metas_encodings( (array) $metas, 'custom-fields/custom-field' );
+ }
+
+ /**
+ * Specifies the encoding for term metas.
+ *
+ * @since 3.6
+ *
+ * @param string[] $metas An array containing meta names as array keys, and their encoding as array values.
+ * @return string[]
+ *
+ * @phpstan-param array $metas
+ */
+ public function add_term_meta_encodings( $metas ) {
+ return $this->add_metas_encodings( (array) $metas, 'custom-term-fields/custom-term-field' );
+ }
+
+ /**
+ * Language and translation management for custom post types.
+ *
+ * @since 1.0
+ *
+ * @param string[] $types The list of post type names for which Polylang manages language and translations.
+ * @param bool $hide True when displaying the list in Polylang settings.
+ * @return string[] The list of post type names for which Polylang manages language and translations.
+ */
+ public function translate_types( $types, $hide ) {
+ foreach ( $this->xmls as $xml ) {
+ $pts = $xml->xpath( 'custom-types/custom-type' );
+
+ if ( ! is_array( $pts ) ) {
+ continue;
+ }
+
+ foreach ( $pts as $pt ) {
+ $translate = $this->get_field_attribute( $pt, 'translate' );
+
+ if ( '1' === $translate && ! $hide ) {
+ $types[ (string) $pt ] = (string) $pt;
+ } else {
+ unset( $types[ (string) $pt ] ); // The theme/plugin author decided what to do with the post type so don't allow the user to change this
+ }
+ }
+ }
+
+ return $types;
+ }
+
+ /**
+ * Language and translation management for custom taxonomies.
+ *
+ * @since 1.0
+ *
+ * @param string[] $taxonomies The list of taxonomy names for which Polylang manages language and translations.
+ * @param bool $hide True when displaying the list in Polylang settings.
+ * @return string[] The list of taxonomy names for which Polylang manages language and translations.
+ */
+ public function translate_taxonomies( $taxonomies, $hide ) {
+ foreach ( $this->xmls as $xml ) {
+ $taxos = $xml->xpath( 'taxonomies/taxonomy' );
+
+ if ( ! is_array( $taxos ) ) {
+ continue;
+ }
+
+ foreach ( $taxos as $tax ) {
+ $translate = $this->get_field_attribute( $tax, 'translate' );
+
+ if ( '1' === $translate && ! $hide ) {
+ $taxonomies[ (string) $tax ] = (string) $tax;
+ } else {
+ unset( $taxonomies[ (string) $tax ] ); // the theme/plugin author decided what to do with the taxonomy so don't allow the user to change this
+ }
+ }
+ }
+
+ return $taxonomies;
+ }
+
+ /**
+ * Translation management for strings in blocks content.
+ *
+ * @since 3.3
+ *
+ * @param string[][] $parsing_rules Rules as Xpath expressions to evaluate in the blocks content.
+ * @return string[][] Rules completed with ones from wpml-config file.
+ *
+ * @phpstan-param array> $parsing_rules
+ * @phpstan-return array>
+ */
+ public function translate_blocks( $parsing_rules ) {
+ return array_merge( $parsing_rules, $this->get_blocks_parsing_rules( 'xpath' ) );
+ }
+
+ /**
+ * Translation management for blocks attributes.
+ *
+ * @since 3.3
+ * @since 3.6 Format changed from `array` to `array`.
+ *
+ * @param array $parsing_rules Rules for blocks attributes to translate.
+ * @return array Rules completed with ones from wpml-config file.
+ *
+ * @phpstan-param array $parsing_rules
+ * @phpstan-return array
+ */
+ public function translate_blocks_attributes( $parsing_rules ) {
+ return array_merge( $parsing_rules, $this->get_blocks_parsing_rules( 'key' ) );
+ }
+
+ /**
+ * Returns rules to extract translatable strings from blocks.
+ *
+ * @since 3.3
+ *
+ * @param string $rule_tag Tag name to extract.
+ * @return string[][] The rules.
+ *
+ * @phpstan-param 'xpath'|'key' $rule_tag
+ * @phpstan-return (
+ * $rule_tag is 'xpath' ? array> : array>
+ * )
+ */
+ protected function get_blocks_parsing_rules( $rule_tag ) {
+
+ if ( null === $this->parsing_rules ) {
+ $this->parsing_rules = $this->extract_blocks_parsing_rules();
+ }
+
+ return isset( $this->parsing_rules[ $rule_tag ] ) ? $this->parsing_rules[ $rule_tag ] : array();
+ }
+
+ /**
+ * Extract all rules from WPML config file to translate strings for blocks.
+ *
+ * @since 3.3
+ *
+ * @return string[][][] Rules completed with ones from wpml-config file.
+ *
+ * @phpstan-return array{
+ * xpath?: array>,
+ * key?: array>
+ * }
+ */
+ protected function extract_blocks_parsing_rules() {
+ $parsing_rules = array();
+
+ foreach ( $this->xmls as $xml ) {
+ $blocks = $xml->xpath( 'gutenberg-blocks/gutenberg-block' );
+
+ if ( ! is_array( $blocks ) ) {
+ continue;
+ }
+
+ foreach ( $blocks as $block ) {
+ $translate = $this->get_field_attribute( $block, 'translate' );
+
+ if ( '1' !== $translate ) {
+ continue;
+ }
+
+ $block_name = $this->get_field_attribute( $block, 'type' );
+
+ if ( '' === $block_name ) {
+ continue;
+ }
+
+ foreach ( $block->children() as $child ) {
+ $rule = '';
+ $child_tag = $child->getName();
+
+ switch ( $child_tag ) {
+ case 'xpath':
+ $rule = trim( (string) $child );
+
+ if ( '' !== $rule ) {
+ $parsing_rules['xpath'][ $block_name ][] = $rule;
+ }
+ break;
+
+ case 'key':
+ $rule = $this->get_field_attributes( $child );
+
+ if ( empty( $rule ) ) {
+ break;
+ }
+
+ if ( isset( $parsing_rules['key'][ $block_name ] ) ) {
+ $parsing_rules['key'][ $block_name ] = $this->array_merge_recursive( $parsing_rules['key'][ $block_name ], $rule );
+ } else {
+ $parsing_rules['key'][ $block_name ] = $rule;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return $parsing_rules;
+ }
+
+ /**
+ * Merge two arrays recursively.
+ * Unlike `array_merge_recursive()`, this method doesn't change the type of the values.
+ *
+ * @since 3.6
+ *
+ * @param array $array1 Array to merge into.
+ * @param array $array2 Array to merge.
+ * @return array
+ */
+ protected function array_merge_recursive( array $array1, array $array2 ): array {
+ foreach ( $array2 as $key => $value ) {
+ if ( is_array( $value ) && isset( $array1[ $key ] ) && is_array( $array1[ $key ] ) ) {
+ $array1[ $key ] = $this->array_merge_recursive( $array1[ $key ], $value );
+ } else {
+ $array1[ $key ] = $value;
+ }
+ }
+
+ return $array1;
+ }
+
+ /**
+ * Registers or translates the strings for an option
+ *
+ * @since 2.8
+ *
+ * @param string $context The group in which the strings will be registered.
+ * @param string $name Option name.
+ * @param SimpleXMLElement $key XML node.
+ * @return void
+ */
+ protected function register_or_translate_option( $context, $name, $key ) {
+ $option_keys = $this->xml_to_array( $key );
+ new PLL_Translate_Option( $name, reset( $option_keys ), array( 'context' => $context ) );
+ }
+
+ /**
+ * Recursively transforms xml nodes to an array, ready for PLL_Translate_Option.
+ *
+ * @since 2.9
+ * @since 3.3 Type-hinted the parameters `$key` and `$arr`.
+ * @since 3.3 `$arr` is not passed by reference anymore.
+ * @since 3.3 Added the parameter `$fill_value`.
+ *
+ * @param SimpleXMLElement $key XML node.
+ * @param array $arr Array of option keys to translate.
+ * @param mixed $fill_value Value to use when filling entries. Default is true.
+ * @return array
+ */
+ protected function xml_to_array( SimpleXMLElement $key, array $arr = array(), $fill_value = true ) {
+ $name = $this->get_field_attribute( $key, 'name' );
+
+ if ( '' === $name ) {
+ return $arr;
+ }
+
+ $children = $key->children();
+
+ if ( count( $children ) ) {
+ foreach ( $children as $child ) {
+ if ( ! isset( $arr[ $name ] ) || ! is_array( $arr[ $name ] ) ) {
+ $arr[ $name ] = array();
+ }
+
+ $arr[ $name ] = $this->xml_to_array( $child, $arr[ $name ], $fill_value );
+ }
+ } else {
+ $arr[ $name ] = $fill_value; // Multiline as in WPML.
+ }
+
+ return $arr;
+ }
+
+ /**
+ * Get the value of an attribute.
+ *
+ * @since 3.3
+ *
+ * @param SimpleXMLElement $field A XML node.
+ * @param string $attribute_name Node of the attribute.
+ * @return string
+ */
+ private function get_field_attribute( SimpleXMLElement $field, $attribute_name ) {
+ $attributes = $field->attributes();
+
+ if ( empty( $attributes ) || ! isset( $attributes[ $attribute_name ] ) ) {
+ return '';
+ }
+
+ return trim( (string) $attributes[ $attribute_name ] );
+ }
+
+ /**
+ * Gets attributes values recursively.
+ *
+ * @since 3.6
+ *
+ * @param SimpleXMLElement $field A XML node.
+ * @return array An array of attributes.
+ *
+ * @phpstan-return array
+ */
+ private function get_field_attributes( SimpleXMLElement $field ): array {
+ $name = $this->get_field_attribute( $field, 'name' );
+
+ if ( '' === $name ) {
+ return array();
+ }
+
+ $children = $field->children();
+
+ if ( 0 === $children->count() ) {
+ return array( $name => true );
+ }
+
+ $sub_attributes = array();
+
+ foreach ( $children as $child ) {
+ $sub = $this->get_field_attributes( $child );
+
+ if ( empty( $sub ) ) {
+ continue;
+ }
+
+ $sub_attributes[ $name ] = array_merge( $sub_attributes[ $name ] ?? array(), $sub );
+ }
+
+ return $sub_attributes;
+ }
+
+ /**
+ * Returns all wpml-config.xml files in MU plugins.
+ *
+ * @since 3.3
+ *
+ * @return string[] A context identifier as array key, a file path as array value.
+ *
+ * @phpstan-return array
+ */
+ private function get_mu_plugin_files() {
+ if ( ! is_readable( WPMU_PLUGIN_DIR ) || ! is_dir( WPMU_PLUGIN_DIR ) ) {
+ return array();
+ }
+
+ $files = array();
+
+ // Search for top level wpml-config.xml file.
+ $file_path = WPMU_PLUGIN_DIR . '/wpml-config.xml';
+
+ if ( is_readable( $file_path ) ) {
+ $files['mu-plugins'] = $file_path;
+ }
+
+ // Search in proxy loaded MU plugins.
+ foreach ( new DirectoryIterator( WPMU_PLUGIN_DIR ) as $file_info ) {
+ if ( ! $this->is_dir( $file_info ) ) {
+ continue;
+ }
+
+ $file_path = $file_info->getPathname() . '/wpml-config.xml';
+
+ if ( is_readable( $file_path ) ) {
+ $files[ 'mu-plugins/' . $file_info->getFilename() ] = $file_path;
+ }
+ }
+
+ return $files;
+ }
+
+ /**
+ * Returns all wpml-config.xml files in plugins.
+ *
+ * @since 3.3
+ *
+ * @return string[] A context identifier as array key, a file path as array value.
+ *
+ * @phpstan-return array
+ */
+ private function get_plugin_files() {
+ $files = array();
+ $plugins = array();
+
+ if ( is_multisite() ) {
+ // Don't forget sitewide active plugins thanks to Reactorshop http://wordpress.org/support/topic/polylang-and-yoast-seo-plugin/page/2?replies=38#post-4801829.
+ $sitewide_plugins = get_site_option( 'active_sitewide_plugins', array() );
+
+ if ( ! empty( $sitewide_plugins ) && is_array( $sitewide_plugins ) ) {
+ $plugins = array_keys( $sitewide_plugins );
+ }
+ }
+
+ // By-site plugins.
+ $active_plugins = get_option( 'active_plugins', array() );
+
+ if ( ! empty( $active_plugins ) && is_array( $active_plugins ) ) {
+ $plugins = array_merge( $plugins, $active_plugins );
+ }
+
+ $plugin_path = trailingslashit( WP_PLUGIN_DIR ) . '%s/wpml-config.xml';
+
+ foreach ( $plugins as $plugin ) {
+ if ( ! is_string( $plugin ) || '' === $plugin ) {
+ continue;
+ }
+
+ $file_dir = dirname( $plugin );
+ $file_path = sprintf( $plugin_path, $file_dir );
+
+ if ( is_readable( $file_path ) ) {
+ $files[ "plugins/{$file_dir}" ] = $file_path;
+ }
+ }
+
+ return $files;
+ }
+
+ /**
+ * Returns all wpml-config.xml files in theme and child theme.
+ *
+ * @since 3.3
+ *
+ * @return string[] A context identifier as array key, a file path as array value.
+ *
+ * @phpstan-return array
+ */
+ private function get_theme_files() {
+ $files = array();
+
+ // Theme.
+ $template_path = get_template_directory();
+ $file_path = "{$template_path}/wpml-config.xml";
+
+ if ( is_readable( $file_path ) ) {
+ $files[ 'themes/' . get_template() ] = $file_path;
+ }
+
+ // Child theme.
+ $stylesheet_path = get_stylesheet_directory();
+ $file_path = "{$stylesheet_path}/wpml-config.xml";
+
+ if ( $stylesheet_path !== $template_path && is_readable( $file_path ) ) {
+ $files[ 'themes/' . get_stylesheet() ] = $file_path;
+ }
+
+ return $files;
+ }
+
+ /**
+ * Returns the wpml-config.xml file in Polylang custom directory.
+ *
+ * @since 3.3
+ *
+ * @return string[] A context identifier as array key, a file path as array value.
+ *
+ * @phpstan-return array
+ */
+ private function get_custom_files() {
+ $file_path = PLL_LOCAL_DIR . '/wpml-config.xml';
+
+ if ( ! is_readable( $file_path ) ) {
+ return array();
+ }
+
+ return array(
+ 'Polylang' => $file_path,
+ );
+ }
+
+ /**
+ * Tells if the given "file info" object represents a directory.
+ * This takes care of not triggering a `open_basedir` restriction error when the file symlinks a file that is not in
+ * `open_basedir`.
+ *
+ * @see https://wordpress.org/support/topic/fatal-error-open_basedir-restricton/
+ *
+ * @since 3.5.1
+ *
+ * @param DirectoryIterator $file_info A "file info" object that we know its path (but maybe not its real path) is
+ * in `open_basedir`.
+ * @return bool
+ */
+ private function is_dir( DirectoryIterator $file_info ): bool {
+ if ( $file_info->isDot() ) {
+ return false;
+ }
+
+ if ( $file_info->getPathname() === $file_info->getRealPath() ) {
+ // Not a symlink: not going to trigger a `open_basedir` restriction error.
+ return $file_info->isDir();
+ }
+
+ /*
+ * Symlink: make sure the file's real path is in `open_basedir` before checking it is a dir.
+ * Which means that the `open_basedir` check is done only for symlinked files.
+ */
+ return $this->is_allowed_dir( $file_info->getRealPath() ) && $file_info->isDir();
+ }
+
+ /**
+ * Checks whether access to a given directory is allowed.
+ * This takes into account the PHP `open_basedir` restrictions, so that Polylang does not try to access directories
+ * it is not allowed to.
+ *
+ * Inspired by `WP_Automatic_Updater::is_allowed_dir()` and `wp-includes/ID3/getid3.php`.
+ *
+ * @since 3.5.1
+ *
+ * @param string $dir The directory to check.
+ * @return bool True if access to the directory is allowed, false otherwise.
+ */
+ private function is_allowed_dir( string $dir ): bool {
+ $dir = trim( $dir );
+
+ if ( '' === $dir ) {
+ return false;
+ }
+
+ $open_basedir_paths = $this->get_open_basedir_paths();
+
+ if ( empty( $open_basedir_paths ) ) {
+ return true;
+ }
+
+ $dir = $this->normalize_path( $dir );
+
+ foreach ( $open_basedir_paths as $path ) {
+ if ( str_starts_with( $dir, $path ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the list of paths in `open_basedir`. The purpose is to compare a formatted path to this list.
+ * Note: all paths are suffixed by `DIRECTORY_SEPARATOR`, even paths to files.
+ *
+ * @since 3.5.1
+ *
+ * @return string[] An array of formatted paths.
+ */
+ private function get_open_basedir_paths(): array {
+ if ( is_array( $this->open_basedir_paths ) ) {
+ return $this->open_basedir_paths;
+ }
+
+ $this->open_basedir_paths = array();
+ $open_basedir = ini_get( 'open_basedir' ); // Can be `false` or an empty string.
+
+ if ( empty( $open_basedir ) ) {
+ return $this->open_basedir_paths;
+ }
+
+ $open_basedir_list = explode( PATH_SEPARATOR, $open_basedir );
+
+ foreach ( $open_basedir_list as $basedir ) {
+ $basedir = trim( $basedir );
+
+ if ( '' === $basedir ) {
+ continue;
+ }
+
+ $this->open_basedir_paths[] = $this->normalize_path( $basedir );
+ }
+
+ $this->open_basedir_paths = array_unique( $this->open_basedir_paths );
+
+ return $this->open_basedir_paths;
+ }
+
+ /**
+ * Formats a path for string comparison.
+ * 1. Slashes and back-slashes are replaced by `DIRECTORY_SEPARATOR`.
+ * 2. The path is suffixed by `DIRECTORY_SEPARATOR` (even non-directory elements).
+ *
+ * @since 3.5.1
+ *
+ * @param string $path A file path.
+ * @return string
+ *
+ * @phpstan-param non-empty-string $path
+ * @phpstan-return non-empty-string
+ */
+ private function normalize_path( string $path ): string {
+ $path = str_replace( array( '/', '\\' ), DIRECTORY_SEPARATOR, $path );
+
+ if ( substr( $path, -1, 1 ) !== DIRECTORY_SEPARATOR ) {
+ $path .= DIRECTORY_SEPARATOR;
+ }
+
+ return $path;
+ }
+
+ /**
+ * Adds (or removes) meta names to the list of metas to copy or synchronize.
+ *
+ * @since 3.6
+ *
+ * @param string[] $metas The list of meta names to copy or synchronize.
+ * @param string $xpath Xpath to the meta fields in the xml files.
+ * @param bool $sync Either sync is enabled or not.
+ * @return string[]
+ *
+ * @phpstan-param array $metas
+ * @phpstan-param non-falsy-string $xpath
+ */
+ private function filter_metas_to_copy( array $metas, string $xpath, bool $sync ): array {
+ $parsed_metas = $this->parse_xml_metas( $xpath );
+ $metas_to_remove = array();
+
+ foreach ( $parsed_metas as $name => $parsed_meta ) {
+ if ( 'copy' === $parsed_meta['action'] || ( ! $sync && in_array( $parsed_meta['action'], array( 'translate', 'copy-once' ), true ) ) ) {
+ $metas[] = $name;
+ } else {
+ $metas_to_remove[] = $name;
+ }
+ }
+
+ return array_diff( $metas, $metas_to_remove );
+ }
+
+ /**
+ * Adds meta keys to export.
+ *
+ * @since 3.6
+ *
+ * @param array $metas {
+ * An array containing meta keys to translate.
+ * Ex: array(
+ * 'meta_to_translate_1' => 1,
+ * 'meta_to_translate_2' => 1,
+ * 'meta_to_translate_3' => array( ... ),
+ * )
+ * }
+ * @param string $xpath Xpath to the meta fields in the xml files.
+ * @return array
+ *
+ * @phpstan-param array $metas
+ * @phpstan-param non-falsy-string $xpath
+ * @phpstan-return array
+ */
+ private function add_metas_to_export( array $metas, string $xpath ) {
+ $fields = $this->parse_xml_metas( $xpath );
+
+ foreach ( $fields as $name => $field ) {
+ if ( 'translate' === $field['action'] ) {
+ $metas[ $name ] = 1;
+ }
+ }
+
+ return $metas;
+ }
+
+ /**
+ * Adds encoding of metas.
+ *
+ * @since 3.6
+ *
+ * @param string[] $metas The list of encodings for each metas. Meta names are array keys, encodings are array values.
+ * @param string $xpath Xpath to the meta fields in the xml files.
+ * @return string[]
+ *
+ * @phpstan-param array $metas
+ * @phpstan-param non-falsy-string $xpath
+ */
+ private function add_metas_encodings( array $metas, string $xpath ): array {
+ $parsed_metas = $this->parse_xml_metas( $xpath );
+
+ foreach ( $parsed_metas as $name => $parsed_meta ) {
+ if ( ! empty( $parsed_meta['encoding'] ) ) {
+ $metas[ $name ] = $parsed_meta['encoding'];
+ }
+ }
+
+ return $metas;
+ }
+
+ /**
+ * Parses all xml files for metas.
+ * Results are cached for each `$xpath`.
+ *
+ * @since 3.6
+ *
+ * @param string $xpath Xpath to the meta fields in the xml files.
+ * @return array
+ *
+ * @phpstan-param non-falsy-string $xpath
+ * @phpstan-return ParsedMetas
+ */
+ private function parse_xml_metas( string $xpath ): array {
+ if ( isset( $this->parsed_metas[ $xpath ] ) ) {
+ return $this->parsed_metas[ $xpath ];
+ }
+
+ $this->parsed_metas[ $xpath ] = array();
+
+ foreach ( $this->xmls as $xml ) {
+ $custom_fields = $xml->xpath( $xpath );
+
+ if ( ! is_array( $custom_fields ) ) {
+ continue;
+ }
+
+ foreach ( $custom_fields as $custom_field ) {
+ $name = (string) $custom_field;
+
+ if ( empty( $name ) ) {
+ continue;
+ }
+
+ $data = array(
+ 'action' => $this->get_field_attribute( $custom_field, 'action' ),
+ 'encoding' => $this->get_field_attribute( $custom_field, 'encoding' ),
+ );
+
+ $data['encoding'] = 'json' === $data['encoding'] ? 'json' : ''; // Only JSON is supported for now.
+
+ $this->parsed_metas[ $xpath ][ $name ] = $data;
+ }
+ }
+
+ return $this->parsed_metas[ $xpath ];
+ }
+}
diff --git a/wp-content/plugins/polylang/modules/wpml/wpml-legacy-api.php b/wp-content/plugins/polylang/modules/wpml/wpml-legacy-api.php
new file mode 100644
index 0000000000..18fba07174
--- /dev/null
+++ b/wp-content/plugins/polylang/modules/wpml/wpml-legacy-api.php
@@ -0,0 +1,418 @@
+ whether to skip missing translation or not, 0 or 1, defaults to 0
+ * orderby => 'id', 'code', 'name', defaults to 'id'
+ * order => 'ASC' or 'DESC', defaults to 'ASC'
+ * link_empty_to => link to use when the translation is missing {$lang} is replaced by the language code
+ *
+ * List of parameters returned per language:
+ *
+ * id => the language id
+ * active => whether this is the active language or no, 0 or 1
+ * native_name => the language name
+ * missing => whether the translation is missing or not, 0 or 1
+ * translated_name => empty, does not exist in Polylang
+ * language_code => the language code ( slug )
+ * country_flag_url => the url of the flag
+ * url => the url of the translation
+ *
+ * @since 1.0
+ *
+ * @param string|array $args optional
+ * @return array array of arrays per language
+ */
+ function icl_get_languages( $args = '' ) {
+ $args = wp_parse_args( $args, array( 'skip_missing' => 0, 'orderby' => 'id', 'order' => 'ASC' ) );
+ $orderby = ( isset( $args['orderby'] ) && 'code' == $args['orderby'] ) ? 'slug' : ( isset( $args['orderby'] ) && 'name' == $args['orderby'] ? 'name' : 'id' );
+ $order = ( ! empty( $args['order'] ) && 'desc' == $args['order'] ) ? 'DESC' : 'ASC';
+
+ $arr = array();
+
+ // NB: When 'skip_missing' is false, WPML returns all languages even if there is no content
+ $languages = PLL()->model->get_languages_list( array( 'hide_empty' => $args['skip_missing'] ) );
+ $languages = wp_list_sort( $languages, $orderby, $order ); // Since WP 4.7
+
+ foreach ( $languages as $lang ) {
+ // We can find a translation only on frontend once the global $wp_query object has been instantiated
+ if ( method_exists( PLL()->links, 'get_translation_url' ) && ! empty( $GLOBALS['wp_query'] ) ) {
+ $url = PLL()->links->get_translation_url( $lang );
+ }
+
+ // It seems that WPML does not bother of skip_missing parameter on admin side and before the $wp_query object has been filled
+ if ( empty( $url ) && ! empty( $args['skip_missing'] ) && ! is_admin() && did_action( 'parse_query' ) ) {
+ continue;
+ }
+
+ $arr[ $lang->slug ] = array(
+ 'id' => $lang->term_id,
+ 'active' => isset( PLL()->curlang->slug ) && PLL()->curlang->slug == $lang->slug ? 1 : 0,
+ 'native_name' => $lang->name,
+ 'missing' => empty( $url ) ? 1 : 0,
+ 'translated_name' => '', // Does not exist in Polylang
+ 'language_code' => $lang->slug,
+ 'country_flag_url' => $lang->get_display_flag_url(),
+ 'url' => ! empty( $url ) ? $url :
+ ( empty( $args['link_empty_to'] ) ? PLL()->links->get_home_url( $lang ) :
+ str_replace( '{$lang}', $lang->slug, $args['link_empty_to'] ) ),
+ );
+ }
+
+ // Apply undocumented WPML filter
+ $arr = apply_filters( 'icl_ls_languages', $arr );
+
+ return $arr;
+ }
+}
+
+if ( ! function_exists( 'icl_link_to_element' ) ) {
+ /**
+ * Used for creating language dependent links in themes
+ *
+ * @since 1.0
+ * @since 2.0 add support for arguments 6 and 7
+ *
+ * @param int $id object id
+ * @param string $type optional, post type or taxonomy name of the object, defaults to 'post'
+ * @param string $text optional, the link text. If not specified will produce the name of the element in the current language
+ * @param array $args optional, an array of arguments to add to the link, defaults to empty
+ * @param string $anchor optional, the anchor to add to the link, defaults to empty
+ * @param bool $echo optional, whether to echo the link, defaults to true
+ * @param bool $return_original_if_missing optional, whether to return a value if the translation is missing
+ * @return string a language dependent link
+ */
+ function icl_link_to_element( $id, $type = 'post', $text = '', $args = array(), $anchor = '', $echo = true, $return_original_if_missing = true ) {
+ if ( 'tag' == $type ) {
+ $type = 'post_tag';
+ }
+
+ $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
+ if ( $pll_type && ( $lang = pll_current_language() ) && ( $tr_id = PLL()->model->$pll_type->get_translation( $id, $lang ) ) && ( 'term' === $pll_type || PLL()->model->post->current_user_can_read( $tr_id ) ) ) {
+ $id = $tr_id;
+ } elseif ( ! $return_original_if_missing ) {
+ return '';
+ }
+
+ if ( post_type_exists( $type ) ) {
+ $link = get_permalink( $id );
+ if ( empty( $text ) ) {
+ $text = get_the_title( $id );
+ }
+ } elseif ( taxonomy_exists( $type ) ) {
+ $link = get_term_link( $id, $type );
+ if ( empty( $text ) && ( $term = get_term( $id, $type ) ) && $term instanceof WP_Term ) {
+ $text = $term->name;
+ }
+ }
+
+ if ( empty( $link ) || is_wp_error( $link ) ) {
+ return '';
+ }
+
+ if ( ! empty( $args ) ) {
+ $link .= ( false === strpos( $link, '?' ) ? '?' : '&' ) . http_build_query( $args );
+ }
+
+ if ( ! empty( $anchor ) ) {
+ $link .= '#' . $anchor;
+ }
+
+ $link = sprintf( '%s ', esc_url( $link ), esc_html( $text ) );
+
+ if ( $echo ) {
+ echo $link; // phpcs:ignore WordPress.Security.EscapeOutput
+ }
+
+ return $link;
+ }
+}
+
+if ( ! function_exists( 'icl_object_id' ) ) {
+ /**
+ * Returns an element’s ID in the current language or in another specified language.
+ *
+ * @since 0.9.5
+ *
+ * @param int $element_id Object id.
+ * @param string $element_type Optional, post type or taxonomy name of the object, defaults to 'post'.
+ * @param bool $return_original_if_missing Optional, true if Polylang should return the original id if the translation is missing, defaults to false.
+ * @param string|null $ulanguage_code Optional, language code, defaults to the current language.
+ * @return int|null The object id of the translation, null if the translation is missing and $return_original_if_missing set to false.
+ */
+ function icl_object_id( $element_id, $element_type = 'post', $return_original_if_missing = false, $ulanguage_code = null ) {
+ if ( empty( $element_id ) ) {
+ return null;
+ }
+
+ $element_id = (int) $element_id;
+
+ if ( 'any' === $element_type ) {
+ $element_type = get_post_type( $element_id );
+ }
+
+ if ( empty( $element_type ) ) {
+ return null;
+ }
+
+ if ( empty( $ulanguage_code ) ) {
+ $ulanguage_code = pll_current_language();
+ }
+
+ if ( 'nav_menu' === $element_type ) {
+ $tr_id = false;
+ $theme = get_option( 'stylesheet' );
+ if ( isset( PLL()->options['nav_menus'][ $theme ] ) ) {
+ foreach ( PLL()->options['nav_menus'][ $theme ] as $menu ) {
+ if ( array_search( $element_id, $menu ) && ! empty( $menu[ $ulanguage_code ] ) ) {
+ $tr_id = $menu[ $ulanguage_code ];
+ break;
+ }
+ }
+ }
+ } elseif ( pll_is_translated_post_type( $element_type ) ) {
+ $tr_id = PLL()->model->post->get_translation( $element_id, $ulanguage_code );
+ } elseif ( pll_is_translated_taxonomy( $element_type ) ) {
+ $tr_id = PLL()->model->term->get_translation( $element_id, $ulanguage_code );
+ }
+
+ if ( ! isset( $tr_id ) ) {
+ return $element_id; // WPML doesn't honor $return_original_if_missing if the post type or taxonomy is not translated.
+ }
+
+ if ( empty( $tr_id ) ) {
+ return $return_original_if_missing ? $element_id : null;
+ }
+
+ return (int) $tr_id;
+ }
+}
+
+if ( ! function_exists( 'wpml_object_id_filter' ) ) {
+ /**
+ * Undocumented alias of `icl_object_id` introduced in WPML 3.2, used by Yith WooCommerce compare
+ *
+ * @since 2.2.4
+ *
+ * @param int $id object id
+ * @param string $type optional, post type or taxonomy name of the object, defaults to 'post'
+ * @param bool $return_original_if_missing optional, true if Polylang should return the original id if the translation is missing, defaults to false
+ * @param string $lang optional, language code, defaults to current language
+ * @return int|null the object id of the translation, null if the translation is missing and $return_original_if_missing set to false
+ */
+ function wpml_object_id_filter( $id, $type = 'post', $return_original_if_missing = false, $lang = null ) {
+ return icl_object_id( $id, $type, $return_original_if_missing, $lang );
+ }
+}
+
+if ( ! function_exists( 'wpml_get_language_information' ) ) {
+ /**
+ * Undocumented function used by the theme Maya
+ * returns the post language
+ *
+ * @see https://wpml.org/forums/topic/canonical-urls-for-wpml-duplicated-posts/#post-52198 for the original WPML code
+ *
+ * @since 1.8
+ *
+ * @param null $empty optional, not used
+ * @param int $post_id optional, post id, defaults to current post
+ * @return array
+ */
+ function wpml_get_language_information( $empty = null, $post_id = null ) {
+ if ( empty( $post_id ) ) {
+ $post_id = get_the_ID();
+ }
+
+ // FIXME WPML may return a WP_Error object
+ return false === ( $lang = PLL()->model->post->get_language( $post_id ) ) ? array() : array(
+ 'language_code' => $lang->slug,
+ 'locale' => $lang->locale,
+ 'text_direction' => (bool) $lang->is_rtl,
+ 'display_name' => $lang->name, // Seems to be the post language name displayed in the current language, not a feature in Polylang
+ 'native_name' => $lang->name,
+ 'different_language' => pll_current_language() !== $lang->slug,
+ );
+ }
+}
+
+if ( ! function_exists( 'icl_register_string' ) ) {
+ /**
+ * Registers a string for translation in the "strings translation" panel
+ *
+ * The 4th and 5th parameters $allow_empty_value and $source_lang are not used by Polylang.
+ *
+ * @since 0.9.3
+ *
+ * @param string $context the group in which the string is registered, defaults to 'polylang'
+ * @param string $name a unique name for the string
+ * @param string $string the string to register
+ * @return void
+ */
+ function icl_register_string( $context, $name, $string ) {
+ PLL_WPML_Compat::instance()->register_string( $context, $name, $string );
+ }
+}
+
+if ( ! function_exists( 'icl_unregister_string' ) ) {
+ /**
+ * Removes a string from the "strings translation" panel
+ *
+ * @since 1.0.2
+ *
+ * @param string $context the group in which the string is registered, defaults to 'polylang'
+ * @param string $name a unique name for the string
+ * @return void
+ */
+ function icl_unregister_string( $context, $name ) {
+ PLL_WPML_Compat::instance()->unregister_string( $context, $name );
+ }
+}
+
+if ( ! function_exists( 'icl_t' ) ) {
+ /**
+ * Gets the translated value of a string ( previously registered with icl_register_string or pll_register_string )
+ *
+ * @since 0.9.3
+ * @since 1.9.2 argument 3 is optional
+ * @since 2.0 add support for arguments 4 to 6
+ *
+ * @param string $context the group in which the string is registered
+ * @param string $name a unique name for the string
+ * @param string $string the string to translate, optional for strings registered with icl_register_string
+ * @param bool|null $has_translation optional, not supported in Polylang
+ * @param bool $bool optional, not used
+ * @param string|null $lang optional, return the translation in this language, defaults to current language
+ * @return string the translated string
+ */
+ function icl_t( $context, $name, $string = '', &$has_translation = null, $bool = false, $lang = null ) {
+ return icl_translate( $context, $name, $string, false, $has_translation, $lang );
+ }
+}
+
+if ( ! function_exists( 'icl_translate' ) ) {
+ /**
+ * Undocumented function used by NextGen Gallery
+ * used in PLL_Plugins_Compat for Jetpack with only 3 arguments
+ *
+ * @since 1.0.2
+ * @since 2.0 add support for arguments 5 and 6, strings are no more automatically registered
+ *
+ * @param string $context the group in which the string is registered
+ * @param string $name a unique name for the string
+ * @param string $string the string to translate, optional for strings registered with icl_register_string
+ * @param bool $bool optional, not used
+ * @param bool|null $has_translation optional, not supported in Polylang
+ * @param string|null $lang optional, return the translation in this language, defaults to current language
+ * @return string the translated string
+ */
+ function icl_translate( $context, $name, $string = '', $bool = false, &$has_translation = null, $lang = null ) {
+ // FIXME WPML can automatically registers the string based on an option
+ if ( empty( $string ) ) {
+ $string = PLL_WPML_Compat::instance()->get_string_by_context_and_name( $context, $name );
+ }
+ return empty( $lang ) ? pll__( $string ) : pll_translate_string( $string, $lang );
+ }
+}
+
+if ( ! function_exists( 'wpml_get_copied_fields_for_post_edit' ) ) {
+ /**
+ * Undocumented function used by Types
+ * FIXME: tested only with Types
+ * probably incomplete as Types locks the custom fields for a new post, but not when edited
+ * This is probably linked to the fact that WPML has always an original post in the default language and not Polylang :)
+ *
+ * @since 1.1.2
+ *
+ * @return array
+ */
+ function wpml_get_copied_fields_for_post_edit() {
+ if ( empty( $_GET['from_post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
+ return array();
+ }
+
+ $arr = array( 'original_post_id' => (int) $_GET['from_post'] ); // phpcs:ignore WordPress.Security.NonceVerification
+
+ // Don't know what WPML does but Polylang does copy all public meta keys by default.
+ $keys = get_post_custom_keys( $arr['original_post_id'] );
+ if ( is_array( $keys ) ) {
+ foreach ( $keys as $k => $meta_key ) {
+ if ( is_protected_meta( $meta_key ) ) {
+ unset( $keys[ $k ] );
+ }
+ }
+ }
+
+ // Apply our filter and fill the expected output ( see /types/embedded/includes/fields-post.php )
+ /** This filter is documented in modules/sync/admin-sync.php */
+ $arr['fields'] = array_unique( apply_filters( 'pll_copy_post_metas', empty( $keys ) ? array() : $keys, false ) );
+ return $arr;
+ }
+}
+
+if ( ! function_exists( 'icl_get_default_language' ) ) {
+ /**
+ * Undocumented function used by Warp 6 by Yootheme
+ *
+ * @since 1.0.5
+ *
+ * @return string default language code
+ */
+ function icl_get_default_language() {
+ return pll_default_language();
+ }
+}
+
+if ( ! function_exists( 'wpml_get_default_language' ) ) {
+ /**
+ * Undocumented function reported to be used by Table Rate Shipping for WooCommerce
+ *
+ * @see https://wordpress.org/support/topic/add-wpml-compatibility-function
+ *
+ * @since 1.8.2
+ *
+ * @return string default language code
+ */
+ function wpml_get_default_language() {
+ return pll_default_language();
+ }
+}
+
+if ( ! function_exists( 'icl_get_current_language' ) ) {
+ /**
+ * Undocumented function used by Ultimate Member
+ *
+ * @since 2.2.4
+ *
+ * @return string Current language code
+ */
+ function icl_get_current_language() {
+ return pll_current_language();
+ }
+}
diff --git a/wp-content/plugins/polylang/polylang.php b/wp-content/plugins/polylang/polylang.php
new file mode 100644
index 0000000000..ca076099ab
--- /dev/null
+++ b/wp-content/plugins/polylang/polylang.php
@@ -0,0 +1,78 @@
+.
+ */
+
+if ( ! defined( 'ABSPATH' ) ) {
+ exit; // Don't access directly.
+}
+
+if ( defined( 'POLYLANG_VERSION' ) ) {
+ // The user is attempting to activate a second plugin instance, typically Polylang and Polylang Pro.
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ require_once ABSPATH . 'wp-includes/pluggable.php';
+ if ( is_plugin_active( plugin_basename( __FILE__ ) ) ) {
+ deactivate_plugins( plugin_basename( __FILE__ ) ); // Deactivate this plugin.
+ // WP does not allow us to send a custom meaningful message, so just tell the plugin has been deactivated.
+ wp_safe_redirect( add_query_arg( 'deactivate', 'true', remove_query_arg( 'activate' ) ) );
+ exit;
+ }
+} else {
+ // Go on loading the plugin
+ define( 'POLYLANG_VERSION', '3.6.5' );
+ define( 'PLL_MIN_WP_VERSION', '6.2' );
+ define( 'PLL_MIN_PHP_VERSION', '7.0' );
+
+ define( 'POLYLANG_FILE', __FILE__ );
+ define( 'POLYLANG_DIR', __DIR__ );
+
+ // Whether we are using Polylang or Polylang Pro, get the filename of the plugin in use.
+ if ( ! defined( 'POLYLANG_ROOT_FILE' ) ) {
+ define( 'POLYLANG_ROOT_FILE', __FILE__ );
+ }
+
+ if ( ! defined( 'POLYLANG_BASENAME' ) ) {
+ define( 'POLYLANG_BASENAME', plugin_basename( __FILE__ ) ); // Plugin name as known by WP.
+ require __DIR__ . '/vendor/autoload.php';
+ }
+
+ define( 'POLYLANG', ucwords( str_replace( '-', ' ', dirname( POLYLANG_BASENAME ) ) ) );
+
+ if ( empty( $_GET['deactivate-polylang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
+ new Polylang();
+ }
+}
diff --git a/wp-content/plugins/polylang/readme.txt b/wp-content/plugins/polylang/readme.txt
new file mode 100644
index 0000000000..7f02bcd007
--- /dev/null
+++ b/wp-content/plugins/polylang/readme.txt
@@ -0,0 +1,167 @@
+=== Polylang ===
+Contributors: Chouby, manooweb, raaaahman, marianne38, sebastienserre, greglone, hugod
+Donate link: https://polylang.pro
+Tags: multilingual, translate, translation, language, localization
+Requires at least: 6.2
+Tested up to: 6.7
+Requires PHP: 7.0
+Stable tag: 3.6.5
+License: GPLv3 or later
+License URI: https://www.gnu.org/licenses/gpl-3.0.html
+
+Go multilingual in a simple and efficient way. Keep writing posts and taxonomy terms as usual while defining their languages all at once.
+
+== Description ==
+
+With Polylang fully integrated to WordPress and using only its built-in core features (taxonomies), keep steady performances on your site and create a multilingual site featuring from just one extra language to 10 or more depending on your needs. There is no limit in the number of languages added and WordPress’ language packs are automatically downloaded when ready.
+
+= Features =
+
+Depending on the type of site you have built or are planning to build, a combination of plugins from the list below might be of interest.
+All plugins include a wizard allowing to setup them in just a few clicks.
+
+### Polylang
+
+Polylang and [Polylang Pro](https://polylang.pro) share the same core providing features such as:
+
+* Translating posts, pages, media, categories, post tags, custom post types and taxonomies, RSS feeds; RTL scripts are supported.
+* The language is either set by the language code in URL, or you can use a different sub-domain or domain per language.
+* Automatic copy of categories, post tags and other metas when creating a new post or page translation.
+* Translating classic menus and classic widgets. Also accessible with [Site Editor Classic Features](https://wordpress.org/plugins/fse-classic/) in block themes.
+* Customizable language switcher available as a classic widget or a classic navigation menu item.
+* Compatibility with Yoast SEO.
+
+### Polylang Pro
+
+Helps optimizing the time spent translating your site with some very useful extra features such as:
+
+* Better integration in the new Block Editor.
+* Language switcher available as a block.
+* Language options available in the widget block editor.
+* Template parts translatable in the site editor (FSE).
+* Duplicate and/or synchronize content across post translations.
+* Improved compatibility with other plugins such as [ACF Pro](https://polylang.pro/doc/working-with-acf-pro/).
+* Share the same URL slug for posts or terms across languages.
+* [Translate URL slugs](https://polylang.pro/doc/translating-urls-slugs/) for categories, author bases, custom post types and more...
+* Machine translation with DeepL.
+* Export and import of content in XLIFF format for outsourced professional translation.
+* **Access to a Premium Support for personalized assistance.**
+
+### Polylang for WooCommerce
+
+[Add-on](https://polylang.pro/downloads/polylang-for-woocommerce/) for the compatibility with WooCommerce which provides features such as:
+
+* Translating WooCommerce pages (shop, check-out, cart, my account), product categories and global attribute terms directly in the WooCommerce interface.
+* Translating WooCommerce e-mails and sending them to customers in their language.
+* Products metadata synchronization.
+* Compatibility with the native WooCommerce CSV import & export tool.
+* Compatibility with popular plugins such as WooCommerce Subscriptions, Product Bundles, WooCommerce Bookings, Shipment Tracking and more.
+* Ability to use the WooCommerce REST API (available with Polylang Pro).
+* **Access to a Premium Support for personalized assistance.**
+
+Neither of them will allow to do automated translation.
+
+= Our other free plugins =
+
+* [WPML to Polylang](https://wordpress.org/plugins/wpml-to-polylang/) allows migrating from WPML to Polylang.
+* [DynaMo](https://wordpress.org/plugins/dynamo/) speeds up the translation of WordPress for all non-English sites.
+* [Site Editor Classic Features](https://wordpress.org/plugins/fse-classic/) allows to use classic widgets (including the Polylang language switcher) and menus in the site editor (FSE).
+
+= Credits =
+
+Thanks a lot to all translators who [help translating Polylang](https://translate.wordpress.org/projects/wp-plugins/polylang).
+Thanks a lot to [Alex Lopez](http://www.alexlopez.rocks/) for the design of the logo.
+Most of the flags included with Polylang are coming from [famfamfam](http://famfamfam.com/) and are public domain.
+Wherever third party code has been used, credit has been given in the code’s comments.
+
+== Installation ==
+
+1. Make sure you are using WordPress 6.2 or later and that your server is running PHP 7.0 or later (same requirement as WordPress itself).
+1. If you tried other multilingual plugins, deactivate them before activating Polylang, otherwise, you may get unexpected results!
+1. Install and activate the plugin as usual from the 'Plugins' menu in WordPress.
+1. The [setup wizard](https://polylang.pro/doc/setup-wizard/) is automatically launched to help you get started more easily with Polylang by configuring the main features.
+
+== Frequently Asked Questions ==
+
+= Where to find help ? =
+
+* First time users should read [Polylang - Getting started](https://polylang.pro/doc-category/getting-started/), which explains the basics and includes a lot of screenshots.
+* Read the [documentation](https://polylang.pro/doc/). It includes a [FAQ](https://polylang.pro/doc-category/faq/) and the [documentation for developers](https://polylang.pro/doc-category/developers/).
+* Search the [community support forum](https://wordpress.org/search/). You will probably find your answers here.
+* Read the sticky posts in the [community support forum](http://wordpress.org/support/plugin/polylang).
+* If you still have a problem, open a new thread in the [community support forum](http://wordpress.org/support/plugin/polylang).
+* [Polylang Pro and Polylang for WooCommerce](https://polylang.pro) users have access to our premium support through helpdesk.
+
+= Is Polylang compatible with WooCommerce? =
+
+* You need [Polylang for WooCommerce](https://polylang.pro/downloads/polylang-for-woocommerce/), premium addon described above, which will make both plugins work together.
+
+== Screenshots ==
+
+1. The Polylang languages admin panel
+2. The Strings translations admin panel
+3. Multilingual media library
+4. The Edit Post screen with the Languages metabox
+
+== Changelog ==
+
+= 3.6.5 (2024-11-05) =
+
+* Add compatibility with WP 6.7
+* Pro: Prevent infinite loop when the locale fallbacks reference each other
+* Pro: Set canResegment attribute to no in XLIFF files
+* Fix empty notice displayed if the plugin upgrade notice is set but empty
+
+= 3.6.4 (2024-07-29) =
+
+* Pro: Fix infinite loop with WP 6.6 when the locale fallbacks include the main locale of a language
+* Pro: Prevent saving the main locale among the locale fallbacks of a language
+* Pro: Hide the characters consumption graph when the DeepL cost control is deactivated
+* Add Yoast SEO social title and social description to the strings translations
+* Fix incorrect page on front and page for posts translations when the option is saved with admin language filter active
+
+= 3.6.3 (2024-06-18) =
+
+* Pro: Fix locale fallback for translations loaded just in time (requires WP 6.6)
+* Allow to pass an array as context to icl_register_string() #1497
+* Fix admin bar search menu in WP 6.6 #1496
+* Fix a regression in the usage of the filter pll_flag #1489
+
+= 3.6.2 (2024-06-03) =
+
+* Pro: Fix XLIFF files not correctly imported when exported from older version than 3.6
+* Pro: Fix translated categories not assigned to translated post when using machine translation
+* Pro: Fix 'lang' param not applied for secondary queries during a REST request
+* Pro: Fix newlines for content edited in classic editor and translated with DeepL
+* Pro: Fix a conflict with the Stream plugin on multisite
+
+= 3.6.1 (2024-04-09) =
+
+* Pro: Fix ACF fields not shown after a post was translated with DeepL
+* Remove rewrite when registering the language taxonomy #1457
+* Fix search block not filtered when displayed as button only #1459
+* Fix current language not kept when using switch_to_blog() in multisite #1458
+
+= 3.6 (2024-03-18) =
+
+* Requires WP 6.2 as minimum version
+* Add compatibility with WP 6.5
+* Pro: Add DeepL machine translation for posts
+* Pro: Add export and import in XLIFF 2.0/2.1 formats
+* Pro: Improve translator comments in exported PO files
+* Pro: Allow to export JSON encoded post and term metas in XLIFF files
+* Pro: Allow to export block sub-attributes in XLIFF files
+* Pro: Add footer notes block to XLIFF files
+* Pro: Single files are now exported directly instead of inside a zip
+* Pro: Reworked the language switcher navigation block
+* Pro: Fix language switcher navigation block justification not aligned with core settings in overlay menu (requires WP 6.5)
+* Pro: Fix a race condition which could lead to display a notice to the wrong user
+* Pro: Fix a conflict with ACF when rewrite rules are flushed with WP-CLI on a multisite
+* Pro: Fix import of several metas with same sources but different translations
+* Add filter `pll_cookie_args` to filter the Polylang cookie arguments #1406
+* Fix wrong translated post types and taxononies after a `switch_to_blog()` #1415
+* Fix a minor performance issue for the page for posts #1412
+* Fix a JS errors after quick edit. Props @mcguffin #1435, #1444
+* Fix a possible warning in view-translations-post.php #1439
+
+See [changelog.txt](https://plugins.svn.wordpress.org/polylang/trunk/changelog.txt) for older changelog
diff --git a/wp-content/plugins/polylang/settings/flags.php b/wp-content/plugins/polylang/settings/flags.php
new file mode 100644
index 0000000000..239ac50fb2
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/flags.php
@@ -0,0 +1,276 @@
+ __( 'Andorra', 'polylang' ),
+ 'ae' => __( 'United Arab Emirates', 'polylang' ),
+ 'af' => __( 'Afghanistan', 'polylang' ),
+ 'ag' => __( 'Antigua and Barbuda', 'polylang' ),
+ 'ai' => __( 'Anguilla', 'polylang' ),
+ 'al' => __( 'Albania', 'polylang' ),
+ 'am' => __( 'Armenia', 'polylang' ),
+ 'an' => __( 'Netherlands Antilles', 'polylang' ),
+ 'ao' => __( 'Angola', 'polylang' ),
+ 'ar' => __( 'Argentina', 'polylang' ),
+ 'arab' => __( 'Arab league', 'polylang' ),
+ 'as' => __( 'American Samoa', 'polylang' ),
+ 'at' => __( 'Austria', 'polylang' ),
+ 'au' => __( 'Australia', 'polylang' ),
+ 'aw' => __( 'Aruba', 'polylang' ),
+ 'ax' => __( 'Åland Islands', 'polylang' ),
+ 'az' => __( 'Azerbaijan', 'polylang' ),
+ 'ba' => __( 'Bosnia and Herzegovina', 'polylang' ),
+ 'basque' => __( 'Basque Country', 'polylang' ),
+ 'bb' => __( 'Barbados', 'polylang' ),
+ 'bd' => __( 'Bangladesh', 'polylang' ),
+ 'be' => __( 'Belgium', 'polylang' ),
+ 'bf' => __( 'Burkina Faso', 'polylang' ),
+ 'bg' => __( 'Bulgaria', 'polylang' ),
+ 'bh' => __( 'Bahrain', 'polylang' ),
+ 'bi' => __( 'Burundi', 'polylang' ),
+ 'bj' => __( 'Benin', 'polylang' ),
+ 'bm' => __( 'Bermuda', 'polylang' ),
+ 'bn' => __( 'Brunei', 'polylang' ),
+ 'bo' => __( 'Bolivia', 'polylang' ),
+ 'br' => __( 'Brazil', 'polylang' ),
+ 'bs' => __( 'Bahamas', 'polylang' ),
+ 'bt' => __( 'Bhutan', 'polylang' ),
+ 'bw' => __( 'Botswana', 'polylang' ),
+ 'by' => __( 'Belarus', 'polylang' ),
+ 'bz' => __( 'Belize', 'polylang' ),
+ 'ca' => __( 'Canada', 'polylang' ),
+ 'catalonia' => __( 'Catalonia', 'polylang' ),
+ 'cc' => __( 'Cocos', 'polylang' ),
+ 'cd' => __( 'Democratic Republic of the Congo', 'polylang' ),
+ 'cf' => __( 'Central African Republic', 'polylang' ),
+ 'cg' => __( 'Congo', 'polylang' ),
+ 'ch' => __( 'Switzerland', 'polylang' ),
+ 'ci' => __( 'Ivory Coast', 'polylang' ),
+ 'ck' => __( 'Cook Islands', 'polylang' ),
+ 'cl' => __( 'Chile', 'polylang' ),
+ 'cm' => __( 'Cameroon', 'polylang' ),
+ 'cn' => __( 'China', 'polylang' ),
+ 'co' => __( 'Colombia', 'polylang' ),
+ 'cr' => __( 'Costa Rica', 'polylang' ),
+ 'cu' => __( 'Cuba', 'polylang' ),
+ 'cv' => __( 'Cape Verde', 'polylang' ),
+ 'cx' => __( 'Christmas Island', 'polylang' ),
+ 'cy' => __( 'Cyprus', 'polylang' ),
+ 'cz' => __( 'Czech Republic', 'polylang' ),
+ 'de' => __( 'Germany', 'polylang' ),
+ 'dj' => __( 'Djibouti', 'polylang' ),
+ 'dk' => __( 'Denmark', 'polylang' ),
+ 'dm' => __( 'Dominica', 'polylang' ),
+ 'do' => __( 'Dominican Republic', 'polylang' ),
+ 'dz' => __( 'Algeria', 'polylang' ),
+ 'ec' => __( 'Ecuador', 'polylang' ),
+ 'ee' => __( 'Estonia', 'polylang' ),
+ 'eg' => __( 'Egypt', 'polylang' ),
+ 'eh' => __( 'Western Sahara', 'polylang' ),
+ 'england' => __( 'England', 'polylang' ),
+ 'er' => __( 'Eritrea', 'polylang' ),
+ 'es' => __( 'Spain', 'polylang' ),
+ 'esperanto' => __( 'Esperanto', 'polylang' ),
+ 'et' => __( 'Ethiopia', 'polylang' ),
+ 'fi' => __( 'Finland', 'polylang' ),
+ 'fj' => __( 'Fiji', 'polylang' ),
+ 'fk' => __( 'Falkland Islands', 'polylang' ),
+ 'fm' => __( 'Micronesia', 'polylang' ),
+ 'fo' => __( 'Faroe Islands', 'polylang' ),
+ 'fr' => __( 'France', 'polylang' ),
+ 'ga' => __( 'Gabon', 'polylang' ),
+ 'galicia' => __( 'Galicia', 'polylang' ),
+ 'gb' => __( 'United Kingdom', 'polylang' ),
+ 'gd' => __( 'Grenada', 'polylang' ),
+ 'ge' => __( 'Georgia', 'polylang' ),
+ 'gh' => __( 'Ghana', 'polylang' ),
+ 'gi' => __( 'Gibraltar', 'polylang' ),
+ 'gl' => __( 'Greenland', 'polylang' ),
+ 'gm' => __( 'Gambia', 'polylang' ),
+ 'gn' => __( 'Guinea', 'polylang' ),
+ 'gp' => __( 'Guadeloupe', 'polylang' ),
+ 'gq' => __( 'Equatorial Guinea', 'polylang' ),
+ 'gr' => __( 'Greece', 'polylang' ),
+ 'gs' => __( 'South Georgia and the South Sandwich Islands', 'polylang' ),
+ 'gt' => __( 'Guatemala', 'polylang' ),
+ 'gu' => __( 'Guam', 'polylang' ),
+ 'gw' => __( 'Guinea-Bissau', 'polylang' ),
+ 'gy' => __( 'Guyana', 'polylang' ),
+ 'hk' => __( 'Hong Kong', 'polylang' ),
+ 'hm' => __( 'Heard Island and McDonald Islands', 'polylang' ),
+ 'hn' => __( 'Honduras', 'polylang' ),
+ 'hr' => __( 'Croatia', 'polylang' ),
+ 'ht' => __( 'Haiti', 'polylang' ),
+ 'hu' => __( 'Hungary', 'polylang' ),
+ 'id' => __( 'Indonesia', 'polylang' ),
+ 'ie' => __( 'Republic of Ireland', 'polylang' ),
+ 'il' => __( 'Israel', 'polylang' ),
+ 'in' => __( 'India', 'polylang' ),
+ 'io' => __( 'British Indian Ocean Territory', 'polylang' ),
+ 'iq' => __( 'Iraq', 'polylang' ),
+ 'ir' => __( 'Iran', 'polylang' ),
+ 'is' => __( 'Iceland', 'polylang' ),
+ 'it' => __( 'Italy', 'polylang' ),
+ 'jm' => __( 'Jamaica', 'polylang' ),
+ 'jo' => __( 'Jordan', 'polylang' ),
+ 'jp' => __( 'Japan', 'polylang' ),
+ 'ke' => __( 'Kenya', 'polylang' ),
+ 'kg' => __( 'Kyrgyzstan', 'polylang' ),
+ 'kh' => __( 'Cambodia', 'polylang' ),
+ 'ki' => __( 'Kiribati', 'polylang' ),
+ 'km' => __( 'Comoros', 'polylang' ),
+ 'kn' => __( 'Saint Kitts and Nevis', 'polylang' ),
+ 'kp' => __( 'North Korea', 'polylang' ),
+ 'kr' => __( 'South Korea', 'polylang' ),
+ 'kurdistan' => __( 'Kurdistan', 'polylang' ),
+ 'kw' => __( 'Kuwait', 'polylang' ),
+ 'ky' => __( 'Cayman Islands', 'polylang' ),
+ 'kz' => __( 'Kazakhstan', 'polylang' ),
+ 'la' => __( 'Laos', 'polylang' ),
+ 'lb' => __( 'Lebanon', 'polylang' ),
+ 'lc' => __( 'Saint Lucia', 'polylang' ),
+ 'li' => __( 'Liechtenstein', 'polylang' ),
+ 'lk' => __( 'Sri Lanka', 'polylang' ),
+ 'lr' => __( 'Liberia', 'polylang' ),
+ 'ls' => __( 'Lesotho', 'polylang' ),
+ 'lt' => __( 'Lithuania', 'polylang' ),
+ 'lu' => __( 'Luxembourg', 'polylang' ),
+ 'lv' => __( 'Latvia', 'polylang' ),
+ 'ly' => __( 'Libya', 'polylang' ),
+ 'ma' => __( 'Morocco', 'polylang' ),
+ 'mc' => __( 'Monaco', 'polylang' ),
+ 'md' => __( 'Moldova', 'polylang' ),
+ 'me' => __( 'Montenegro', 'polylang' ),
+ 'mg' => __( 'Madagascar', 'polylang' ),
+ 'mh' => __( 'Marshall Islands', 'polylang' ),
+ 'mk' => __( 'North Macedonia', 'polylang' ),
+ 'ml' => __( 'Mali', 'polylang' ),
+ 'mm' => __( 'Myanmar', 'polylang' ),
+ 'mn' => __( 'Mongolia', 'polylang' ),
+ 'mo' => __( 'Macao', 'polylang' ),
+ 'mp' => __( 'Northern Mariana Islands', 'polylang' ),
+ 'mq' => __( 'Martinique', 'polylang' ),
+ 'mr' => __( 'Mauritania', 'polylang' ),
+ 'ms' => __( 'Montserrat', 'polylang' ),
+ 'mt' => __( 'Malta', 'polylang' ),
+ 'mu' => __( 'Mauritius', 'polylang' ),
+ 'mv' => __( 'Maldives', 'polylang' ),
+ 'mw' => __( 'Malawi', 'polylang' ),
+ 'mx' => __( 'Mexico', 'polylang' ),
+ 'my' => __( 'Malaysia', 'polylang' ),
+ 'mz' => __( 'Mozambique', 'polylang' ),
+ 'na' => __( 'Namibia', 'polylang' ),
+ 'nc' => __( 'New Caledonia', 'polylang' ),
+ 'ne' => __( 'Niger', 'polylang' ),
+ 'nf' => __( 'Norfolk Island', 'polylang' ),
+ 'ng' => __( 'Nigeria', 'polylang' ),
+ 'ni' => __( 'Nicaragua', 'polylang' ),
+ 'nl' => __( 'Netherlands', 'polylang' ),
+ 'no' => __( 'Norway', 'polylang' ),
+ 'np' => __( 'Nepal', 'polylang' ),
+ 'nr' => __( 'Nauru', 'polylang' ),
+ 'nu' => __( 'Niue', 'polylang' ),
+ 'nz' => __( 'New Zealand', 'polylang' ),
+ 'occitania' => __( 'Occitania', 'polylang' ),
+ 'om' => __( 'Oman', 'polylang' ),
+ 'pa' => __( 'Panama', 'polylang' ),
+ 'pe' => __( 'Peru', 'polylang' ),
+ 'pf' => __( 'French Polynesia', 'polylang' ),
+ 'pg' => __( 'Papua New Guinea', 'polylang' ),
+ 'ph' => __( 'Philippines', 'polylang' ),
+ 'pk' => __( 'Pakistan', 'polylang' ),
+ 'pl' => __( 'Poland', 'polylang' ),
+ 'pm' => __( 'Saint Pierre and Miquelon', 'polylang' ),
+ 'pn' => __( 'Pitcairn', 'polylang' ),
+ 'pr' => __( 'Puerto Rico', 'polylang' ),
+ 'ps' => __( 'Palestinian Territory', 'polylang' ),
+ 'pt' => __( 'Portugal', 'polylang' ),
+ 'pw' => __( 'Belau', 'polylang' ),
+ 'py' => __( 'Paraguay', 'polylang' ),
+ 'qa' => __( 'Qatar', 'polylang' ),
+ 'quebec' => __( 'Quebec', 'polylang' ),
+ 'ro' => __( 'Romania', 'polylang' ),
+ 'rs' => __( 'Serbia', 'polylang' ),
+ 'ru' => __( 'Russia', 'polylang' ),
+ 'rw' => __( 'Rwanda', 'polylang' ),
+ 'sa' => __( 'Saudi Arabia', 'polylang' ),
+ 'sb' => __( 'Solomon Islands', 'polylang' ),
+ 'sc' => __( 'Seychelles', 'polylang' ),
+ 'scotland' => __( 'Scotland', 'polylang' ),
+ 'sd' => __( 'Sudan', 'polylang' ),
+ 'se' => __( 'Sweden', 'polylang' ),
+ 'sg' => __( 'Singapore', 'polylang' ),
+ 'sh' => __( 'Saint Helena', 'polylang' ),
+ 'si' => __( 'Slovenia', 'polylang' ),
+ 'sk' => __( 'Slovakia', 'polylang' ),
+ 'sl' => __( 'Sierra Leone', 'polylang' ),
+ 'sm' => __( 'San Marino', 'polylang' ),
+ 'sn' => __( 'Senegal', 'polylang' ),
+ 'so' => __( 'Somalia', 'polylang' ),
+ 'sr' => __( 'Suriname', 'polylang' ),
+ 'ss' => __( 'South Sudan', 'polylang' ),
+ 'st' => __( 'São Tomé and Príncipe', 'polylang' ),
+ 'sv' => __( 'El Salvador', 'polylang' ),
+ 'sy' => __( 'Syria', 'polylang' ),
+ 'sz' => __( 'Swaziland', 'polylang' ),
+ 'tc' => __( 'Turks and Caicos Islands', 'polylang' ),
+ 'td' => __( 'Chad', 'polylang' ),
+ 'tf' => __( 'French Southern Territories', 'polylang' ),
+ 'tg' => __( 'Togo', 'polylang' ),
+ 'th' => __( 'Thailand', 'polylang' ),
+ 'tibet' => __( 'Tibet', 'polylang' ),
+ 'tj' => __( 'Tajikistan', 'polylang' ),
+ 'tk' => __( 'Tokelau', 'polylang' ),
+ 'tl' => __( 'Timor-Leste', 'polylang' ),
+ 'tm' => __( 'Turkmenistan', 'polylang' ),
+ 'tn' => __( 'Tunisia', 'polylang' ),
+ 'to' => __( 'Tonga', 'polylang' ),
+ 'tr' => __( 'Turkey', 'polylang' ),
+ 'tt' => __( 'Trinidad and Tobago', 'polylang' ),
+ 'tv' => __( 'Tuvalu', 'polylang' ),
+ 'tw' => __( 'Taiwan', 'polylang' ),
+ 'tz' => __( 'Tanzania', 'polylang' ),
+ 'ua' => __( 'Ukraine', 'polylang' ),
+ 'ug' => __( 'Uganda', 'polylang' ),
+ 'us' => __( 'United States', 'polylang' ),
+ 'uy' => __( 'Uruguay', 'polylang' ),
+ 'uz' => __( 'Uzbekistan', 'polylang' ),
+ 'va' => __( 'Vatican', 'polylang' ),
+ 'vc' => __( 'Saint Vincent and the Grenadines', 'polylang' ),
+ 've' => __( 'Venezuela', 'polylang' ),
+ 'veneto' => __( 'Veneto', 'polylang' ),
+ 'vg' => __( 'British Virgin Islands', 'polylang' ),
+ 'vi' => __( 'United States Virgin Islands', 'polylang' ),
+ 'vn' => __( 'Vietnam', 'polylang' ),
+ 'vu' => __( 'Vanuatu', 'polylang' ),
+ 'wales' => __( 'Wales', 'polylang' ),
+ 'wf' => __( 'Wallis and Futuna', 'polylang' ),
+ 'ws' => __( 'Western Samoa', 'polylang' ),
+ 'ye' => __( 'Yemen', 'polylang' ),
+ 'yt' => __( 'Mayotte', 'polylang' ),
+ 'za' => __( 'South Africa', 'polylang' ),
+ 'zm' => __( 'Zambia', 'polylang' ),
+ 'zw' => __( 'Zimbabwe', 'polylang' ),
+);
+
+/**
+ * Filter the list of predefined flags
+ *
+ * @since 1.8
+ *
+ * @param array $flags
+ */
+return apply_filters( 'pll_predefined_flags', $flags );
diff --git a/wp-content/plugins/polylang/settings/languages.php b/wp-content/plugins/polylang/settings/languages.php
new file mode 100644
index 0000000000..3b5a7468ae
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/languages.php
@@ -0,0 +1,1292 @@
+ ISO 639-1 language code
+ * [locale] => WordPress locale
+ * [name] => name
+ * [dir] => text direction
+ * [flag] => flag code
+ * [w3c] => W3C locale
+ * [facebook] => Facebook locale
+ *
+ * Facebook locales without equivalent WordPress locale:
+ * 'ay_BO' (Aymara)
+ * 'bp_IN' (Bhojpuri)
+ * 'ck_US' (Cherokee)
+ * 'en_IN' (English India)
+ * 'gx_GR' (Classical Greek)
+ * 'ig_NG' (Igbo)
+ * 'ik_US' (Inupiak)
+ * 'iu_CA' (Inuktitut)
+ * 'ja_KS' (Japanese Kansai)
+ * 'ks_IN' (Cachemiri)
+ * 'lg_UG' (Ganda)
+ * 'nd_ZW' (Ndebele)
+ * 'nr_ZA' (Southern Ndebele)
+ * 'ns_ZA' (Northern Sotho)
+ * 'ny_MW' (Chewa)
+ * 'qc_GT' (Quiché)
+ * 'qu_PE' (Quechua)
+ * 'se_NO' (Northern Sami)
+ * 'ss_SZ' (Swazi)
+ * 'st_ZA' (Southern Sotho)
+ * 'tl_ST' (Klingon)
+ * 'tn_BW' (Tswana)
+ * 'ts_ZA' (Tsonga)
+ * 've_ZA' (Venda)
+ * 'wo_SN' (Wolof)
+ * 'yi_DE' (Yiddish)
+ * 'zu_ZA' (Zulu)
+ * 'zz_TR' (Zazaki)
+ */
+return array(
+ 'af' => array(
+ 'code' => 'af',
+ 'locale' => 'af',
+ 'name' => 'Afrikaans',
+ 'dir' => 'ltr',
+ 'flag' => 'za',
+ 'facebook' => 'af_ZA',
+ ),
+ 'ak' => array(
+ 'facebook' => 'ak_GH',
+ ),
+ 'am' => array(
+ 'code' => 'am',
+ 'locale' => 'am',
+ 'name' => 'አማርኛ',
+ 'dir' => 'ltr',
+ 'flag' => 'et',
+ 'facebook' => 'am_ET',
+ ),
+ 'ar' => array(
+ 'code' => 'ar',
+ 'locale' => 'ar',
+ 'name' => 'العربية',
+ 'dir' => 'rtl',
+ 'flag' => 'arab',
+ 'facebook' => 'ar_AR',
+ 'deepl' => 'AR',
+ ),
+ 'arg' => array(
+ 'code' => 'an',
+ 'locale' => 'arg',
+ 'name' => 'Aragonés',
+ 'dir' => 'ltr',
+ 'flag' => 'es',
+ ),
+ 'arq' => array(
+ 'facebook' => 'ar_AR',
+ ),
+ 'ary' => array(
+ 'code' => 'ar',
+ 'locale' => 'ary',
+ 'name' => 'العربية المغربية',
+ 'dir' => 'rtl',
+ 'flag' => 'ma',
+ 'facebook' => 'ar_AR',
+ 'deepl' => 'AR',
+ ),
+ 'as' => array(
+ 'code' => 'as',
+ 'locale' => 'as',
+ 'name' => 'অসমীয়া',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'as_IN',
+ ),
+ 'az' => array(
+ 'code' => 'az',
+ 'locale' => 'az',
+ 'name' => 'Azərbaycan',
+ 'dir' => 'ltr',
+ 'flag' => 'az',
+ 'facebook' => 'az_AZ',
+ ),
+ 'azb' => array(
+ 'code' => 'az',
+ 'locale' => 'azb',
+ 'name' => 'گؤنئی آذربایجان',
+ 'dir' => 'rtl',
+ 'flag' => 'az',
+ ),
+ 'bel' => array(
+ 'code' => 'be',
+ 'locale' => 'bel',
+ 'name' => 'Беларуская мова',
+ 'dir' => 'ltr',
+ 'flag' => 'by',
+ 'w3c' => 'be',
+ 'facebook' => 'be_BY',
+ ),
+ 'bg_BG' => array(
+ 'code' => 'bg',
+ 'locale' => 'bg_BG',
+ 'name' => 'български',
+ 'dir' => 'ltr',
+ 'flag' => 'bg',
+ 'facebook' => 'bg_BG',
+ 'deepl' => 'BG',
+ ),
+ 'bn_BD' => array(
+ 'code' => 'bn',
+ 'locale' => 'bn_BD',
+ 'name' => 'বাংলা',
+ 'dir' => 'ltr',
+ 'flag' => 'bd',
+ 'facebook' => 'bn_IN',
+ ),
+ 'bo' => array(
+ 'code' => 'bo',
+ 'locale' => 'bo',
+ 'name' => 'བོད་ཡིག',
+ 'dir' => 'ltr',
+ 'flag' => 'tibet',
+ ),
+ 'bre' => array(
+ 'w3c' => 'br',
+ 'facebook' => 'br_FR',
+ ),
+ 'bs_BA' => array(
+ 'code' => 'bs',
+ 'locale' => 'bs_BA',
+ 'name' => 'Bosanski',
+ 'dir' => 'ltr',
+ 'flag' => 'ba',
+ 'facebook' => 'bs_BA',
+ ),
+ 'ca' => array(
+ 'code' => 'ca',
+ 'locale' => 'ca',
+ 'name' => 'Català',
+ 'dir' => 'ltr',
+ 'flag' => 'catalonia',
+ 'facebook' => 'ca_ES',
+ ),
+ 'ceb' => array(
+ 'code' => 'ceb',
+ 'locale' => 'ceb',
+ 'name' => 'Cebuano',
+ 'dir' => 'ltr',
+ 'flag' => 'ph',
+ 'facebook' => 'cx_PH',
+ ),
+ 'ckb' => array(
+ 'code' => 'ku',
+ 'locale' => 'ckb',
+ 'name' => 'کوردی',
+ 'dir' => 'rtl',
+ 'flag' => 'kurdistan',
+ 'facebook' => 'cb_IQ',
+ ),
+ 'co' => array(
+ 'facebook' => 'co_FR',
+ ),
+ 'cs_CZ' => array(
+ 'code' => 'cs',
+ 'locale' => 'cs_CZ',
+ 'name' => 'Čeština',
+ 'dir' => 'ltr',
+ 'flag' => 'cz',
+ 'facebook' => 'cs_CZ',
+ 'deepl' => 'CS',
+ ),
+ 'cy' => array(
+ 'code' => 'cy',
+ 'locale' => 'cy',
+ 'name' => 'Cymraeg',
+ 'dir' => 'ltr',
+ 'flag' => 'wales',
+ 'facebook' => 'cy_GB',
+ ),
+ 'da_DK' => array(
+ 'code' => 'da',
+ 'locale' => 'da_DK',
+ 'name' => 'Dansk',
+ 'dir' => 'ltr',
+ 'flag' => 'dk',
+ 'facebook' => 'da_DK',
+ 'deepl' => 'DA',
+ ),
+ 'de_AT' => array(
+ 'code' => 'de',
+ 'locale' => 'de_AT',
+ 'name' => 'Deutsch',
+ 'dir' => 'ltr',
+ 'flag' => 'at',
+ 'facebook' => 'de_DE',
+ 'deepl' => 'DE',
+ ),
+ 'de_CH' => array(
+ 'code' => 'de',
+ 'locale' => 'de_CH',
+ 'name' => 'Deutsch',
+ 'dir' => 'ltr',
+ 'flag' => 'ch',
+ 'facebook' => 'de_DE',
+ 'deepl' => 'DE',
+ ),
+ 'de_CH_informal' => array(
+ 'code' => 'de',
+ 'locale' => 'de_CH_informal',
+ 'name' => 'Deutsch',
+ 'dir' => 'ltr',
+ 'flag' => 'ch',
+ 'w3c' => 'de-CH',
+ 'facebook' => 'de_DE',
+ 'deepl' => 'DE',
+ ),
+ 'de_DE' => array(
+ 'code' => 'de',
+ 'locale' => 'de_DE',
+ 'name' => 'Deutsch',
+ 'dir' => 'ltr',
+ 'flag' => 'de',
+ 'facebook' => 'de_DE',
+ 'deepl' => 'DE',
+ ),
+ 'de_DE_formal' => array(
+ 'code' => 'de',
+ 'locale' => 'de_DE_formal',
+ 'name' => 'Deutsch',
+ 'dir' => 'ltr',
+ 'flag' => 'de',
+ 'w3c' => 'de-DE',
+ 'facebook' => 'de_DE',
+ 'deepl' => 'DE',
+ ),
+ 'dsb' => array(
+ 'code' => 'dsb',
+ 'locale' => 'dsb',
+ 'name' => 'Dolnoserbšćina',
+ 'dir' => 'ltr',
+ 'flag' => 'de',
+ ),
+ 'dzo' => array(
+ 'code' => 'dz',
+ 'locale' => 'dzo',
+ 'name' => 'རྫོང་ཁ',
+ 'dir' => 'ltr',
+ 'flag' => 'bt',
+ 'w3c' => 'dz',
+ ),
+ 'el' => array(
+ 'code' => 'el',
+ 'locale' => 'el',
+ 'name' => 'Ελληνικά',
+ 'dir' => 'ltr',
+ 'flag' => 'gr',
+ 'facebook' => 'el_GR',
+ 'deepl' => 'EL',
+ ),
+ 'en_AU' => array(
+ 'code' => 'en',
+ 'locale' => 'en_AU',
+ 'name' => 'English',
+ 'dir' => 'ltr',
+ 'flag' => 'au',
+ 'facebook' => 'en_US',
+ 'deepl' => 'EN-US',
+ ),
+ 'en_CA' => array(
+ 'code' => 'en',
+ 'locale' => 'en_CA',
+ 'name' => 'English',
+ 'dir' => 'ltr',
+ 'flag' => 'ca',
+ 'facebook' => 'en_US',
+ 'deepl' => 'EN-US',
+ ),
+ 'en_GB' => array(
+ 'code' => 'en',
+ 'locale' => 'en_GB',
+ 'name' => 'English',
+ 'dir' => 'ltr',
+ 'flag' => 'gb',
+ 'facebook' => 'en_GB',
+ 'deepl' => 'EN-GB',
+ ),
+ 'en_NZ' => array(
+ 'code' => 'en',
+ 'locale' => 'en_NZ',
+ 'name' => 'English',
+ 'dir' => 'ltr',
+ 'flag' => 'nz',
+ 'facebook' => 'en_US',
+ 'deepl' => 'EN-US',
+ ),
+ 'en_US' => array(
+ 'code' => 'en',
+ 'locale' => 'en_US',
+ 'name' => 'English',
+ 'dir' => 'ltr',
+ 'flag' => 'us',
+ 'facebook' => 'en_US',
+ 'deepl' => 'EN-US',
+ ),
+ 'en_ZA' => array(
+ 'code' => 'en',
+ 'locale' => 'en_ZA',
+ 'name' => 'English',
+ 'dir' => 'ltr',
+ 'flag' => 'za',
+ 'facebook' => 'en_US',
+ 'deepl' => 'EN-US',
+ ),
+ 'eo' => array(
+ 'code' => 'eo',
+ 'locale' => 'eo',
+ 'name' => 'Esperanto',
+ 'dir' => 'ltr',
+ 'flag' => 'esperanto',
+ 'facebook' => 'eo_EO',
+ ),
+ 'es_AR' => array(
+ 'code' => 'es',
+ 'locale' => 'es_AR',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'ar',
+ 'facebook' => 'es_LA',
+ 'deepl' => 'ES',
+ ),
+ 'es_CL' => array(
+ 'code' => 'es',
+ 'locale' => 'es_CL',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'cl',
+ 'facebook' => 'es_CL',
+ 'deepl' => 'ES',
+ ),
+ 'es_CO' => array(
+ 'code' => 'es',
+ 'locale' => 'es_CO',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'co',
+ 'facebook' => 'es_CO',
+ 'deepl' => 'ES',
+ ),
+ 'es_CR' => array(
+ 'code' => 'es',
+ 'locale' => 'es_CR',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'cr',
+ 'facebook' => 'es_LA',
+ 'deepl' => 'ES',
+ ),
+ 'es_DO' => array(
+ 'code' => 'es',
+ 'locale' => 'es_DO',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'do',
+ 'facebook' => 'es_LA',
+ 'deepl' => 'ES',
+ ),
+ 'es_EC' => array(
+ 'code' => 'es',
+ 'locale' => 'es_EC',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'ec',
+ 'facebook' => 'es_LA',
+ 'deepl' => 'ES',
+ ),
+ 'es_ES' => array(
+ 'code' => 'es',
+ 'locale' => 'es_ES',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'es',
+ 'facebook' => 'es_ES',
+ 'deepl' => 'ES',
+ ),
+ 'es_GT' => array(
+ 'code' => 'es',
+ 'locale' => 'es_GT',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'gt',
+ 'facebook' => 'es_LA',
+ 'deepl' => 'ES',
+ ),
+ 'es_MX' => array(
+ 'code' => 'es',
+ 'locale' => 'es_MX',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'mx',
+ 'facebook' => 'es_MX',
+ 'deepl' => 'ES',
+ ),
+ 'es_PE' => array(
+ 'code' => 'es',
+ 'locale' => 'es_PE',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'pe',
+ 'facebook' => 'es_LA',
+ 'deepl' => 'ES',
+ ),
+ 'es_PR' => array(
+ 'code' => 'es',
+ 'locale' => 'es_PR',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'pr',
+ 'facebook' => 'es_LA',
+ 'deepl' => 'ES',
+ ),
+ 'es_UY' => array(
+ 'code' => 'es',
+ 'locale' => 'es_UY',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 'uy',
+ 'facebook' => 'es_LA',
+ 'deepl' => 'ES',
+ ),
+ 'es_VE' => array(
+ 'code' => 'es',
+ 'locale' => 'es_VE',
+ 'name' => 'Español',
+ 'dir' => 'ltr',
+ 'flag' => 've',
+ 'facebook' => 'es_VE',
+ 'deepl' => 'ES',
+ ),
+ 'et' => array(
+ 'code' => 'et',
+ 'locale' => 'et',
+ 'name' => 'Eesti',
+ 'dir' => 'ltr',
+ 'flag' => 'ee',
+ 'facebook' => 'et_EE',
+ 'deepl' => 'ET',
+ ),
+ 'eu' => array(
+ 'code' => 'eu',
+ 'locale' => 'eu',
+ 'name' => 'Euskara',
+ 'dir' => 'ltr',
+ 'flag' => 'basque',
+ 'facebook' => 'eu_ES',
+ ),
+ 'fa_AF' => array(
+ 'code' => 'fa',
+ 'locale' => 'fa_AF',
+ 'name' => 'فارسی',
+ 'dir' => 'rtl',
+ 'flag' => 'af',
+ 'facebook' => 'fa_IR',
+ ),
+ 'fa_IR' => array(
+ 'code' => 'fa',
+ 'locale' => 'fa_IR',
+ 'name' => 'فارسی',
+ 'dir' => 'rtl',
+ 'flag' => 'ir',
+ 'facebook' => 'fa_IR',
+ ),
+ 'fi' => array(
+ 'code' => 'fi',
+ 'locale' => 'fi',
+ 'name' => 'Suomi',
+ 'dir' => 'ltr',
+ 'flag' => 'fi',
+ 'facebook' => 'fi_FI',
+ 'deepl' => 'FI',
+ ),
+ 'fo' => array(
+ 'code' => 'fo',
+ 'locale' => 'fo',
+ 'name' => 'Føroyskt',
+ 'dir' => 'ltr',
+ 'flag' => 'fo',
+ 'facebook' => 'fo_FO',
+ ),
+ 'fr_BE' => array(
+ 'code' => 'fr',
+ 'locale' => 'fr_BE',
+ 'name' => 'Français',
+ 'dir' => 'ltr',
+ 'flag' => 'be',
+ 'facebook' => 'fr_FR',
+ 'deepl' => 'FR',
+ ),
+ 'fr_CA' => array(
+ 'code' => 'fr',
+ 'locale' => 'fr_CA',
+ 'name' => 'Français',
+ 'dir' => 'ltr',
+ 'flag' => 'quebec',
+ 'facebook' => 'fr_CA',
+ 'deepl' => 'FR',
+ ),
+ 'fr_FR' => array(
+ 'code' => 'fr',
+ 'locale' => 'fr_FR',
+ 'name' => 'Français',
+ 'dir' => 'ltr',
+ 'flag' => 'fr',
+ 'facebook' => 'fr_FR',
+ 'deepl' => 'FR',
+ ),
+ 'fuc' => array(
+ 'facebook' => 'ff_NG',
+ ),
+ 'fur' => array(
+ 'code' => 'fur',
+ 'locale' => 'fur',
+ 'name' => 'Furlan',
+ 'dir' => 'ltr',
+ 'flag' => 'it',
+ ),
+ 'fy' => array(
+ 'code' => 'fy',
+ 'locale' => 'fy',
+ 'name' => 'Frysk',
+ 'dir' => 'ltr',
+ 'flag' => 'nl',
+ 'facebook' => 'fy_NL',
+ ),
+ 'ga' => array(
+ 'facebook' => 'ga_IE',
+ ),
+ 'gax' => array(
+ 'facebook' => 'om_ET',
+ ),
+ 'gd' => array(
+ 'code' => 'gd',
+ 'locale' => 'gd',
+ 'name' => 'Gàidhlig',
+ 'dir' => 'ltr',
+ 'flag' => 'scotland',
+ ),
+ 'gl_ES' => array(
+ 'code' => 'gl',
+ 'locale' => 'gl_ES',
+ 'name' => 'Galego',
+ 'dir' => 'ltr',
+ 'flag' => 'galicia',
+ 'facebook' => 'gl_ES',
+ ),
+ 'gn' => array(
+ 'facebook' => 'gn_PY',
+ ),
+ 'gu' => array(
+ 'code' => 'gu',
+ 'locale' => 'gu',
+ 'name' => 'ગુજરાતી',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'gu_IN',
+ ),
+ 'hat' => array(
+ 'facebook' => 'ht_HT',
+ ),
+ 'hau' => array(
+ 'facebook' => 'ha_NG',
+ ),
+ 'haz' => array(
+ 'code' => 'haz',
+ 'locale' => 'haz',
+ 'name' => 'هزاره گی',
+ 'dir' => 'rtl',
+ 'flag' => 'af',
+ ),
+ 'he_IL' => array(
+ 'code' => 'he',
+ 'locale' => 'he_IL',
+ 'name' => 'עברית',
+ 'dir' => 'rtl',
+ 'flag' => 'il',
+ 'facebook' => 'he_IL',
+ ),
+ 'hi_IN' => array(
+ 'code' => 'hi',
+ 'locale' => 'hi_IN',
+ 'name' => 'हिन्दी',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'hi_IN',
+ ),
+ 'hr' => array(
+ 'code' => 'hr',
+ 'locale' => 'hr',
+ 'name' => 'Hrvatski',
+ 'dir' => 'ltr',
+ 'flag' => 'hr',
+ 'facebook' => 'hr_HR',
+ ),
+ 'hu_HU' => array(
+ 'code' => 'hu',
+ 'locale' => 'hu_HU',
+ 'name' => 'Magyar',
+ 'dir' => 'ltr',
+ 'flag' => 'hu',
+ 'facebook' => 'hu_HU',
+ 'deepl' => 'HU',
+ ),
+ 'hsb' => array(
+ 'code' => 'hsb',
+ 'locale' => 'hsb',
+ 'name' => 'Hornjoserbšćina',
+ 'dir' => 'ltr',
+ 'flag' => 'de',
+ ),
+ 'hy' => array(
+ 'code' => 'hy',
+ 'locale' => 'hy',
+ 'name' => 'Հայերեն',
+ 'dir' => 'ltr',
+ 'flag' => 'am',
+ 'facebook' => 'hy_AM',
+ ),
+ 'id_ID' => array(
+ 'code' => 'id',
+ 'locale' => 'id_ID',
+ 'name' => 'Bahasa Indonesia',
+ 'dir' => 'ltr',
+ 'flag' => 'id',
+ 'facebook' => 'id_ID',
+ 'deepl' => 'ID',
+ ),
+ 'ido' => array(
+ 'w3c' => 'io',
+ ),
+ 'is_IS' => array(
+ 'code' => 'is',
+ 'locale' => 'is_IS',
+ 'name' => 'Íslenska',
+ 'dir' => 'ltr',
+ 'flag' => 'is',
+ 'facebook' => 'is_IS',
+ ),
+ 'it_IT' => array(
+ 'code' => 'it',
+ 'locale' => 'it_IT',
+ 'name' => 'Italiano',
+ 'dir' => 'ltr',
+ 'flag' => 'it',
+ 'facebook' => 'it_IT',
+ 'deepl' => 'IT',
+ ),
+ 'ja' => array(
+ 'code' => 'ja',
+ 'locale' => 'ja',
+ 'name' => '日本語',
+ 'dir' => 'ltr',
+ 'flag' => 'jp',
+ 'facebook' => 'ja_JP',
+ 'deepl' => 'JA',
+ ),
+ 'jv_ID' => array(
+ 'code' => 'jv',
+ 'locale' => 'jv_ID',
+ 'name' => 'Basa Jawa',
+ 'dir' => 'ltr',
+ 'flag' => 'id',
+ 'facebook' => 'jv_ID',
+ ),
+ 'ka_GE' => array(
+ 'code' => 'ka',
+ 'locale' => 'ka_GE',
+ 'name' => 'ქართული',
+ 'dir' => 'ltr',
+ 'flag' => 'ge',
+ 'facebook' => 'ka_GE',
+ ),
+ 'kab' => array(
+ 'code' => 'kab',
+ 'locale' => 'kab',
+ 'name' => 'Taqbaylit',
+ 'dir' => 'ltr',
+ 'flag' => 'dz',
+ ),
+ 'kin' => array(
+ 'w3c' => 'rw',
+ 'facebook' => 'rw_RW',
+ ),
+ 'kir' => array(
+ 'code' => 'ky',
+ 'locale' => 'kir',
+ 'name' => 'Кыргызча',
+ 'dir' => 'ltr',
+ 'flag' => 'kg',
+ ),
+ 'kk' => array(
+ 'code' => 'kk',
+ 'locale' => 'kk',
+ 'name' => 'Қазақ тілі',
+ 'dir' => 'ltr',
+ 'flag' => 'kz',
+ 'facebook' => 'kk_KZ',
+ ),
+ 'km' => array(
+ 'code' => 'km',
+ 'locale' => 'km',
+ 'name' => 'ភាសាខ្មែរ',
+ 'dir' => 'ltr',
+ 'flag' => 'kh',
+ 'facebook' => 'km_KH',
+ ),
+ 'kn' => array(
+ 'code' => 'kn',
+ 'locale' => 'kn',
+ 'name' => 'ಕನ್ನಡ',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'kn_IN',
+ ),
+ 'ko_KR' => array(
+ 'code' => 'ko',
+ 'locale' => 'ko_KR',
+ 'name' => '한국어',
+ 'dir' => 'ltr',
+ 'flag' => 'kr',
+ 'facebook' => 'ko_KR',
+ 'deepl' => 'KO',
+ ),
+ 'ku' => array(
+ 'facebook' => 'ku_TR',
+ ),
+ 'ky_KY' => array(
+ 'facebook' => 'ky_KG',
+ ),
+ 'la' => array(
+ 'facebook' => 'la_VA',
+ ),
+ 'li' => array(
+ 'facebook' => 'li_NL',
+ ),
+ 'lin' => array(
+ 'facebook' => 'ln_CD',
+ ),
+ 'lo' => array(
+ 'code' => 'lo',
+ 'locale' => 'lo',
+ 'name' => 'ພາສາລາວ',
+ 'dir' => 'ltr',
+ 'flag' => 'la',
+ 'facebook' => 'lo_LA',
+ ),
+ 'lt_LT' => array(
+ 'code' => 'lt',
+ 'locale' => 'lt_LT',
+ 'name' => 'Lietuviškai',
+ 'dir' => 'ltr',
+ 'flag' => 'lt',
+ 'facebook' => 'lt_LT',
+ 'deepl' => 'LT',
+ ),
+ 'lv' => array(
+ 'code' => 'lv',
+ 'locale' => 'lv',
+ 'name' => 'Latviešu valoda',
+ 'dir' => 'ltr',
+ 'flag' => 'lv',
+ 'facebook' => 'lv_LV',
+ 'deepl' => 'LV',
+ ),
+ 'mg_MG' => array(
+ 'facebook' => 'mg_MG',
+ ),
+ 'mk_MK' => array(
+ 'code' => 'mk',
+ 'locale' => 'mk_MK',
+ 'name' => 'македонски јазик',
+ 'dir' => 'ltr',
+ 'flag' => 'mk',
+ 'facebook' => 'mk_MK',
+ ),
+ 'ml_IN' => array(
+ 'code' => 'ml',
+ 'locale' => 'ml_IN',
+ 'name' => 'മലയാളം',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'ml_IN',
+ ),
+ 'mlt' => array(
+ 'facebook' => 'mt_MT',
+ ),
+ 'mn' => array(
+ 'code' => 'mn',
+ 'locale' => 'mn',
+ 'name' => 'Монгол хэл',
+ 'dir' => 'ltr',
+ 'flag' => 'mn',
+ 'facebook' => 'mn_MN',
+ ),
+ 'mr' => array(
+ 'code' => 'mr',
+ 'locale' => 'mr',
+ 'name' => 'मराठी',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'mr_IN',
+ ),
+ 'mri' => array(
+ 'w3c' => 'mi',
+ 'facebook' => 'mi_NZ',
+ ),
+ 'ms_MY' => array(
+ 'code' => 'ms',
+ 'locale' => 'ms_MY',
+ 'name' => 'Bahasa Melayu',
+ 'dir' => 'ltr',
+ 'flag' => 'my',
+ 'facebook' => 'ms_MY',
+ ),
+ 'my_MM' => array(
+ 'code' => 'my',
+ 'locale' => 'my_MM',
+ 'name' => 'ဗမာစာ',
+ 'dir' => 'ltr',
+ 'flag' => 'mm',
+ 'facebook' => 'my_MM',
+ ),
+ 'nb_NO' => array(
+ 'code' => 'nb',
+ 'locale' => 'nb_NO',
+ 'name' => 'Norsk Bokmål',
+ 'dir' => 'ltr',
+ 'flag' => 'no',
+ 'facebook' => 'nb_NO',
+ 'deepl' => 'NB',
+ ),
+ 'ne_NP' => array(
+ 'code' => 'ne',
+ 'locale' => 'ne_NP',
+ 'name' => 'नेपाली',
+ 'dir' => 'ltr',
+ 'flag' => 'np',
+ 'facebook' => 'ne_NP',
+ ),
+ 'nl_BE' => array(
+ 'code' => 'nl',
+ 'locale' => 'nl_BE',
+ 'name' => 'Nederlands',
+ 'dir' => 'ltr',
+ 'flag' => 'be',
+ 'facebook' => 'nl_BE',
+ 'deepl' => 'NL',
+ ),
+ 'nl_NL' => array(
+ 'code' => 'nl',
+ 'locale' => 'nl_NL',
+ 'name' => 'Nederlands',
+ 'dir' => 'ltr',
+ 'flag' => 'nl',
+ 'facebook' => 'nl_NL',
+ 'deepl' => 'NL',
+ ),
+ 'nl_NL_formal' => array(
+ 'code' => 'nl',
+ 'locale' => 'nl_NL_formal',
+ 'name' => 'Nederlands',
+ 'dir' => 'ltr',
+ 'flag' => 'nl',
+ 'w3c' => 'nl-NL',
+ 'facebook' => 'nl_NL',
+ 'deepl' => 'NL',
+ ),
+ 'nn_NO' => array(
+ 'code' => 'nn',
+ 'locale' => 'nn_NO',
+ 'name' => 'Norsk Nynorsk',
+ 'dir' => 'ltr',
+ 'flag' => 'no',
+ 'facebook' => 'nn_NO',
+ ),
+ 'oci' => array(
+ 'code' => 'oc',
+ 'locale' => 'oci',
+ 'name' => 'Occitan',
+ 'dir' => 'ltr',
+ 'flag' => 'occitania',
+ 'w3c' => 'oc',
+ ),
+ 'ory' => array(
+ 'facebook' => 'or_IN',
+ ),
+ 'pa_IN' => array(
+ 'code' => 'pa',
+ 'locale' => 'pa_IN',
+ 'name' => 'ਪੰਜਾਬੀ',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'pa_IN',
+ ),
+ 'pl_PL' => array(
+ 'code' => 'pl',
+ 'locale' => 'pl_PL',
+ 'name' => 'Polski',
+ 'dir' => 'ltr',
+ 'flag' => 'pl',
+ 'facebook' => 'pl_PL',
+ 'deepl' => 'PL',
+ ),
+ 'ps' => array(
+ 'code' => 'ps',
+ 'locale' => 'ps',
+ 'name' => 'پښتو',
+ 'dir' => 'rtl',
+ 'flag' => 'af',
+ 'facebook' => 'ps_AF',
+ ),
+ 'pt_AO' => array(
+ 'code' => 'pt',
+ 'locale' => 'pt_AO',
+ 'name' => 'Português',
+ 'dir' => 'ltr',
+ 'flag' => 'ao',
+ 'facebook' => 'pt_PT',
+ 'deepl' => 'PT-PT',
+ ),
+ 'pt_BR' => array(
+ 'code' => 'pt',
+ 'locale' => 'pt_BR',
+ 'name' => 'Português',
+ 'dir' => 'ltr',
+ 'flag' => 'br',
+ 'facebook' => 'pt_BR',
+ 'deepl' => 'PT-BR',
+ ),
+ 'pt_PT' => array(
+ 'code' => 'pt',
+ 'locale' => 'pt_PT',
+ 'name' => 'Português',
+ 'dir' => 'ltr',
+ 'flag' => 'pt',
+ 'facebook' => 'pt_PT',
+ 'deepl' => 'PT-PT',
+ ),
+ 'pt_PT_ao90' => array(
+ 'code' => 'pt',
+ 'locale' => 'pt_PT_ao90',
+ 'name' => 'Português',
+ 'dir' => 'ltr',
+ 'flag' => 'pt',
+ 'facebook' => 'pt_PT',
+ 'deepl' => 'PT-PT',
+ ),
+ 'rhg' => array(
+ 'code' => 'rhg',
+ 'locale' => 'rhg',
+ 'name' => 'Ruáinga',
+ 'dir' => 'ltr',
+ 'flag' => 'mm',
+ ),
+ 'ro_RO' => array(
+ 'code' => 'ro',
+ 'locale' => 'ro_RO',
+ 'name' => 'Română',
+ 'dir' => 'ltr',
+ 'flag' => 'ro',
+ 'facebook' => 'ro_RO',
+ 'deepl' => 'RO',
+ ),
+ 'roh' => array(
+ 'w3c' => 'rm',
+ 'facebook' => 'rm_CH',
+ ),
+ 'ru_RU' => array(
+ 'code' => 'ru',
+ 'locale' => 'ru_RU',
+ 'name' => 'Русский',
+ 'dir' => 'ltr',
+ 'flag' => 'ru',
+ 'facebook' => 'ru_RU',
+ 'deepl' => 'RU',
+ ),
+ 'sa_IN' => array(
+ 'facebook' => 'sa_IN',
+ ),
+ 'sah' => array(
+ 'code' => 'sah',
+ 'locale' => 'sah',
+ 'name' => 'Сахалыы',
+ 'dir' => 'ltr',
+ 'flag' => 'ru',
+ ),
+ 'si_LK' => array(
+ 'code' => 'si',
+ 'locale' => 'si_LK',
+ 'name' => 'සිංහල',
+ 'dir' => 'ltr',
+ 'flag' => 'lk',
+ 'facebook' => 'si_LK',
+ ),
+ 'sk_SK' => array(
+ 'code' => 'sk',
+ 'locale' => 'sk_SK',
+ 'name' => 'Slovenčina',
+ 'dir' => 'ltr',
+ 'flag' => 'sk',
+ 'facebook' => 'sk_SK',
+ 'deepl' => 'SK',
+ ),
+ 'skr' => array(
+ 'code' => 'skr',
+ 'locale' => 'skr',
+ 'name' => 'سرائیکی',
+ 'dir' => 'rtl',
+ 'flag' => 'pk',
+ ),
+ 'sl_SI' => array(
+ 'code' => 'sl',
+ 'locale' => 'sl_SI',
+ 'name' => 'Slovenščina',
+ 'dir' => 'ltr',
+ 'flag' => 'si',
+ 'facebook' => 'sl_SI',
+ 'deepl' => 'SL',
+ ),
+ 'sna' => array(
+ 'facebook' => 'sn_ZW',
+ ),
+ 'snd' => array(
+ 'code' => 'sd',
+ 'locale' => 'snd',
+ 'name' => 'سنڌي',
+ 'dir' => 'rtl',
+ 'flag' => 'pk',
+ ),
+ 'so_SO' => array(
+ 'code' => 'so',
+ 'locale' => 'so_SO',
+ 'name' => 'Af-Soomaali',
+ 'dir' => 'ltr',
+ 'flag' => 'so',
+ 'facebook' => 'so_SO',
+ ),
+ 'sq' => array(
+ 'code' => 'sq',
+ 'locale' => 'sq',
+ 'name' => 'Shqip',
+ 'dir' => 'ltr',
+ 'flag' => 'al',
+ 'facebook' => 'sq_AL',
+ ),
+ 'sr_RS' => array(
+ 'code' => 'sr',
+ 'locale' => 'sr_RS',
+ 'name' => 'Српски језик',
+ 'dir' => 'ltr',
+ 'flag' => 'rs',
+ 'facebook' => 'sr_RS',
+ ),
+ 'srd' => array(
+ 'w3c' => 'sc',
+ 'facebook' => 'sc_IT',
+ ),
+ 'su_ID' => array(
+ 'code' => 'su',
+ 'locale' => 'su_ID',
+ 'name' => 'Basa Sunda',
+ 'dir' => 'ltr',
+ 'flag' => 'id',
+ 'facebook' => 'su_ID',
+ ),
+ 'sv_SE' => array(
+ 'code' => 'sv',
+ 'locale' => 'sv_SE',
+ 'name' => 'Svenska',
+ 'dir' => 'ltr',
+ 'flag' => 'se',
+ 'facebook' => 'sv_SE',
+ 'deepl' => 'SV',
+ ),
+ 'sw' => array(
+ 'code' => 'sw',
+ 'locale' => 'sw',
+ 'name' => 'Kiswahili',
+ 'dir' => 'ltr',
+ 'flag' => 'ke',
+ 'facebook' => 'sw_KE',
+ ),
+ 'syr' => array(
+ 'facebook' => 'sy_SY',
+ ),
+ 'szl' => array(
+ 'code' => 'szl',
+ 'locale' => 'szl',
+ 'name' => 'Ślōnskŏ gŏdka',
+ 'dir' => 'ltr',
+ 'flag' => 'pl',
+ 'facebook' => 'sz_PL',
+ ),
+ 'ta_IN' => array(
+ 'code' => 'ta',
+ 'locale' => 'ta_IN',
+ 'name' => 'தமிழ்',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'ta_IN',
+ ),
+ 'ta_LK' => array(
+ 'code' => 'ta',
+ 'locale' => 'ta_LK',
+ 'name' => 'தமிழ்',
+ 'dir' => 'ltr',
+ 'flag' => 'lk',
+ 'facebook' => 'ta_IN',
+ ),
+ 'tah' => array(
+ 'code' => 'ty',
+ 'locale' => 'tah',
+ 'name' => 'Reo Tahiti',
+ 'dir' => 'ltr',
+ 'flag' => 'pf',
+ ),
+ 'te' => array(
+ 'code' => 'te',
+ 'locale' => 'te',
+ 'name' => 'తెలుగు',
+ 'dir' => 'ltr',
+ 'flag' => 'in',
+ 'facebook' => 'te_IN',
+ ),
+ 'tg' => array(
+ 'facebook' => 'tg_TJ',
+ ),
+ 'th' => array(
+ 'code' => 'th',
+ 'locale' => 'th',
+ 'name' => 'ไทย',
+ 'dir' => 'ltr',
+ 'flag' => 'th',
+ 'facebook' => 'th_TH',
+ ),
+ 'tl' => array(
+ 'code' => 'tl',
+ 'locale' => 'tl',
+ 'name' => 'Tagalog',
+ 'dir' => 'ltr',
+ 'flag' => 'ph',
+ 'facebook' => 'tl_PH',
+ ),
+ 'tr_TR' => array(
+ 'code' => 'tr',
+ 'locale' => 'tr_TR',
+ 'name' => 'Türkçe',
+ 'dir' => 'ltr',
+ 'flag' => 'tr',
+ 'facebook' => 'tr_TR',
+ 'deepl' => 'TR',
+ ),
+ 'tt_RU' => array(
+ 'code' => 'tt',
+ 'locale' => 'tt_RU',
+ 'name' => 'Татар теле',
+ 'dir' => 'ltr',
+ 'flag' => 'ru',
+ 'facebook' => 'tt_RU',
+ ),
+ 'tuk' => array(
+ 'w3c' => 'tk',
+ 'facebook' => 'tk_TM',
+ ),
+ 'tzm' => array(
+ 'facebook' => 'tz_MA',
+ ),
+ 'ug_CN' => array(
+ 'code' => 'ug',
+ 'locale' => 'ug_CN',
+ 'name' => 'Uyƣurqə',
+ 'dir' => 'ltr',
+ 'flag' => 'cn',
+ ),
+ 'uk' => array(
+ 'code' => 'uk',
+ 'locale' => 'uk',
+ 'name' => 'Українська',
+ 'dir' => 'ltr',
+ 'flag' => 'ua',
+ 'facebook' => 'uk_UA',
+ 'deepl' => 'UK',
+ ),
+ 'ur' => array(
+ 'code' => 'ur',
+ 'locale' => 'ur',
+ 'name' => 'اردو',
+ 'dir' => 'rtl',
+ 'flag' => 'pk',
+ 'facebook' => 'ur_PK',
+ ),
+ 'uz_UZ' => array(
+ 'code' => 'uz',
+ 'locale' => 'uz_UZ',
+ 'name' => 'Oʻzbek',
+ 'dir' => 'ltr',
+ 'flag' => 'uz',
+ 'facebook' => 'uz_UZ',
+ ),
+ 'vec' => array(
+ 'code' => 'vec',
+ 'locale' => 'vec',
+ 'name' => 'Vèneto',
+ 'dir' => 'ltr',
+ 'flag' => 'veneto',
+ ),
+ 'vi' => array(
+ 'code' => 'vi',
+ 'locale' => 'vi',
+ 'name' => 'Tiếng Việt',
+ 'dir' => 'ltr',
+ 'flag' => 'vn',
+ 'facebook' => 'vi_VN',
+ ),
+ 'xho' => array(
+ 'facebook' => 'xh_ZA',
+ ),
+ 'yor' => array(
+ 'facebook' => 'yo_NG',
+ ),
+ 'zh_CN' => array(
+ 'code' => 'zh',
+ 'locale' => 'zh_CN',
+ 'name' => '中文 (中国)',
+ 'dir' => 'ltr',
+ 'flag' => 'cn',
+ 'facebook' => 'zh_CN',
+ 'deepl' => 'ZH',
+ ),
+ 'zh_HK' => array(
+ 'code' => 'zh',
+ 'locale' => 'zh_HK',
+ 'name' => '中文 (香港)',
+ 'dir' => 'ltr',
+ 'flag' => 'hk',
+ 'facebook' => 'zh_HK',
+ 'deepl' => 'ZH',
+ ),
+ 'zh_TW' => array(
+ 'code' => 'zh',
+ 'locale' => 'zh_TW',
+ 'name' => '中文 (台灣)',
+ 'dir' => 'ltr',
+ 'flag' => 'tw',
+ 'facebook' => 'zh_TW',
+ 'deepl' => 'ZH',
+ ),
+);
diff --git a/wp-content/plugins/polylang/settings/settings-browser.php b/wp-content/plugins/polylang/settings/settings-browser.php
new file mode 100644
index 0000000000..80b4e30852
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/settings-browser.php
@@ -0,0 +1,107 @@
+is_available()`, which is used before calling the parent's constructor.
+ $this->options = &$polylang->options;
+
+ parent::__construct(
+ $polylang,
+ array(
+ 'module' => 'browser',
+ 'title' => __( 'Detect browser language', 'polylang' ),
+ 'description' => __( 'When the front page is visited, redirects to itself in the browser preferred language. As this doesn\'t work if it is cached, Polylang will attempt to disable the front page cache for known cache plugins.', 'polylang' ),
+ 'active_option' => $this->is_available() ? 'browser' : 'none',
+ )
+ );
+
+ if ( ! class_exists( 'PLL_Xdata_Domain', true ) ) {
+ add_action( 'admin_print_footer_scripts', array( $this, 'print_js' ) );
+ }
+ }
+
+ /**
+ * Tells if the option is available
+ *
+ * @since 2.0
+ *
+ * @return bool
+ */
+ protected function is_available() {
+ return ( 3 > $this->options['force_lang'] ) || class_exists( 'PLL_Xdata_Domain', true );
+ }
+
+ /**
+ * Tells if the module is active
+ *
+ * @since 1.8
+ *
+ * @return bool
+ */
+ public function is_active() {
+ return $this->is_available() ? parent::is_active() : false;
+ }
+
+ /**
+ * Displays the javascript to handle dynamically the change in url modifications
+ * as the preferred browser language is not used when the language is set from different domains
+ *
+ * @since 1.8
+ *
+ * @return void
+ */
+ public function print_js() {
+ wp_enqueue_script( 'jquery' );
+
+ if ( parent::is_active() && 3 > $this->options['force_lang'] ) {
+ $func = 'removeClass( "inactive" ).addClass( "active" )';
+ $link = sprintf( '%s ', $this->action_links['deactivate'] );
+ }
+ else {
+ $func = 'removeClass( "active" ).addClass( "inactive" )';
+ $link = sprintf( '%s ', $this->action_links['activate'] );
+ }
+
+ $deactivated = sprintf( '%s ', $this->action_links['deactivated'] );
+
+ ?>
+
+ 'cpt',
+ 'title' => __( 'Custom post types and Taxonomies', 'polylang' ),
+ 'description' => __( 'Activate languages and translations management for the custom post types and the taxonomies.', 'polylang' ),
+ )
+ );
+
+ $public_post_types = get_post_types( array( 'public' => true, '_builtin' => false ) );
+ /** This filter is documented in include/model.php */
+ $this->post_types = array_unique( apply_filters( 'pll_get_post_types', $public_post_types, true ) );
+
+ /** This filter is documented in include/model.php */
+ $programmatically_active_post_types = array_unique( apply_filters( 'pll_get_post_types', array(), false ) );
+ $this->disabled_post_types = array_intersect( $programmatically_active_post_types, $this->post_types );
+
+ $public_taxonomies = get_taxonomies( array( 'public' => true, '_builtin' => false ) );
+ $public_taxonomies = array_diff( $public_taxonomies, get_taxonomies( array( '_pll' => true ) ) );
+ /** This filter is documented in include/model.php */
+ $this->taxonomies = array_unique( apply_filters( 'pll_get_taxonomies', $public_taxonomies, true ) );
+
+ /** This filter is documented in include/model.php */
+ $programmatically_active_taxonomies = array_unique( apply_filters( 'pll_get_taxonomies', array(), false ) );
+ $this->disabled_taxonomies = array_intersect( $programmatically_active_taxonomies, $this->taxonomies );
+ }
+
+ /**
+ * Tells if the module is active
+ *
+ * @since 1.8
+ *
+ * @return bool
+ */
+ public function is_active() {
+ return ! empty( $this->post_types ) || ! empty( $this->taxonomies );
+ }
+
+ /**
+ * Displays the settings form
+ *
+ * @since 1.8
+ */
+ protected function form() {
+ if ( ! empty( $this->post_types ) ) {?>
+
+
+ post_types as $post_type ) {
+ $pt = get_post_type_object( $post_type );
+ if ( ! empty( $pt ) ) {
+ $disabled = in_array( $post_type, $this->disabled_post_types );
+ printf(
+ ' %s ',
+ esc_attr( $post_type ),
+ checked( $disabled || in_array( $post_type, $this->options['post_types'], true ), true, false ),
+ disabled( $disabled, true, false ),
+ esc_html(
+ sprintf(
+ /* translators: 1 is a post type or taxonomy label, 2 is a post type or taxonomy key. */
+ _x( '%1$s (%2$s)', 'content type setting choice', 'polylang' ),
+ $pt->labels->name,
+ $pt->name
+ )
+ )
+ );
+ }
+ }
+ ?>
+
+
+ taxonomies ) ) {
+ ?>
+
+
+ taxonomies as $taxonomy ) {
+ $tax = get_taxonomy( $taxonomy );
+ if ( ! empty( $tax ) ) {
+ $disabled = in_array( $taxonomy, $this->disabled_taxonomies );
+ printf(
+ ' %s ',
+ esc_attr( $taxonomy ),
+ checked( $disabled || in_array( $taxonomy, $this->options['taxonomies'], true ), true, false ),
+ disabled( $disabled, true, false ),
+ esc_html(
+ sprintf(
+ /* translators: 1 is a post type or taxonomy label, 2 is a post type or taxonomy key. */
+ _x( '%1$s (%2$s)', 'content type setting choice', 'polylang' ),
+ $tax->labels->name,
+ $tax->name
+ )
+ )
+ );
+ }
+ }
+ ?>
+
+
+ 'licenses',
+ 'title' => __( 'License keys', 'polylang' ),
+ 'description' => __( 'Manage licenses for Polylang Pro and add-ons.', 'polylang' ),
+ )
+ );
+
+ $this->buttons['cancel'] = sprintf( '%s ', __( 'Close', 'polylang' ) );
+
+ $this->items = apply_filters( 'pll_settings_licenses', array() );
+
+ add_action( 'wp_ajax_pll_deactivate_license', array( $this, 'deactivate_license' ) );
+ }
+
+ /**
+ * Tells if the module is active
+ *
+ * @since 1.9
+ *
+ * @return bool
+ */
+ public function is_active() {
+ return ! empty( $this->items );
+ }
+
+ /**
+ * Displays the settings form
+ *
+ * @since 1.9
+ */
+ protected function form() {
+ if ( ! empty( $this->items ) ) { ?>
+
+ get_form_field();
+ }
+
+ /**
+ * Ajax method to save the license keys and activate the licenses at the same time
+ * Overrides parent's method
+ *
+ * @since 1.9
+ */
+ public function save_options() {
+ check_ajax_referer( 'pll_options', '_pll_nonce' );
+ if ( ! current_user_can( 'manage_options' ) ) {
+ wp_die( -1 );
+ }
+
+ if ( isset( $_POST['module'] ) && $this->module === $_POST['module'] && ! empty( $_POST['licenses'] ) ) {
+ $x = new WP_Ajax_Response();
+ foreach ( $this->items as $item ) {
+ if ( ! empty( $_POST['licenses'][ $item->id ] ) ) {
+ $updated_item = $item->activate_license( sanitize_key( $_POST['licenses'][ $item->id ] ) );
+ $x->Add( array( 'what' => 'license-update', 'data' => $item->id, 'supplemental' => array( 'html' => $this->get_row( $updated_item ) ) ) );
+ }
+ }
+
+ // Updated message
+ pll_add_notice( new WP_Error( 'settings_updated', __( 'Settings saved.', 'polylang' ), 'success' ) );
+ ob_start();
+ settings_errors( 'polylang' );
+ $x->Add( array( 'what' => 'success', 'data' => ob_get_clean() ) );
+ $x->send();
+ }
+ }
+
+ /**
+ * Ajax method to deactivate a license
+ *
+ * @since 1.9
+ *
+ * @return void
+ */
+ public function deactivate_license() {
+ check_ajax_referer( 'pll_options', '_pll_nonce' );
+
+ if ( ! current_user_can( 'manage_options' ) ) {
+ wp_die( -1 );
+ }
+
+ if ( ! isset( $_POST['id'] ) ) {
+ wp_die( 0 );
+ }
+
+ $id = substr( sanitize_text_field( wp_unslash( $_POST['id'] ) ), 11 );
+ wp_send_json(
+ array(
+ 'id' => $id,
+ 'html' => $this->get_row( $this->items[ $id ]->deactivate_license() ),
+ )
+ );
+ }
+}
diff --git a/wp-content/plugins/polylang/settings/settings-media.php b/wp-content/plugins/polylang/settings/settings-media.php
new file mode 100644
index 0000000000..1f7ea61592
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/settings-media.php
@@ -0,0 +1,37 @@
+ 'media',
+ 'title' => __( 'Media', 'polylang' ),
+ 'description' => __( 'Activate languages and translations for media only if you need to translate the text attached to the media: the title, the alternative text, the caption, the description... Note that the file is not duplicated.', 'polylang' ),
+ 'active_option' => 'media_support',
+ )
+ );
+ }
+}
diff --git a/wp-content/plugins/polylang/settings/settings-module.php b/wp-content/plugins/polylang/settings/settings-module.php
new file mode 100644
index 0000000000..1201559b53
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/settings-module.php
@@ -0,0 +1,379 @@
+options = &$polylang->options;
+ $this->model = &$polylang->model;
+ $this->links_model = &$polylang->links_model;
+
+ $args = wp_parse_args(
+ $args,
+ array(
+ 'title' => '',
+ 'description' => '',
+ 'active_option' => 'none',
+ )
+ );
+
+ if ( empty( $args['active_option'] ) ) {
+ // Backward compatibility.
+ $args['active_option'] = 'none';
+ }
+
+ foreach ( $args as $prop => $value ) {
+ $this->$prop = $value;
+ }
+
+ // All possible action links, even if not always a link ;-)
+ $this->action_links = array(
+ 'configure' => sprintf(
+ '%s ',
+ esc_attr__( 'Configure this module', 'polylang' ),
+ '#',
+ esc_html__( 'Settings', 'polylang' )
+ ),
+ 'deactivate' => sprintf(
+ '%s ',
+ esc_attr__( 'Deactivate this module', 'polylang' ),
+ esc_url( wp_nonce_url( '?page=mlang&tab=modules&pll_action=deactivate&noheader=true&module=' . $this->module, 'pll_deactivate' ) ),
+ esc_html__( 'Deactivate', 'polylang' )
+ ),
+ 'activate' => sprintf(
+ '%s ',
+ esc_attr__( 'Activate this module', 'polylang' ),
+ esc_url( wp_nonce_url( '?page=mlang&tab=modules&pll_action=activate&noheader=true&module=' . $this->module, 'pll_activate' ) ),
+ esc_html__( 'Activate', 'polylang' )
+ ),
+ 'activated' => esc_html__( 'Activated', 'polylang' ),
+ 'deactivated' => esc_html__( 'Deactivated', 'polylang' ),
+ );
+
+ $this->buttons = array(
+ 'cancel' => sprintf( '%s ', esc_html__( 'Cancel', 'polylang' ) ),
+ 'save' => sprintf( '%s ', esc_html__( 'Save Changes', 'polylang' ) ),
+ );
+
+ // Ajax action to save options.
+ add_action( 'wp_ajax_pll_save_options', array( $this, 'save_options' ) );
+ }
+
+ /**
+ * Tells if the module is active.
+ *
+ * @since 1.8
+ *
+ * @return bool
+ */
+ public function is_active() {
+ return 'none' === $this->active_option || ( 'preview' !== $this->active_option && ! empty( $this->options[ $this->active_option ] ) );
+ }
+
+ /**
+ * Activates the module.
+ *
+ * @since 1.8
+ *
+ * @return void
+ */
+ public function activate() {
+ if ( 'none' !== $this->active_option && 'preview' !== $this->active_option ) {
+ $this->options[ $this->active_option ] = true;
+ update_option( 'polylang', $this->options );
+ }
+ }
+
+ /**
+ * Deactivates the module.
+ *
+ * @since 1.8
+ *
+ * @return void
+ */
+ public function deactivate() {
+ if ( 'none' !== $this->active_option && 'preview' !== $this->active_option ) {
+ $this->options[ $this->active_option ] = false;
+ update_option( 'polylang', $this->options );
+ }
+ }
+
+ /**
+ * Protected method to display a configuration form.
+ *
+ * @since 1.8
+ *
+ * @return void
+ */
+ protected function form() {
+ // Child classes can provide a form.
+ }
+
+ /**
+ * Public method returning the form if any.
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ public function get_form() {
+ if ( ! $this->is_active() ) {
+ return '';
+ }
+
+ // Read the form only once
+ if ( false === $this->form ) {
+ ob_start();
+ $this->form();
+ $this->form = ob_get_clean();
+ }
+
+ return $this->form;
+ }
+
+ /**
+ * Allows child classes to validate their options before saving.
+ *
+ * @since 1.8
+ *
+ * @param array $options Unsanitized options to save.
+ * @return array Options
+ */
+ protected function update( $options ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return array(); // It's responsibility of the child class to decide what is saved.
+ }
+
+ /**
+ * Ajax method to save the options.
+ *
+ * @since 1.8
+ *
+ * @return void
+ */
+ public function save_options() {
+ check_ajax_referer( 'pll_options', '_pll_nonce' );
+ if ( ! current_user_can( 'manage_options' ) ) {
+ wp_die( -1 );
+ }
+
+ if ( isset( $_POST['module'] ) && $this->module === $_POST['module'] ) {
+ // It's up to the child class to decide which options are saved, whether there are errors or not
+ $post = array_diff_key( $_POST, array_flip( array( 'action', 'module', 'pll_ajax_backend', '_pll_nonce' ) ) );
+ $options = $this->update( $post );
+ $this->options = array_merge( $this->options, $options );
+ update_option( 'polylang', $this->options );
+
+ // Refresh language cache in case home urls have been modified
+ $this->model->clean_languages_cache();
+
+ // Refresh rewrite rules in case rewrite, hide_default, post types or taxonomies options have been modified
+ // Don't use flush_rewrite_rules as we don't have the right links model and permastruct
+ delete_option( 'rewrite_rules' );
+
+
+ ob_start();
+
+ if ( empty( get_settings_errors( 'polylang' ) ) ) {
+ // Send update message
+ pll_add_notice( new WP_Error( 'settings_updated', __( 'Settings saved.', 'polylang' ), 'success' ) );
+ settings_errors( 'polylang' );
+ $x = new WP_Ajax_Response( array( 'what' => 'success', 'data' => ob_get_clean() ) );
+ $x->send();
+ } else {
+ // Send error messages
+ settings_errors( 'polylang' );
+ $x = new WP_Ajax_Response( array( 'what' => 'error', 'data' => ob_get_clean() ) );
+ $x->send();
+ }
+ }
+ }
+
+ /**
+ * Get the row actions.
+ *
+ * @since 1.8
+ *
+ * @return string[]
+ */
+ protected function get_actions() {
+ $actions = array();
+
+ if ( $this->is_active() && $this->get_form() ) {
+ $actions[] = 'configure';
+ }
+
+ if ( 'none' !== $this->active_option && 'preview' !== $this->active_option ) {
+ $actions[] = $this->is_active() ? 'deactivate' : 'activate';
+ }
+
+ if ( empty( $actions ) ) {
+ $actions[] = $this->is_active() ? 'activated' : 'deactivated';
+ }
+
+ return $actions;
+ }
+
+ /**
+ * Get the actions links.
+ *
+ * @since 1.8
+ *
+ * @return string[] Action links.
+ */
+ public function get_action_links() {
+ return array_intersect_key( $this->action_links, array_flip( $this->get_actions() ) );
+ }
+
+ /**
+ * Default upgrade message (to Pro version).
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ protected function default_upgrade_message() {
+ return sprintf(
+ '%s %s ',
+ __( 'To enable this feature, you need Polylang Pro.', 'polylang' ),
+ 'https://polylang.pro',
+ __( 'Upgrade now.', 'polylang' )
+ );
+ }
+
+ /**
+ * Allows child classes to display an upgrade message.
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function get_upgrade_message() {
+ return 'preview' === $this->active_option ? $this->default_upgrade_message() : '';
+ }
+
+ /**
+ * Get the buttons.
+ *
+ * @since 1.9
+ *
+ * @return string[] An array of html fragment for the buttons.
+ */
+ public function get_buttons() {
+ return $this->buttons;
+ }
+}
diff --git a/wp-content/plugins/polylang/settings/settings-url.php b/wp-content/plugins/polylang/settings/settings-url.php
new file mode 100644
index 0000000000..784f35eebf
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/settings-url.php
@@ -0,0 +1,332 @@
+ 'url',
+ 'title' => __( 'URL modifications', 'polylang' ),
+ 'description' => __( 'Decide how your URLs will look like.', 'polylang' ),
+ )
+ );
+
+ $this->page_on_front = &$polylang->static_pages->page_on_front;
+ }
+
+ /**
+ * Displays the fieldset to choose how the language is set
+ *
+ * @since 1.8
+ *
+ * @return void
+ */
+ protected function force_lang() {
+ ?>
+
+
+ %s',
+ checked( $this->options['force_lang'], 0, false ),
+ esc_html__( 'The language is set from content', 'polylang' )
+ );
+ ?>
+
+
+
+ %s',
+ checked( $this->options['force_lang'], 1, false ),
+ ( $this->links_model->using_permalinks ? esc_html__( 'The language is set from the directory name in pretty permalinks', 'polylang' ) : esc_html__( 'The language is set from the code in the URL', 'polylang' ) )
+ );
+ ?>
+
+ ' . esc_html( home_url( $this->links_model->using_permalinks ? 'en/my-post/' : '?lang=en&p=1' ) ) . ''; ?>
+
+ %s',
+ disabled( $this->links_model->using_permalinks, false, false ),
+ checked( $this->options['force_lang'], 2, false ),
+ esc_html__( 'The language is set from the subdomain name in pretty permalinks', 'polylang' )
+ );
+ ?>
+
+ ' . esc_html( str_replace( array( '://', 'www.' ), array( '://en.', '' ), home_url( 'my-post/' ) ) ) . ''; ?>
+
+ %s',
+ disabled( $this->links_model->using_permalinks, false, false ),
+ checked( $this->options['force_lang'], 3, false ),
+ esc_html__( 'The language is set from different domains', 'polylang' )
+ );
+ ?>
+
+
+
+
+ %s',
+ checked( $this->options['hide_default'], 1, false ),
+ esc_html__( 'Hide URL language information for default language', 'polylang' )
+ );
+ ?>
+
+
+
+ %s',
+ disabled( $this->links_model->using_permalinks, false, false ),
+ checked( $this->options['rewrite'], 1, false ),
+ esc_html__( 'Remove /language/ in pretty permalinks', 'polylang' )
+ );
+ ?>
+
+ ' . esc_html( home_url( 'en/' ) ) . ''; ?>
+
+ %s',
+ disabled( $this->links_model->using_permalinks, false, false ),
+ checked( $this->options['rewrite'], 0, false ),
+ esc_html__( 'Keep /language/ in pretty permalinks', 'polylang' )
+ );
+ ?>
+
+ ' . esc_html( home_url( 'language/en/' ) ) . ''; ?>
+
+
+ %s',
+ checked( $this->options['redirect_lang'], 1, false ),
+ esc_html__( 'The front page URL contains the language code instead of the page name or page id', 'polylang' )
+ );
+ ?>
+
+
+ model->post->get_language( $this->page_on_front );
+ /** @var PLL_Language $lang */
+ $lang = $lang ? $lang : $this->model->get_default_language();
+ printf(
+ /* translators: %1$s example url when the option is active. %2$s example url when the option is not active */
+ esc_html__( 'Example: %1$s instead of %2$s', 'polylang' ),
+ '' . esc_html( $this->links_model->home_url( $lang ) ) . '
',
+ '' . esc_html( _get_page_link( $this->page_on_front ) ) . '
'
+ );
+ ?>
+
+
+
+
+ force_lang(); ?>
+
+
+
+
+
$this->options['force_lang'] ? '' : 'style="display: none;"'; ?>>
+ hide_default(); ?>
+
+ links_model->using_permalinks ) {
+ ?>
+ $this->options['force_lang'] ? '' : 'style="display: none;"'; ?>>
+ rewrite(); ?>
+
+ page_on_front ) {
+ ?>
+ $this->options['force_lang'] ? '' : 'style="display: none;"'; ?>>
+ redirect_lang(); ?>
+
+
+
+ $domain ) {
+ if ( empty( $domain ) ) {
+ $lang = $this->model->get_language( $key );
+ pll_add_notice(
+ new WP_Error(
+ sprintf( 'pll_invalid_domain_%s', $key ),
+ sprintf(
+ /* translators: %s is a native language name */
+ __( 'Please enter a valid URL for %s.', 'polylang' ),
+ $lang->name
+ )
+ )
+ );
+ }
+ else {
+ $newoptions['domains'][ $key ] = esc_url_raw( trim( $domain ) );
+ }
+ }
+ }
+
+ foreach ( array( 'hide_default', 'redirect_lang' ) as $key ) {
+ $newoptions[ $key ] = isset( $options[ $key ] ) ? 1 : 0;
+ }
+
+ if ( 3 == $options['force_lang'] ) {
+ if ( ! class_exists( 'PLL_Xdata_Domain', true ) ) {
+ $newoptions['browser'] = 0;
+ }
+ $newoptions['hide_default'] = 0;
+ }
+
+ // Check if domains exist
+ if ( $newoptions['force_lang'] > 1 ) {
+ $this->check_domains( $newoptions );
+ }
+
+ return $newoptions; // Take care to return only validated options
+ }
+
+ /**
+ * Check if subdomains or domains are accessible
+ *
+ * @since 1.8
+ *
+ * @param array $options new set of options to test
+ * @return void
+ */
+ protected function check_domains( $options ) {
+ $options = array_merge( $this->options, $options );
+ $model = new PLL_Model( $options );
+ $links_model = $model->get_links_model();
+ foreach ( $this->model->get_languages_list() as $lang ) {
+ $url = add_query_arg( 'deactivate-polylang', 1, $links_model->home_url( $lang ) );
+ // Don't redefine vip_safe_wp_remote_get() as it has not the same signature as wp_remote_get()
+ $response = function_exists( 'vip_safe_wp_remote_get' ) ? vip_safe_wp_remote_get( esc_url_raw( $url ) ) : wp_remote_get( esc_url_raw( $url ) );
+ $response_code = wp_remote_retrieve_response_code( $response );
+
+ if ( 200 != $response_code ) {
+ pll_add_notice(
+ new WP_Error(
+ sprintf( 'pll_invalid_domain_%s', $lang->slug ),
+ sprintf(
+ /* translators: %s is an url */
+ __( 'Polylang was unable to access the %s URL. Please check that the URL is valid.', 'polylang' ),
+ $url
+ )
+ )
+ );
+ }
+ }
+ }
+}
diff --git a/wp-content/plugins/polylang/settings/settings.php b/wp-content/plugins/polylang/settings/settings.php
new file mode 100644
index 0000000000..fbdb57e3ff
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/settings.php
@@ -0,0 +1,414 @@
+active_tab = 'mlang' === $_GET['page'] ? 'lang' : substr( sanitize_key( $_GET['page'] ), 6 ); // phpcs:ignore WordPress.Security.NonceVerification
+ }
+
+ PLL_Admin_Strings::init();
+
+ add_action( 'admin_init', array( $this, 'register_settings_modules' ) );
+
+ // Adds screen options and the about box in the languages admin panel.
+ add_action( 'load-toplevel_page_mlang', array( $this, 'load_page' ) );
+ add_action( 'load-languages_page_mlang_strings', array( $this, 'load_page_strings' ) );
+
+ // Saves the per-page value in screen options.
+ add_filter( 'set_screen_option_pll_lang_per_page', array( $this, 'set_screen_option' ), 10, 3 );
+ add_filter( 'set_screen_option_pll_strings_per_page', array( $this, 'set_screen_option' ), 10, 3 );
+ }
+
+ /**
+ * Initializes the modules
+ *
+ * @since 1.8
+ *
+ * @return void
+ */
+ public function register_settings_modules() {
+ $modules = array();
+
+ if ( $this->model->has_languages() ) {
+ $modules = array(
+ 'PLL_Settings_Url',
+ 'PLL_Settings_Browser',
+ 'PLL_Settings_Media',
+ 'PLL_Settings_CPT',
+ );
+ }
+
+ $modules[] = 'PLL_Settings_Licenses';
+
+ /**
+ * Filter the list of setting modules
+ *
+ * @since 1.8
+ *
+ * @param array $modules the list of module classes
+ */
+ $modules = apply_filters( 'pll_settings_modules', $modules );
+
+ foreach ( $modules as $key => $class ) {
+ $key = is_numeric( $key ) ? strtolower( str_replace( 'PLL_Settings_', '', $class ) ) : $key;
+ $this->modules[ $key ] = new $class( $this );
+ }
+ }
+
+ /**
+ * Loads the about metabox
+ *
+ * @since 0.8
+ *
+ * @return void
+ */
+ public function metabox_about() {
+ include __DIR__ . '/view-about.php';
+ }
+
+ /**
+ * Adds screen options and the about box in the languages admin panel
+ *
+ * @since 0.9.5
+ *
+ * @return void
+ */
+ public function load_page() {
+ if ( ! defined( 'PLL_DISPLAY_ABOUT' ) || PLL_DISPLAY_ABOUT ) {
+ add_meta_box(
+ 'pll-about-box',
+ __( 'About Polylang', 'polylang' ),
+ array( $this, 'metabox_about' ),
+ 'toplevel_page_mlang',
+ 'normal'
+ );
+ }
+
+ add_screen_option(
+ 'per_page',
+ array(
+ 'label' => __( 'Languages', 'polylang' ),
+ 'default' => 10,
+ 'option' => 'pll_lang_per_page',
+ )
+ );
+
+ add_action( 'admin_notices', array( $this, 'notice_objects_with_no_lang' ) );
+ }
+
+ /**
+ * Adds screen options in the strings translations admin panel
+ *
+ * @since 2.1
+ *
+ * @return void
+ */
+ public function load_page_strings() {
+ add_screen_option(
+ 'per_page',
+ array(
+ 'label' => __( 'Strings translations', 'polylang' ),
+ 'default' => 10,
+ 'option' => 'pll_strings_per_page',
+ )
+ );
+ }
+
+ /**
+ * Saves the number of rows in the languages or strings table set by this user.
+ *
+ * @since 0.9.5
+ *
+ * @param mixed $screen_option False or value returned by a previous filter, not used.
+ * @param string $option The name of the option, not used.
+ * @param int $value The new value of the option to save.
+ * @return int The new value of the option.
+ */
+ public function set_screen_option( $screen_option, $option, $value ) {
+ return (int) $value;
+ }
+
+ /**
+ * Manages the user input for the languages pages.
+ *
+ * @since 1.9
+ *
+ * @param string $action The action name.
+ * @return void
+ */
+ public function handle_actions( $action ) {
+ switch ( $action ) {
+ case 'add':
+ check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
+ $errors = $this->model->add_language( $_POST );
+
+ if ( is_wp_error( $errors ) ) {
+ pll_add_notice( $errors );
+ } else {
+ pll_add_notice( new WP_Error( 'pll_languages_created', __( 'Language added.', 'polylang' ), 'success' ) );
+ $locale = sanitize_locale_name( $_POST['locale'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
+
+ if ( 'en_US' !== $locale && current_user_can( 'install_languages' ) ) {
+ // Attempts to install the language pack
+ require_once ABSPATH . 'wp-admin/includes/translation-install.php';
+ if ( ! wp_download_language_pack( $locale ) ) {
+ pll_add_notice( new WP_Error( 'pll_download_mo', __( 'The language was created, but the WordPress language file was not downloaded. Please install it manually.', 'polylang' ), 'warning' ) );
+ }
+
+ // Force checking for themes and plugins translations updates
+ wp_clean_themes_cache();
+ wp_clean_plugins_cache();
+ }
+ }
+ self::redirect(); // To refresh the page ( possible thanks to the $_GET['noheader']=true )
+ break;
+
+ case 'delete':
+ check_admin_referer( 'delete-lang' );
+
+ if ( ! empty( $_GET['lang'] ) && $this->model->delete_language( (int) $_GET['lang'] ) ) {
+ pll_add_notice( new WP_Error( 'pll_languages_deleted', __( 'Language deleted.', 'polylang' ), 'success' ) );
+ }
+
+ self::redirect(); // To refresh the page ( possible thanks to the $_GET['noheader']=true )
+ break;
+
+ case 'update':
+ check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
+ $errors = $this->model->update_language( $_POST );
+
+ if ( is_wp_error( $errors ) ) {
+ pll_add_notice( $errors );
+ } else {
+ pll_add_notice( new WP_Error( 'pll_languages_updated', __( 'Language updated.', 'polylang' ), 'success' ) );
+ }
+
+ self::redirect(); // To refresh the page ( possible thanks to the $_GET['noheader']=true )
+ break;
+
+ case 'default-lang':
+ check_admin_referer( 'default-lang' );
+
+ if ( $lang = $this->model->get_language( (int) $_GET['lang'] ) ) {
+ $this->model->update_default_lang( $lang->slug );
+ }
+
+ self::redirect(); // To refresh the page ( possible thanks to the $_GET['noheader']=true )
+ break;
+
+ case 'content-default-lang':
+ check_admin_referer( 'content-default-lang' );
+
+ $this->model->set_language_in_mass();
+
+ self::redirect(); // To refresh the page ( possible thanks to the $_GET['noheader']=true )
+ break;
+
+ case 'activate':
+ check_admin_referer( 'pll_activate' );
+ if ( isset( $_GET['module'] ) ) {
+ $module = sanitize_key( $_GET['module'] );
+ if ( isset( $this->modules[ $module ] ) ) {
+ $this->modules[ $module ]->activate();
+ }
+ }
+ self::redirect();
+ break;
+
+ case 'deactivate':
+ check_admin_referer( 'pll_deactivate' );
+ if ( isset( $_GET['module'] ) ) {
+ $module = sanitize_key( $_GET['module'] );
+ if ( isset( $this->modules[ $module ] ) ) {
+ $this->modules[ $module ]->deactivate();
+ }
+ }
+ self::redirect();
+ break;
+
+ default:
+ /**
+ * Fires when a non default action has been sent to Polylang settings
+ *
+ * @since 1.8
+ */
+ do_action( "mlang_action_$action" );
+ break;
+ }
+ }
+
+ /**
+ * Displays the 3 tabs pages: languages, strings translations, settings
+ * Also manages user input for these pages
+ *
+ * @since 0.1
+ *
+ * @return void
+ */
+ public function languages_page() {
+ switch ( $this->active_tab ) {
+ case 'lang':
+ // Prepare the list table of languages
+ $list_table = new PLL_Table_Languages();
+ $list_table->prepare_items( $this->model->get_languages_list() );
+ break;
+
+ case 'strings':
+ $string_table = new PLL_Table_String( $this->model->get_languages_list() );
+ $string_table->prepare_items();
+ break;
+ }
+
+ // Handle user input
+ $action = isset( $_REQUEST['pll_action'] ) ? sanitize_key( $_REQUEST['pll_action'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
+ if ( 'edit' === $action && ! empty( $_GET['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
+ // phpcs:ignore WordPress.Security.NonceVerification, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $edit_lang = $this->model->get_language( (int) $_GET['lang'] );
+ } else {
+ $this->handle_actions( $action );
+ }
+
+ // Displays the page
+ include __DIR__ . '/view-languages.php';
+ }
+
+ /**
+ * Enqueues scripts and styles
+ *
+ * @return void
+ */
+ public function admin_enqueue_scripts() {
+ parent::admin_enqueue_scripts();
+
+ $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
+
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $suffix . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'wp-ajax-response', 'postbox', 'jquery-ui-selectmenu', 'wp-hooks' ), POLYLANG_VERSION, true );
+ wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
+
+ wp_enqueue_style( 'pll_selectmenu', plugins_url( '/css/build/selectmenu' . $suffix . '.css', POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
+ }
+
+ /**
+ * Displays a notice when there are objects with no language assigned
+ *
+ * @since 1.8
+ *
+ * @return void
+ */
+ public function notice_objects_with_no_lang() {
+ if ( ! empty( $this->options['default_lang'] ) && $this->model->get_objects_with_no_lang( 1 ) ) {
+ printf(
+ '',
+ esc_html__( 'There are posts, pages, categories or tags without language.', 'polylang' ),
+ esc_url( wp_nonce_url( '?page=mlang&pll_action=content-default-lang&noheader=true', 'content-default-lang' ) ),
+ esc_html__( 'You can set them all to the default language.', 'polylang' )
+ );
+ }
+ }
+
+ /**
+ * Redirects to language page ( current active tab )
+ * saves error messages in a transient for reuse in redirected page
+ *
+ * @since 1.5
+ *
+ * @param array $args query arguments to add to the url
+ * @return void
+ */
+ public static function redirect( $args = array() ) {
+ $errors = get_settings_errors( 'polylang' );
+ if ( ! empty( $errors ) ) {
+ set_transient( 'settings_errors', $errors, 30 );
+ $args['settings-updated'] = 1;
+ }
+ // Remove possible 'pll_action' and 'lang' query args from the referer before redirecting
+ wp_safe_redirect( add_query_arg( $args, remove_query_arg( array( 'pll_action', 'lang' ), wp_get_referer() ) ) );
+ exit;
+ }
+
+ /**
+ * Get the list of predefined languages
+ *
+ * @since 2.3
+ *
+ * @return string[] {
+ * @type string $code ISO 639-1 language code.
+ * @type string $locale WordPress locale.
+ * @type string $name Native language name.
+ * @type string $dir Text direction: 'ltr' or 'rtl'.
+ * @type string $flag Flag code, generally the country code.
+ * @type string $w3c W3C locale.
+ * @type string $facebook Facebook locale.
+ * }
+ */
+ public static function get_predefined_languages() {
+ require_once ABSPATH . 'wp-admin/includes/translation-install.php';
+
+ $languages = include __DIR__ . '/languages.php';
+ $translations = wp_get_available_translations();
+
+ // Keep only languages with existing WP language pack
+ // Unless the transient has expired and we don't have an internet connection to refresh it
+ if ( ! empty( $translations ) ) {
+ $translations['en_US'] = ''; // Languages packs don't include en_US
+ $languages = array_intersect_key( $languages, $translations );
+ }
+
+ /**
+ * Filter the list of predefined languages
+ *
+ * @since 1.7.10
+ * @since 2.3 The languages arrays use associative keys instead of numerical keys
+ * @see https://github.com/polylang/polylang/blob/2.8.2/settings/languages.php the list of predefined languages
+ *
+ * @param array $languages
+ */
+ $languages = apply_filters( 'pll_predefined_languages', $languages );
+
+ // Keep only languages with all necessary information
+ foreach ( $languages as $k => $lang ) {
+ if ( ! isset( $lang['code'], $lang['locale'], $lang['name'], $lang['dir'], $lang['flag'] ) ) {
+ unset( $languages[ $k ] );
+ }
+ }
+
+ return $languages;
+ }
+}
diff --git a/wp-content/plugins/polylang/settings/table-languages.php b/wp-content/plugins/polylang/settings/table-languages.php
new file mode 100644
index 0000000000..c27b5b9938
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/table-languages.php
@@ -0,0 +1,277 @@
+ 'Languages', // Do not translate ( used for css class )
+ 'ajax' => false,
+ )
+ );
+ }
+
+ /**
+ * Generates content for a single row of the table.
+ *
+ * @since 1.8
+ *
+ * @param PLL_Language $item The language item.
+ * @return void
+ */
+ public function single_row( $item ) {
+ /**
+ * Filter the list of classes assigned a row in the languages list table
+ *
+ * @since 1.8
+ *
+ * @param array $classes The list of class names.
+ * @param PLL_Language $item The language item.
+ */
+ $classes = apply_filters( 'pll_languages_row_classes', array(), $item );
+ echo '' : ' class="' . esc_attr( implode( ' ', $classes ) ) . '">' );
+ $this->single_row_columns( $item );
+ echo ' ';
+ }
+
+ /**
+ * Displays the item information in a column ( default case ).
+ *
+ * @since 0.1
+ *
+ * @param PLL_Language $item The language item.
+ * @param string $column_name The column name.
+ * @return string|int
+ */
+ public function column_default( $item, $column_name ) {
+ switch ( $column_name ) {
+ case 'locale':
+ case 'slug':
+ return esc_html( $item->$column_name );
+
+ case 'term_group':
+ return (int) $item->$column_name;
+
+ case 'count':
+ return $item->get_tax_prop( 'language', $column_name );
+
+ default:
+ return $item->$column_name; // Flag.
+ }
+ }
+
+ /**
+ * Displays the item information in the column 'name'
+ * Displays the edit and delete action links
+ *
+ * @since 0.1
+ *
+ * @param PLL_Language $item The language item.
+ * @return string
+ */
+ public function column_name( $item ) {
+ return sprintf(
+ '%s ',
+ esc_attr__( 'Edit this language', 'polylang' ),
+ esc_url( admin_url( 'admin.php?page=mlang&pll_action=edit&lang=' . $item->term_id ) ),
+ esc_html( $item->name )
+ );
+ }
+
+ /**
+ * Displays the item information in the default language
+ * Displays the 'make default' action link
+ *
+ * @since 1.8
+ *
+ * @param PLL_Language $item The language item.
+ * @return string
+ */
+ public function column_default_lang( $item ) {
+ if ( ! $item->is_default ) {
+ $s = sprintf(
+ '',
+ esc_attr__( 'Select as default language', 'polylang' ),
+ wp_nonce_url( '?page=mlang&pll_action=default-lang&noheader=true&lang=' . $item->term_id, 'default-lang' ),
+ /* translators: accessibility text, %s is a native language name */
+ esc_html( sprintf( __( 'Choose %s as default language', 'polylang' ), $item->name ) )
+ );
+
+ /**
+ * Filters the default language row action in the languages list table.
+ *
+ * @since 1.8
+ *
+ * @param string $s The html markup of the action.
+ * @param PLL_Language $item The language item.
+ */
+ $s = apply_filters( 'pll_default_lang_row_action', $s, $item );
+ } else {
+ $s = sprintf(
+ '%1$s ',
+ /* translators: accessibility text */
+ esc_html__( 'Default language', 'polylang' )
+ );
+ }
+
+ return $s;
+ }
+
+ /**
+ * Gets the list of columns
+ *
+ * @since 0.1
+ *
+ * @return string[] The list of column titles.
+ */
+ public function get_columns() {
+ return array(
+ 'name' => esc_html__( 'Full name', 'polylang' ),
+ 'locale' => esc_html__( 'Locale', 'polylang' ),
+ 'slug' => esc_html__( 'Code', 'polylang' ),
+ 'default_lang' => sprintf( '%2$s ', esc_attr__( 'Default language', 'polylang' ), esc_html__( 'Default language', 'polylang' ) ),
+ 'term_group' => esc_html__( 'Order', 'polylang' ),
+ 'flag' => esc_html__( 'Flag', 'polylang' ),
+ 'count' => esc_html__( 'Posts', 'polylang' ),
+ );
+ }
+
+ /**
+ * Gets the list of sortable columns
+ *
+ * @since 0.1
+ *
+ * @return array
+ */
+ public function get_sortable_columns() {
+ return array(
+ 'name' => array( 'name', true ), // sorted by name by default
+ 'locale' => array( 'locale', false ),
+ 'slug' => array( 'slug', false ),
+ 'term_group' => array( 'term_group', false ),
+ 'count' => array( 'count', false ),
+ );
+ }
+
+ /**
+ * Gets the name of the default primary column.
+ *
+ * @since 2.1
+ *
+ * @return string Name of the default primary column, in this case, 'name'.
+ */
+ protected function get_default_primary_column_name() {
+ return 'name';
+ }
+
+ /**
+ * Generates and display row actions links for the list table.
+ *
+ * @since 1.8
+ *
+ * @param PLL_Language $item The language item being acted upon.
+ * @param string $column_name Current column name.
+ * @param string $primary Primary column name.
+ * @return string The row actions output.
+ */
+ protected function handle_row_actions( $item, $column_name, $primary ) {
+ if ( $primary !== $column_name ) {
+ return '';
+ }
+
+ $actions = array(
+ 'edit' => sprintf(
+ '%s ',
+ esc_attr__( 'Edit this language', 'polylang' ),
+ esc_url( admin_url( 'admin.php?page=mlang&pll_action=edit&lang=' . $item->term_id ) ),
+ esc_html__( 'Edit', 'polylang' )
+ ),
+ 'delete' => sprintf(
+ '%s ',
+ esc_attr__( 'Delete this language and all its associated data', 'polylang' ),
+ wp_nonce_url( '?page=mlang&pll_action=delete&noheader=true&lang=' . $item->term_id, 'delete-lang' ),
+ esc_js( __( 'You are about to permanently delete this language. Are you sure?', 'polylang' ) ),
+ esc_html__( 'Delete', 'polylang' )
+ ),
+ );
+
+ /**
+ * Filters the list of row actions in the languages list table.
+ *
+ * @since 1.8
+ *
+ * @param array $actions A list of html markup actions.
+ * @param PLL_Language $item The language item.
+ */
+ $actions = apply_filters( 'pll_languages_row_actions', $actions, $item );
+
+ return $this->row_actions( $actions );
+ }
+
+ /**
+ * Sorts language items.
+ *
+ * @since 0.1
+ *
+ * @param PLL_Language $a The first language to compare.
+ * @param PLL_Language $b The second language to compare.
+ * @return int -1 or 1 if $a is considered to be respectively less than or greater than $b.
+ */
+ protected function usort_reorder( $a, $b ) {
+ $orderby = ! empty( $_GET['orderby'] ) ? sanitize_key( $_GET['orderby'] ) : 'name'; // phpcs:ignore WordPress.Security.NonceVerification
+ // Determine sort order
+ if ( is_numeric( $a->$orderby ) ) {
+ $result = $a->$orderby > $b->$orderby ? 1 : -1;
+ } else {
+ $result = strcmp( $a->$orderby, $b->$orderby );
+ }
+ // Send final sort direction to usort.
+ return ( empty( $_GET['order'] ) || 'asc' === $_GET['order'] ) ? $result : -$result; // phpcs:ignore WordPress.Security.NonceVerification
+ }
+
+ /**
+ * Prepares the list of languages for display.
+ *
+ * @since 0.1
+ *
+ * @param PLL_Language[] $data The list of languages.
+ * @return void
+ */
+ public function prepare_items( $data = array() ) {
+ $per_page = $this->get_items_per_page( 'pll_lang_per_page' );
+ $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
+
+ usort( $data, array( $this, 'usort_reorder' ) );
+
+ $total_items = count( $data );
+ $this->items = array_slice( $data, ( $this->get_pagenum() - 1 ) * $per_page, $per_page );
+
+ $this->set_pagination_args(
+ array(
+ 'total_items' => $total_items,
+ 'per_page' => $per_page,
+ 'total_pages' => (int) ceil( $total_items / $per_page ),
+ )
+ );
+ }
+}
diff --git a/wp-content/plugins/polylang/settings/table-settings.php b/wp-content/plugins/polylang/settings/table-settings.php
new file mode 100644
index 0000000000..e11a89e6b8
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/table-settings.php
@@ -0,0 +1,194 @@
+ 'Settings', // Do not translate ( used for css class )
+ 'ajax' => false,
+ )
+ );
+ }
+
+ /**
+ * Get the table classes for styling
+ *
+ * @since 1.8
+ */
+ protected function get_table_classes() {
+ return array( 'wp-list-table', 'widefat', 'plugins', 'pll-settings' ); // get the style of the plugins list table + one specific class
+ }
+
+ /**
+ * Displays a single row.
+ *
+ * @since 1.8
+ *
+ * @param PLL_Settings_Module $item Settings module item.
+ * @return void
+ */
+ public function single_row( $item ) {
+ // Classes to reuse css from the plugins list table
+ $classes = $item->is_active() ? 'active' : 'inactive';
+ if ( $message = $item->get_upgrade_message() ) {
+ $classes .= ' update';
+ }
+
+ // Display the columns
+ printf( '', esc_attr( $item->module ), esc_attr( $classes ) );
+ $this->single_row_columns( $item );
+ echo ' ';
+
+ // Display an upgrade message if there is any, reusing css from the plugins updates
+ if ( $message = $item->get_upgrade_message() ) {
+ printf(
+ '
+ %s
+ ',
+ sprintf( '', $message ) // phpcs:ignore WordPress.Security.EscapeOutput
+ );
+ }
+
+ // The settings if there are
+ // "inactive" class to reuse css from the plugins list table
+ if ( $form = $item->get_form() ) {
+ printf(
+ '
+
+ %s
+ %s
+
+ %s
+
+
+ ',
+ esc_attr( $item->module ),
+ esc_html( $item->title ),
+ $form, // phpcs:ignore
+ implode( $item->get_buttons() ) // phpcs:ignore
+ );
+ }
+ }
+
+ /**
+ * Generates the columns for a single row of the table.
+ *
+ * @since 1.8
+ *
+ * @param PLL_Settings_Module $item Settings module item.
+ * @return void
+ */
+ protected function single_row_columns( $item ) {
+ $column_info = $this->get_column_info();
+ $columns = $column_info[0];
+ $primary = $column_info[3];
+
+ foreach ( array_keys( $columns ) as $column_name ) {
+ $classes = "$column_name column-$column_name";
+ if ( $primary === $column_name ) {
+ $classes .= ' column-primary';
+ }
+
+ if ( 'cb' == $column_name ) {
+ echo '';
+ echo $this->column_cb( $item ); // phpcs:ignore WordPress.Security.EscapeOutput
+ echo ' ';
+ } else {
+ printf( '', esc_attr( $classes ) );
+ echo $this->column_default( $item, $column_name ); // phpcs:ignore WordPress.Security.EscapeOutput
+ echo ' ';
+ }
+ }
+ }
+
+ /**
+ * Displays the item information in a column (default case).
+ *
+ * @since 1.8
+ *
+ * @param PLL_Settings_Module $item Settings module item.
+ * @param string $column_name Column name.
+ * @return string The column name.
+ */
+ protected function column_default( $item, $column_name ) {
+ if ( 'plugin-title' == $column_name ) {
+ return sprintf( '%s ', esc_html( $item->title ) ) . $this->row_actions( $item->get_action_links(), true /*always visible*/ );
+ }
+ return $item->$column_name;
+ }
+
+ /**
+ * Gets the list of columns.
+ *
+ * @since 1.8
+ *
+ * @return string[] The list of column titles.
+ */
+ public function get_columns() {
+ return array(
+ 'cb' => '', // For the 4px border inherited from plugins when the module is activated
+ 'plugin-title' => esc_html__( 'Module', 'polylang' ), // plugin-title for styling
+ 'description' => esc_html__( 'Description', 'polylang' ),
+ );
+ }
+
+ /**
+ * Gets the name of the primary column.
+ *
+ * @since 1.8
+ *
+ * @return string The name of the primary column.
+ */
+ protected function get_primary_column_name() {
+ return 'plugin-title';
+ }
+
+ /**
+ * Prepares the list of items for displaying
+ *
+ * @since 1.8
+ *
+ * @param PLL_Settings_Module[] $items Array of settings module items.
+ * @return void
+ */
+ public function prepare_items( $items = array() ) {
+ $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns(), $this->get_primary_column_name() );
+
+ // Sort rows, lowest priority on top.
+ usort(
+ $items,
+ function ( $a, $b ) {
+ return $a->priority > $b->priority ? 1 : -1;
+ }
+ );
+ $this->items = $items;
+ }
+
+ /**
+ * Avoids displaying an empty tablenav
+ *
+ * @since 2.1
+ *
+ * @param string $which 'top' or 'bottom'
+ * @return void
+ */
+ protected function display_tablenav( $which ) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+}
diff --git a/wp-content/plugins/polylang/settings/table-string.php b/wp-content/plugins/polylang/settings/table-string.php
new file mode 100644
index 0000000000..64ef0c1a06
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/table-string.php
@@ -0,0 +1,449 @@
+ 'Strings translations', // Do not translate ( used for css class )
+ 'ajax' => false,
+ )
+ );
+
+ $this->languages = $languages;
+ $this->strings = PLL_Admin_Strings::get_strings();
+ $this->groups = array_unique( wp_list_pluck( $this->strings, 'context' ) );
+
+ $this->selected_group = -1;
+
+ if ( ! empty( $_GET['group'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
+ $group = sanitize_text_field( wp_unslash( $_GET['group'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
+ if ( in_array( $group, $this->groups ) ) {
+ $this->selected_group = $group;
+ }
+ }
+
+ add_action( 'mlang_action_string-translation', array( $this, 'save_translations' ) );
+ }
+
+ /**
+ * Displays the item information in a column (default case).
+ *
+ * @since 0.6
+ *
+ * @param array $item Data related to the current string.
+ * @param string $column_name The current column name.
+ * @return string
+ */
+ public function column_default( $item, $column_name ) {
+ return $item[ $column_name ];
+ }
+
+ /**
+ * Displays the checkbox in first column.
+ *
+ * @since 1.1
+ *
+ * @param array $item Data related to the current string.
+ * @return string
+ */
+ public function column_cb( $item ) {
+ return sprintf(
+ '%2$s ',
+ esc_attr( $item['row'] ),
+ /* translators: accessibility text, %s is a string potentially in any language */
+ sprintf( __( 'Select %s', 'polylang' ), format_to_edit( $item['string'] ) ),
+ empty( $item['icl'] ) ? 'disabled' : '' // Only strings registered with WPML API can be removed.
+ );
+ }
+
+ /**
+ * Displays the string to translate.
+ *
+ * @since 1.0
+ *
+ * @param array $item Data related to the current string.
+ * @return string
+ */
+ public function column_string( $item ) {
+ return format_to_edit( $item['string'] ); // Don't interpret special chars for the string column.
+ }
+
+ /**
+ * Displays the translations to edit.
+ *
+ * @since 0.6
+ *
+ * @param array $item Data related to the current string.
+ * @return string
+ */
+ public function column_translations( $item ) {
+ $out = '';
+ $languages = array();
+
+ foreach ( $this->languages as $language ) {
+ $languages[ $language->slug ] = $language->name;
+ }
+
+ foreach ( $item['translations'] as $key => $translation ) {
+ $input_type = $item['multiline'] ?
+ '' :
+ ' ';
+ $out .= sprintf(
+ '%3$s ' . $input_type . '
' . "\n",
+ esc_attr( $key ),
+ esc_attr( $item['row'] ),
+ esc_html( $languages[ $key ] ),
+ format_to_edit( $translation ) // Don't interpret special chars.
+ );
+ }
+
+ return $out;
+ }
+
+ /**
+ * Gets the list of columns.
+ *
+ * @since 0.6
+ *
+ * @return string[] The list of column titles.
+ */
+ public function get_columns() {
+ return array(
+ 'cb' => ' ', // Checkbox.
+ 'string' => esc_html__( 'String', 'polylang' ),
+ 'name' => esc_html__( 'Name', 'polylang' ),
+ 'context' => esc_html__( 'Group', 'polylang' ),
+ 'translations' => esc_html__( 'Translations', 'polylang' ),
+ );
+ }
+
+ /**
+ * Gets the list of sortable columns
+ *
+ * @since 0.6
+ *
+ * @return array
+ */
+ public function get_sortable_columns() {
+ return array(
+ 'string' => array( 'string', false ),
+ 'name' => array( 'name', false ),
+ 'context' => array( 'context', false ),
+ );
+ }
+
+ /**
+ * Gets the name of the default primary column.
+ *
+ * @since 2.1
+ *
+ * @return string Name of the default primary column, in this case, 'string'.
+ */
+ protected function get_default_primary_column_name() {
+ return 'string';
+ }
+
+ /**
+ * Search for a string in translations. Case insensitive.
+ *
+ * @since 2.6
+ *
+ * @param PLL_MO[] $mos An array of PLL_MO objects.
+ * @param string $s Searched string.
+ * @return string[] Found strings.
+ */
+ protected function search_in_translations( $mos, $s ) {
+ $founds = array();
+
+ foreach ( $mos as $mo ) {
+ foreach ( wp_list_pluck( $mo->entries, 'translations' ) as $string => $translation ) {
+ if ( false !== stripos( $translation[0], $s ) ) {
+ $founds[] = $string;
+ }
+ }
+ }
+
+ return array_unique( $founds );
+ }
+
+ /**
+ * Sorts registered string items.
+ *
+ * @since 0.6
+ *
+ * @param array $a The first item to compare.
+ * @param array $b The second item to compare.
+ * @return int -1 or 1 if $a is considered to be respectively less than or greater than $b.
+ */
+ protected function usort_reorder( $a, $b ) {
+ if ( ! empty( $_GET['orderby'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
+ $orderby = sanitize_key( $_GET['orderby'] ); // phpcs:ignore WordPress.Security.NonceVerification
+ if ( isset( $a[ $orderby ], $b[ $orderby ] ) ) {
+ $result = strcmp( $a[ $orderby ], $b[ $orderby ] ); // Determine sort order
+ return ( empty( $_GET['order'] ) || 'asc' === $_GET['order'] ) ? $result : -$result; // phpcs:ignore WordPress.Security.NonceVerification
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Prepares the list of registered strings for display.
+ *
+ * @since 0.6
+ *
+ * @return void
+ */
+ public function prepare_items() {
+ // Is admin language filter active?
+ if ( $lg = get_user_meta( get_current_user_id(), 'pll_filter_content', true ) ) {
+ $languages = wp_list_filter( $this->languages, array( 'slug' => $lg ) );
+ } else {
+ $languages = $this->languages;
+ }
+
+ // Load translations
+ $mo = array();
+ foreach ( $languages as $language ) {
+ $mo[ $language->slug ] = new PLL_MO();
+ $mo[ $language->slug ]->import_from_db( $language );
+ }
+
+ $data = $this->strings;
+
+ // Filter by selected group
+ if ( -1 !== $this->selected_group ) {
+ $data = wp_list_filter( $data, array( 'context' => $this->selected_group ) );
+ }
+
+ // Filter by searched string
+ $s = empty( $_GET['s'] ) ? '' : wp_unslash( $_GET['s'] ); // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
+
+ if ( ! empty( $s ) ) {
+ // Search in translations
+ $in_translations = $this->search_in_translations( $mo, $s );
+
+ foreach ( $data as $key => $row ) {
+ if ( stripos( $row['name'], $s ) === false && stripos( $row['string'], $s ) === false && ! in_array( $row['string'], $in_translations ) ) {
+ unset( $data[ $key ] );
+ }
+ }
+ }
+
+ // Sorting
+ uasort( $data, array( $this, 'usort_reorder' ) );
+
+ // Paging
+ $per_page = $this->get_items_per_page( 'pll_strings_per_page' );
+ $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
+
+ $total_items = count( $data );
+ $this->items = array_slice( $data, ( $this->get_pagenum() - 1 ) * $per_page, $per_page, true );
+
+ $this->set_pagination_args(
+ array(
+ 'total_items' => $total_items,
+ 'per_page' => $per_page,
+ 'total_pages' => (int) ceil( $total_items / $per_page ),
+ )
+ );
+
+ // Translate strings
+ // Kept for the end as it is a slow process
+ foreach ( $languages as $language ) {
+ foreach ( $this->items as $key => $row ) {
+ $this->items[ $key ]['translations'][ $language->slug ] = $mo[ $language->slug ]->translate( $row['string'] );
+ $this->items[ $key ]['row'] = $key; // Store the row number for convenience
+ }
+ }
+ }
+
+ /**
+ * Get the list of possible bulk actions.
+ *
+ * @since 1.1
+ *
+ * @return string[] Array of bulk actions.
+ */
+ public function get_bulk_actions() {
+ return array( 'delete' => __( 'Delete', 'polylang' ) );
+ }
+
+ /**
+ * Get the current action selected from the bulk actions dropdown.
+ * overrides parent function to avoid submit button to trigger bulk actions
+ *
+ * @since 1.8
+ *
+ * @return string|false The action name or False if no action was selected
+ */
+ public function current_action() {
+ return empty( $_POST['submit'] ) ? parent::current_action() : false; // phpcs:ignore WordPress.Security.NonceVerification
+ }
+
+ /**
+ * Displays the dropdown list to filter strings per group
+ *
+ * @since 1.1
+ *
+ * @param string $which only 'top' is supported
+ * @return void
+ */
+ public function extra_tablenav( $which ) {
+ if ( 'top' !== $which ) {
+ return;
+ }
+
+ echo '';
+ printf(
+ '%s ',
+ /* translators: accessibility text */
+ esc_html__( 'Filter by group', 'polylang' )
+ );
+ echo '' . "\n";
+ printf(
+ '%s ' . "\n",
+ selected( $this->selected_group, -1, false ),
+ esc_html__( 'View all groups', 'polylang' )
+ );
+
+ foreach ( $this->groups as $group ) {
+ printf(
+ '%s ' . "\n",
+ esc_attr( urlencode( $group ) ),
+ selected( $this->selected_group, $group, false ),
+ esc_html( $group )
+ );
+ }
+ echo ' ' . "\n";
+
+ submit_button( __( 'Filter', 'polylang' ), 'button', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
+ echo '
';
+ }
+
+ /**
+ * Saves the strings translations in DB
+ * Optionally clean the DB
+ *
+ * @since 1.9
+ *
+ * @return void
+ */
+ public function save_translations() {
+ check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
+
+ if ( ! empty( $_POST['submit'] ) ) {
+ foreach ( $this->languages as $language ) {
+ if ( empty( $_POST['translation'][ $language->slug ] ) || ! is_array( $_POST['translation'][ $language->slug ] ) ) { // In case the language filter is active ( thanks to John P. Bloch )
+ continue;
+ }
+
+ $translations = array_map( 'trim', (array) wp_unslash( $_POST['translation'][ $language->slug ] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+
+ $mo = new PLL_MO();
+ $mo->import_from_db( $language );
+
+ foreach ( $translations as $key => $translation ) {
+ /**
+ * Filter the string translation before it is saved in DB
+ * Allows to sanitize strings registered with pll_register_string
+ *
+ * @since 1.6
+ * @since 2.7 The translation passed to the filter is unslashed.
+ *
+ * @param string $translation The string translation.
+ * @param string $name The name as defined in pll_register_string.
+ * @param string $context The context as defined in pll_register_string.
+ */
+ $translation = apply_filters( 'pll_sanitize_string_translation', $translation, $this->strings[ $key ]['name'], $this->strings[ $key ]['context'] );
+ $mo->add_entry( $mo->make_entry( $this->strings[ $key ]['string'], $translation ) );
+ }
+
+ // Clean database ( removes all strings which were registered some day but are no more )
+ if ( ! empty( $_POST['clean'] ) ) {
+ $new_mo = new PLL_MO();
+
+ foreach ( $this->strings as $string ) {
+ $new_mo->add_entry( $mo->make_entry( $string['string'], $mo->translate( $string['string'] ) ) );
+ }
+ }
+
+ isset( $new_mo ) ? $new_mo->export_to_db( $language ) : $mo->export_to_db( $language );
+ }
+
+ pll_add_notice( new WP_Error( 'pll_strings_translations_updated', __( 'Translations updated.', 'polylang' ), 'success' ) );
+
+ /**
+ * Fires after the strings translations are saved in DB
+ *
+ * @since 1.2
+ */
+ do_action( 'pll_save_strings_translations' );
+ }
+
+ // Unregisters strings registered through WPML API
+ if ( $this->current_action() === 'delete' && ! empty( $_POST['strings'] ) && function_exists( 'icl_unregister_string' ) ) {
+ foreach ( array_map( 'sanitize_key', $_POST['strings'] ) as $key ) {
+ icl_unregister_string( $this->strings[ $key ]['context'], $this->strings[ $key ]['name'] );
+ }
+ }
+
+ // To refresh the page ( possible thanks to the $_GET['noheader']=true )
+ $args = array_intersect_key( $_REQUEST, array_flip( array( 's', 'paged', 'group' ) ) );
+ if ( ! empty( $_GET['paged'] ) && ! empty( $_POST['submit'] ) ) {
+ $args['paged'] = (int) $_GET['paged']; // Don't rely on $_REQUEST['paged'] or $_POST['paged']. See #14
+ }
+ if ( ! empty( $args['s'] ) ) {
+ $args['s'] = urlencode( $args['s'] ); // Searched string needs to be encoded as it comes from $_POST
+ }
+ PLL_Settings::redirect( $args );
+ }
+}
diff --git a/wp-content/plugins/polylang/settings/view-about.php b/wp-content/plugins/polylang/settings/view-about.php
new file mode 100644
index 0000000000..9bad4e9b88
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/view-about.php
@@ -0,0 +1,30 @@
+
+
+ ',
+ '
'
+ );
+ if ( ! defined( 'POLYLANG_PRO' ) ) {
+ echo ' ';
+ printf(
+ /* translators: %1$s is link start tag, %2$s is link end tag. */
+ esc_html__( 'Support and extra features are available to %1$sPolylang Pro%2$s users.', 'polylang' ),
+ '',
+ ' '
+ );
+ }
+ ?>
+
diff --git a/wp-content/plugins/polylang/settings/view-languages.php b/wp-content/plugins/polylang/settings/view-languages.php
new file mode 100644
index 0000000000..d7e5e52aaa
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/view-languages.php
@@ -0,0 +1,35 @@
+
+
+
+ active_tab ) {
+ case 'lang': // Languages tab
+ case 'strings': // String translations tab
+ case 'settings': // Settings tab
+ include __DIR__ . '/view-tab-' . $this->active_tab . '.php';
+ // Intentional fall-through to upgrade to fire the action below.
+
+ default:
+ /**
+ * Fires when loading the active Polylang settings tab
+ * Allows plugins to add their own tab
+ *
+ * @since 1.5.1
+ */
+ do_action( 'pll_settings_active_tab_' . $this->active_tab );
+ break;
+ }
+ ?>
+
diff --git a/wp-content/plugins/polylang/settings/view-tab-lang.php b/wp-content/plugins/polylang/settings/view-tab-lang.php
new file mode 100644
index 0000000000..c2fa7ecb42
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/view-tab-lang.php
@@ -0,0 +1,178 @@
+
+
+
+
+ display();
+ ?>
+
+
+
+
+
+
+
+
diff --git a/wp-content/plugins/polylang/settings/view-tab-settings.php b/wp-content/plugins/polylang/settings/view-tab-settings.php
new file mode 100644
index 0000000000..77571dd448
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/view-tab-settings.php
@@ -0,0 +1,19 @@
+
+
+ prepare_items( $this->modules );
+ $list_table->display();
+ ?>
+
diff --git a/wp-content/plugins/polylang/settings/view-tab-strings.php b/wp-content/plugins/polylang/settings/view-tab-strings.php
new file mode 100644
index 0000000000..f303c8f4a0
--- /dev/null
+++ b/wp-content/plugins/polylang/settings/view-tab-strings.php
@@ -0,0 +1,33 @@
+
+
diff --git a/wp-content/plugins/polylang/uninstall.php b/wp-content/plugins/polylang/uninstall.php
new file mode 100644
index 0000000000..c2367fde98
--- /dev/null
+++ b/wp-content/plugins/polylang/uninstall.php
@@ -0,0 +1,136 @@
+get_col( "SELECT blog_id FROM $wpdb->blogs" ) as $blog_id ) {
+ switch_to_blog( $blog_id );
+ $this->uninstall();
+ }
+ restore_current_blog();
+ }
+ else {
+ $this->uninstall();
+ }
+ }
+
+ /**
+ * Removes ALL plugin data
+ * only when the relevant option is active
+ *
+ * @since 0.5
+ */
+ public function uninstall() {
+ global $wpdb;
+
+ do_action( 'pll_uninstall' );
+
+ // Need to register the taxonomies
+ $pll_taxonomies = array( 'language', 'term_language', 'post_translations', 'term_translations' );
+ foreach ( $pll_taxonomies as $taxonomy ) {
+ register_taxonomy( $taxonomy, null, array( 'label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false ) );
+ }
+
+ $languages = get_terms( array( 'taxonomy' => 'language', 'hide_empty' => false ) );
+
+ // Delete users options
+ delete_metadata( 'user', 0, 'pll_filter_content', '', true );
+ delete_metadata( 'user', 0, 'pll_dismissed_notices', '', true ); // Legacy meta.
+ foreach ( $languages as $lang ) {
+ delete_metadata( 'user', 0, "description_{$lang->slug}", '', true );
+ }
+
+ // Delete menu language switchers
+ $ids = get_posts(
+ array(
+ 'post_type' => 'nav_menu_item',
+ 'numberposts' => -1,
+ 'nopaging' => true,
+ 'fields' => 'ids',
+ 'meta_key' => '_pll_menu_item',
+ )
+ );
+
+ foreach ( $ids as $id ) {
+ wp_delete_post( $id, true );
+ }
+
+ /*
+ * Backward compatibility with Polylang < 3.4.
+ * Delete the legacy strings translations.
+ */
+ register_post_type( 'polylang_mo', array( 'rewrite' => false, 'query_var' => false ) );
+ $ids = get_posts(
+ array(
+ 'post_type' => 'polylang_mo',
+ 'post_status' => 'any',
+ 'numberposts' => -1,
+ 'nopaging' => true,
+ 'fields' => 'ids',
+ )
+ );
+ foreach ( $ids as $id ) {
+ wp_delete_post( $id, true );
+ }
+
+ // Delete all what is related to languages and translations
+ $term_ids = array();
+ $tt_ids = array();
+
+ foreach ( get_terms( array( 'taxonomy' => $pll_taxonomies, 'hide_empty' => false ) ) as $term ) {
+ $term_ids[] = (int) $term->term_id;
+ $tt_ids[] = (int) $term->term_taxonomy_id;
+ }
+
+ if ( ! empty( $term_ids ) ) {
+ $term_ids = array_unique( $term_ids );
+ $wpdb->query( "DELETE FROM {$wpdb->terms} WHERE term_id IN ( " . implode( ',', $term_ids ) . ' )' ); // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $wpdb->query( "DELETE FROM {$wpdb->term_taxonomy} WHERE term_id IN ( " . implode( ',', $term_ids ) . ' )' ); // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $wpdb->query( "DELETE FROM {$wpdb->termmeta} WHERE term_id IN ( " . implode( ',', $term_ids ) . " ) AND meta_key='_pll_strings_translations'" ); // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
+ }
+
+ if ( ! empty( $tt_ids ) ) {
+ $tt_ids = array_unique( $tt_ids );
+ $wpdb->query( "DELETE FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ( " . implode( ',', $tt_ids ) . ' )' ); // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
+ }
+
+ // Delete options
+ delete_option( 'polylang' );
+ delete_option( 'widget_polylang' ); // Automatically created by WP
+ delete_option( 'polylang_wpml_strings' ); // Strings registered with icl_register_string
+ delete_option( 'polylang_licenses' );
+ delete_option( 'pll_dismissed_notices' );
+
+ // Delete transients
+ delete_transient( 'pll_languages_list' );
+ }
+}
+
+new PLL_Uninstall();
diff --git a/wp-content/plugins/polylang/vendor/autoload.php b/wp-content/plugins/polylang/vendor/autoload.php
new file mode 100644
index 0000000000..4d7c2db8a7
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/autoload.php
@@ -0,0 +1,25 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ /** @var \Closure(string):void */
+ private static $includeFile;
+
+ /** @var string|null */
+ private $vendorDir;
+
+ // PSR-4
+ /**
+ * @var array>
+ */
+ private $prefixLengthsPsr4 = array();
+ /**
+ * @var array>
+ */
+ private $prefixDirsPsr4 = array();
+ /**
+ * @var list
+ */
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ /**
+ * List of PSR-0 prefixes
+ *
+ * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
+ *
+ * @var array>>
+ */
+ private $prefixesPsr0 = array();
+ /**
+ * @var list
+ */
+ private $fallbackDirsPsr0 = array();
+
+ /** @var bool */
+ private $useIncludePath = false;
+
+ /**
+ * @var array
+ */
+ private $classMap = array();
+
+ /** @var bool */
+ private $classMapAuthoritative = false;
+
+ /**
+ * @var array
+ */
+ private $missingClasses = array();
+
+ /** @var string|null */
+ private $apcuPrefix;
+
+ /**
+ * @var array
+ */
+ private static $registeredLoaders = array();
+
+ /**
+ * @param string|null $vendorDir
+ */
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ self::initializeIncludeClosure();
+ }
+
+ /**
+ * @return array>
+ */
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ /**
+ * @return array>
+ */
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ /**
+ * @return list
+ */
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ /**
+ * @return list
+ */
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ /**
+ * @return array Array of classname => path
+ */
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ *
+ * @return void
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list|string $paths The PSR-0 base directories
+ *
+ * @return void
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ *
+ * @return void
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ *
+ * @return void
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ *
+ * @return void
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ *
+ * @return void
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return true|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ $includeFile = self::$includeFile;
+ $includeFile($file);
+
+ return true;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders keyed by their corresponding vendor directories.
+ *
+ * @return array
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return void
+ */
+ private static function initializeIncludeClosure()
+ {
+ if (self::$includeFile !== null) {
+ return;
+ }
+
+ /**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ */
+ self::$includeFile = \Closure::bind(static function($file) {
+ include $file;
+ }, null, null);
+ }
+}
diff --git a/wp-content/plugins/polylang/vendor/composer/InstalledVersions.php b/wp-content/plugins/polylang/vendor/composer/InstalledVersions.php
new file mode 100644
index 0000000000..51e734a774
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/composer/InstalledVersions.php
@@ -0,0 +1,359 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ *
+ * @final
+ */
+class InstalledVersions
+{
+ /**
+ * @var mixed[]|null
+ * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null
+ */
+ private static $installed;
+
+ /**
+ * @var bool|null
+ */
+ private static $canGetVendors;
+
+ /**
+ * @var array[]
+ * @psalm-var array}>
+ */
+ private static $installedByVendor = array();
+
+ /**
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
+ *
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackages()
+ {
+ $packages = array();
+ foreach (self::getInstalled() as $installed) {
+ $packages[] = array_keys($installed['versions']);
+ }
+
+ if (1 === \count($packages)) {
+ return $packages[0];
+ }
+
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+ }
+
+ /**
+ * Returns a list of all package names with a specific type e.g. 'library'
+ *
+ * @param string $type
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackagesByType($type)
+ {
+ $packagesByType = array();
+
+ foreach (self::getInstalled() as $installed) {
+ foreach ($installed['versions'] as $name => $package) {
+ if (isset($package['type']) && $package['type'] === $type) {
+ $packagesByType[] = $name;
+ }
+ }
+ }
+
+ return $packagesByType;
+ }
+
+ /**
+ * Checks whether the given package is installed
+ *
+ * This also returns true if the package name is provided or replaced by another package
+ *
+ * @param string $packageName
+ * @param bool $includeDevRequirements
+ * @return bool
+ */
+ public static function isInstalled($packageName, $includeDevRequirements = true)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (isset($installed['versions'][$packageName])) {
+ return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package satisfies a version constraint
+ *
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+ *
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+ *
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
+ * @param string $packageName
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+ * @return bool
+ */
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
+ {
+ $constraint = $parser->parseConstraints((string) $constraint);
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+ return $provided->matches($constraint);
+ }
+
+ /**
+ * Returns a version constraint representing all the range(s) which are installed for a given package
+ *
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+ * whether a given version of a package is installed, and not just whether it exists
+ *
+ * @param string $packageName
+ * @return string Version constraint usable with composer/semver
+ */
+ public static function getVersionRanges($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ $ranges = array();
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+ }
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+ }
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+ }
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+ }
+
+ return implode(' || ', $ranges);
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getPrettyVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['pretty_version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+ */
+ public static function getReference($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['reference'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['reference'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+ */
+ public static function getInstallPath($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @return array
+ * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
+ */
+ public static function getRootPackage()
+ {
+ $installed = self::getInstalled();
+
+ return $installed[0]['root'];
+ }
+
+ /**
+ * Returns the raw installed.php data for custom implementations
+ *
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+ * @return array[]
+ * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}
+ */
+ public static function getRawData()
+ {
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = include __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ return self::$installed;
+ }
+
+ /**
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
+ *
+ * @return array[]
+ * @psalm-return list}>
+ */
+ public static function getAllRawData()
+ {
+ return self::getInstalled();
+ }
+
+ /**
+ * Lets you reload the static array from another file
+ *
+ * This is only useful for complex integrations in which a project needs to use
+ * this class but then also needs to execute another project's autoloader in process,
+ * and wants to ensure both projects have access to their version of installed.php.
+ *
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
+ * the data it needs from this class, then call reload() with
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+ * the project in which it runs can then also use this class safely, without
+ * interference between PHPUnit's dependencies and the project's dependencies.
+ *
+ * @param array[] $data A vendor/composer/installed.php data set
+ * @return void
+ *
+ * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data
+ */
+ public static function reload($data)
+ {
+ self::$installed = $data;
+ self::$installedByVendor = array();
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return list}>
+ */
+ private static function getInstalled()
+ {
+ if (null === self::$canGetVendors) {
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+ }
+
+ $installed = array();
+
+ if (self::$canGetVendors) {
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ if (isset(self::$installedByVendor[$vendorDir])) {
+ $installed[] = self::$installedByVendor[$vendorDir];
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */
+ $required = require $vendorDir.'/composer/installed.php';
+ $installed[] = self::$installedByVendor[$vendorDir] = $required;
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+ self::$installed = $installed[count($installed) - 1];
+ }
+ }
+ }
+ }
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */
+ $required = require __DIR__ . '/installed.php';
+ self::$installed = $required;
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ if (self::$installed !== array()) {
+ $installed[] = self::$installed;
+ }
+
+ return $installed;
+ }
+}
diff --git a/wp-content/plugins/polylang/vendor/composer/LICENSE b/wp-content/plugins/polylang/vendor/composer/LICENSE
new file mode 100644
index 0000000000..f27399a042
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/wp-content/plugins/polylang/vendor/composer/autoload_classmap.php b/wp-content/plugins/polylang/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000000..e17efd17b5
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/composer/autoload_classmap.php
@@ -0,0 +1,140 @@
+ $vendorDir . '/composer/InstalledVersions.php',
+ 'PLL_AS3CF' => $baseDir . '/integrations/wp-offload-media/as3cf.php',
+ 'PLL_Abstract_Sitemaps' => $baseDir . '/modules/sitemaps/abstract-sitemaps.php',
+ 'PLL_Accept_Language' => $baseDir . '/frontend/accept-language.php',
+ 'PLL_Accept_Languages_Collection' => $baseDir . '/frontend/accept-languages-collection.php',
+ 'PLL_Admin' => $baseDir . '/admin/admin.php',
+ 'PLL_Admin_Base' => $baseDir . '/admin/admin-base.php',
+ 'PLL_Admin_Block_Editor' => $baseDir . '/admin/admin-block-editor.php',
+ 'PLL_Admin_Classic_Editor' => $baseDir . '/admin/admin-classic-editor.php',
+ 'PLL_Admin_Default_Term' => $baseDir . '/admin/admin-default-term.php',
+ 'PLL_Admin_Filters' => $baseDir . '/admin/admin-filters.php',
+ 'PLL_Admin_Filters_Columns' => $baseDir . '/admin/admin-filters-columns.php',
+ 'PLL_Admin_Filters_Media' => $baseDir . '/admin/admin-filters-media.php',
+ 'PLL_Admin_Filters_Post' => $baseDir . '/admin/admin-filters-post.php',
+ 'PLL_Admin_Filters_Post_Base' => $baseDir . '/admin/admin-filters-post-base.php',
+ 'PLL_Admin_Filters_Term' => $baseDir . '/admin/admin-filters-term.php',
+ 'PLL_Admin_Filters_Widgets_Options' => $baseDir . '/admin/admin-filters-widgets-options.php',
+ 'PLL_Admin_Links' => $baseDir . '/admin/admin-links.php',
+ 'PLL_Admin_Model' => $baseDir . '/admin/admin-model.php',
+ 'PLL_Admin_Nav_Menu' => $baseDir . '/admin/admin-nav-menu.php',
+ 'PLL_Admin_Notices' => $baseDir . '/admin/admin-notices.php',
+ 'PLL_Admin_Site_Health' => $baseDir . '/modules/site-health/admin-site-health.php',
+ 'PLL_Admin_Static_Pages' => $baseDir . '/admin/admin-static-pages.php',
+ 'PLL_Admin_Strings' => $baseDir . '/admin/admin-strings.php',
+ 'PLL_Admin_Sync' => $baseDir . '/modules/sync/admin-sync.php',
+ 'PLL_Aqua_Resizer' => $baseDir . '/integrations/aqua-resizer/aqua-resizer.php',
+ 'PLL_Base' => $baseDir . '/include/base.php',
+ 'PLL_CRUD_Posts' => $baseDir . '/include/crud-posts.php',
+ 'PLL_CRUD_Terms' => $baseDir . '/include/crud-terms.php',
+ 'PLL_Cache' => $baseDir . '/include/cache.php',
+ 'PLL_Cache_Compat' => $baseDir . '/integrations/cache/cache-compat.php',
+ 'PLL_Canonical' => $baseDir . '/frontend/canonical.php',
+ 'PLL_Cft' => $baseDir . '/integrations/custom-field-template/cft.php',
+ 'PLL_Choose_Lang' => $baseDir . '/frontend/choose-lang.php',
+ 'PLL_Choose_Lang_Content' => $baseDir . '/frontend/choose-lang-content.php',
+ 'PLL_Choose_Lang_Domain' => $baseDir . '/frontend/choose-lang-domain.php',
+ 'PLL_Choose_Lang_Url' => $baseDir . '/frontend/choose-lang-url.php',
+ 'PLL_Cookie' => $baseDir . '/include/cookie.php',
+ 'PLL_Db_Tools' => $baseDir . '/include/db-tools.php',
+ 'PLL_Domain_Mapping' => $baseDir . '/integrations/domain-mapping/domain-mapping.php',
+ 'PLL_Duplicate_Post' => $baseDir . '/integrations/duplicate-post/duplicate-post.php',
+ 'PLL_Featured_Content' => $baseDir . '/integrations/jetpack/featured-content.php',
+ 'PLL_Filter_REST_Routes' => $baseDir . '/include/filter-rest-routes.php',
+ 'PLL_Filters' => $baseDir . '/include/filters.php',
+ 'PLL_Filters_Links' => $baseDir . '/include/filters-links.php',
+ 'PLL_Filters_Sanitization' => $baseDir . '/include/filters-sanitization.php',
+ 'PLL_Filters_Widgets_Options' => $baseDir . '/include/filters-widgets-options.php',
+ 'PLL_Frontend' => $baseDir . '/frontend/frontend.php',
+ 'PLL_Frontend_Auto_Translate' => $baseDir . '/frontend/frontend-auto-translate.php',
+ 'PLL_Frontend_Filters' => $baseDir . '/frontend/frontend-filters.php',
+ 'PLL_Frontend_Filters_Links' => $baseDir . '/frontend/frontend-filters-links.php',
+ 'PLL_Frontend_Filters_Search' => $baseDir . '/frontend/frontend-filters-search.php',
+ 'PLL_Frontend_Filters_Widgets' => $baseDir . '/frontend/frontend-filters-widgets.php',
+ 'PLL_Frontend_Links' => $baseDir . '/frontend/frontend-links.php',
+ 'PLL_Frontend_Nav_Menu' => $baseDir . '/frontend/frontend-nav-menu.php',
+ 'PLL_Frontend_Static_Pages' => $baseDir . '/frontend/frontend-static-pages.php',
+ 'PLL_Install' => $baseDir . '/install/install.php',
+ 'PLL_Install_Base' => $baseDir . '/install/install-base.php',
+ 'PLL_Integrations' => $baseDir . '/integrations/integrations.php',
+ 'PLL_Jetpack' => $baseDir . '/integrations/jetpack/jetpack.php',
+ 'PLL_Language' => $baseDir . '/include/language.php',
+ 'PLL_Language_Deprecated' => $baseDir . '/include/language-deprecated.php',
+ 'PLL_Language_Factory' => $baseDir . '/include/language-factory.php',
+ 'PLL_License' => $baseDir . '/include/license.php',
+ 'PLL_Links' => $baseDir . '/include/links.php',
+ 'PLL_Links_Abstract_Domain' => $baseDir . '/include/links-abstract-domain.php',
+ 'PLL_Links_Default' => $baseDir . '/include/links-default.php',
+ 'PLL_Links_Directory' => $baseDir . '/include/links-directory.php',
+ 'PLL_Links_Domain' => $baseDir . '/include/links-domain.php',
+ 'PLL_Links_Model' => $baseDir . '/include/links-model.php',
+ 'PLL_Links_Permalinks' => $baseDir . '/include/links-permalinks.php',
+ 'PLL_Links_Subdomain' => $baseDir . '/include/links-subdomain.php',
+ 'PLL_MO' => $baseDir . '/include/mo.php',
+ 'PLL_Model' => $baseDir . '/include/model.php',
+ 'PLL_Multilingual_Sitemaps_Provider' => $baseDir . '/modules/sitemaps/multilingual-sitemaps-provider.php',
+ 'PLL_Nav_Menu' => $baseDir . '/include/nav-menu.php',
+ 'PLL_No_Category_Base' => $baseDir . '/integrations/no-category-base/no-category-base.php',
+ 'PLL_OLT_Manager' => $baseDir . '/include/olt-manager.php',
+ 'PLL_Plugin_Updater' => $baseDir . '/install/plugin-updater.php',
+ 'PLL_Query' => $baseDir . '/include/query.php',
+ 'PLL_REST_Request' => $baseDir . '/include/rest-request.php',
+ 'PLL_Settings' => $baseDir . '/settings/settings.php',
+ 'PLL_Settings_Browser' => $baseDir . '/settings/settings-browser.php',
+ 'PLL_Settings_CPT' => $baseDir . '/settings/settings-cpt.php',
+ 'PLL_Settings_Licenses' => $baseDir . '/settings/settings-licenses.php',
+ 'PLL_Settings_Media' => $baseDir . '/settings/settings-media.php',
+ 'PLL_Settings_Module' => $baseDir . '/settings/settings-module.php',
+ 'PLL_Settings_Preview_Machine_Translation' => $baseDir . '/modules/machine-translation/settings-preview-machine-translation.php',
+ 'PLL_Settings_Preview_Share_Slug' => $baseDir . '/modules/share-slug/settings-preview-share-slug.php',
+ 'PLL_Settings_Preview_Translate_Slugs' => $baseDir . '/modules/translate-slugs/settings-preview-translate-slugs.php',
+ 'PLL_Settings_Sync' => $baseDir . '/modules/sync/settings-sync.php',
+ 'PLL_Settings_Url' => $baseDir . '/settings/settings-url.php',
+ 'PLL_Sitemaps' => $baseDir . '/modules/sitemaps/sitemaps.php',
+ 'PLL_Sitemaps_Domain' => $baseDir . '/modules/sitemaps/sitemaps-domain.php',
+ 'PLL_Static_Pages' => $baseDir . '/include/static-pages.php',
+ 'PLL_Switcher' => $baseDir . '/include/switcher.php',
+ 'PLL_Sync' => $baseDir . '/modules/sync/sync.php',
+ 'PLL_Sync_Metas' => $baseDir . '/modules/sync/sync-metas.php',
+ 'PLL_Sync_Post_Metas' => $baseDir . '/modules/sync/sync-post-metas.php',
+ 'PLL_Sync_Tax' => $baseDir . '/modules/sync/sync-tax.php',
+ 'PLL_Sync_Term_Metas' => $baseDir . '/modules/sync/sync-term-metas.php',
+ 'PLL_T15S' => $baseDir . '/install/t15s.php',
+ 'PLL_Table_Languages' => $baseDir . '/settings/table-languages.php',
+ 'PLL_Table_Settings' => $baseDir . '/settings/table-settings.php',
+ 'PLL_Table_String' => $baseDir . '/settings/table-string.php',
+ 'PLL_Translatable_Object' => $baseDir . '/include/translatable-object.php',
+ 'PLL_Translatable_Object_With_Types_Interface' => $baseDir . '/include/translatable-object-with-types-interface.php',
+ 'PLL_Translatable_Object_With_Types_Trait' => $baseDir . '/include/translatable-object-with-types-trait.php',
+ 'PLL_Translatable_Objects' => $baseDir . '/include/translatable-objects.php',
+ 'PLL_Translate_Option' => $baseDir . '/include/translate-option.php',
+ 'PLL_Translated_Object' => $baseDir . '/include/translated-object.php',
+ 'PLL_Translated_Post' => $baseDir . '/include/translated-post.php',
+ 'PLL_Translated_Term' => $baseDir . '/include/translated-term.php',
+ 'PLL_Twenty_Seventeen' => $baseDir . '/integrations/twenty-seventeen/twenty-seven-teen.php',
+ 'PLL_Upgrade' => $baseDir . '/install/upgrade.php',
+ 'PLL_WPML_API' => $baseDir . '/modules/wpml/wpml-api.php',
+ 'PLL_WPML_Compat' => $baseDir . '/modules/wpml/wpml-compat.php',
+ 'PLL_WPML_Config' => $baseDir . '/modules/wpml/wpml-config.php',
+ 'PLL_WPSEO' => $baseDir . '/integrations/wpseo/wpseo.php',
+ 'PLL_WPSEO_OGP' => $baseDir . '/integrations/wpseo/wpseo-ogp.php',
+ 'PLL_WP_Import' => $baseDir . '/integrations/wp-importer/wp-import.php',
+ 'PLL_WP_Sweep' => $baseDir . '/integrations/wp-sweep/wp-sweep.php',
+ 'PLL_Walker' => $baseDir . '/include/walker.php',
+ 'PLL_Walker_Dropdown' => $baseDir . '/include/walker-dropdown.php',
+ 'PLL_Walker_List' => $baseDir . '/include/walker-list.php',
+ 'PLL_Widget_Calendar' => $baseDir . '/include/widget-calendar.php',
+ 'PLL_Widget_Languages' => $baseDir . '/include/widget-languages.php',
+ 'PLL_Wizard' => $baseDir . '/modules/wizard/wizard.php',
+ 'PLL_WordPress_Importer' => $baseDir . '/integrations/wp-importer/wordpress-importer.php',
+ 'PLL_Yarpp' => $baseDir . '/integrations/yarpp/yarpp.php',
+ 'Polylang' => $baseDir . '/include/class-polylang.php',
+);
diff --git a/wp-content/plugins/polylang/vendor/composer/autoload_namespaces.php b/wp-content/plugins/polylang/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000000..15a2ff3ad6
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+register(true);
+
+ return $loader;
+ }
+}
diff --git a/wp-content/plugins/polylang/vendor/composer/autoload_static.php b/wp-content/plugins/polylang/vendor/composer/autoload_static.php
new file mode 100644
index 0000000000..86aef19c82
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/composer/autoload_static.php
@@ -0,0 +1,150 @@
+ __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'PLL_AS3CF' => __DIR__ . '/../..' . '/integrations/wp-offload-media/as3cf.php',
+ 'PLL_Abstract_Sitemaps' => __DIR__ . '/../..' . '/modules/sitemaps/abstract-sitemaps.php',
+ 'PLL_Accept_Language' => __DIR__ . '/../..' . '/frontend/accept-language.php',
+ 'PLL_Accept_Languages_Collection' => __DIR__ . '/../..' . '/frontend/accept-languages-collection.php',
+ 'PLL_Admin' => __DIR__ . '/../..' . '/admin/admin.php',
+ 'PLL_Admin_Base' => __DIR__ . '/../..' . '/admin/admin-base.php',
+ 'PLL_Admin_Block_Editor' => __DIR__ . '/../..' . '/admin/admin-block-editor.php',
+ 'PLL_Admin_Classic_Editor' => __DIR__ . '/../..' . '/admin/admin-classic-editor.php',
+ 'PLL_Admin_Default_Term' => __DIR__ . '/../..' . '/admin/admin-default-term.php',
+ 'PLL_Admin_Filters' => __DIR__ . '/../..' . '/admin/admin-filters.php',
+ 'PLL_Admin_Filters_Columns' => __DIR__ . '/../..' . '/admin/admin-filters-columns.php',
+ 'PLL_Admin_Filters_Media' => __DIR__ . '/../..' . '/admin/admin-filters-media.php',
+ 'PLL_Admin_Filters_Post' => __DIR__ . '/../..' . '/admin/admin-filters-post.php',
+ 'PLL_Admin_Filters_Post_Base' => __DIR__ . '/../..' . '/admin/admin-filters-post-base.php',
+ 'PLL_Admin_Filters_Term' => __DIR__ . '/../..' . '/admin/admin-filters-term.php',
+ 'PLL_Admin_Filters_Widgets_Options' => __DIR__ . '/../..' . '/admin/admin-filters-widgets-options.php',
+ 'PLL_Admin_Links' => __DIR__ . '/../..' . '/admin/admin-links.php',
+ 'PLL_Admin_Model' => __DIR__ . '/../..' . '/admin/admin-model.php',
+ 'PLL_Admin_Nav_Menu' => __DIR__ . '/../..' . '/admin/admin-nav-menu.php',
+ 'PLL_Admin_Notices' => __DIR__ . '/../..' . '/admin/admin-notices.php',
+ 'PLL_Admin_Site_Health' => __DIR__ . '/../..' . '/modules/site-health/admin-site-health.php',
+ 'PLL_Admin_Static_Pages' => __DIR__ . '/../..' . '/admin/admin-static-pages.php',
+ 'PLL_Admin_Strings' => __DIR__ . '/../..' . '/admin/admin-strings.php',
+ 'PLL_Admin_Sync' => __DIR__ . '/../..' . '/modules/sync/admin-sync.php',
+ 'PLL_Aqua_Resizer' => __DIR__ . '/../..' . '/integrations/aqua-resizer/aqua-resizer.php',
+ 'PLL_Base' => __DIR__ . '/../..' . '/include/base.php',
+ 'PLL_CRUD_Posts' => __DIR__ . '/../..' . '/include/crud-posts.php',
+ 'PLL_CRUD_Terms' => __DIR__ . '/../..' . '/include/crud-terms.php',
+ 'PLL_Cache' => __DIR__ . '/../..' . '/include/cache.php',
+ 'PLL_Cache_Compat' => __DIR__ . '/../..' . '/integrations/cache/cache-compat.php',
+ 'PLL_Canonical' => __DIR__ . '/../..' . '/frontend/canonical.php',
+ 'PLL_Cft' => __DIR__ . '/../..' . '/integrations/custom-field-template/cft.php',
+ 'PLL_Choose_Lang' => __DIR__ . '/../..' . '/frontend/choose-lang.php',
+ 'PLL_Choose_Lang_Content' => __DIR__ . '/../..' . '/frontend/choose-lang-content.php',
+ 'PLL_Choose_Lang_Domain' => __DIR__ . '/../..' . '/frontend/choose-lang-domain.php',
+ 'PLL_Choose_Lang_Url' => __DIR__ . '/../..' . '/frontend/choose-lang-url.php',
+ 'PLL_Cookie' => __DIR__ . '/../..' . '/include/cookie.php',
+ 'PLL_Db_Tools' => __DIR__ . '/../..' . '/include/db-tools.php',
+ 'PLL_Domain_Mapping' => __DIR__ . '/../..' . '/integrations/domain-mapping/domain-mapping.php',
+ 'PLL_Duplicate_Post' => __DIR__ . '/../..' . '/integrations/duplicate-post/duplicate-post.php',
+ 'PLL_Featured_Content' => __DIR__ . '/../..' . '/integrations/jetpack/featured-content.php',
+ 'PLL_Filter_REST_Routes' => __DIR__ . '/../..' . '/include/filter-rest-routes.php',
+ 'PLL_Filters' => __DIR__ . '/../..' . '/include/filters.php',
+ 'PLL_Filters_Links' => __DIR__ . '/../..' . '/include/filters-links.php',
+ 'PLL_Filters_Sanitization' => __DIR__ . '/../..' . '/include/filters-sanitization.php',
+ 'PLL_Filters_Widgets_Options' => __DIR__ . '/../..' . '/include/filters-widgets-options.php',
+ 'PLL_Frontend' => __DIR__ . '/../..' . '/frontend/frontend.php',
+ 'PLL_Frontend_Auto_Translate' => __DIR__ . '/../..' . '/frontend/frontend-auto-translate.php',
+ 'PLL_Frontend_Filters' => __DIR__ . '/../..' . '/frontend/frontend-filters.php',
+ 'PLL_Frontend_Filters_Links' => __DIR__ . '/../..' . '/frontend/frontend-filters-links.php',
+ 'PLL_Frontend_Filters_Search' => __DIR__ . '/../..' . '/frontend/frontend-filters-search.php',
+ 'PLL_Frontend_Filters_Widgets' => __DIR__ . '/../..' . '/frontend/frontend-filters-widgets.php',
+ 'PLL_Frontend_Links' => __DIR__ . '/../..' . '/frontend/frontend-links.php',
+ 'PLL_Frontend_Nav_Menu' => __DIR__ . '/../..' . '/frontend/frontend-nav-menu.php',
+ 'PLL_Frontend_Static_Pages' => __DIR__ . '/../..' . '/frontend/frontend-static-pages.php',
+ 'PLL_Install' => __DIR__ . '/../..' . '/install/install.php',
+ 'PLL_Install_Base' => __DIR__ . '/../..' . '/install/install-base.php',
+ 'PLL_Integrations' => __DIR__ . '/../..' . '/integrations/integrations.php',
+ 'PLL_Jetpack' => __DIR__ . '/../..' . '/integrations/jetpack/jetpack.php',
+ 'PLL_Language' => __DIR__ . '/../..' . '/include/language.php',
+ 'PLL_Language_Deprecated' => __DIR__ . '/../..' . '/include/language-deprecated.php',
+ 'PLL_Language_Factory' => __DIR__ . '/../..' . '/include/language-factory.php',
+ 'PLL_License' => __DIR__ . '/../..' . '/include/license.php',
+ 'PLL_Links' => __DIR__ . '/../..' . '/include/links.php',
+ 'PLL_Links_Abstract_Domain' => __DIR__ . '/../..' . '/include/links-abstract-domain.php',
+ 'PLL_Links_Default' => __DIR__ . '/../..' . '/include/links-default.php',
+ 'PLL_Links_Directory' => __DIR__ . '/../..' . '/include/links-directory.php',
+ 'PLL_Links_Domain' => __DIR__ . '/../..' . '/include/links-domain.php',
+ 'PLL_Links_Model' => __DIR__ . '/../..' . '/include/links-model.php',
+ 'PLL_Links_Permalinks' => __DIR__ . '/../..' . '/include/links-permalinks.php',
+ 'PLL_Links_Subdomain' => __DIR__ . '/../..' . '/include/links-subdomain.php',
+ 'PLL_MO' => __DIR__ . '/../..' . '/include/mo.php',
+ 'PLL_Model' => __DIR__ . '/../..' . '/include/model.php',
+ 'PLL_Multilingual_Sitemaps_Provider' => __DIR__ . '/../..' . '/modules/sitemaps/multilingual-sitemaps-provider.php',
+ 'PLL_Nav_Menu' => __DIR__ . '/../..' . '/include/nav-menu.php',
+ 'PLL_No_Category_Base' => __DIR__ . '/../..' . '/integrations/no-category-base/no-category-base.php',
+ 'PLL_OLT_Manager' => __DIR__ . '/../..' . '/include/olt-manager.php',
+ 'PLL_Plugin_Updater' => __DIR__ . '/../..' . '/install/plugin-updater.php',
+ 'PLL_Query' => __DIR__ . '/../..' . '/include/query.php',
+ 'PLL_REST_Request' => __DIR__ . '/../..' . '/include/rest-request.php',
+ 'PLL_Settings' => __DIR__ . '/../..' . '/settings/settings.php',
+ 'PLL_Settings_Browser' => __DIR__ . '/../..' . '/settings/settings-browser.php',
+ 'PLL_Settings_CPT' => __DIR__ . '/../..' . '/settings/settings-cpt.php',
+ 'PLL_Settings_Licenses' => __DIR__ . '/../..' . '/settings/settings-licenses.php',
+ 'PLL_Settings_Media' => __DIR__ . '/../..' . '/settings/settings-media.php',
+ 'PLL_Settings_Module' => __DIR__ . '/../..' . '/settings/settings-module.php',
+ 'PLL_Settings_Preview_Machine_Translation' => __DIR__ . '/../..' . '/modules/machine-translation/settings-preview-machine-translation.php',
+ 'PLL_Settings_Preview_Share_Slug' => __DIR__ . '/../..' . '/modules/share-slug/settings-preview-share-slug.php',
+ 'PLL_Settings_Preview_Translate_Slugs' => __DIR__ . '/../..' . '/modules/translate-slugs/settings-preview-translate-slugs.php',
+ 'PLL_Settings_Sync' => __DIR__ . '/../..' . '/modules/sync/settings-sync.php',
+ 'PLL_Settings_Url' => __DIR__ . '/../..' . '/settings/settings-url.php',
+ 'PLL_Sitemaps' => __DIR__ . '/../..' . '/modules/sitemaps/sitemaps.php',
+ 'PLL_Sitemaps_Domain' => __DIR__ . '/../..' . '/modules/sitemaps/sitemaps-domain.php',
+ 'PLL_Static_Pages' => __DIR__ . '/../..' . '/include/static-pages.php',
+ 'PLL_Switcher' => __DIR__ . '/../..' . '/include/switcher.php',
+ 'PLL_Sync' => __DIR__ . '/../..' . '/modules/sync/sync.php',
+ 'PLL_Sync_Metas' => __DIR__ . '/../..' . '/modules/sync/sync-metas.php',
+ 'PLL_Sync_Post_Metas' => __DIR__ . '/../..' . '/modules/sync/sync-post-metas.php',
+ 'PLL_Sync_Tax' => __DIR__ . '/../..' . '/modules/sync/sync-tax.php',
+ 'PLL_Sync_Term_Metas' => __DIR__ . '/../..' . '/modules/sync/sync-term-metas.php',
+ 'PLL_T15S' => __DIR__ . '/../..' . '/install/t15s.php',
+ 'PLL_Table_Languages' => __DIR__ . '/../..' . '/settings/table-languages.php',
+ 'PLL_Table_Settings' => __DIR__ . '/../..' . '/settings/table-settings.php',
+ 'PLL_Table_String' => __DIR__ . '/../..' . '/settings/table-string.php',
+ 'PLL_Translatable_Object' => __DIR__ . '/../..' . '/include/translatable-object.php',
+ 'PLL_Translatable_Object_With_Types_Interface' => __DIR__ . '/../..' . '/include/translatable-object-with-types-interface.php',
+ 'PLL_Translatable_Object_With_Types_Trait' => __DIR__ . '/../..' . '/include/translatable-object-with-types-trait.php',
+ 'PLL_Translatable_Objects' => __DIR__ . '/../..' . '/include/translatable-objects.php',
+ 'PLL_Translate_Option' => __DIR__ . '/../..' . '/include/translate-option.php',
+ 'PLL_Translated_Object' => __DIR__ . '/../..' . '/include/translated-object.php',
+ 'PLL_Translated_Post' => __DIR__ . '/../..' . '/include/translated-post.php',
+ 'PLL_Translated_Term' => __DIR__ . '/../..' . '/include/translated-term.php',
+ 'PLL_Twenty_Seventeen' => __DIR__ . '/../..' . '/integrations/twenty-seventeen/twenty-seven-teen.php',
+ 'PLL_Upgrade' => __DIR__ . '/../..' . '/install/upgrade.php',
+ 'PLL_WPML_API' => __DIR__ . '/../..' . '/modules/wpml/wpml-api.php',
+ 'PLL_WPML_Compat' => __DIR__ . '/../..' . '/modules/wpml/wpml-compat.php',
+ 'PLL_WPML_Config' => __DIR__ . '/../..' . '/modules/wpml/wpml-config.php',
+ 'PLL_WPSEO' => __DIR__ . '/../..' . '/integrations/wpseo/wpseo.php',
+ 'PLL_WPSEO_OGP' => __DIR__ . '/../..' . '/integrations/wpseo/wpseo-ogp.php',
+ 'PLL_WP_Import' => __DIR__ . '/../..' . '/integrations/wp-importer/wp-import.php',
+ 'PLL_WP_Sweep' => __DIR__ . '/../..' . '/integrations/wp-sweep/wp-sweep.php',
+ 'PLL_Walker' => __DIR__ . '/../..' . '/include/walker.php',
+ 'PLL_Walker_Dropdown' => __DIR__ . '/../..' . '/include/walker-dropdown.php',
+ 'PLL_Walker_List' => __DIR__ . '/../..' . '/include/walker-list.php',
+ 'PLL_Widget_Calendar' => __DIR__ . '/../..' . '/include/widget-calendar.php',
+ 'PLL_Widget_Languages' => __DIR__ . '/../..' . '/include/widget-languages.php',
+ 'PLL_Wizard' => __DIR__ . '/../..' . '/modules/wizard/wizard.php',
+ 'PLL_WordPress_Importer' => __DIR__ . '/../..' . '/integrations/wp-importer/wordpress-importer.php',
+ 'PLL_Yarpp' => __DIR__ . '/../..' . '/integrations/yarpp/yarpp.php',
+ 'Polylang' => __DIR__ . '/../..' . '/include/class-polylang.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->classMap = ComposerStaticInit7dc73dfbbc007ce0d677088d041ad7d4::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/wp-content/plugins/polylang/vendor/composer/installed.json b/wp-content/plugins/polylang/vendor/composer/installed.json
new file mode 100644
index 0000000000..f20a6c47c6
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/composer/installed.json
@@ -0,0 +1,5 @@
+{
+ "packages": [],
+ "dev": false,
+ "dev-package-names": []
+}
diff --git a/wp-content/plugins/polylang/vendor/composer/installed.php b/wp-content/plugins/polylang/vendor/composer/installed.php
new file mode 100644
index 0000000000..d11dcab36c
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/composer/installed.php
@@ -0,0 +1,23 @@
+ array(
+ 'name' => 'wpsyntex/polylang',
+ 'pretty_version' => '3.6.x-dev',
+ 'version' => '3.6.9999999.9999999-dev',
+ 'reference' => 'a3b0bdc67851fdf91f76ad00d4eb7f8bcaf5d87b',
+ 'type' => 'wordpress-plugin',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'dev' => false,
+ ),
+ 'versions' => array(
+ 'wpsyntex/polylang' => array(
+ 'pretty_version' => '3.6.x-dev',
+ 'version' => '3.6.9999999.9999999-dev',
+ 'reference' => 'a3b0bdc67851fdf91f76ad00d4eb7f8bcaf5d87b',
+ 'type' => 'wordpress-plugin',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ ),
+);
diff --git a/wp-content/plugins/polylang/vendor/composer/platform_check.php b/wp-content/plugins/polylang/vendor/composer/platform_check.php
new file mode 100644
index 0000000000..f79e574be7
--- /dev/null
+++ b/wp-content/plugins/polylang/vendor/composer/platform_check.php
@@ -0,0 +1,26 @@
+= 70000)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 7.0.0". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+ if (!headers_sent()) {
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ if (!ini_get('display_errors')) {
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+ } elseif (!headers_sent()) {
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+ }
+ }
+ trigger_error(
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
+ E_USER_ERROR
+ );
+}