HEX
Server: Apache
System: Linux hvh16.mirohost.net 6.14.0-29-generic #29~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Aug 14 16:52:50 UTC 2 x86_64
User: likoholding (1037)
PHP: 7.3.33-25+0~20250707.133+debian12~1.gbp70fb14
Disabled: apache_child_terminate, dl, exec, imap_body, imap_createmailbox, imap_deletemailbox, imap_list, imap_open, imap_renamemailbox, inject_code, mb_send_mail, passthru, pcntl_alarm, pcntl_async_signals, pcntl_errno, pcntl_exec, pcntl_fork, pcntl_get_last_error, pcntl_getpriority, pcntl_setpriority, pcntl_signal, pcntl_signal_dispatch, pcntl_signal_get_handler, pcntl_sigprocmask, pcntl_sigtimedwait, pcntl_sigwaitinfo, pcntl_strerror, pcntl_wait, pcntl_waitpid, pcntl_wexitstatus, pcntl_wifcontinued, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, set_time_limit, shell_exec, symlink, system
Upload Files
File: /var/www/likoholding/liko-holding.com.ua/wp-content/plugins/polylang-pro/modules/plugins/acf.php
<?php

/**
 * Manages compatibility with Advanced Custom Fields Pro
 * Version tested 5.6.0
 *
 * @since 2.0
 */
class PLL_ACF {
	/**
	 * Initializes filters for ACF
	 *
	 * @since 2.0
	 */
	public function init() {
		add_action( 'add_meta_boxes_acf-field-group', array( $this, 'remove_sync' ) );
		add_action( 'add_meta_boxes_acf-field-group', array( $this, 'duplicate_field_group' ) );
		add_filter( 'acf/duplicate_field/type=clone', array( $this, 'duplicate_clone_field' ) );

		add_filter( 'acf/location/rule_match/page_type', array( $this, 'rule_match_page_type' ), 20, 3 ); // After ACF

		add_filter( 'pll_get_post_types', array( $this, 'get_post_types' ), 10, 2 );

		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );

		add_action( 'wp_ajax_acf_post_lang_choice', array( $this, 'acf_post_lang_choice' ) );

		add_filter( 'acf/load_value', array( $this, 'load_value' ), 10, 3 );
		add_filter( 'acf/load_value/type=repeater', array( $this, 'load_value' ), 20, 3 );
		add_filter( 'acf/load_value/type=flexible_content', array( $this, 'load_value' ), 20, 3 );

		add_filter( 'acf/update_value', array( $this, 'store_updated_field' ), 10, 3 );
		add_filter( 'acf/delete_value', array( $this, 'store_updated_field' ), 10, 3 );
		add_action( 'pll_save_term', array( $this, 'store_term_fields' ), 5, 2 ); // Before PLL_Sync_Metas

