WordPress как на ладони
wordpress jino

wp_insert_post() WP 1.0.0

Безопасно вставляет запись в базу данных.

Функция очищает передаваемые данные; делает некоторые проверки; заполняет пропущенные переменные: например, если не передать дату поста, то дата будет получена автоматически.

Ожидает экранированные данные. Это значит, если в функцию передаются не POST данные, то перед отправкой нужно обработать массив данных функцией wp_slash()

Имейте ввиду, в функции срабатывают хуки (например save_post) функции которых могут использоваться $_POST или $_GET данные. Эти данные могут отсутствовать в лицевой части темы (фронт-энде). Поэтому когда функция используется во фронте, это может вызвать ошибки. В таких случаях ищите ошибки в функциях хуков wp_insert_post.

Что конкретно понимается под записями, читайте в этой статье: Записи в WordPress

Категории

Категории нужно передавать в виде массива чисел, совпадающих с ID категорий в базе данных. Это относится и к случаю, когда передается одна категория.

Смотрите также: wp_set_post_terms()

Безопасность

wp_insert_post() передает данные через sanitize_post(), которая проверяет и очищает все поля данных (kses и т.д.). Поэтому нет необходимости беспокоится об очистке передаваемых данных.

Однако, возможно вам нужно будет удалить HTML, JavaScript или PHP теги из заголовка записи или других полей. WordPress не делает это автоматически. Это можно легко сделать использовав wp_strip_all_tags(), особенно когда на вашем сайте можно публиковать посты с фронт-энда (лицевая часть):

// Создаем массив данных новой записи
$post_data = array(
  'post_title'    => wp_strip_all_tags( $_POST['post_title'] ),
  'post_content'  => $_POST['post_content'],
  'post_status'   => 'publish',
  'post_author'   => 1,
  'post_category' => array( 8,39 )
);

// Вставляем запись в базу данных
$post_id = wp_insert_post( $post_data );
emoji заметка

C версии 4.2, с введением смайликов эмодзи. Функция автоматически конвертирует символы-смайликов в полях 'post_title', 'post_content', 'post_excerpt', если кодировка таблицы utf8, а не utf8mb4. К этим полям применяется функция wp_encode_emoji().

Используется в: wp_insert_attachment(), wp_update_post().
Хуки из функции:
Возвращает

Число/0/WP_Error.

  • ID записи, если удалось вставить запись.
  • 0 - при ошибке вставки и если параметр $wp_error отключен.
  • WP_Error объект с сообщением об ошибке, если не удалось добавить запись и параметр $wp_error включен.

Использование

$post_id = wp_insert_post( $postarr, $wp_error );
$postarr(массив) (обязательный)

Массив данных, которые нужно вставить в Базу Данных. Ключи массива в точности равны полям таблицы wp_posts, передаваемые значения ключей будут вставлены в значение полей таблицы.

Если передать параметр ID, то будет отредактирована уже существующая запись с таким же ID, т.е. при указании параметра ID, мы обновляем запись, а не создаем новую.

Параметры передаваемого массива:

$post = array(
	'ID'             => <post id>,                                                     // Вы обновляете существующий пост?
	'menu_order'     => <order>,                                                       // Если запись "постоянная страница", установите её порядок в меню.
	'comment_status' => 'closed' | 'open',                                             // 'closed' означает, что комментарии закрыты.
	'ping_status'    => 'closed' | 'open',                                             // 'closed' означает, что пинги и уведомления выключены.
	'pinged'         => ?,                                                             //?
	'post_author'    => <user ID>,                                                     // ID автора записи
	'post_category'  => array(<category id>, <...>),                                   // Категория к которой относится пост.
	'post_content'   => <the text of the post>,                                        // Полный текст записи.
	'post_date'      => Y-m-d H:i:s,                                                   // Время, когда запись была создана.
	'post_date_gmt'  => Y-m-d H:i:s,                                                   // Время, когда запись была создана в GMT.
	'post_excerpt'   => <an excerpt>,                                                  // Цитата (пояснительный текст) записи.
	'post_name'      => <the name>,                                                    // Альтернативное название записи (slug) будет использовано в УРЛе.
	'post_parent'    => <post ID>,                                                     // ID родительской записи, если нужно.
	'post_password'  => ?,                                                             // Пароль для просмотра записи.
	'post_status'    => 'draft' | 'publish' | 'pending'| 'future' | 'private',         // Статус создаваемой записи.
	'post_title'     => <the title>,                                                   // Заголовок (название) записи.
	'post_type'      => 'post' | 'page' | 'link' | 'nav_menu_item' | custom post type, // Тип записи.
	'tags_input'     => '<tag>, <tag>, <...>',                                         // Метки поста (указываем ярлыки).
	'tax_input'      => array( 'taxonomy_name' => array( 'term', 'term2', 'term3' ) ), // К каким таксам прикрепить запись. Аналог 'post_category', только для для новых такс.
	'to_ping'        => ?,                                                             //?
	'meta_input'     => array( 'meta_key'=>'meta_value' ),                             // добавит указанные мета поля. По умолчанию: ''. с версии 4.4.
);

Заметка: поле таблицы page_template было удалено, теперь, шаблон для постоянной страницы задается в таблице wp_postmeta, для этого в функции используется update_post_meta($post_ID, '_wp_page_template',  $page_template);

Заметка: если статус поста указывается как future, то обязательно нужно указать дату поста post_date, чтобы WordPress знал когда опубликовать запись.

Заметка: если пользователь не имеет прав работать с таксономиями то определение параметра tax_input, чтобы прикрепить запись к термину, работать не будет. Для этого нужно использовать wp_set_object_terms().

По умолчанию: нет

$wp_error(логический)
Позволяет получить WP_Error объект, в случае ошибки. Установите в true, чтобы функция вернула WP_Error объект при ошибке.
По умолчанию: false

Шаблон использования

// Вставляем запись в базу данных
$defaults = array(
	'post_status'   => 'draft',
	'post_type'     => 'post',
	'post_author'   => $user_ID,
	'ping_status'   => get_option('default_ping_status'),
	'post_parent'   => 0,
	'menu_order'    => 0,
	'to_ping'       => '',
	'pinged'        => '',
	'post_password' => '',
	'guid'          => '',
	'post_content_filtered' => '',
	'post_excerpt'  => '',
	'import_id'     => 0
);

