Añadir un post desde el frontend con imagen destacada

Esta semana me he encontrado con un problema:
Un cliente nos pidió que sus usuarios de BuddyPress pudiesen publicar post desde el frontend, sin tener que entrar el backend.
Estos post tienen que permitir añadir links, videos e imagen destacada.
Creamos los Custom Post Types (CPT) necesarios y les añadimos un formulario a la página donde se mostrarían los loops de cada CPT para poder añadir post nuevos por los usuarios.
El mayor reto fue poder insertar las imágenes en los post.

Lo primero es crear el formulario con sus campos:

<div class="cpt_form_container">

	<!-- Es IMPORTANTE añadir 'enctype="multipart/form-data"' al formulario para permitir la carga de datos y archivos -->
	<form action="" method="post" id="whats-new-form" name="whats-new-form" enctype="multipart/form-data">

		<div id="whats-new-avatar">
			<!-- AVATAR DEL USUARIO -->
			<a href="<?php echo bp_loggedin_user_domain(); ?>">
				<?php bp_loggedin_user_avatar('width=' . bp_core_avatar_thumb_width() . '&height=' . bp_core_avatar_thumb_height()); ?>
			</a> <!-- #whats-new-avatar -->
		</div>

		<div id="whats-new-content">

			<p class="titulo-cpt">
				<?php _e('Titula tu asociación', 'ainder'); ?>
			</p>
			<input type="text" class="bp-suggestions" name="titulo-asociate" id="titulo-cpt" maxlength="60"></input>

			<p class="input-cpt">
				<?php printf(__('%s, explica en qué consiste y lo que necesitas', 'ainder'), bp_get_user_firstname(bp_get_loggedin_user_fullname())); ?>
			</p>
			<textarea class="bp-suggestions" name="contenido-asociate" id="whats-new" required maxlength="800"></textarea>

			<p class="input-cpt">
				<?php _e('País', 'ainder'); ?>
			</p>
			<input type="text" class="bp-suggestions" name="pais-asociate" id="pais-cpt" required></input>

			<p class="input-cpt">
				<?php _e('Localidad', 'ainder'); ?>
			</p>
			<input type="text" class="bp-suggestions" name="localidad-asociate" id="localidad-cpt" required></input>

			<p class="input-cpt">
				<?php _e('Periodo de máxima vigencia de la oferta', 'ainder'); ?>
			</p>
			<input type="date" class="bp-suggestions" name="tiempo-asociate" id="tiempo-cpt" min="<?php echo date('Y-m-d') ?>" required></input>

			<p class="input-cpt">
				<?php _e('Añade una imagen', 'ainder'); ?>
			</p>
			<input type="file" class="bp-suggestions fileinputs" name="imagen-asociate" id="imagen-cpt">

			<p class="input-cpt">
				<?php _e('Añade un video', 'ainder'); ?>
			</p>
			<input type="url" class="bp-suggestions" title="Si pegas el enlace para compartir de Youtube, aparecerá el vídeo, si sólo pegas la ruta normal aparecerá el enlace vinculado al vídeo" name="video-asociate" id="video-cpt" cols="50" rows="10">

			<p class="input-cpt">
				<?php _e('Añade un enlace', 'ainder'); ?>
			</p>
			<input type="url" class="bp-suggestions" name="enlace-asociate" id="enlace-cpt">

			<!-- Boton -->
			<input type="submit" name="submit" id="submit" value="<?php esc_attr_e('Publicar', 'ainder'); ?>" />

			<div id="whats-new-post-in-box">

				<?php _e('Publicar en', 'ainder'); ?>:

				<!-- Aquí creamos un desplegable con un 'select' en el que volcamos las categorías (realmente son taxonomías) del Custom Post Type -->
				<select id="whats-new-post-in" name="categoria-asociate" required>
					<option selected="selected" value="">
						<?php
						// Creamos los argumentos de la consulta
						$args = array(
							// tipo de post que buscamos (es un Custom Post Type llamado 'asociate')
							'post_type' => 'asociate',
							// nombre da la taxonomías (en esa taxonomía están guardadaas todas las categorías pertenecientes al Custom POst Type 'asociate')
							'taxonomy' => 'categoria-asociate',
							// Mostramos todas las categorías, incluidas las que aún no tengan post publicados
							'hide_empty' => 0,
							// las ordenamos de forma ASCendente
							'order' => 'ASC'
						);

						$categories = get_categories($args);

						// Recorremos cada una de las categorías del CPT 'asociate' y las imprimimos con un 'echo' en un <option></option>
						foreach ($categories as $category) {
							echo "<option value='" . $category->name . "'>$category->name</option>"; // $category->term_id
						} // end foreach
						?>
				</select>
			</div> <!-- #whats-new-post-in-box -->
		</div><!-- #whats-new-content -->

		<!-- Securizamos un poco el formlario usando la función de WordPress wp_nonce_field() -->
		<?php wp_nonce_field('post_update', '_wpnonce_post_update'); ?>

	</form><!-- #whats-new-form -->
