As the sun rises and the forest mist clears, and the clouds return and the caves darken, these changes of light and shadow are the morning and evening in the mountains. Wildflowers bloom with their subtle fragrance, fine trees flourish with their dense shade, the wind and frost are pure and clean, and the water recedes to reveal the rocks—these are the four seasons in the mountains. Going out in the morning and returning in the evening, the scenery of the four seasons is different, and the joy is endless.至于负者歌于途,行者休于树,前者呼,后者应,伛偻提携,往来而不绝者,滁人游也。临溪而渔,溪深而鱼肥,酿泉为酒,泉香而酒洌,山肴野蔌,杂然而前陈者,太守宴也。宴酣之乐,非丝非竹,射者中,弈者胜,觥筹交错,起坐而喧哗者,众宾欢也。苍颜白发,颓然乎其间者,太守醉也。 HEX
HEX
Server: Apache
System: Linux webd003.cluster106.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
User: labeautef (51223)
PHP: 8.0.30
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/labeautef/labeautedegaby.com/wp-content/plugins/polylang/src/crud-posts.php
<?php
/**
 * @package Polylang
 */

use WP_Syntex\Polylang\REST\Request;
use WP_Syntex\Polylang\Capabilities\Capabilities;
use WP_Syntex\Polylang\Capabilities\Create\Post as Create_Post;

/**
 * Adds actions and filters related to languages when creating, updating or deleting posts.
 * Actions and filters triggered when reading posts are handled separately.
 *
 * @since 2.4
 */
class PLL_CRUD_Posts {
	/**
	 * @var PLL_Model
	 */
	protected $model;

	/**
	 * Preferred language to assign to a new post.
	 *
	 * @var PLL_Language|null
	 */
	protected $pref_lang;

	/**
	 * Current language.
	 *
	 * @var PLL_Language|null
	 */
	protected $curlang;

	/**
	 * Reference to the Polylang options array.
	 *
	 * @var \WP_Syntex\Polylang\Options\Options
	 */
	protected $options;

	/**
	 * Reference to the Polylang Request object.
	 *
	 * @var Request
	 */
	private $request;

	/**
	 * Constructor
	 *
	 * @since 2.4
	 *
	 * @param PLL_Base $polylang The Polylang object.
	 */
	public function __construct( PLL_Base &$polylang ) {
		$this->options   = $polylang->options;
		$this->model     = &$polylang->model;
		$this->pref_lang = &$polylang->pref_lang;
		$this->curlang   = &$polylang->curlang;
		$this->request   = &$polylang->request;

		add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
		add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 4 );
		add_filter( 'wp_insert_post_parent', array( $this, 'wp_insert_post_parent' ), 10, 2 );
		add_action( 'before_delete_post', array( $this, 'delete_post' ) );
		add_action( 'post_updated', array( $this, 'force_tags_translation' ), 10, 3 );