$post_id = wp_insert_post( $defaults );

Примеры

Перед тем как использовать wp_insert_post нужно создать массив данных, который затем передать функции. В массиве, как минимум нужно указать заголовок записи (post_title) и её текст (post_content) иначе запись не создастся. Остальные поля, данные для которых не указаны, будут созданы автоматически, со значениями по умолчанию.

В массиве можно указать больше данных, чем это представлено ниже, ключи массива - это название полей таблицы wp_posts Базы Данных WordPress.

#1. Простой пример вставки новой записи в Базу Данных WordPress:

// Создаем массив
  $post_data = array(
	 'post_title'    => 'Заголовок записи',
	 'post_content'  => 'Здесь должен быть контент (текст) записи.',
	 'post_status'   => 'publish',
	 'post_author'   => 1,
	 'post_category' => array(8,39)
  );

// Вставляем данные в БД
$post_id = wp_insert_post( wp_slash($post_data) );

Категории нужно передавать в массиве, даже если указывается одна категория. Например: 'post_category' => array(8)

Смотрите также: wp_set_post_terms(), когда нужно установить произвольные таксономии для поста или типа записи.

#2. Вставим запись и получи её ID

$post_id = wp_insert_post( $post, $wp_error );
// Теперь можно использовать $post_id, чтобы например добавить
// произвольные поля записи с помощью add_post_meta или update_post_meta

Заметки

  • Использует: wp_transition_post_status()
  • Использует: _wp_put_post_revision()
  • Использует: $user_ID
  • Использует: $wpdb
  • Использует: $wp_rewrite

  • Использует: do_action() - edit_post, если производится обновление записи. Передаются ID и Данные.

  • Использует: do_action() - save_post и wp_insert_post. Передаются ID и Данные.

  • Использует: apply_filters() - wp_insert_post_data передает $data, $postarr с целью обновления/вставки данных в БД.

Код wp insert post: wp-includes/post.php WP 4.8.2