</div> <!-- .cpt_form_container -->

<!-- /FORMULARIO -->

Después hay que tratar los datos. En este caso los tratamos con PHP en el servidor así:

<?php
// Priemro comprobamos que haya datos
if ($_POST && ($_POST['titulo-asociate'] != '')) {

	// Guardamos los datos en variables
	$post_type = 'asociate'; // Qué tipo de post es (en este caso en un CPT llamado 'asociate'. Si fuese un post normal pondríamos 'post' en lugar de la variable $post_type)
	$post_title = $_POST['titulo-asociate'];
	$post_content = $_POST['contenido-asociate'];
	$post_category = $_POST['categoria-asociate'];
	$video_url = $_POST['video-asociate'];
	$enlace_url = $_POST['enlace-asociate'];
	$pais = $_POST['pais-asociate'];
	$localidad = $_POST['localidad-asociate'];
	$fecha_expi = $_POST['tiempo-asociate'];

	// Los datos del archivo (imagen) se almacenan en la variable global $_FILES en lugar de en $_POST
	$image = $_FILES['imagen-asociate'];
	$nombre_archivo = $_FILES['imagen-asociate']['name'];
	$tipo_archivo = $_FILES['imagen-asociate']['type'];
	$tamano_archivo = $_FILES['imagen-asociate']['size'];

	// Creamos los argumentos del nuevo post que vamos a crear
	$new_post = array(
		// El autor va a ser el usuario actual que está visitando la página
		'post_author' => $user->ID,
		'post_title' => $post_title,
		'post_content' => $post_content,
		'post_category' => array($post_category),
		// Lo publicamos directamente (podríamos dejarlo en borrador para revisarlo antes si queremos)
		'post_status' => 'publish',
		// Le dejamos los comentarios abiertos (pordiamos cerrarselos desde aquí)
		'comment_status' => 'open',
		// Aquí le decimos qué tipo de post es (lo tenemos definido en la primera variable de este script)
		'post_type' => $post_type
	);

	// Creamos el post con los datos de $new_post y obtenemos su ID nuevecita
	$post_id = wp_insert_post($new_post);

	// Insertamos la "categoría" (realmente es una taxonomía)
	wp_set_object_terms($post_id, $post_category, 'categoria-asociate');

	if (!empty($video_url)) {
		// Insertamos en DDBB la URL del video si lo hay
		update_post_meta($post_id, 'video_url', $video_url);
	}

	if (!empty($enlace_url)) {
		// Insertamos en DDBB la URL del enlace si lo hay
		update_post_meta($post_id, 'enlace_url', $enlace_url);
	}

	if (!empty($pais)) {
		// Insertamos en DDBB el pais si lo hay
		update_post_meta($post_id, 'pais', $pais);
	}

	if (!empty($localidad)) {
		// Insertamos en DDBB la localidad si la hay
		update_post_meta($post_id, 'localidad', $localidad);
	}

	if (!empty($fecha_expi)) {
		// Insertamos en DDBB la fecha de expiración del post si la hay
		update_post_meta($post_id, 'fecha_expiracion', $fecha_expi);
	}

	// PARA LA IMAGEN LO TENEMOS UN POCO MÁS COMPLICADO
	if ($image['error'] == 0) {
		// Comprobamos que es el tipo de archivo que queremos y su tamaño no es excesivo
		if (!((strpos($tipo_archivo, "gif") || strpos($tipo_archivo, "jpeg") || strpos($tipo_archivo, "png")) && ($tamano_archivo < 10000000))) {
			echo "La extensión o el tamaño de los archivos no es correcta. Póngase en contacto con el administrador.<br><br><table><tr><td><li>Sólo se permiten archivos .jpg, .png o .gif<br><li>Sólo se permiten archivos de 10 Mb máximo.</td></tr></table>";
		} else {
			if ($post_id) {
				_e('¡Enhorabuena! Publicación correcta', 'ainder');
				echo "<br>";
			} // end if ($post_id)
		} // end if/else
	} // end if (imagen)

	// Guardamos la ruta del directorio upload en una variable
	$upload_dir = wp_upload_dir();

	// Guardamos la ruta del archivo temporal para cuando lo movamos en una variable
	$uploadfile = $upload_dir["path"] . "/" . $_FILES['imagen-asociate']['name'];

	if (move_uploaded_file($_FILES['imagen-asociate']['tmp_name'], $uploadfile)) {
		// Leemos el archivo desde su nueva ubicación y lo guardamos en una variable
		$image_data = file_get_contents($uploadfile);

		// Nombramos la imagen con el nombre del archivo
		$filename = $nombre_archivo;

		if (wp_mkdir_p($upload_dir['path']))
			$file = $upload_dir['path'] . '/' . $filename;
		else
			$file = $upload_dir['basedir'] . '/' . $filename;

		// Insertamos los datos de la imagen en la variable
		file_put_contents($file, $image_data);

		// Comprobamos el tipo de archivo a partir de su nombre
		$wp_filetype = wp_check_filetype($filename, null);

		$attachment = array(
			'post_mime_type' => $wp_filetype['type'],
			'post_title' => sanitize_file_name($filename),
			'post_content' => '',
			'post_status' => 'inherit'
		);

		// Creamos un nuevo post (este es sólo para el archivo. Sí, WordPress crea post para casi todo jeje )
		$attach_id = wp_insert_attachment($attachment, $file, $post_id);

		// Involucramos al archivo del core de WordPress 'wp-admin/includes/image.php'
		require_once(ABSPATH . 'wp-admin/includes/image.php');

		// Generamos los metadatos de la imagen (si los tiene)
		$attach_data = wp_generate_attachment_metadata($attach_id, $file);

		// Añadimos dichos metadatos
		wp_update_attachment_metadata($attach_id, $attach_data);

		// Le asignamos la imagen al post como imagen destacada
		set_post_thumbnail($post_id, $attach_id);
	} else {
		echo "¡Enhorabuena! Publicación correcta sin imagen";
	} // end if/else

} // end if ($_POST)

Como vemos, primero creamos el post y luego le añadimos la imagen.

Espero que le sirva a alguien, porque yo me pasé un tiempo buscando información y probando (perooo…¿no es así como mejor se aprende?).

Pues eso es todo en esta entrada. Espero que la pongáis en práctica y recordad: Sólo se aprende rompiendo y destripando el código, practicando.

Compartidlo si lo veis útil.

3 Replies to "Añadir un post desde el frontend con imagen destacada"

    • Jose Lazo

      No sé qué puede estar pasando. Añadiendo «`wp_nonce_field( ‘post_update’, ‘_wpnonce_post_update’ );«` al final del formulario nos aseguramos que sólo se envíe una vez (entre otras cosas).

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.