first commit

This commit is contained in:
2023-09-12 21:41:04 +02:00
commit 3361a7f053
13284 changed files with 2116755 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
<?php
namespace WPML\Compatibility\Divi;
use WPML\LIB\WP\Hooks;
use function WPML\FP\spreadArgs;
use WPML\FP\Obj;
class DisplayConditions implements \IWPML_Frontend_Action {
const BASE64_EMPTY_ARRAY = 'W10=';
public function add_hooks() {
Hooks::onFilter( 'et_pb_module_shortcode_attributes' )
->then( spreadArgs( [ $this, 'translateAttributes' ] ) );
}
/**
* @param array $atts
* @return array
*/
public function translateAttributes( $atts ) {
$displayConditions = Obj::prop( 'display_conditions', $atts );
if ( $displayConditions && self::BASE64_EMPTY_ARRAY !== $displayConditions ) {
/* phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode */
$conditions = json_decode( base64_decode( $atts['display_conditions'] ), true );
foreach ( $conditions as &$condition ) {
if ( 'categoryPage' === $condition['condition'] ) {
foreach ( $condition['conditionSettings']['categories'] as &$category ) {
$category['value'] = (string) apply_filters( 'wpml_object_id', $category['value'], $category['groupSlug'] );
}
}
}
/* phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode */
$atts['display_conditions'] = base64_encode( wp_json_encode( $conditions ) );
}
return $atts;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace WPML\Compatibility\Divi;
/**
* Divi replaces double quotes with %22 when saving shortcode attributes.
* ATE needs valid HTML so we temporarily decode the double quotes.
* When we receive the translation we undo the change.
*
* @package WPML\Compatibility\Divi
*/
class DoubleQuotes implements \IWPML_Backend_Action, \IWPML_Frontend_Action {
public function add_hooks() {
add_filter( 'wpml_pb_shortcode_decode', [ $this, 'decode' ], -PHP_INT_MAX, 2 );
add_filter( 'wpml_pb_shortcode_encode', [ $this, 'encode' ], PHP_INT_MAX, 2 );
}
/**
* @param string $string
* @param string $encoding
*
* @return string
*/
public function decode( $string, $encoding ) {
if ( self::canHaveDoubleQuotes( $string, $encoding ) ) {
$string = str_replace( '%22', '"', $string );
}
return $string;
}
/**
* @param string $string
* @param string $encoding
*
* @return string
*/
public function encode( $string, $encoding ) {
if ( self::canHaveDoubleQuotes( $string, $encoding ) ) {
$string = str_replace( '"', '%22', $string );
}
return $string;
}
/**
* @param string $string
* @param string $encoding
*
* @return bool
*/
private static function canHaveDoubleQuotes( $string, $encoding ) {
return is_string( $string ) && 'allow_html_tags' === $encoding;
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace WPML\Compatibility\Divi\Hooks;
use WPML\FP\Obj;
use WPML\LIB\WP\Hooks;
use function WPML\FP\spreadArgs;
use WPML\PB\Helper\LanguageNegotiation;
class DomainsBackendEditor implements \IWPML_Backend_Action {
public function add_hooks() {
if ( LanguageNegotiation::isUsingDomains()
&& self::isPostEditor()
&& self::getDomainByCurrentPostLanguage() !== $_SERVER['HTTP_HOST']
) {
Hooks::onAction( 'admin_notices' )
->then( spreadArgs( [ $this, 'displayNotice' ] ) );
}
}
public function displayNotice() {
$url = ( is_ssl() ? 'https://' : 'http://' ) . self::getDomainByCurrentPostLanguage() . $_SERVER['REQUEST_URI'];
?>
<div class="notice notice-warning is-dismissible">
<p>
<?php
echo sprintf(
// translators: placeholders are opening and closing <a> tag.
esc_html__( "It is not possible to use Divi's backend builder to edit a post in a different language than your domain. Please use Divi's frontend builder to edit this post or %1\$s switch to the correct domain %2\$s to use the backend builder.", 'sitepress' ),
sprintf( '<a href="%s">', esc_url( $url ) ),
'</a>'
);
?>
</p>
<button type="button" class="notice-dismiss">
<span class="screen-reader-text">Dismiss this notice.</span>
</button>
</div>
<?php
}
/**
* @return bool
*/
private static function isPostEditor() {
global $pagenow;
return 'post.php' === $pagenow
&& self::getPostId();
}
/**
* @return int
*/
private static function getPostId() {
/* phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification */
return (int) Obj::prop( 'post', $_GET );
}
/**
* @return string|null
*/
private static function getDomainByCurrentPostLanguage() {
$postDetails = apply_filters( 'wpml_post_language_details', null, self::getPostId() );
$language = Obj::prop( 'language_code', $postDetails );
return LanguageNegotiation::getDomainByLanguage( $language );
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace WPML\Compatibility\Divi\Hooks;
use WPML\FP\Obj;
use WPML\FP\Relation;
use WPML\LIB\WP\Hooks;
use function WPML\FP\spreadArgs;
class Editor implements \IWPML_Backend_Action {
public function add_hooks() {
Hooks::onFilter( 'wpml_pb_is_editing_translation_with_native_editor', 10, 2 )
->then( spreadArgs( function( $isTranslationWithNativeEditor, $translatedPostId ) {
return $isTranslationWithNativeEditor
|| (
Relation::propEq( 'action', 'et_fb_ajax_save', $_POST )
&& (int) Obj::prop( 'post_id', $_POST ) === $translatedPostId
);
} ) );
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace WPML\Compatibility\Divi\Hooks;
use WPML\LIB\WP\Hooks;
use function WPML\FP\spreadArgs;
class GutenbergUpdate implements \IWPML_Backend_Action {
public function add_hooks() {
Hooks::onFilter( 'wpml_pb_is_post_built_with_shortcodes', 10, 2 )
->then( spreadArgs( [ $this, 'isPostBuiltWithShortcodes' ] ) );
}
/**
* @param string $builtWithShortcodes
* @param \WP_Post $post
*
* @return bool
*/
public static function isPostBuiltWithShortcodes( $builtWithShortcodes, $post ) {
return self::isDiviPost( $post->ID ) || $builtWithShortcodes;
}
/**
* @param int $postId
*
* @return bool
*/
private static function isDiviPost( $postId ) {
return 'on' === get_post_meta( $postId, '_et_pb_use_builder', true );
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace WPML\Compatibility\Divi;
use WPML\FP\Obj;
class TinyMCE implements \IWPML_Backend_Action {
public function add_hooks() {
if ( defined( 'WPML_TM_FOLDER' ) ) {
add_filter( 'tiny_mce_before_init', [ $this, 'filterEditorAutoTags' ] );
}
}
/**
* @param array $config
*
* @return array
*/
public function filterEditorAutoTags( $config ) {
if ( did_action( 'admin_init' ) ) {
$screen = get_current_screen();
$cteUrl = 'wpml_page_' . constant( 'WPML_TM_FOLDER' ) . '/menu/translations-queue';
if ( Obj::prop( 'id', $screen ) === $cteUrl ) {
$config['wpautop'] = false;
$config['indent'] = true;
$config['tadv_noautop'] = true;
}
}
return $config;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace WPML\Compatibility\Divi;
use WPML\LIB\WP\Hooks;
use function WPML\FP\spreadArgs;
use WPML\FP\Obj;
class WooShortcodes implements \IWPML_Frontend_Action {
const WOO_SHORTCODES = [
'et_pb_wc_description',
'et_pb_wc_title',
];
public function add_hooks() {
Hooks::onFilter( 'et_pb_module_shortcode_attributes', 10, 3 )
->then( spreadArgs( [ $this, 'translateAttributes' ] ) );
}
/**
* @param array $shortcodeAttrs
* @param array $attrs
* @param string $slug
*
* @return array
*/
public function translateAttributes( $shortcodeAttrs, $attrs, $slug ) {
if ( in_array( $slug, self::WOO_SHORTCODES, true ) && (int) Obj::prop( 'product', $shortcodeAttrs ) ) {
$shortcodeAttrs['product'] = apply_filters( 'wpml_object_id', $shortcodeAttrs['product'], 'product', true );
}
return $shortcodeAttrs;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace WPML\Compatibility\Divi;
class Builder implements \IWPML_Frontend_Action, \IWPML_Backend_Action, \IWPML_AJAX_Action {
public function add_hooks() {
add_filter( 'theme_locale', [ $this, 'switch_to_user_language' ] );
}
public function switch_to_user_language( $locale ) {
if ( isset( $_POST['action'] ) && ( 'et_fb_update_builder_assets' === $_POST['action'] ) ) { // phpcs:ignore WordPress.CSRF.NonceVerification
return get_user_locale();
}
return $locale;
}
}

View File

@@ -0,0 +1,281 @@
<?php
class WPML_Compatibility_Divi implements \IWPML_DIC_Action, \IWPML_Backend_Action, \IWPML_Frontend_Action {
const REGEX_REMOVE_OPENING_PARAGRAPH = '/(<p>[\n\r]*)([\n\r]{1}\[\/et_)/m';
const REGEX_REMOVE_CLOSING_PARAGRAPH = '/(\[et_.*\][\n\r]{1})([\n\r]*<\/p>)/m';
/** @var SitePress */
private $sitepress;
/**
* @param SitePress $sitepress
*/
public function __construct( SitePress $sitepress ) {
$this->sitepress = $sitepress;
}
public function add_hooks() {
if ( $this->sitepress->is_setup_complete() ) {
add_action( 'init', [ $this, 'load_resources_if_they_are_required' ], 10, 0 );
add_filter( 'et_builder_load_actions', [ $this, 'load_builder_for_ajax_actions' ] );
add_action( 'admin_init', [ $this, 'display_warning_notice' ], 10, 0 );
add_filter( 'wpml_pb_should_handle_content', [ $this, 'should_handle_shortcode_content' ], 10, 2 );
add_filter( 'wpml_pb_shortcode_content_for_translation', [ $this, 'cleanup_global_layout_content' ], 10, 2 );
add_filter( 'icl_job_elements', [ $this, 'remove_old_content_from_translation' ], 10, 2 );
add_filter( 'wpml_words_count_custom_fields_to_count', [ $this, 'remove_old_content_from_words_count' ], 10, 2 );
}
}
/**
* @return bool
*/
private function is_standard_editor_used() {
$tm_settings = $this->sitepress->get_setting( 'translation-management', [] );
return ! isset( $tm_settings['doc_translation_method'] ) ||
ICL_TM_TMETHOD_MANUAL === $tm_settings['doc_translation_method'];
}
public function display_warning_notice() {
$notices = wpml_get_admin_notices();
if ( $this->is_standard_editor_used() ) {
$notices->add_notice( new WPML_Compatibility_Divi_Notice() );
} elseif ( $notices->get_notice( WPML_Compatibility_Divi_Notice::ID, WPML_Compatibility_Divi_Notice::GROUP ) ) {
$notices->remove_notice( WPML_Compatibility_Divi_Notice::GROUP, WPML_Compatibility_Divi_Notice::ID );
}
}
/**
* These actions require the custom widget area to be initialized.
*
* @param array $actions
* @return array
*/
public function load_builder_for_ajax_actions( $actions ) {
$actions[] = 'save-widget';
$actions[] = 'widgets-order';
$actions[] = 'wpml-ls-save-settings';
return $actions;
}
public function load_resources_if_they_are_required() {
if ( ! isset( $_GET['page'] ) || ! is_admin() ) { /* phpcs:ignore */
return;
}
$pages = [ self::get_duplication_action_page() ];
if ( self::is_tm_active() ) {
$pages[] = self::get_translation_dashboard_page();
$pages[] = self::get_translation_editor_page();
}
if ( self::is_sl_active() ) {
$pages[] = self::get_sl_page();
}
if ( in_array( $_GET['page'], $pages, true ) ) { /* phpcs:ignore */
$this->register_layouts();
}
}
private static function get_translation_dashboard_page() {
return constant( 'WPML_TM_FOLDER' ) . '/menu/main.php';
}
private static function get_translation_editor_page() {
return constant( 'WPML_TM_FOLDER' ) . '/menu/translations-queue.php';
}
private static function get_duplication_action_page() {
return constant( 'WPML_PLUGIN_FOLDER' ) . '/menu/languages.php';
}
private static function get_sl_page() {
return 'wpml-sticky-links';
}
private static function is_tm_active() {
return defined( 'WPML_TM_FOLDER' );
}
private static function is_sl_active() {
return defined( 'WPML_STICKY_LINKS_VERSION' );
}
private function register_layouts() {
/**
* @phpstan-ignore-next-line
*/
if ( function_exists( 'et_builder_should_load_framework' ) && ! et_builder_should_load_framework() ) {
if ( function_exists( 'et_builder_register_layouts' ) ) {
/**
* @phpstan-ignore-next-line
*/
et_builder_register_layouts();
} else {
$lib_file = ET_BUILDER_DIR . 'feature/Library.php';
if ( ! class_exists( 'ET_Builder_Library' )
&& defined( 'ET_BUILDER_DIR' )
&& file_exists( $lib_file )
) {
require_once $lib_file;
}
if ( class_exists( 'ET_Builder_Library' ) ) {
ET_Builder_Library::instance();
}
}
}
}
/**
* The global layout is not properly extracted from the page
* because it adds <p> tags either not opened or not closed.
*
* See the global content below as an example:
*
* [et_pb_section prev_background_color="#000000" next_background_color="#000000"][et_pb_text]
*
* </p>
* <p>Global text 1 EN5</p>
* <p>
*
* [/et_pb_text][/et_pb_section]
*
* We also need to remove `prev_background` and `next_background` attributes which are added from the page.
*
* @param string $content
* @param int $post_id
*/
public function cleanup_global_layout_content( $content, $post_id ) {
if ( 'et_pb_layout' === get_post_type( $post_id ) ) {
$content = preg_replace( self::REGEX_REMOVE_OPENING_PARAGRAPH, '$2', $content );
$content = preg_replace( self::REGEX_REMOVE_CLOSING_PARAGRAPH, '$1', $content );
$content = preg_replace( '/( prev_background_color="#[0-9a-f]*")/', '', $content );
$content = preg_replace( '/( next_background_color="#[0-9a-f]*")/', '', $content );
}
return $content;
}
public function should_handle_shortcode_content( $handle_content, $shortcode ) {
if (
strpos( $shortcode['tag'], 'et_' ) === 0 &&
strpos( $shortcode['attributes'], 'global_module=' ) !== false
) {
// If a translatable attribute has been excluded from sync, we need to handle it.
$handle_content = $this->is_excluded_from_sync( $shortcode );
}
return $handle_content;
}
/**
* Check if a global module has excluded any translatable text that we need to handle
*
* @param array $shortcode
* {
* @type string $tag.
* @type string $content.
* @type string $attributes.
* }
* @return bool
*/
private function is_excluded_from_sync( $shortcode ) {
$handle_content = false;
preg_match( '/global_module="([0-9]+)"/', $shortcode['attributes'], $matches );
$excluded = json_decode( get_post_meta( $matches[1], '_et_pb_excluded_global_options', true ), true );
if ( is_array( $excluded ) && count( $excluded ) > 0 ) {
$attributes = $this->get_translatable_shortcode_attributes( $shortcode['tag'] );
foreach ( $excluded as $field ) {
if ( in_array( $field, $attributes, true ) ) {
$handle_content = true;
break;
}
}
}
return $handle_content;
}
/**
* Get a list of translatable attributes for a shortcode tag.
* This includes the inner content and any attributes found in XML configuration.
*
* @param string $tag The shortcode tag.
* @return array
*/
private function get_translatable_shortcode_attributes( $tag ) {
$attributes = [ 'et_pb_content_field' ];
$settings = get_option( 'icl_st_settings', [] );
if ( ! isset( $settings['pb_shortcode'] ) ) {
return $attributes;
}
foreach ( $settings['pb_shortcode'] as $setting ) {
if ( $tag === $setting['tag']['value'] ) {
foreach ( $setting['attributes'] as $attribute ) {
if ( empty( $attribute['type'] ) ) {
$attributes[] = $attribute['value'];
}
}
break;
}
}
return $attributes;
}
/**
* Remove the `_et_pb_old_content` meta field from translation jobs, except for products.
*
* @param array $fields Array of fields to translate.
* @param object $post_id The ID of the post being translated.
*
* @return array
*/
public function remove_old_content_from_translation( $fields, $post_id ) {
// Bail out early if its a product.
if ( 'product' === get_post_type( $post_id ) ) {
return $fields;
}
// Search for the _et_pb_old_content element and empty it.
$field_types = wp_list_pluck( $fields, 'field_type' );
$index = array_search( 'field-_et_pb_old_content-0', $field_types, true );
if ( false !== $index ) {
$fields[ $index ]->field_data = '';
$fields[ $index ]->field_data_translated = '';
}
return $fields;
}
/**
* Remove the `_et_pb_old_content` meta field from words count, except for products.
*
* @param array $fields_to_count Array of custom fields to count.
* @param object $post_id The ID of the post for which we are counting the words.
*
* @return array
*/
public function remove_old_content_from_words_count( $fields_to_count, $post_id ) {
if ( 'product' !== get_post_type( $post_id ) ) {
$index = array_search( '_et_pb_old_content', $fields_to_count, true );
if ( false !== $index ) {
unset( $fields_to_count[ $index ] );
}
}
return $fields_to_count;
}
}

View File

@@ -0,0 +1,42 @@
<?php
class WPML_Compatibility_Divi_Notice extends WPML_Notice {
const ID = 'wpml-compatibility-divi-editor-warning';
const GROUP = 'wpml-compatibility-divi';
public function __construct() {
parent::__construct( self::ID, $this->get_message(), self::GROUP );
$this->set_dismissible( true );
$this->set_css_class_types( 'warning' );
}
/**
* @return string
*/
private function get_message() {
$msg = esc_html_x(
'You are using DIVI theme, and you have chosen to use the standard editor for translating content.',
'Use Translation Editor notice 1/3',
'sitepress'
);
$msg .= ' ' . esc_html_x(
'Some functionalities may not work properly. We encourage you to switch to use the Translation Editor.',
'Use Translation Editor notice 2/3',
'sitepress'
);
$msg .= ' ' . sprintf(
/* translators: %s will be replaced with a URL. */
esc_html_x(
'You can find more information here: %s',
'Use Translation Editor notice 2/3',
'sitepress'
),
'<a href="https://wpml.org/errata/some-internal-taxonomies-will-be-missing-when-you-translate-divi-layouts/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmlcore">Some internal taxonomies will be missing when you translate Divi layouts</a>'
);
return $msg;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace WPML\Compatibility\Divi;
class DiviOptionsEncoding implements \IWPML_Backend_Action, \IWPML_Frontend_Action {
const CHARS_ENCODED = [ '%22', '%91', '%93' ];
const CHARS_DECODED = [ '"', '[', ']' ];
const DELIMITER = '_';
const TRANSLATABLE_KEYS = [ 'value', 'link_url', 'link_text' ];
public function add_hooks() {
add_filter( 'wpml_pb_shortcode_decode', [ $this, 'decode_divi_options' ], 10, 2 );
add_filter( 'wpml_pb_shortcode_encode', [ $this, 'encode_divi_options' ], 10, 2 );
}
public function decode_divi_options( $string, $encoding ) {
if ( 'divi_options' === $encoding ) {
$options = str_replace( self::CHARS_ENCODED, self::CHARS_DECODED, $string );
$options = json_decode( $options, true );
$string = [];
foreach ( $options as $index => $option ) {
foreach ( $option as $key => $value ) {
$string[ $key . self::DELIMITER . $index ] = [
'value' => $value,
'translate' => in_array( $key, self::TRANSLATABLE_KEYS, true ),
];
}
}
}
return $string;
}
public function encode_divi_options( $string, $encoding ) {
if ( 'divi_options' === $encoding ) {
$output = [];
foreach ( $string as $combined_key => $value ) {
$parts = explode( self::DELIMITER, $combined_key );
$index = array_pop( $parts );
$key = implode( self::DELIMITER, $parts );
$output[ $index ][ $key ] = $value;
}
$output = wp_json_encode( $output, JSON_UNESCAPED_UNICODE );
$string = str_replace( self::CHARS_DECODED, self::CHARS_ENCODED, $output );
}
return $string;
}
}

View File

@@ -0,0 +1,112 @@
<?php
namespace WPML\Compatibility\Divi;
use WPML\Compatibility\BaseDynamicContent;
class DynamicContent extends BaseDynamicContent {
const ENCODED_CONTENT_START = '@ET-DC@';
const ENCODED_CONTENT_END = '@';
/** @var array */
protected $positions = [ 'before', 'after' ];
/**
* Sets $positions dynamic content to be translatable.
*
* @param string $string The decoded string so far.
* @param string $encoding The encoding used.
*
* @return string|array
*/
public function decode_dynamic_content( $string, $encoding ) {
if ( $this->is_dynamic_content( $string ) ) {
$field = $this->decode_field( $string );
$decodedContent = [
'et-dynamic-content' => [
'value' => $string,
'translate' => false,
],
];
foreach ( $this->positions as $position ) {
if ( ! empty( $field['settings'][ $position ] ) ) {
$decodedContent[ $position ] = [
'value' => $field['settings'][ $position ],
'translate' => true,
];
}
}
return $decodedContent;
}
return $string;
}
/**
* Rebuilds dynamic content with translated strings.
*
* @param string|array $string The field array or string.
* @param string $encoding The encoding used.
*
* @return string
*/
public function encode_dynamic_content( $string, $encoding ) {
if ( is_array( $string ) && isset( $string['et-dynamic-content'] ) ) {
$field = $this->decode_field( $string['et-dynamic-content'] );
foreach ( $this->positions as $position ) {
if ( isset( $string[ $position ] ) ) {
$field['settings'][ $position ] = $string[ $position ];
}
}
return $this->encode_field( $field );
}
return $string;
}
/**
* Decode a dynamic-content field.
*
* @param string $string The string to decode.
*
* @return bool
*/
protected function is_dynamic_content( $string ) {
return substr( $string, 0, strlen( self::ENCODED_CONTENT_START ) ) === self::ENCODED_CONTENT_START;
}
/**
* Decode a dynamic-content field.
*
* @param string $string The string to decode.
*
* @return array
*/
protected function decode_field( $string ) {
$start = strlen( self::ENCODED_CONTENT_START );
$end = strlen( self::ENCODED_CONTENT_END );
// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
return json_decode( base64_decode( substr( $string, $start, -$end ) ), true );
}
/**
* Encode a dynamic-content field.
*
* @param array $field The field to encode.
*
* @return string
*/
protected function encode_field( $field ) {
// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
return self::ENCODED_CONTENT_START
. base64_encode( wp_json_encode( $field ) )
. self::ENCODED_CONTENT_END;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace WPML\Compatibility\Divi;
class Search implements \IWPML_Frontend_Action {
public function add_hooks() {
add_action( 'et_search_form_fields', [ $this, 'add_language_form_field' ] );
}
public function add_language_form_field() {
do_action( 'wpml_add_language_form_field' );
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace WPML\Compatibility\Divi;
class ThemeBuilderFactory implements \IWPML_Deferred_Action_Loader, \IWPML_Backend_Action_Loader, \IWPML_Frontend_Action_Loader {
public function get_load_action() {
return 'init';
}
public function create() {
global $sitepress;
return new ThemeBuilder( $sitepress );
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace WPML\Compatibility\Divi;
use SitePress;
class ThemeBuilder implements \IWPML_Action {
/** @var SitePress */
private $sitepress;
/**
* @param SitePress $sitepress
*/
public function __construct( SitePress $sitepress ) {
$this->sitepress = $sitepress;
}
/**
* Add filters and actions.
*/
public function add_hooks() {
if ( ! defined( 'ET_THEME_BUILDER_DIR' ) ) {
return;
}
if ( $this->sitepress->is_setup_complete() ) {
if ( is_admin() ) {
add_action( 'init', [ $this, 'make_layouts_editable' ], 1000 ); // Before WPML_Sticky_Links::init.
add_filter( 'wpml_document_view_item_link', [ $this, 'document_view_layout_link' ], 10, 5 );
} else {
add_filter( 'get_post_metadata', [ $this, 'translate_layout_ids' ], 10, 4 );
}
}
}
/**
* Gets all post types that are layouts.
*/
private static function get_types() {
return [
ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE,
ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE,
ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE,
];
}
/**
* Access the global post types array to tweak the settings for layouts
*/
public function make_layouts_editable() {
global $wp_post_types;
foreach ( $this->get_types() as $type ) {
$wp_post_types[ $type ]->show_ui = true;
$wp_post_types[ $type ]->show_in_menu = false;
$wp_post_types[ $type ]->_edit_link = 'post.php?post=%d';
}
}
/**
* Translate theme builder layout ids in the frontend.
*
* @param string $value The layout id.
* @param int $post_id The post it belongs to.
* @param string $key The meta key we are handling.
* @param bool $single Fetch a single row or an array.
* @return string
*/
public function translate_layout_ids( $value, $post_id, $key, $single ) {
if ( in_array( $key, [ '_et_header_layout_id', '_et_body_layout_id', '_et_footer_layout_id' ], true ) ) {
/**
* The `get_post_metadata` filter provides `null` as the initial `$value`.
* When we return a different $value it is used directly, to avoid a second query.
* This means that we have to get the original value first, removing ourselves so
* we don't fall into an infinite loop.
*/
remove_filter( 'get_post_metadata', [ $this, 'translate_layout_ids' ], 10 );
$original_id = get_post_meta( $post_id, $key, true );
add_filter( 'get_post_metadata', [ $this, 'translate_layout_ids' ], 10, 4 );
$type = substr( $key, 1, -3 );
$value = $this->sitepress->get_object_id( $original_id, $type, true );
if ( ! $single ) {
$value = [ $value ];
}
}
return $value;
}
/**
* Remove the 'View' link because you can't view layouts alone.
*
* @param string $link The complete link.
* @param string $text The text to link.
* @param object $job The corresponding translation job.
* @param string $prefix The prefix of the element type.
* @param string $type The element type.
*
* @return string
*/
public function document_view_layout_link( $link, $text, $job, $prefix, $type ) {
if ( 'post' === $prefix && $this->is_theme_layout( $type ) ) {
$link = '';
}
return $link;
}
/**
* Check if a certain Type is a theme builder layout.
*
* @param string $type The type to check.
*
* @return bool
*/
private function is_theme_layout( $type ) {
return in_array( $type, $this->get_types(), true );
}
}