		add_filter( 'pll_copy_post_metas', array( $this, 'get_post_metas_to_copy' ), 999, 3 ); // Very late to wait for the complete list of synchronized fields
		add_filter( 'pll_copy_term_metas', array( $this, 'get_term_metas_to_copy' ), 10, 3 );
		add_filter( 'pll_translate_post_meta', array( $this, 'translate_meta' ), 10, 5 );
		add_filter( 'pll_translate_term_meta', array( $this, 'translate_meta' ), 10, 4 );
	}

	/**
	 * Deactivate synchronization for ACF field groups
	 *
	 * @since 2.1
	 */
	public function remove_sync() {
		foreach ( pll_languages_list() as $lang ) {
			remove_action( "pll_before_post_translation_{$lang}", array( PLL()->sync_post->buttons[ $lang ], 'add_icon' ) );
		}
	}

	/**
	 * Duplicate the field group if content duplication is activated
	 *
	 * @since 2.3
	 *
	 * @param object $post Current post object
	 */
	public function duplicate_field_group( $post ) {
		if ( PLL()->model->is_translated_post_type( 'acf-field-group' ) && 'post-new.php' === $GLOBALS['pagenow'] && isset( $_GET['from_post'], $_GET['new_lang'] ) ) {
			// Capability check already done in post-new.php
			check_admin_referer( 'new-post-translation' );

			$duplicate_options = get_user_meta( get_current_user_id(), 'pll_duplicate_content', true );

			$active = ! empty( $duplicate_options ) && ! empty( $duplicate_options['acf-field-group'] );

			if ( $active ) {
				acf_duplicate_field_group( (int) $_GET['from_post'], $post->ID );
				if ( function_exists( 'acf_delete_cache' ) ) {
					acf_delete_cache( 'get_fields/ID=' . $post->ID ); // Needed for ACF 5.4.0, removed in ACF 5.7.11
				}
			}
		}
	}

	/**
	 * Recursively searches a field by its name in an array of fields
	 *
	 * @since 2.3
	 *
	 * @param string $name   Field name
	 * @param array  $fields An array of fields
	 * @return string Field key, empty string if not found
	 */
	protected function search_field_by_name( $name, $fields ) {
		foreach ( $fields as $field ) {
			if ( $name === $field['name'] ) {
				return $field['key'];
			} elseif ( ! empty( $field['sub_fields'] ) && $key = $this->search_field_by_name( $name, $field['sub_fields'] ) ) {
				return $key;
			} elseif ( ! empty( $field['layouts'] ) ) {
				foreach ( $field['layouts'] as $row => $layout ) {
					if ( ! empty( $layout['sub_fields'] ) && $key = $this->search_field_by_name( $name, $layout['sub_fields'] ) ) {
						return $key;
					}
				}
			}
		}
		return '';
	}

	/**
	 * Translates a clone field when creating a new field group translation
	 *
	 * @since 2.3
	 *
	 * @param array $field
	 * @return array
	 */
	public function duplicate_clone_field( $field ) {
		if ( PLL()->model->is_translated_post_type( 'acf-field-group' ) && ! empty( $field['clone'] ) && 'post-new.php' === $GLOBALS['pagenow'] && isset( $_GET['from_post'], $_GET['new_lang'] ) ) {
			check_admin_referer( 'new-post-translation' );

			foreach ( $field['clone'] as $k => $selector ) {
				if ( acf_is_field_group_key( $selector ) ) {
					// Can't use acf_get_field_group() as it is filtered by language
					$posts = get_posts(
						array(
							'post_type'              => 'acf-field-group',
							'post_status'            => array( 'publish', 'acf-disabled', 'trash' ),
							'name'                   => $selector,
							'update_post_meta_cache' => false,
							'lang'                   => '',
						)
					);

					if ( ! empty( $posts ) && $tr_id = pll_get_post( $posts[0]->ID, $_GET['new_lang'] ) ) {
						$tr_group = acf_get_field_group( $tr_id );

						$field['clone'][ $k ] = $tr_group['key'];
					}
				} elseif ( acf_is_field_key( $selector ) ) {
					$_field    = acf_get_field( $selector );
					$ancestors = get_post_ancestors( $_field['ID'] );
					$group_id  = end( $ancestors );

					if ( $tr_id = pll_get_post( $group_id, $_GET['new_lang'] ) ) {
						$keys      = array();
						$tr_fields = acf_get_fields( $tr_id );

						if ( $key = $this->search_field_by_name( $_field['name'], $tr_fields ) ) {
							$field['clone'][ $k ] = $key;
						}
					}
				}
			}
		}
		return $field;
	}

	/**
	 * Allow page on front and page for posts translations to match the corresponding page type
	 *
	 * @since 2.0
	 *
	 * @param bool  $match
	 * @param array $rule
	 * @param array $options
	 * @return bool
	 */
	public function rule_match_page_type( $match, $rule, $options ) {
		if ( ! empty( $options['post_id'] ) ) {
			$post = get_post( $options['post_id'] );

			if ( 'front_page' === $rule['value'] && $front_page = (int) get_option( 'page_on_front' ) ) {
				$translations = pll_get_post_translations( $front_page );

				if ( '==' === $rule['operator'] ) {
					$match = in_array( $post->ID, $translations );
				} elseif ( '!=' === $rule['operator'] ) {
					$match = ! in_array( $post->ID, $translations );
				}
			} elseif ( 'posts_page' === $rule['value'] && $posts_page = (int) get_option( 'page_for_posts' ) ) {
				$translations = pll_get_post_translations( $posts_page );

				if ( '==' === $rule['operator'] ) {
					$match = in_array( $post->ID, $translations );
				} elseif ( '!=' === $rule['operator'] ) {
					$match = ! in_array( $post->ID, $translations );
				}
			}
		}

		return $match;
	}

	/**
	 * Add the Field Groups post type to the list of translatable post types
	 *
	 * @since 2.0
	 *
	 * @param array $post_types  List of post types
	 * @param bool  $is_settings True when displaying the list of custom post types in Polylang settings
	 * @return array
	 */
	public function get_post_types( $post_types, $is_settings ) {
		if ( $is_settings ) {
			$post_types['acf-field-group'] = 'acf-field-group';
		}
		return $post_types;
	}

	/**
	 * Enqueues javascript to react to a language change in the post metabox
	 *
	 * @since 2.0
	 */
	public function admin_enqueue_scripts() {
		global $pagenow, $typenow;

		if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) && ! in_array( $typenow, array( 'acf-field-group', 'attachment' ) ) && PLL()->model->is_translated_post_type( $typenow ) ) {
			$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
			wp_enqueue_script( 'pll_acf', plugins_url( '/js/acf' . $suffix . '.js', POLYLANG_FILE ), array( 'acf-input' ), POLYLANG_VERSION );
		}
	}

	/**
	 * Ajax response for changing the language in the post metabox
	 *
	 * @since 2.0
	 */
	public function acf_post_lang_choice() {
		check_ajax_referer( 'pll_language', '_pll_nonce' );

		$x = new WP_Ajax_Response();
		foreach ( $_POST['fields'] as $field ) {
			ob_start();
			acf_render_field_wrap( acf_get_field( $field ), 'div', 'label' );
			$x->Add( array( 'what' => str_replace( '_', '-', $field ), 'data' => ob_get_contents() ) );
			ob_end_clean();
		}

		$x->send();
	}

	/**
	 * Copy and possibly translate custom fields when creating a new term translation
	 *
	 * @since 2.2
	 *
	 * @param mixed  $value   Custom field value of the source term
	 * @param string $post_id Expects term_{$term_id} for a term
	 * @param array  $field   Custom field
	 * @return mixed
	 */
	public function load_value( $value, $post_id, $field ) {
		if ( 'term_0' === $post_id && isset( $_GET['taxonomy'], $_GET['from_tag'], $_GET['new_lang'] ) && taxonomy_exists( $_GET['taxonomy'] ) && $lang = PLL()->model->get_language( $_GET['new_lang'] ) ) {

			$tr_id  = acf_get_term_post_id( $_GET['taxonomy'], (int) $_GET['from_tag'] );
			$fields = get_field_objects( $tr_id );

			if ( ! empty( $fields ) ) {
				$keys = array_keys( $fields );

				/** This filter is documented in modules/sync/admin-sync.php */
				$keys = array_unique( apply_filters( 'pll_copy_term_metas', $keys, false, (int) $_GET['from_tag'], 0, $lang->slug ) );

				// Second test to load the values of subfields of accepted fields
				if ( in_array( $field['name'], $keys ) || preg_match( '#^(' . implode( '|', $keys ) . ')_(.+)#', $field['name'] ) ) {
					$value = acf_get_value( $tr_id, $field );
					$empty = null; // Parameter 1 is useless in this context
					$value = $this->translate_fields( $empty, $value, $field['name'], $field, $lang );

					if ( pll_is_translated_post_type( 'acf-field-group' ) ) {
						$references = $this->translate_fields_references( $tr_id, $lang->slug );
						$this->translate_references_in_value( $value, $references );
					}
				}
			}
		}
		return $value;
	}

	/**
	 * Store updated or deleted fields for future usage
	 *
	 * @since 2.3
	 *
	 * @param mixed $value   Not used
	 * @param mixed $post_id Not used
	 * @param array $field   Custom field
	 * @return mixed Unmodified custom field value
	 */
	public function store_updated_field( $value, $post_id, $field ) {
		$this->fields[ $field['name'] ] = $field;
		return $value;
	}

	/**
	 * Store fields when saving a term
	 *
	 * @since 2.3
	 *
	 * @param int    $term_id
	 * @param string $taxonomy
	 */
	public function store_term_fields( $term_id, $taxonomy ) {
		$this->fields = get_field_objects( acf_get_term_post_id( $taxonomy, $term_id ) );
	}

	/**
	 * Get the custom fields to copy or synchronize
	 *
	 * @since 2.3
	 *
	 * @param array $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 post from which we copy informations
	 * @return array
	 */
	public function get_post_metas_to_copy( $metas, $sync, $from ) {
		// FIXME public metas are copied if ! $sync which wastes DB requests
		if ( $sync ) {
			foreach ( get_post_custom( $from ) as $key => $value ) {
				if ( acf_is_field_key( reset( $value ) ) && array_search( substr( $key, 1 ), $metas ) ) {
					$metas[] = $key; // Private keys added to non private
				}
			}
		}

		return $metas;
	}

	/**
	 * Get the (term) custom fields to copy or synchronize
	 *
	 * @since 2.3
	 *
	 * @param array $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 term from which we copy informations
	 * @return array
	 */
	public function get_term_metas_to_copy( $metas, $sync, $from ) {
		if ( ! $sync || in_array( 'post_meta', PLL()->options['sync'] ) ) {
			foreach ( array_keys( get_term_meta( $from ) ) as $key ) {
				if ( isset( $this->fields[ $key ] ) || isset( $this->fields[ substr( $key, 1 ) ] ) ) {
					$metas[] = $key;
				}
			}
		}

		return $metas;
	}

	/**
	 * Translate a custom field before it is copied or synchronized
	 *
	 * @since 2.3
	 * @since 2.4 Added parameter $to
	 *
	 * @param mixed  $value Meta value
	 * @param string $key   Meta key
	 * @param string $lang  Language of target
	 * @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 mixed
	 */
	public function translate_meta( $value, $key, $lang, $from, $to = 0 ) {
		if ( ! empty( $value ) && $field = isset( $this->fields[ $key ] ) ? $this->fields[ $key ] : get_field_object( $key, $from ) ) {
			$create_if_not_exists = false;

			// Check if we should create translations if they don't exist
			// $to is not empty only when translating posts
			if ( ! empty( $to ) && ( $post_type = get_post_type( $to ) ) && pll_is_translated_post_type( $post_type ) ) {
				$duplicate_options    = get_user_meta( get_current_user_id(), 'pll_duplicate_content', true );
				$active               = ! empty( $duplicate_options ) && ! empty( $duplicate_options[ $post_type ] );
				$create_if_not_exists = $active || PLL()->sync_post->are_synchronized( $from, $to );
			}

			$value = $this->translate_field( $value, $lang, $field, $create_if_not_exists );
		}

		if ( pll_is_translated_post_type( 'acf-field-group' ) && acf_is_field_key( $value ) ) {
			$references = $this->translate_fields_references( $from, $lang );

			if ( isset( $references[ $value ] ) ) {
				$value = $references[ $value ];
			}
		}

		return $value;
	}

	/**
	 * Translate a CPT archive link in a page link field
	 *
	 * @since 2.3.6
	 *
	 * @param string $link CPT archive link
	 * @param string $lang Language slug
	 * @return string Modified link
	 */
	public function translate_cpt_archive_link( $link, $lang ) {
		$lang = PLL()->model->get_language( $lang );
		$link = PLL()->links_model->switch_language_in_link( $link, $lang );

		foreach ( PLL()->translate_slugs->slugs_model->get_translatable_slugs() as $type => $slugs ) {
			// Unfortunately ACF does not pass the post type, so let's try with all post type archives
			if ( 0 === strpos( $type, 'archive_' ) ) {
				$link = PLL()->translate_slugs->slugs_model->switch_translated_slug( $link, $lang, $type );
			}
		}
		return $link;
	}

	/**
	 * Translate a custom field value
	 *
	 * @since 2.3
	 * @since 2.4 Added parameter $create_if_not_exists
	 *
	 * @param mixed  $value                Custom field value
	 * @param string $lang                 Language slug
	 * @param array  $field                Custom field
	 * @param bool   $create_if_not_exists Should we create the translation if it does not exist
	 * @return mixed
	 */
	protected function translate_field( $value, $lang, $field, $create_if_not_exists = false ) {
		switch ( $field['type'] ) {
			case 'image':
			case 'file':
				if ( PLL()->options['media_support'] ) {
					if ( $tr_id = pll_get_post( $value, $lang ) ) {
						$return = $tr_id;
					} elseif ( $create_if_not_exists ) {
						$return = PLL()->posts->create_media_translation( $value, $lang );
					}
				}
				break;

			case 'gallery':
				if ( PLL()->options['media_support'] && is_array( $value ) ) {
					foreach ( $value as $img ) {
						if ( $tr_id = pll_get_post( $img, $lang ) ) {
							$return[] = $tr_id;
						} elseif ( $create_if_not_exists ) {
							$return[] = PLL()->posts->create_media_translation( $img, $lang );
						}
					}
				}
				break;

			case 'post_object':
			case 'relationship':
				if ( is_numeric( $value ) && $tr_id = pll_get_post( $value, $lang ) ) {
					$return = $tr_id;
				} elseif ( is_array( $value ) ) {
					foreach ( $value as $p ) {
						if ( $tr_id = pll_get_post( $p, $lang ) ) {
							$return[] = $tr_id;
						}
					}
				}
				break;

			case 'page_link':
				if ( is_numeric( $value ) && $tr_id = pll_get_post( $value, $lang ) ) {
					// Unique translated post
					$return = $tr_id;
				} elseif ( is_array( $value ) ) {
					// Multiple choice
					foreach ( $value as $p ) {
						if ( is_numeric( $p ) && $tr_id = pll_get_post( $p, $lang ) ) {
							$return[] = $tr_id;
						} else {
							$return[] = $this->translate_cpt_archive_link( $p, $lang ); // Archive
						}
					}
				} else {
					$return = $this->translate_cpt_archive_link( $value, $lang ); // Archive
				}
				break;

			case 'taxonomy':
				if ( pll_is_translated_taxonomy( $field['taxonomy'] ) ) {
					if ( is_numeric( $value ) && $tr_id = pll_get_term( $value, $lang ) ) {
						$return = $tr_id;
					} elseif ( is_array( $value ) ) {
						foreach ( $value as $t ) {
							if ( $tr_id = pll_get_term( $t, $lang ) ) {
								$return[] = $tr_id;
							}
						}
					}
				}
				break;
		}

		return empty( $return ) ? $value : $return;
	}

	/**
	 * Translate repeater and flexible content sub fields
	 *
	 * @since 2.2
	 *
	 * @param array  $r     Reference to a flat list of translated custom fields
	 * @param mixed  $value Custom field value
	 * @param string $name  Custom field name
	 * @param array  $field ACF field or subfield
	 * @param string $lang  Language slug
	 * @return array Hierarchical list of custom fields values
	 */
	protected function translate_sub_fields( &$r, $value, $name, $field, $lang ) {
		$return = array();

		foreach ( $value as $row => $sub_fields ) {
			$sub = array();
			foreach ( $sub_fields as $id => $sub_value ) {
				if ( $field = acf_get_field( $id ) ) {
					$sub[ $id ] = $this->translate_fields( $r, $sub_value, $name . '_' . $row . '_' . $field['name'], $field, $lang );
				} else {
					$sub[ $id ] = $sub_value;
				}
			}
			$return[] = $sub;
		}

		return $return;
	}

	/**
	 * Translate custom fields if needed
	 * Recursive for repeaters and flexible content
	 *
	 * @since 2.0
	 *
	 * @param array  $r     Reference to a flat list of translated custom fields
	 * @param mixed  $value Custom field value
	 * @param string $name  Custom field name
	 * @param array  $field ACF field or subfield
	 * @param string $lang  Language slug
	 * @return array Hierarchical list of custom fields values
	 */
	protected function translate_fields( &$r, $value, $name, $field, $lang ) {
		if ( empty( $value ) ) {
			return;
		}

		$r[ '_' . $name ] = $field['key'];

		$return = array();

		switch ( $field['type'] ) {
			case 'group':
				foreach ( $value as $id => $sub_value ) {
					if ( $field = acf_get_field( $id ) ) {
						$sub[ $id ] = $this->translate_fields( $r, $sub_value, $name . '_' . $field['name'], $field, $lang );
					} else {
						$sub[ $id ] = $sub_value;
					}
				}
				$return[] = $sub;
				break;

			case 'repeater':
			case 'flexible_content':
				$return = $this->translate_sub_fields( $r, $value, $name, $field, $lang );
				break;

			default:
				$return = $this->translate_field( $value, $lang, $field );
				break;
		}

		return empty( $return ) ? $value : $return;
	}

	/**
	 * Recursively translates the references in value for repeaters and flexible content
	 *
	 * @since 2.2
	 *
	 * @param array $value      Reference to a custom field value
	 * @param array $references List of custom fields references with source as key and translation as value
	 */
	protected function translate_references_in_value( &$value, $references ) {
		if ( is_array( $value ) ) {
			foreach ( $value as $row => $sub_fields ) {
				if ( is_array( $sub_fields ) ) {
					foreach ( $sub_fields as $id => $sub_value ) {
						if ( is_array( $sub_value ) ) {
							$this->translate_references_in_value( $sub_value, $references );
						}
						if ( isset( $references[ $id ] ) ) {
							$value[ $row ][ $references[ $id ] ] = $sub_value;
							unset( $value[ $row ][ $id ] );
						}
					}
				}
			}
		}
	}

	/**
	 * Searches for fields having the same name in translated posts
	 *
	 * @since 2.2
	 *
	 * @param int|string $from Source post id
	 * @param string     $lang Target language code
	 * @return array
	 */
	protected function translate_fields_references( $from, $lang ) {
		$keys   = array();
		$fields = get_field_objects( $from );

		if ( is_array( $fields ) ) {
			foreach ( $fields as $field ) {
				if ( $tr_group = pll_get_post( $field['parent'], $lang ) ) {
					$tr_fields = acf_get_fields( $tr_group );
					$this->translate_field_references( $keys, $field, $tr_fields );
				}
			}
		}

		return $keys;
	}

	/**
	 * Loops through sub fields in the recursive search for fields
	 * having the same name among translated fields groups
	 *
	 * @since 2.2
	 *
	 * @param array $keys
	 * @param array $fields
	 * @param array $tr_fields
	 */
	protected function translate_sub_fields_references( &$keys, $fields, $tr_fields ) {
		foreach ( $fields as $field ) {
			$this->translate_field_references( $keys, $field, $tr_fields );
		}
	}

	/**
	 * Recursively searches for fields having the same name among translated fields groups
	 *
	 * @since 2.2
	 *
	 * @param array $keys
	 * @param array $field
	 * @param array $tr_fields
	 */
	protected function translate_field_references( &$keys, $field, $tr_fields ) {
		$k = array_search( $field['name'], wp_list_pluck( $tr_fields, 'name' ) );
		if ( false !== $k ) {
			$keys[ $field['key'] ] = $tr_fields[ $k ]['key'];
			if ( ! empty( $field['sub_fields'] ) ) {
				$this->translate_sub_fields_references( $keys, $field['sub_fields'], $tr_fields[ $k ]['sub_fields'] );
			}

			if ( ! empty( $field['layouts'] ) ) {
				foreach ( $field['layouts'] as $row => $layout ) {
					if ( ! empty( $layout['sub_fields'] ) ) {
						$this->translate_sub_fields_references( $keys, $layout['sub_fields'], $tr_fields[ $k ]['layouts'][ $row ]['sub_fields'] );
					}
				}
			}
		}
	}
}