<?php
function wp_insert_post( $postarr, $wp_error = false ) {
	global $wpdb;

	$user_id = get_current_user_id();

	$defaults = array(
		'post_author' => $user_id,
		'post_content' => '',
		'post_content_filtered' => '',
		'post_title' => '',
		'post_excerpt' => '',
		'post_status' => 'draft',
		'post_type' => 'post',
		'comment_status' => '',
		'ping_status' => '',
		'post_password' => '',
		'to_ping' =>  '',
		'pinged' => '',
		'post_parent' => 0,
		'menu_order' => 0,
		'guid' => '',
		'import_id' => 0,
		'context' => '',
	);

	$postarr = wp_parse_args($postarr, $defaults);

	unset( $postarr[ 'filter' ] );

	$postarr = sanitize_post($postarr, 'db');

	// Are we updating or creating?
	$post_ID = 0;
	$update = false;
	$guid = $postarr['guid'];

	if ( ! empty( $postarr['ID'] ) ) {
		$update = true;

		// Get the post ID and GUID.
		$post_ID = $postarr['ID'];
		$post_before = get_post( $post_ID );
		if ( is_null( $post_before ) ) {
			if ( $wp_error ) {
				return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
			}
			return 0;
		}

		$guid = get_post_field( 'guid', $post_ID );
		$previous_status = get_post_field('post_status', $post_ID );
	} else {
		$previous_status = 'new';
	}

	$post_type = empty( $postarr['post_type'] ) ? 'post' : $postarr['post_type'];

	$post_title = $postarr['post_title'];
	$post_content = $postarr['post_content'];
	$post_excerpt = $postarr['post_excerpt'];
	if ( isset( $postarr['post_name'] ) ) {
		$post_name = $postarr['post_name'];
	} elseif ( $update ) {
		// For an update, don't modify the post_name if it wasn't supplied as an argument.
		$post_name = $post_before->post_name;
	}

	$maybe_empty = 'attachment' !== $post_type
		&& ! $post_content && ! $post_title && ! $post_excerpt
		&& post_type_supports( $post_type, 'editor' )
		&& post_type_supports( $post_type, 'title' )
		&& post_type_supports( $post_type, 'excerpt' );

	/**
	 * Filters whether the post should be considered "empty".
	 *
	 * The post is considered "empty" if both:
	 * 1. The post type supports the title, editor, and excerpt fields
	 * 2. The title, editor, and excerpt fields are all empty
	 *
	 * Returning a truthy value to the filter will effectively short-circuit
	 * the new post being inserted, returning 0. If $wp_error is true, a WP_Error
	 * will be returned instead.
	 *
	 * @since 3.3.0
	 *
	 * @param bool  $maybe_empty Whether the post should be considered "empty".
	 * @param array $postarr     Array of post data.
	 */
	if ( apply_filters( 'wp_insert_post_empty_content', $maybe_empty, $postarr ) ) {
		if ( $wp_error ) {
			return new WP_Error( 'empty_content', __( 'Content, title, and excerpt are empty.' ) );
		} else {
			return 0;
		}
	}

	$post_status = empty( $postarr['post_status'] ) ? 'draft' : $postarr['post_status'];
	if ( 'attachment' === $post_type && ! in_array( $post_status, array( 'inherit', 'private', 'trash', 'auto-draft' ), true ) ) {
		$post_status = 'inherit';
	}

	if ( ! empty( $postarr['post_category'] ) ) {
		// Filter out empty terms.
		$post_category = array_filter( $postarr['post_category'] );
	}

	// Make sure we set a valid category.
	if ( empty( $post_category ) || 0 == count( $post_category ) || ! is_array( $post_category ) ) {
		// 'post' requires at least one category.
		if ( 'post' == $post_type && 'auto-draft' != $post_status ) {
			$post_category = array( get_option('default_category') );
		} else {
			$post_category = array();
		}
	}

	// Don't allow contributors to set the post slug for pending review posts.
	if ( 'pending' == $post_status && !current_user_can( 'publish_posts' ) ) {
		$post_name = '';
	}

	/*
	 * Create a valid post name. Drafts and pending posts are allowed to have
	 * an empty post name.
	 */
	if ( empty($post_name) ) {
		if ( !in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) {
			$post_name = sanitize_title($post_title);
		} else {
			$post_name = '';
		}
	} else {
		// On updates, we need to check to see if it's using the old, fixed sanitization context.
		$check_name = sanitize_title( $post_name, '', 'old-save' );
		if ( $update && strtolower( urlencode( $post_name ) ) == $check_name && get_post_field( 'post_name', $post_ID ) == $check_name ) {
			$post_name = $check_name;
		} else { // new post, or slug has changed.
			$post_name = sanitize_title($post_name);
		}
	}

	/*
	 * If the post date is empty (due to having been new or a draft) and status
	 * is not 'draft' or 'pending', set date to now.
	 */
	if ( empty( $postarr['post_date'] ) || '0000-00-00 00:00:00' == $postarr['post_date'] ) {
		if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) {
			$post_date = current_time( 'mysql' );
		} else {
			$post_date = get_date_from_gmt( $postarr['post_date_gmt'] );
		}
	} else {
		$post_date = $postarr['post_date'];
	}

	// Validate the date.
	$mm = substr( $post_date, 5, 2 );
	$jj = substr( $post_date, 8, 2 );
	$aa = substr( $post_date, 0, 4 );
	$valid_date = wp_checkdate( $mm, $jj, $aa, $post_date );
	if ( ! $valid_date ) {
		if ( $wp_error ) {
			return new WP_Error( 'invalid_date', __( 'Invalid date.' ) );
		} else {
			return 0;
		}
	}

	if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) {
		if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) {
			$post_date_gmt = get_gmt_from_date( $post_date );
		} else {
			$post_date_gmt = '0000-00-00 00:00:00';
		}
	} else {
		$post_date_gmt = $postarr['post_date_gmt'];
	}

	if ( $update || '0000-00-00 00:00:00' == $post_date ) {
		$post_modified     = current_time( 'mysql' );
		$post_modified_gmt = current_time( 'mysql', 1 );
	} else {
		$post_modified     = $post_date;
		$post_modified_gmt = $post_date_gmt;
	}

	if ( 'attachment' !== $post_type ) {
		if ( 'publish' == $post_status ) {
			$now = gmdate('Y-m-d H:i:59');
			if ( mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false) ) {
				$post_status = 'future';
			}
		} elseif ( 'future' == $post_status ) {
			$now = gmdate('Y-m-d H:i:59');
			if ( mysql2date('U', $post_date_gmt, false) <= mysql2date('U', $now, false) ) {
				$post_status = 'publish';
			}
		}
	}

	// Comment status.
	if ( empty( $postarr['comment_status'] ) ) {
		if ( $update ) {
			$comment_status = 'closed';
		} else {
			$comment_status = get_default_comment_status( $post_type );
		}
	} else {
		$comment_status = $postarr['comment_status'];
	}

	// These variables are needed by compact() later.
	$post_content_filtered = $postarr['post_content_filtered'];
	$post_author = isset( $postarr['post_author'] ) ? $postarr['post_author'] : $user_id;
	$ping_status = empty( $postarr['ping_status'] ) ? get_default_comment_status( $post_type, 'pingback' ) : $postarr['ping_status'];
	$to_ping = isset( $postarr['to_ping'] ) ? sanitize_trackback_urls( $postarr['to_ping'] ) : '';
	$pinged = isset( $postarr['pinged'] ) ? $postarr['pinged'] : '';
	$import_id = isset( $postarr['import_id'] ) ? $postarr['import_id'] : 0;

	/*
	 * The 'wp_insert_post_parent' filter expects all variables to be present.
	 * Previously, these variables would have already been extracted
	 */
	if ( isset( $postarr['menu_order'] ) ) {
		$menu_order = (int) $postarr['menu_order'];
	} else {
		$menu_order = 0;
	}

	$post_password = isset( $postarr['post_password'] ) ? $postarr['post_password'] : '';
	if ( 'private' == $post_status ) {
		$post_password = '';
	}

	if ( isset( $postarr['post_parent'] ) ) {
		$post_parent = (int) $postarr['post_parent'];
	} else {
		$post_parent = 0;
	}

	/**
	 * Filters the post parent -- used to check for and prevent hierarchy loops.
	 *
	 * @since 3.1.0
	 *
	 * @param int   $post_parent Post parent ID.
	 * @param int   $post_ID     Post ID.
	 * @param array $new_postarr Array of parsed post data.
	 * @param array $postarr     Array of sanitized, but otherwise unmodified post data.
	 */
	$post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_ID, compact( array_keys( $postarr ) ), $postarr );

	/*
	 * If the post is being untrashed and it has a desired slug stored in post meta,
	 * reassign it.
	 */
	if ( 'trash' === $previous_status && 'trash' !== $post_status ) {
		$desired_post_slug = get_post_meta( $post_ID, '_wp_desired_post_slug', true );
		if ( $desired_post_slug ) {
			delete_post_meta( $post_ID, '_wp_desired_post_slug' );
			$post_name = $desired_post_slug;
		}
	}

	// If a trashed post has the desired slug, change it and let this post have it.
	if ( 'trash' !== $post_status && $post_name ) {
		wp_add_trashed_suffix_to_post_name_for_trashed_posts( $post_name, $post_ID );
	}

	// When trashing an existing post, change its slug to allow non-trashed posts to use it.
	if ( 'trash' === $post_status && 'trash' !== $previous_status && 'new' !== $previous_status ) {
		$post_name = wp_add_trashed_suffix_to_post_name_for_post( $post_ID );
	}

	$post_name = wp_unique_post_slug( $post_name, $post_ID, $post_status, $post_type, $post_parent );

	// Don't unslash.
	$post_mime_type = isset( $postarr['post_mime_type'] ) ? $postarr['post_mime_type'] : '';

	// Expected_slashed (everything!).
	$data = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_content_filtered', 'post_title', 'post_excerpt', 'post_status', 'post_type', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_parent', 'menu_order', 'post_mime_type', 'guid' );

	$emoji_fields = array( 'post_title', 'post_content', 'post_excerpt' );

	foreach ( $emoji_fields as $emoji_field ) {
		if ( isset( $data[ $emoji_field ] ) ) {
			$charset = $wpdb->get_col_charset( $wpdb->posts, $emoji_field );
			if ( 'utf8' === $charset ) {
				$data[ $emoji_field ] = wp_encode_emoji( $data[ $emoji_field ] );
			}
		}
	}

	if ( 'attachment' === $post_type ) {
		/**
		 * Filters attachment post data before it is updated in or added to the database.
		 *
		 * @since 3.9.0
		 *
		 * @param array $data    An array of sanitized attachment post data.
		 * @param array $postarr An array of unsanitized attachment post data.
		 */
		$data = apply_filters( 'wp_insert_attachment_data', $data, $postarr );
	} else {
		/**
		 * Filters slashed post data just before it is inserted into the database.
		 *
		 * @since 2.7.0
		 *
		 * @param array $data    An array of slashed post data.
		 * @param array $postarr An array of sanitized, but otherwise unmodified post data.
		 */
		$data = apply_filters( 'wp_insert_post_data', $data, $postarr );
	}
	$data = wp_unslash( $data );
	$where = array( 'ID' => $post_ID );

	if ( $update ) {
		/**
		 * Fires immediately before an existing post is updated in the database.
		 *
		 * @since 2.5.0
		 *
		 * @param int   $post_ID Post ID.
		 * @param array $data    Array of unslashed post data.
		 */
		do_action( 'pre_post_update', $post_ID, $data );
		if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) {
			if ( $wp_error ) {
				return new WP_Error('db_update_error', __('Could not update post in the database'), $wpdb->last_error);
			} else {
				return 0;
			}
		}
	} else {
		// If there is a suggested ID, use it if not already present.
		if ( ! empty( $import_id ) ) {
			$import_id = (int) $import_id;
			if ( ! $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE ID = %d", $import_id) ) ) {
				$data['ID'] = $import_id;
			}
		}
		if ( false === $wpdb->insert( $wpdb->posts, $data ) ) {
			if ( $wp_error ) {
				return new WP_Error('db_insert_error', __('Could not insert post into the database'), $wpdb->last_error);
			} else {
				return 0;
			}
		}
		$post_ID = (int) $wpdb->insert_id;

		// Use the newly generated $post_ID.
		$where = array( 'ID' => $post_ID );
	}

	if ( empty( $data['post_name'] ) && ! in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
		$data['post_name'] = wp_unique_post_slug( sanitize_title( $data['post_title'], $post_ID ), $post_ID, $data['post_status'], $post_type, $post_parent );
		$wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where );
		clean_post_cache( $post_ID );
	}

	if ( is_object_in_taxonomy( $post_type, 'category' ) ) {
		wp_set_post_categories( $post_ID, $post_category );
	}

	if ( isset( $postarr['tags_input'] ) && is_object_in_taxonomy( $post_type, 'post_tag' ) ) {
		wp_set_post_tags( $post_ID, $postarr['tags_input'] );
	}

	// New-style support for all custom taxonomies.
	if ( ! empty( $postarr['tax_input'] ) ) {
		foreach ( $postarr['tax_input'] as $taxonomy => $tags ) {
			$taxonomy_obj = get_taxonomy($taxonomy);
			if ( ! $taxonomy_obj ) {
				/* translators: %s: taxonomy name */
				_doing_it_wrong( __FUNCTION__, sprintf( __( 'Invalid taxonomy: %s.' ), $taxonomy ), '4.4.0' );
				continue;
			}

			// array = hierarchical, string = non-hierarchical.
			if ( is_array( $tags ) ) {
				$tags = array_filter($tags);
			}
			if ( current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
				wp_set_post_terms( $post_ID, $tags, $taxonomy );
			}
		}
	}

	if ( ! empty( $postarr['meta_input'] ) ) {
		foreach ( $postarr['meta_input'] as $field => $value ) {
			update_post_meta( $post_ID, $field, $value );
		}
	}

	$current_guid = get_post_field( 'guid', $post_ID );

	// Set GUID.
	if ( ! $update && '' == $current_guid ) {
		$wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where );
	}

	if ( 'attachment' === $postarr['post_type'] ) {
		if ( ! empty( $postarr['file'] ) ) {
			update_attached_file( $post_ID, $postarr['file'] );
		}

		if ( ! empty( $postarr['context'] ) ) {
			add_post_meta( $post_ID, '_wp_attachment_context', $postarr['context'], true );
		}
	}

	// Set or remove featured image.
	if ( isset( $postarr['_thumbnail_id'] ) ) {
		$thumbnail_support = current_theme_supports( 'post-thumbnails', $post_type ) && post_type_supports( $post_type, 'thumbnail' ) || 'revision' === $post_type;
		if ( ! $thumbnail_support && 'attachment' === $post_type && $post_mime_type ) {
			if ( wp_attachment_is( 'audio', $post_ID ) ) {
				$thumbnail_support = post_type_supports( 'attachment:audio', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:audio' );
			} elseif ( wp_attachment_is( 'video', $post_ID ) ) {
				$thumbnail_support = post_type_supports( 'attachment:video', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:video' );
			}
		}

		if ( $thumbnail_support ) {
			$thumbnail_id = intval( $postarr['_thumbnail_id'] );
			if ( -1 === $thumbnail_id ) {
				delete_post_thumbnail( $post_ID );
			} else {
				set_post_thumbnail( $post_ID, $thumbnail_id );
			}
		}
	}

	clean_post_cache( $post_ID );

	$post = get_post( $post_ID );

	if ( ! empty( $postarr['page_template'] ) ) {
		$post->page_template = $postarr['page_template'];
		$page_templates = wp_get_theme()->get_page_templates( $post );
		if ( 'default' != $postarr['page_template'] && ! isset( $page_templates[ $postarr['page_template'] ] ) ) {
			if ( $wp_error ) {
				return new WP_Error( 'invalid_page_template', __( 'Invalid page template.' ) );
			}
			update_post_meta( $post_ID, '_wp_page_template', 'default' );
		} else {
			update_post_meta( $post_ID, '_wp_page_template', $postarr['page_template'] );
		}
	}

	if ( 'attachment' !== $postarr['post_type'] ) {
		wp_transition_post_status( $data['post_status'], $previous_status, $post );
	} else {
		if ( $update ) {
			/**
			 * Fires once an existing attachment has been updated.
			 *
			 * @since 2.0.0
			 *
			 * @param int $post_ID Attachment ID.
			 */
			do_action( 'edit_attachment', $post_ID );
			$post_after = get_post( $post_ID );

			/**
			 * Fires once an existing attachment has been updated.
			 *
			 * @since 4.4.0
			 *
			 * @param int     $post_ID      Post ID.
			 * @param WP_Post $post_after   Post object following the update.
			 * @param WP_Post $post_before  Post object before the update.
			 */
			do_action( 'attachment_updated', $post_ID, $post_after, $post_before );
		} else {

			/**
			 * Fires once an attachment has been added.
			 *
			 * @since 2.0.0
			 *
			 * @param int $post_ID Attachment ID.
			 */
			do_action( 'add_attachment', $post_ID );
		}

		return $post_ID;
	}

	if ( $update ) {
		/**
		 * Fires once an existing post has been updated.
		 *
		 * @since 1.2.0
		 *
		 * @param int     $post_ID Post ID.
		 * @param WP_Post $post    Post object.
		 */
		do_action( 'edit_post', $post_ID, $post );
		$post_after = get_post($post_ID);

		/**
		 * Fires once an existing post has been updated.
		 *
		 * @since 3.0.0
		 *
		 * @param int     $post_ID      Post ID.
		 * @param WP_Post $post_after   Post object following the update.
		 * @param WP_Post $post_before  Post object before the update.
		 */
		do_action( 'post_updated', $post_ID, $post_after, $post_before);
	}

	/**
	 * Fires once a post has been saved.
	 *
	 * The dynamic portion of the hook name, `$post->post_type`, refers to
	 * the post type slug.
	 *
	 * @since 3.7.0
	 *
	 * @param int     $post_ID Post ID.
	 * @param WP_Post $post    Post object.
	 * @param bool    $update  Whether this is an existing post being updated or not.
	 */
	do_action( "save_post_{$post->post_type}", $post_ID, $post, $update );

	/**
	 * Fires once a post has been saved.
	 *
	 * @since 1.5.0
	 *
	 * @param int     $post_ID Post ID.
	 * @param WP_Post $post    Post object.
	 * @param bool    $update  Whether this is an existing post being updated or not.
	 */
	do_action( 'save_post', $post_ID, $post, $update );

	/**
	 * Fires once a post has been saved.
	 *
	 * @since 2.0.0
	 *
	 * @param int     $post_ID Post ID.
	 * @param WP_Post $post    Post object.
	 * @param bool    $update  Whether this is an existing post being updated or not.
	 */
	do_action( 'wp_insert_post', $post_ID, $post, $update );

	return $post_ID;
}

Cвязанные функции

Из раздела: Вставка, удаление, обновл.

wp_insert_post 54 комментария
Полезные 2 Все
  • Сергей @

    Здравствуйте Kama у меня вопрос, не могу вытолкнуть посты в базу а если точнее :
    у меня есть отдельная таблица в базе которую создает плагин пользовался через класс $wpdb далее я хочу каким то образом через хуки или хоть как то сделать так : админ заходит и добавляет пост с произвольного типа поста и этот пост не приходя в таблицу wp_posts переходил на мою созданную таблицу
    можете подсказать ? как я могу это сделать, куда копать?
    делаю всё это для оптимизации базы, т.к произвольных полей будет не мало, да и этих самых постов так же, хотелось бы это сделать правильно заранее большое спасибо

    Ответить2.8 года назад #
    • Kama4489

      Не понял, зачем вам отдельная таблица? В wp_posts как новый тип записей записывайте, если там все грамотно делать, то нагрузка сильно не увеличится. ВП кроме прочего хорошо умеет работать с этой таблицей и произвольными полями - базовые функции ВП кэшируют получаемые данные.

      Но все же если вам нужна именно отдельная таблица, то вам полностью нужно обрабатывать запись и обновление через wpdb. Для удобства работы с метаданными посмотрите в сторону стандартных ВП функций: update_metadata() add_metadata() delete_metadata() get_metadata()

      Ответить2.8 года назад #
      • Сергей @

        Делаю сайт посвященный туризму, и внутри каждого произвольного поста там будут куча всяких метаданных, ну и кроме того, там так же будут сами посты, хочу все распределить все по своим местам, именно поэтому хотел сделать отдельной таблицей, посмотрю ссылки что вы дали, думаю обязательно пригодятся, большое спасибо уже не в первый раз выручаете, у вас отличный сайт! smile

        Ответить2.8 года назад #
        • Kama4489

          Еще для метаданных рубрик рекомендую установить этот плагин http://wp-kama.ru/id_5150/taxonomy-metadata-metadannyie-dlya-taksonomiy.html он ничего не делает, но это фундамент. В его коде как раз посмотрите как можно использовать функции *_metadata(), чтобы грамотно расширить возможность использования метаданных для вашего нового типа записей с новой таблицей...

          П.С. все же не рекомендую создавать отдельную таблицу. Куда проще, просто создать новый тип записей, там по нагрузке нет ничего такого, отдельный тип записей, по сути, это и есть отдельная таблица (в общей wp_posts), когда выборка идет по типу, там по индексам SQL работает...

          Ответить2.8 года назад #
          • Сергей @

            Большое спасибо, обязательно посмотрю, так же по результату обязательно постараюсь отписаться что же вышло в итоге smile, а пока что буду биться с WordPress)

            Ответить2.8 года назад #
  • Сергей @

    Здравствуйте, Тимур.

    Если не затруднит, можете ответить на технический вопрос. Возникла необходимость перенести контент с одного блога WP на другой и настроить 301 редирект. Контент планирую перенести через стандартный сервис WP - Инструменты - Экспорт/Импорт. Дело в том, что после того, как мы создали файл экспорта и дальнейшего его импорта в новый блог. Все статьи импортированные сразу публикуются. Можно ли сделать так, чтобы они не публиковались сразу, а сохранялись в черновики?

    Буду благодарен за любую консультацию.

    Ответить2.6 года назад #
    • Я как то переносил, давно правда. Можно попробовать до переноса сделать запрос в бд, чтобы все записи с post_status ='publish'(опубликовано) изменить на post_status ='draft'(черновик) таблице posts. А потом уже их экспортировать и импортировать.

      UPDATE `wp_posts` SET `post_status` = 'draft' WHERE `post_status` = 'publish' AND `post_type`='post'

      Только перед манипуляциями сделайте резервную копию БД.

      Ответить2.6 года назад #
  • Егор @

    Как в форму добавить одно произвольное поле, но в него можно добавлять несколько значений. То есть к примеру произвольное поле parts в админке я вписываю в него двигатель, затем кнопка добавить, снова выбираю parts и вписываю коробка, как такую возможность вынести в форму добавления записей?

    Ответить2.1 года назад #
    • Kama4489

      При добавлении поля используйте add_post_meta(), где последний параметр $unique ставьте в false

      Далее, при обновлении с помощью update_post_meta() указывайте параметр $prev_value.

      П.С. Также можете записывать в одно поле массив...

      Ответить2.1 года назад #
  • campusboy1837 cайт: wp-plus.ru

    Тест производительности WordPress.

    Локальный сервер Denwer, обновление страницы 10 раз, тема Twenty Fifteen, 1 плагин - WP Page Load Stats, вставка текста 2к знаков.

    Главная страница - 1 запись
    20 queries in 0,361 seconds.
    Average load time of 0 (10 runs).
    24.32 out of 40 MB (61%) memory used.
    Peak memory usage 24.46 MB.
    
    Страница записи №1
    21 queries in 0,475 seconds.
    Average load time of 0 (10 runs).
    24.33 out of 40 MB (61%) memory used.
    Peak memory usage 24.47 MB.
    
    ---
    Главная страница - 10 записей
    21 queries in 0,597 seconds.
    Average load time of 0 (10 runs).
    24.47 out of 40 MB (61%) memory used.
    Peak memory usage 24.63 MB.
    
    Страница записи №10
    27 queries in 0,394 seconds.
    Average load time of 0 (10 runs).
    24.37 out of 40 MB (61%) memory used.
    Peak memory usage 24.53 MB.
    
    ---
    Главная страница - 1000 записей
    24 queries in 0,632 seconds.
    Average load time of 0 (10 runs).
    24.52 out of 40 MB (61%) memory used.
    Peak memory usage 24.68 MB.
    
    Страница записи №1000
    27 queries in 0,432 seconds.
    Average load time of 0 (10 runs).
    24.38 out of 40 MB (61%) memory used.
    Peak memory usage 24.53 MB
    ---
    Главная страница - 5000 записей
    21 queries in 0,600 seconds.
    Average load time of 0 (10 runs).
    24.47 out of 40 MB (61%) memory used.
    Peak memory usage 24.63 MB.
    
    Страница записи №5000
    27 queries in 0,421 seconds.
    Average load time of 0 (10 runs).
    24.37 out of 40 MB (61%) memory used.
    Peak memory usage 24.53 MB.
    ---
    ---
    Добавлено 5000 записей, характеристики добавления
    110038 queries in 358,133 seconds.
    Average load time of 32.5455 (1 runs).
    56.93 out of 40 MB (142%) memory used.
    Peak memory usage 57.09 MB.
    ---
    ---
    Главная страница - 10000 записей
    21 queries in 0,628 seconds.
    Average load time of 0 (10 runs).
    24.47 out of 40 MB (61%) memory used.
    Peak memory usage 24.63 MB.
    
    Страница записи №10000
    27 queries in 0,427 seconds.
    Average load time of 0 (10 runs).
    24.37 out of 40 MB (61%) memory used.
    Peak memory usage 24.53 MB.
    ---
    ---
    Добавлено 10000 записей, характеристики добавления
    220716 queries in 1 283,994 seconds.
    Average load time of 0 (1 runs).
    89.63 out of 40 MB (224%) memory used.
    Peak memory usage 91.25 MB.
    ---
    ---
    Главная страница - 20000 записей
    21 queries in 0,867 seconds.
    Average load time of 0.1 (10 runs).
    24.56 out of 40 MB (61%) memory used.
    Peak memory usage 24.73 MB.
    
    Страница записи №20000
    27 queries in 0,508 seconds.
    Average load time of 0 (10 runs).
    24.45 out of 40 MB (61%) memory used.
    Peak memory usage 24.61 MB.
    ---
    ---
    Вставка 50 000 записей не удалась, ошибка:
    WordPress database error: [Got a packet bigger than 'max_allowed_packet' bytes]
    UPDATE `wp_options` SET `option_value` = 'a:8063:{i:1436836979;a:1:{s:8:\"do_pings\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:2:{s:8:\"schedule\";b:0;s:4:\"args\";a:0:{}}}}i:1436837580;a:1:{s:8:\"do_pings\";a:1:{s:32:\"40cd750bba9870f18aada2478b24840a\";a:2: ...
    До появлении ошибки успело добавиться 18734 записи. Процесс был запущен перед сном, скорее всего ноутбук не дождался выполнения операции и "уснул", отключив некоторые процессы.
    ---
    ---
    Теперь добавление 100 записей стало происходить по времени как до этого добавлялось 5000 записей
    2309 queries in 323,524 seconds.
    Average load time of 323 (1 runs).
    26.01 out of 40 MB (65%) memory used.
    Peak memory usage 42.51 MB.
    До этого сделал оптимизацию Базы, выдало на всех таблицах "Table does not support optimize, doing recreate +", что привело к увеличению объема данных в таблице wp_posts с 175МБ до 212Мб. После этого ноутбук был перезапущен.
    После перезагрузки ситуация не изменилась, добавление 100 статей заняло 302 секунды.
    В настройках убрано "Форматирование->Преобразовывать смайлики" - нет изменений, добавление 100 статей составило 332 сек.
    Очищен список Сервисов обновления - добавление 100 статей заняло 274 сек.
    Добавление записей стало занимать не просто больше времени, но и сильнее нагружать процессор (до 60% вместо 10% в начале тестов) и SSD (теперь индикатор SSD ярко горит во время операции постоянно, изредка мигая, хотя в начале теста мигал очень часто и тускло).
    ---
    Главная страница - 20000 записей
    23 queries in 2,711 seconds.
    Average load time of 1.1 (10 runs).
    25.23 out of 40 MB (63%) memory used.
    Peak memory usage 35.12 MB.
    Во время теста даже без перегрузки страницы процесс mysqld потребялял до 40% процессора. Denwer перезагружен. Повтор теста.
    Первычный запуск главной страницы:
    21 queries in 1,490 seconds.
    Average load time of 1 (1 runs).
    25.19 out of 40 MB (63%) memory used.
    Peak memory usage 29.68 MB.
    10 запусков главной страницы
    21 queries in 1,445 seconds.
    Average load time of 1.1 (10 runs).
    25.18 out of 40 MB (63%) memory used.
    Peak memory usage 29.64 MB.
    Процессор даже во время простоя теста работает на 50%, его нагружает mysqld, а также SSD постоянно проводит какую-то работу. Лишь перезапуск Denwer освобождает процессор и SSD, но только до 1 запуска любой страницы тестового сайта.

    Далее тест продолжать не стал, так как добавление статей стало слишком долгим, а также постоянная нагрузка на процессор и SSD, возможно, делают тест бессмысленным. Итоговое количество статей 39300 штук.

    Параметры ноутбука CPU i5-3230M 2.6G, ОЗУ 4ГБ, SSD ocz vertex 460a, ОС Windows 7.

    Тест показывает лишь относительную производительность. Код добавления статей

    <?php
    				$text = "<p>Гордая и в открытую форточку ворвался сквозняк, шустрый. Комнате громко тикали солнечные часы поза её кружевного фартука кроме вороны. Бросился спать и в горницу вошел негр, румяный с высоким жабо. Вешала на лбу стал грызть дерево безухов носил. Сидит мой пернатый друг на уши лапшу. чеканя шаг, прошли танки. Бы так сделать! длинными зимними холодными вечерами она не пожалел. Невиданное зрелище была маша головы, туловища и жену дочерью мензурку поводу.</p>
    				<p>Составляет квадратных человека на груди. Него ни кармане у поросят находится. Форточку ворвался сквозняк, шустрый. Любила природу и взвыл от него была маруся стал грызть. Была маша шелковистые, белокурые локоны выбивались из двух яиц, сбивая. Поразила поза её лица длинными зимними холодными вечерами она вешала на друга. Объяснить, как будто все вымерли меня напала мысль кащей бессмертный хранил свою. Настоящим мужчиной и задушило дездемону приставала к автобусу бежала одевающаяся. Смерть в кармане у знамя, по полю, слегка попахивая слышала.</p>
    				<p>Гордая и огрел кукушонка лапу и задушило дездемону. Друг хомячок истинно русской натурой, очень любила природу. Природу и тут боец вспомнил, что в клетке сидит. Пальмой, открыл пасть, засунул в космос четырёх пар ног румяный. Поняли: здесь была распахнута настежь вспомнил, что постель медвежонка измята. Поняли: здесь была истинно русской натурой, очень любила. Заду у поэта прошли танки большую потенцию. Объяснить, как танкист аленушка на двор и взвыл. Приставала к автобусу бежала одевающаяся по площади чеканя шаг.</p>
    				<p>Быстро греб коромыслами моде женщина. Четырёх пар ног могли бы так сделать! длинными. Зажиточный: он имел свиней и четырёх пар ног большое животное с благодарностью. Дочерью мензурку род ходит с двойной подошвой так сделать! длинными зимними. У нее темный лес чернеется пьер безухов носил. Румяный с точками на земле. Мальчик в космос сенатскую площадь пар ног прошли танки. Поросят находится кудрявый хвостик, по площади чеканя шаг, прошли танки послал русскому. Фрукты с благодарностью виляя хвостом.</p>";
    
    				$published_posts = wp_count_posts()->publish;
    				echo 'Опубликовано: '.$published_posts.'<br>';
    				$plus_post = $published_posts + 100; // количество добавления статей определяется цифрой
    				echo 'Последняя запись будет: '.$plus_post;
    
    				while ($published_posts < $plus_post){
    					$published_posts++;
    					$my_postarr = array(
    						'post_title'    => 'Запись '.$published_posts,
    						'post_content'  => $text, // контент
    						'post_status'   => 'publish' // опубликованный пост
    					);
    					wp_insert_post( $my_postarr );
    				}
    				?>
    2
    Ответить2.1 года назад #
    • tkapluk @

      так и не получилось понять причину таких тормозов?
      Сейчас у меня 55 тыс. постов. Добавление новых 40 штук занимает 3 минуты...

      Ответить1.9 года назад #
      • tkapluk cайт: alldoodles.net @

        Проблема была в пигах. Очистка очереди крона решает проблему с производительностью при добавлении постов.

        Ответить1.1 года назад #
        • campusboy1837 cайт: wp-plus.ru

          Поделитесь кодом, пожалуйста, заинтересован. Кстати, может тогда проще отключить пинг? Тестировали?

          Ответить1.1 года назад #
          • tkapluk cайт: alldoodles.net @

            Пинг отключал но, по-моему, не помогало. unknw
            после добавления пачки постов просто чищу очередь крона:

            update_option('cron', '');

            Ответить1.1 года назад #
  • Vladislav

    Внутри самописного плагина создаю пост, после чего добавляю данные в произвольное поле, вот так

    $result = wp_insert_post( $my_post );
    update_post_meta($result, 'field_name', 'field_value');

    Но в $result ничего не возвращается. Дебаггером дошел до места, где все обрывается, вот эта строчка в wp-includes/post.php:

    do_action( 'transition_post_status', $new_status, $old_status, $post );

    Закоментировал ее – работает все, но неспокойно, правильно ли сделал? Если нет – как правильно поступить в ситуации? И почему вообще обрывается на ней? Точнее не на ней - а внутри нее, вот на этих строчках в wp-includes/plugin.php:

    do {
    		foreach ( (array) current($wp_filter[$tag]) as $the_ ) //ВОТ ТУТ!!!
    			if ( !is_null($the_['function']) )
    				call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    
    	} while ( next($wp_filter[$tag]) !== false );
    
    	array_pop($wp_current_filter);
    Ответить2 года назад #
    • Kama4489

      Нельзя этот хук убирать, он слишком важен: изменяет статус поста и обновляет число записей в такономии.

      Смотрите дальше, во время этого хука срабатывает такая функция, думаю она обрывает:

      /**
       * Hook for managing future post transitions to published.
       *
       * @since 2.3.0
       * @access private
       *
       * @see wp_clear_scheduled_hook()
       * @global wpdb $wpdb WordPress database abstraction object.
       *
       * @param string  $new_status New post status.
       * @param string  $old_status Previous post status.
       * @param WP_Post $post       Post object.
       */
      function _transition_post_status( $new_status, $old_status, $post ) {
      	global $wpdb;
      
      	if ( $old_status != 'publish' && $new_status == 'publish' ) {
      		// Reset GUID if transitioning to publish and it is empty.
      		if ( '' == get_the_guid($post->ID) )
      			$wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post->ID ) ), array( 'ID' => $post->ID ) );
      
      		/**
      		 * Fires when a post's status is transitioned from private to published.
      		 *
      		 * @since 1.5.0
      		 * @deprecated 2.3.0 Use 'private_to_publish' instead.
      		 *
      		 * @param int $post_id Post ID.
      		 */
      		do_action('private_to_published', $post->ID);
      	}
      
      	// If published posts changed clear the lastpostmodified cache.
      	if ( 'publish' == $new_status || 'publish' == $old_status) {
      		foreach ( array( 'server', 'gmt', 'blog' ) as $timezone ) {
      			wp_cache_delete( "lastpostmodified:$timezone", 'timeinfo' );
      			wp_cache_delete( "lastpostdate:$timezone", 'timeinfo' );
      		}
      	}
      
      	if ( $new_status !== $old_status ) {
      		wp_cache_delete( _count_posts_cache_key( $post->post_type ), 'counts' );
      		wp_cache_delete( _count_posts_cache_key( $post->post_type, 'readable' ), 'counts' );
      	}
      
      	// Always clears the hook in case the post status bounced from future to draft.
      	wp_clear_scheduled_hook('publish_future_post', array( $post->ID ) );
      }

      и вот остальные:

      add_action( 'transition_post_status',     '_update_term_count_on_transition_post_status', 10, 3 );
      add_action( 'transition_post_status',     '_wp_auto_add_pages_to_menu', 10, 3 );
      add_action( 'transition_post_status', '__clear_multi_author_cache' );
      Ответить2 года назад #
  • campusboy1837 cайт: wp-plus.ru

    Кстати, если в параметр tags_input передать названия тегов, то они будут созданы, если их нет. Это очень круто. К сожалению, кириллистические названия передаются в slug, то есть такие плагины как cry-to-lat бессильны. Вот бы ещё с рубриками так было, вообще шоколад smile

    1
    Ответить1.1 года назад #
    • Kama4489

      Я думаю, если в 'tax_input' указать category будет тоже самое с категориями smile

      'tax_input' => ['category' => array('new_term', 'new_term2') ]

      Обновление: а нет я ошибся, посмотрев сюда wp_set_post_terms() для древовидных таксономий, указанные термины превращаются в числа принудительно. Поэтому нельзя указать названия.

      А вот почему для меток не работает плагин cry-to-lat это не понятно - он должен срабатывать!

      Ответить1.1 года назад #
  • campusboy1837 cайт: wp-plus.ru

    Можно проделать какой-нибудь финт ушами и принудительно прописать ID статьи при использовании этой функции?

    Ответить1.1 года назад #
    • tkapluk cайт: alldoodles.net @

      При указании в параметре ID идентификатор существующего поста происходит его редактирование.

          $post = array(
      		'post_title' => $params['post_title'],
      		'post_author' => $params['post_author'],
      		'post_category' => array($params['post_category']),
      		'post_content' => $params['post_content'],
      		'post_status' => $params['post_status'],
      		'ping_status' => 'closed',
      		'post_date' => $params['post_date'],
      		'post_date_gmt' => $params['post_date_gmt'],
      		'tags_input' => implode(',', $params['tags'])
      	);
      	if (isset($params['ID']) and $params['ID'])
      		$post['ID'] = $params['ID'];    
      
      	$post_id = wp_insert_post($post);
      Ответить1.1 года назад #
      • campusboy1837 cайт: wp-plus.ru

        Я это прочёл и код функции самой поглядел. Там если записи с переданным ID нет, то она ошибку выдаст. Нужно обойти это, так как мне не редактировать надо. Мне при вставки новой записи надо жёстко указывать её ID. Я точно знаю, что она не совпадёт с другими, что она уникальна.

        Ответить1.1 года назад #
        • tkapluk cайт: alldoodles.net @

          Попробуй указать ID в параметр import_id.

          2
          Ответить1.1 года назад #
          • campusboy1837 cайт: wp-plus.ru

            Дружище, я тебя расцеловать готов! Уже чуть-чуть и началось бы строительство велосипеда с костыльной тягой. Спасибо большое!

            1
            Ответить1.1 года назад #
  • campusboy1837 cайт: wp-plus.ru

    Имейте ввиду, в функции срабатывают хуки (например save_post) при обработке которых могут использоваться $_POST или $_GET данные, которые будут отсутствовать в лицевой части темы (фронт-энде). Поэтому когда функция используется во фронт-энде, это может вызвать ошибки. В таких случаях ищите хуки используемые wp_insert_post.

    Какие ошибки? Сейчас вот делаю подобную штуку, всё как часы работает.

    1
    • Kama4489

      Это заметка о возможных ошибках во фронте. Если все правильно сделано, то их не будет, а если не грамотно то они могут быть...

  • Разработчикам "Kama"! Большое Вам спасибо, за все статью которые вы пишите! С праздниками! drinks

  • Mish

    Здраствуйте Кама !

    я тупа вставил посты в таблицу wp_posts у всех постов был (тайтл дата и контент) также я их связал в wp_relotionsheeps к нужной категории НО вордпресс хочет видить новые внесенные посты . как быть Кама?

    Ответитьмесяц назад #

Здравствуйте, !

Ваш комментарий