306 lines
9.8 KiB
PHP
306 lines
9.8 KiB
PHP
<?php
|
|
/**
|
|
* @package Polylang-Pro
|
|
*/
|
|
|
|
/**
|
|
* This class is part of the ACF compatibility.
|
|
* Adds a field setting to decide if the field must be copied, translated or synchronized.
|
|
* And handles the list of metas to copy.
|
|
*
|
|
* @since 2.7
|
|
*/
|
|
class PLL_ACF_Sync_Metas {
|
|
|
|
/**
|
|
* Constructor.
|
|
* Setups actions and filters.
|
|
*
|
|
* @since 2.7
|
|
*/
|
|
public function __construct() {
|
|
// Adds the field setting, except for fields of type layout.
|
|
foreach ( acf_get_field_types() as $type ) { // Since ACF 5.6.0.
|
|
if ( 'layout' !== $type->category ) {
|
|
add_action( "acf/render_field_settings/type={$type->name}", array( $this, 'render_field_settings' ) );
|
|
}
|
|
}
|
|
|
|
// Get the metas to copy or synchronize.
|
|
add_filter( 'pll_copy_post_metas', array( $this, 'copy_metas' ), 1, 4 );
|
|
add_filter( 'pll_copy_term_metas', array( $this, 'copy_term_metas' ), 1, 4 );
|
|
|
|
// Get the private metas to synchronize. Very late to wait for the complete list.
|
|
add_filter( 'pll_copy_post_metas', array( $this, 'copy_private_metas' ), 999, 3 );
|
|
add_filter( 'pll_copy_term_metas', array( $this, 'copy_private_metas' ), 998, 3 );
|
|
|
|
// It handles the translations of meta fields for import export.
|
|
add_filter( 'pll_post_metas_to_export', array( $this, 'get_fields_to_export' ), 10, 2 );
|
|
add_filter( 'pll_term_metas_to_export', array( $this, 'get_fields_to_export' ), 10, 2 );
|
|
}
|
|
|
|
/**
|
|
* Renders the translations setting.
|
|
*
|
|
* @since 2.7
|
|
*
|
|
* @param array $field ACF field.
|
|
* @param array $choices An array of choices for the select (value as key and label as value).
|
|
* @param string $default Default value for the select.
|
|
* @return void
|
|
*/
|
|
protected function render_field_setting( $field, $choices, $default ) {
|
|
acf_render_field_setting( // Since ACF 5.7.10.
|
|
$field,
|
|
array(
|
|
'label' => __( 'Translations', 'polylang-pro' ),
|
|
'instructions' => '',
|
|
'name' => 'translations',
|
|
'type' => 'select',
|
|
'choices' => $choices,
|
|
'default_value' => $default,
|
|
),
|
|
false // The setting is depending on the type of field.
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Renders a default translations setting (no translate option).
|
|
*
|
|
* @since 2.7
|
|
* @since 3.3.1 Renamed and merged two methods.
|
|
*
|
|
* @param array $field ACF field.
|
|
* @return void
|
|
*/
|
|
public function render_field_settings( $field ) {
|
|
$choices = array(
|
|
'ignore' => __( 'Ignore', 'polylang-pro' ),
|
|
'copy_once' => __( 'Copy once', 'polylang-pro' ),
|
|
'sync' => __( 'Synchronize', 'polylang-pro' ),
|
|
);
|
|
$default = in_array( 'post_meta', PLL()->options['sync'] ) ? 'sync' : 'copy_once';
|
|
|
|
switch ( $field['type'] ) {
|
|
case 'text':
|
|
case 'textarea':
|
|
case 'wysiwyg':
|
|
if ( empty( $field['ID'] ) ) { // Workaround a bug in ACF which doesn't save options added after a field has been created.
|
|
$default = 'translate';
|
|
}
|
|
// Intentional fall-through to add the translate option below.
|
|
|
|
case 'oembed':
|
|
case 'url':
|
|
case 'email':
|
|
// Add translate option at the 3rd position.
|
|
$choices = array_merge(
|
|
array_slice( $choices, 0, 2 ),
|
|
array( 'translate' => __( 'Translate', 'polylang-pro' ) ),
|
|
array_slice( $choices, -1 )
|
|
);
|
|
break;
|
|
}
|
|
$this->render_field_setting( $field, $choices, $default );
|
|
}
|
|
|
|
/**
|
|
* Recursively constructs the map of translations properties for all metas.
|
|
*
|
|
* @since 2.7
|
|
* @since 3.2 Added the $sync_layout parameter.
|
|
*
|
|
* @param array $translations Reference to an array of meta keys.
|
|
* @param string $name Meta key.
|
|
* @param array $value ACF field value.
|
|
* @param array $field ACF field.
|
|
* @param bool $sync_layout Whether the layout field must be synchronized, passsed by reference.
|
|
* @return void
|
|
*/
|
|
protected function get_translations( &$translations, $name, $value, $field, &$sync_layout = false ) {
|
|
switch ( $field['type'] ) {
|
|
case 'group':
|
|
foreach ( $field['sub_fields'] as $sub_field ) {
|
|
if ( isset( $value[ $sub_field['name'] ] ) ) {
|
|
$this->get_translations( $translations, "{$name}_{$sub_field['name']}", $value[ $sub_field['name'] ], $sub_field, $sync_layout );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'repeater':
|
|
if ( is_array( $value ) ) {
|
|
foreach ( array_keys( $value ) as $row ) {
|
|
foreach ( $field['sub_fields'] as $sub_field ) {
|
|
if ( is_array( $value[ $row ] ) && isset( $value[ $row ][ $sub_field['name'] ] ) ) {
|
|
$this->get_translations( $translations, "{$name}_{$row}_{$sub_field['name']}", $value[ $row ][ $sub_field['name'] ], $sub_field, $sync_layout );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// A child field is synchronized or translatable. Let's synchronize the repeater.
|
|
if ( $sync_layout ) {
|
|
$translations['sync'][] = $name;
|
|
}
|
|
break;
|
|
|
|
case 'flexible_content':
|
|
if ( is_array( $value ) ) {
|
|
foreach ( array_keys( $value ) as $row ) {
|
|
foreach ( $field['layouts'] as $layout ) {
|
|
foreach ( $layout['sub_fields'] as $sub_field ) {
|
|
if ( is_array( $value[ $row ] ) && isset( $value[ $row ][ $sub_field['name'] ] ) ) {
|
|
$this->get_translations( $translations, "{$name}_{$row}_{$sub_field['name']}", $value[ $row ][ $sub_field['name'] ], $sub_field, $sync_layout );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// A child field is synchronized or translatable. Let's synchronize the flexible.
|
|
if ( $sync_layout ) {
|
|
$translations['sync'][] = $name;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if ( isset( $field['translations'] ) ) {
|
|
$translations[ $field['translations'] ][] = $name;
|
|
|
|
// If a field is synchronize or translatable, then all its parent layout fields must be synchronized.
|
|
if ( 'sync' === $field['translations'] || 'translate' === $field['translations'] ) {
|
|
$sync_layout = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the meta fields to translate.
|
|
*
|
|
* @since 3.3
|
|
*
|
|
* @param array $keys Array of metas keys to translate.
|
|
* @param int $from ID of the source object.
|
|
* @return array Array of updated metas keys to translate.
|
|
*/
|
|
public function get_fields_to_export( $keys, $from ) {
|
|
$translations = $this->get_translation_options( $from );
|
|
|
|
$fields_to_export = array();
|
|
if ( ! empty( $translations['translate'] ) ) {
|
|
$fields_to_export = array_fill_keys( array_values( $translations['translate'] ), 1 );
|
|
}
|
|
|
|
return array_merge( $keys, $fields_to_export );
|
|
}
|
|
|
|
/**
|
|
* Gets the translation options.
|
|
*
|
|
* @since 2.7
|
|
* @since 3.3 The function has been split from copy_metas().
|
|
*
|
|
* @param string|int $from Id of the object from which we copy information.
|
|
* @param string|int $to Id of the object to which we copy information.
|
|
* @param bool $sync True if it is synchronization, false if it is a copy.
|
|
* @return array[] A list of arrays 'ignore', 'copy_once', 'translate' and 'sync' with their associated metas.
|
|
*
|
|
* @phpstan-return array{'ignore': array<int, string>, 'copy_once': array<int, string>, 'translate': array<int, string>, 'sync': array<int, string>}
|
|
*/
|
|
protected function get_translation_options( $from, $to = 0, $sync = false ) {
|
|
// Init the translations array.
|
|
$translations = array_fill_keys(
|
|
array(
|
|
'ignore',
|
|
'copy_once',
|
|
'translate',
|
|
'sync',
|
|
),
|
|
array()
|
|
);
|
|
|
|
$objects = get_field_objects( $from );
|
|
|
|
if ( $sync && empty( $objects ) ) {
|
|
// When saving a translation for the first, we don't have any field objects, so let's try to use fields of the target instead.
|
|
$objects = get_field_objects( $to );
|
|
}
|
|
|
|
// Get the metas sorted by their translations setting.
|
|
if ( is_array( $objects ) ) {
|
|
foreach ( $objects as $name => $object ) {
|
|
if ( ! empty( $object['value'] ) ) {
|
|
$this->get_translations( $translations, $name, $object['value'], $object );
|
|
}
|
|
}
|
|
}
|
|
|
|
return $translations;
|
|
}
|
|
|
|
/**
|
|
* Selects the metas to be copied or synchronized.
|
|
*
|
|
* @since 2.7
|
|
*
|
|
* @param string[] $metas List of custom fields names.
|
|
* @param bool $sync True if it is synchronization, false if it is a copy.
|
|
* @param string|int $from Id of the object from which we copy information.
|
|
* @param string|int $to Id of the object to which we copy information.
|
|
* @return string[]
|
|
*/
|
|
public function copy_metas( $metas, $sync, $from, $to ) {
|
|
$translations = $this->get_translation_options( $from, $to, $sync );
|
|
|
|
if ( $sync ) {
|
|
$metas = array_diff( $metas, $translations['ignore'], $translations['copy_once'], $translations['translate'] );
|
|
$metas = array_merge( $metas, $translations['sync'] );
|
|
} else {
|
|
$metas = array_diff( $metas, $translations['ignore'] );
|
|
$metas = array_merge( $metas, $translations['sync'], $translations['copy_once'], $translations['translate'] );
|
|
}
|
|
|
|
return $metas;
|
|
}
|
|
|
|
/**
|
|
* Selects the term metas to be copied or synchronized.
|
|
*
|
|
* @since 2.7.4
|
|
*
|
|
* @param string[] $metas 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 object from which we copy informations.
|
|
* @param int $to Id of the object to which we copy informations.
|
|
* @return string[]
|
|
*/
|
|
public function copy_term_metas( $metas, $sync, $from, $to ) {
|
|
return $this->copy_metas( $metas, $sync, 'term_' . $from, 'term_' . $to );
|
|
}
|
|
|
|
/**
|
|
* Selects the private ACF metas to be synchronized.
|
|
*
|
|
* @since 2.7
|
|
*
|
|
* @param string[] $metas 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 object from which we copy informations.
|
|
* @return string[]
|
|
*/
|
|
public function copy_private_metas( $metas, $sync, $from ) {
|
|
$meta_type = substr( current_filter(), 9, 4 );
|
|
|
|
foreach ( get_metadata( $meta_type, $from ) as $key => $value ) {
|
|
$value = reset( $value );
|
|
if ( is_string( $value ) && acf_is_field_key( $value ) ) {
|
|
$metas[] = $key; // Private keys added to non private.
|
|
}
|
|
}
|
|
|
|
return $metas;
|
|
}
|
|
}
|