		// Specific for media
		if ( $polylang->options['media_support'] ) {
			add_action( 'add_attachment', array( $this, 'set_default_language' ) );
			add_action( 'delete_attachment', array( $this, 'delete_post' ) );
			add_filter( 'wp_delete_file', array( $this, 'wp_delete_file' ) );
		}
	}

	/**
	 * Allows to set a language by default for posts if it has no language yet.
	 *
	 * @since 1.5
	 *
	 * @param int $post_id Post ID.
	 * @return void
	 */
	public function set_default_language( $post_id ) {
		if ( is_multisite() && ms_is_switched() && ! $this->model->has_languages() ) {
			return;
		}

		if ( $this->model->post->get_language( $post_id ) ) {
			return;
		}

		$post_language = new Create_Post(
			$this->model,
			$this->request,
			$this->pref_lang instanceof PLL_Language ? $this->pref_lang : null, // Can be `false` as well...
			$this->curlang instanceof PLL_Language ? $this->curlang : null // Can be `false` as well...
		);

		$this->model->post->set_language(
			$post_id,
			$post_language->get_language( (int) $post_id )
		);
	}

	/**
	 * Called when a post ( or page ) is saved, published or updated.
	 *
	 * @since 0.1
	 * @since 2.3 Does not save the language and translations anymore, unless the post has no language yet.
	 *
	 * @param int     $post_id Post id of the post being saved.
	 * @param WP_Post $post    The post being saved.
	 * @return void
	 */
	public function save_post( $post_id, $post ) {
		if ( is_multisite() && ms_is_switched() && ! $this->model->has_languages() ) {
			return;
		}

		if ( ! $this->model->is_translated_post_type( $post->post_type ) ) {
			return;
		}

		if ( $id = wp_is_post_revision( $post_id ) ) {
			$post_id = $id;
		}

		$lang = $this->model->post->get_language( $post_id );

		if ( empty( $lang ) ) {
			$this->set_default_language( $post_id );
		}

		/**
		 * Fires after the post language and translations are saved.
		 *
		 * @since 1.2
		 *
		 * @param int     $post_id      Post id.
		 * @param WP_Post $post         Post object.
		 * @param int[]   $translations The list of translations post ids.
		 */
		do_action( 'pll_save_post', $post_id, $post, $this->model->post->get_translations( $post_id ) );
	}

	/**
	 * Makes sure that saved terms are in the right language.
	 *
	 * @since 2.3
	 *
	 * @param int            $object_id Object ID.
	 * @param int[]|string[] $terms     An array of object term IDs or slugs.
	 * @param int[]          $tt_ids    An array of term taxonomy IDs.
	 * @param string         $taxonomy  Taxonomy slug.
	 * @return void
	 */
	public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy ) {
		static $avoid_recursion;

		if ( $avoid_recursion || empty( $terms ) || ! is_array( $terms ) || empty( $tt_ids )
			|| ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
			return;
		}

		$lang = $this->model->post->get_language( $object_id );

		if ( empty( $lang ) ) {
			return;
		}

		// Use the term_taxonomy_ids to get all the requested terms in 1 query.
		$new_terms = get_terms(
			array(
				'taxonomy'         => $taxonomy,
				'term_taxonomy_id' => array_map( 'intval', $tt_ids ),
				'lang'             => '',
			)
		);

		if ( empty( $new_terms ) || ! is_array( $new_terms ) ) {
			// Terms not found.
			return;
		}

		$new_term_ids_translated = $this->translate_terms( $new_terms, $taxonomy, $lang );

		// Query the object's term.
		$orig_terms = get_terms(
			array(
				'taxonomy'   => $taxonomy,
				'object_ids' => $object_id,
				'lang'       => '',
			)
		);

		if ( is_array( $orig_terms ) ) {
			$orig_term_ids            = wp_list_pluck( $orig_terms, 'term_id' );
			$orig_term_ids_translated = $this->translate_terms( $orig_terms, $taxonomy, $lang );

			// Terms that are not in the translated list.
			$remove_term_ids = array_diff( $orig_term_ids, $orig_term_ids_translated );

			if ( ! empty( $remove_term_ids ) ) {
				wp_remove_object_terms( $object_id, $remove_term_ids, $taxonomy );
			}
		} else {
			$orig_term_ids            = array();
			$orig_term_ids_translated = array();
		}

		// Terms to add.
		$add_term_ids = array_unique( array_merge( $orig_term_ids_translated, $new_term_ids_translated ) );
		$add_term_ids = array_diff( $add_term_ids, $orig_term_ids );

		if ( ! empty( $add_term_ids ) ) {
			$avoid_recursion = true;
			wp_set_object_terms( $object_id, $add_term_ids, $taxonomy, true ); // Append.
			$avoid_recursion = false;
		}
	}

	/**
	 * Make sure that the post parent is in the correct language.
	 *
	 * @since 1.8
	 *
	 * @param int $post_parent Post parent ID.
	 * @param int $post_id     Post ID.
	 * @return int
	 */
	public function wp_insert_post_parent( $post_parent, $post_id ) {
		$lang = $this->model->post->get_language( $post_id );
		$parent_post_type = $post_parent > 0 ? get_post_type( $post_parent ) : null;
		// Dont break the hierarchy in case the post has no language
		if ( ! empty( $lang ) && ! empty( $parent_post_type ) && $this->model->is_translated_post_type( $parent_post_type ) ) {
			$post_parent = $this->model->post->get_translation( $post_parent, $lang );
		}

		return $post_parent;
	}

	/**
	 * Called when a post, page or media is deleted
	 * Don't delete translations if this is a post revision thanks to AndyDeGroo who caught this bug
	 * http://wordpress.org/support/topic/plugin-polylang-quick-edit-still-breaks-translation-linking-of-pages-in-072
	 *
	 * @since 0.1
	 *
	 * @param int $post_id Post ID.
	 * @return void
	 */
	public function delete_post( $post_id ) {
		if ( ! wp_is_post_revision( $post_id ) ) {
			$this->model->post->delete_translation( $post_id );
		}
	}

	/**
	 * Prevents WP deleting files when there are still media using them.
	 *
	 * @since 0.9
	 *
	 * @param string $file Path to the file to delete.
	 * @return string Empty or unmodified path.
	 */
	public function wp_delete_file( $file ) {
		global $wpdb;

		$uploadpath = wp_upload_dir();

		// Get the main attached file.
		$attached_file = substr_replace( $file, '', 0, strlen( trailingslashit( $uploadpath['basedir'] ) ) );
		$attached_file = preg_replace( '#-\d+x\d+\.([a-z]+)$#', '.$1', $attached_file );

		$ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT post_id FROM $wpdb->postmeta
				WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
				$attached_file
			)
		);

		if ( ! empty( $ids ) ) {
			return ''; // Prevent deleting the file.
		}

		return $file;
	}

	/**
	 * Creates a media translation
	 *
	 * @since 1.8
	 * @since 3.7 Deprecated in favor of PLL_Translated_Post::create_media_translation().
	 *
	 * @param int                 $post_id Original attachment id.
	 * @param string|PLL_Language $lang    New translation language.
	 * @return int Attachment id of the translated media.
	 */
	public function create_media_translation( $post_id, $lang ) {
		_deprecated_function( __METHOD__, '3.7', 'PLL_Translated_Post::create_media_translation()' );
		return $this->model->post->create_media_translation( $post_id, $lang );
	}

	/**
	 * Ensure that tags are in the correct language when a post is updated, due to `tags_input` parameter being removed in `wp_update_post()`.
	 *
	 * @since 3.4.5
	 *
	 * @param int     $post_id      Post ID, unused.
	 * @param WP_Post $post_after   Post object following the update.
	 * @param WP_Post $post_before  Post object before the update.
	 * @return void
	 */
	public function force_tags_translation( $post_id, $post_after, $post_before ) {
		if ( ! is_object_in_taxonomy( $post_before->post_type, 'post_tag' ) ) {
			return;
		}

		$terms = get_the_terms( $post_before, 'post_tag' );

		if ( empty( $terms ) || ! is_array( $terms ) ) {
			return;
		}

		$term_ids = wp_list_pluck( $terms, 'term_id' );

		// Let's ensure that `PLL_CRUD_Posts::set_object_terms()` will do its job.
		wp_set_post_terms( $post_id, $term_ids, 'post_tag' );
	}

	/**
	 * Makes sure that all terms in the given list are in the given language.
	 * If not the case, the terms are translated or created (for a hierarchical taxonomy, terms are created recursively).
	 *
	 * @since 3.5
	 *
	 * @param WP_Term[]    $terms    List of terms to translate.
	 * @param string       $taxonomy The terms' taxonomy.
	 * @param PLL_Language $language The language to translate the terms into.
	 * @return int[] List of `term_id`s.
	 *
	 * @phpstan-return array<positive-int>
	 */
	private function translate_terms( array $terms, string $taxonomy, PLL_Language $language ): array {
		$term_ids_translated = array();

		foreach ( $terms as $term ) {
			$term_ids_translated[] = $this->translate_term( $term, $taxonomy, $language );
		}

		return array_filter( $term_ids_translated );
	}

	/**
	 * Translates the given term into the given language.
	 * If the translation doesn't exist, it is created (for a hierarchical taxonomy, terms are created recursively).
	 *
	 * @since 3.5
	 *
	 * @param WP_Term      $term     The term to translate.
	 * @param string       $taxonomy The term's taxonomy.
	 * @param PLL_Language $language The language to translate the term into.
	 * @return int A `term_id` on success, `0` on failure.
	 *
	 * @phpstan-return int<0, max>
	 */
	private function translate_term( WP_Term $term, string $taxonomy, PLL_Language $language ): int {
		// Check if the term is in the correct language or if a translation exists.
		$tr_term_id = $this->model->term->get( $term->term_id, $language );

		if ( ! empty( $tr_term_id ) ) {
			// Already in the correct language.
			return $tr_term_id;
		}

		// Or choose the correct language for tags (initially defined by name).
		$tr_term_id = $this->model->term_exists( $term->name, $taxonomy, $term->parent, $language );

		if ( ! empty( $tr_term_id ) ) {
			return $tr_term_id;
		}

		// Or create the term in the correct language.
		$tr_parent_term_id = 0;

		if ( $term->parent > 0 && is_taxonomy_hierarchical( $taxonomy ) ) {
			$parent = get_term( $term->parent, $taxonomy );

			if ( $parent instanceof WP_Term ) {
				// Translate the parent recursively.
				$tr_parent_term_id = $this->translate_term( $parent, $taxonomy, $language );
			}
		}

		$lang_callback   = function ( $lang, $tax, $slug ) use ( $language, $term, $taxonomy ) {
			if ( ! $lang instanceof PLL_Language && $tax === $taxonomy && $slug === $term->slug ) {
				return $language;
			}
			return $lang;
		};
		$parent_callback = function ( $parent_id, $tax, $slug ) use ( $tr_parent_term_id, $term, $taxonomy ) {
			if ( empty( $parent_id ) && $tax === $taxonomy && $slug === $term->slug ) {
				return $tr_parent_term_id;
			}
			return $parent_id;
		};
		add_filter( 'pll_inserted_term_language', $lang_callback, 10, 3 );
		add_filter( 'pll_inserted_term_parent', $parent_callback, 10, 3 );
		$new_term_info = wp_insert_term(
			$term->name,
			$taxonomy,
			array(
				'parent' => $tr_parent_term_id,
				'slug'   => $term->slug, // Useless but prevents the use of `sanitize_title()` and for consistency with `$lang_callback`.
			)
		);
		remove_filter( 'pll_inserted_term_language', $lang_callback );
		remove_filter( 'pll_inserted_term_parent', $parent_callback );

		if ( is_wp_error( $new_term_info ) ) {
			// Term creation failed.
			return 0;
		}

		$tr_term_id = max( 0, (int) $new_term_info['term_id'] );

		if ( empty( $tr_term_id ) ) {
			return 0;
		}

		$this->model->term->set_language( $tr_term_id, $language );

		$trs = $this->model->term->get_translations( $term->term_id );

		$trs[ $language->slug ] = $tr_term_id;

		$this->model->term->save_translations( $term->term_id, $trs );

		return $tr_term_id;
	}
}