Como ya hemos dicho anteriormente en otros artículos, las versiones AMP de nuestras páginas son una herramienta imprescindible para mejorar nuestro SEO y a la vez mejorar la experiencia de los clientes que lleguen a nuestro sitio web vía móvil. Aún así, hay funciones como crear un formulario para AMP que son a priori un poco complicadas, sobre todo si usamos un plugin como AMP for WP (que, por otro lado, nos parece excelente).

¿Cómo activar los formularios de AMP en #WordPress sin add-ons de pago? Clic para tuitear

Por ello, y ante la falta de información existente en internet sobre el tema (aunque esperamos que se vaya corrigiendo según AMP se vaya desarrollando), hemos creado esta guía sobre cómo crear un formulario para AMP con el plugin AMP for WP.

Aviso: Este tutorial puede resultar un poco complejo. Si prefieres que nosotros nos encarguemos de crear las versiones para AMP de tus páginas, contacta con nosotros en el 91 218 13 79.

Cómo crear un formulario para AMP con el plugin AMP for WP

Lo primero que tenemos que hacer es habilitar la creación de formularios en MP for WP. Nos explicamos. La creación de formularios viene “capada” por defecto en este plugin. Cada vez que usamos etiquetas como “form” en nuestro formulario nos vamos a encontrar con que al visualizar la página no se ve el formulario, y si miramos el código fuente que nos da el navegador vemos que el html relacionado con nuestro formulario no aparece.

Esto es debido a que AMP for WP elimina el contenido de una serie de etiquetas, entre ellas las referidas a los formularios. Esto puede ser debido a dos razones:

  1. Las primeras implementaciones de AMP no incluían la posibilidad de tener formularios. Esto se ha añadido en posteriores implementaciones, pero los autores del plugin aún no las han habilitado aunque planean hacerlo en el futuro.
  2. Los autores del plugin quieren que compremos la extensión de pago que venden para dar compatibilidad a plugins de formularios.

Nosotros, como buenos pensantes, nos inclinamos a pensar que se trata de la primera opción.

De todas maneras, vamos a dar la solución.

Habilitar los formularios en AMP for WP

Para habilitar los formularios en AMP for WP (o, para ser más exactos, para quitar las etiquetas relacionadas con los formularios de la lista negra del plugin), tenemos dos opciones.

La primera es editar el plugin. Esto tiene el problema de que, cada vez que se actualice, tendremos que volver a modificar el archivo.

La segunda, que es la que vamos a llevar a cabo, es crear un plugin adicional que se encargue de habilitar los formularios en AMP for WP. Vamos a explicar cómo crear dicho plugin. También puedes descargártelo más abajo si quieres ahorrarte el trabajo.

Para ello crearemos en nuestro ordenador un directorio que se llamará AMPforWP-form-enabler. Dentro de él crearemos dos archivos (podemos usar cualquier editor de texto, pero recomendamos Notepad++). El primero se llamará AMPforWP-form-enabler.php y su contenido será el siguiente:

<?php
/*
Plugin Name: AMPforWP Form Enabler
Plugin URI: https://www.wonderfulpartner.com/crear-un-formulario-para-amp
Description: Habilita la creación de formularios en amp for wp
Version: 1.0
Author: Marketing Partner
Author URI: https://marketingonline.wonderfulpartner.com
License: GPL2
*/

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) exit;

/* Llamada al procedimiento */
add_action('init', 'amp_new_class_sanitizer');
function amp_new_class_sanitizer() {
 require_once plugin_dir_path(__FILE__) .'/class-amp-blacklist.php';
}

add_filter('amp_content_sanitizers','amp_custom_blacklist', 100);
function amp_custom_blacklist( $data ){
 unset($data['AMP_Blacklist_Sanitizer']);
 $data[ 'AMP_New_Blacklist' ] = array();
 return $data;
}

add_filter('the_content','new_form_content');
function new_form_content($content){
 if ( function_exists( 'ampforwp_is_amp_endpoint' ) && ampforwp_is_amp_endpoint() ) {
 $content = preg_replace('#<form action="(.*?)" (.*?)>#ius', '<form action-xhr="$1" $2 target="_blank">', $content);
 
 }
return $content;
}

Este archivo es el principal del plugin. Llama a la acción que definiremos en el otro archivo y se encarga de filtrar y aplicar los cambios a la “lista negra”.

Necesitaremos además otro archivo que llamaremos class-amp-blacklist.php. Su contenido será el siguiente:

<?php
$amp_blacklist_sanitizer = realpath( '/wp-content/plugins/accelerated-mobile-pages/includes/vendor/amp/includes/sanitizers/class-amp-blacklist-sanitizer.php' );
if ( $amp_blacklist_sanitizer ) {
 include_once $amp_blacklist_sanitizer;
}
if ( class_exists( 'AMP_Blacklist_Sanitizer' ) ) {
 class AMP_New_Blacklist extends AMP_Blacklist_Sanitizer {
 const PATTERN_REL_WP_ATTACHMENT = '#wp-att-([\d]+)#';

protected $DEFAULT_ARGS = array(
 'add_blacklisted_protocols' => array(),
 'add_blacklisted_tags' => array(),
 'add_blacklisted_attributes' => array(),
 );

public function sanitize() {
 $blacklisted_tags = $this->get_blacklisted_tags();
 $blacklisted_attributes = $this->get_blacklisted_attributes();
 $blacklisted_protocols = $this->get_blacklisted_protocols();
 $body = $this->get_body_node();
 $this->strip_tags( $body, $blacklisted_tags );
 $this->strip_attributes_recursive( $body, $blacklisted_attributes, $blacklisted_protocols );
 }
 
 private function strip_attributes_recursive( $node, $bad_attributes, $bad_protocols ) {
 if ( $node->nodeType !== XML_ELEMENT_NODE ) {
 return;
 }

$node_name = $node->nodeName;

// Some nodes may contain valid content but are themselves invalid.
 // Remove the node but preserve the children.
 if ( 'font' === $node_name ) {
 $this->replace_node_with_children( $node, $bad_attributes, $bad_protocols );
 return;
 } elseif ( 'a' === $node_name && false === $this->validate_a_node( $node ) ) {
 $this->replace_node_with_children( $node, $bad_attributes, $bad_protocols );
 return;
 }

if ( $node->hasAttributes() ) {
 $length = $node->attributes->length;
 for ( $i = $length - 1; $i >= 0; $i-- ) {
 $attribute = $node->attributes->item( $i );
 $attribute_name = strtolower( $attribute->name );
 if ( in_array( $attribute_name, $bad_attributes ) ) {
 $node->removeAttribute( $attribute_name );
 continue;
 }

// on* attributes (like onclick) are a special case
 if ( 0 === stripos( $attribute_name, 'on' ) && $attribute_name != 'on' ) {
 $node->removeAttribute( $attribute_name );
 continue;
 } elseif ( 'a' === $node_name ) {
 $this->sanitize_a_attribute( $node, $attribute );
 }
 }
 }

$length = $node->childNodes->length;
 for ( $i = $length - 1; $i >= 0; $i-- ) {
 $child_node = $node->childNodes->item( $i );

$this->strip_attributes_recursive( $child_node, $bad_attributes, $bad_protocols );
 }
 }
 
 private function strip_tags( $node, $tag_names ) {
 foreach ( $tag_names as $tag_name ) {
 $elements = $node->getElementsByTagName( $tag_name );
 $length = $elements->length;
 if ( 0 === $length ) {
 continue;
 }

for ( $i = $length - 1; $i >= 0; $i-- ) {
 $element = $elements->item( $i );
 $parent_node = $element->parentNode;
 $parent_node->removeChild( $element );

if ( 'body' !== $parent_node->nodeName && AMP_DOM_Utils::is_node_empty( $parent_node ) ) {
 $parent_node->parentNode->removeChild( $parent_node );
 }
 }
 }
 }

private function sanitize_a_attribute( $node, $attribute ) {
 $attribute_name = strtolower( $attribute->name );

if ( 'rel' === $attribute_name ) {
 $old_value = $attribute->value;
 $new_value = trim( preg_replace( self::PATTERN_REL_WP_ATTACHMENT, '', $old_value ) );
 if ( empty( $new_value ) ) {
 $node->removeAttribute( $attribute_name );
 } elseif ( $old_value !== $new_value ) {
 $node->setAttribute( $attribute_name, $new_value );
 }
 } elseif ( 'rev' === $attribute_name ) {
 // rev removed from HTML5 spec, which was used by Jetpack Markdown.
 $node->removeAttribute( $attribute_name );
 } elseif ( 'target' === $attribute_name ) {
 // _blank is the only allowed value and it must be lowercase.
 // replace _new with _blank and others should simply be removed.
 $old_value = strtolower( $attribute->value );
 if ( '_blank' === $old_value || '_new' === $old_value ) {
 // _new is not allowed; swap with _blank
 $node->setAttribute( $attribute_name, '_blank' );
 } else {
 // only _blank is allowed
 $node->removeAttribute( $attribute_name );
 }
 }
 }

private function validate_a_node( $node ) {
 // Get the href attribute
 $href = $node->getAttribute( 'href' );

// If no href is set and this isn't an anchor, it's invalid
 if ( empty( $href ) ) {
 $name_attr = $node->getAttribute( 'name' );
 if ( ! empty( $name_attr ) ) {
 // No further validation is required
 return true;
 } else {
 return false;
 }
 }

// If this is an anchor link, just return true
 if ( 0 === strpos( $href, '#' ) ) {
 return true;
 }

// If the href starts with a '/', append the home_url to it for validation purposes.
 if ( 0 === stripos( $href, '/' ) ) {
 $href = untrailingslashit( get_home_url() ) . $href;
 }

$valid_protocols = array( 'http', 'https', 'mailto', 'sms', 'tel', 'viber', 'whatsapp' );
 $special_protocols = array( 'tel', 'sms' ); // these ones don't valid with `filter_var+FILTER_VALIDATE_URL`
 $protocol = strtok( $href, ':' );

if ( false === filter_var( $href, FILTER_VALIDATE_URL )
 && ! in_array( $protocol, $special_protocols ) ) {
 return false;
 }

if ( ! in_array( $protocol, $valid_protocols ) ) {
 return false;
 }

return true;
 }

private function replace_node_with_children( $node, $bad_attributes, $bad_protocols ) {
 // If the node has children and also has a parent node,
 // clone and re-add all the children just before current node.
 if ( $node->hasChildNodes() && $node->parentNode ) {
 foreach ( $node->childNodes as $child_node ) {
 $new_child = $child_node->cloneNode( true );
 $this->strip_attributes_recursive( $new_child, $bad_attributes, $bad_protocols );
 $node->parentNode->insertBefore( $new_child, $node );
 }
 }

// Remove the node from the parent, if defined.
 if ( $node->parentNode ) {
 $node->parentNode->removeChild( $node );
 }
 }

private function merge_defaults_with_args( $key, $values ) {
 // Merge default values with user specified args
 if ( ! empty( $this->args[ $key ] )
 && is_array( $this->args[ $key ] ) ) {
 $values = array_merge( $values, $this->args[ $key ] );
 }

return $values;
 }

private function get_blacklisted_protocols() {
 return $this->merge_defaults_with_args( 'add_blacklisted_protocols', array(
 'javascript',
 ) );
 }
 
 private function get_blacklisted_tags() {
 return $this->merge_defaults_with_args( 'add_blacklisted_tags', array(
 'script',
 'noscript',
 'style',
 'frame',
 'frameset',
 'object',
 'param',
 'applet',
 
 // Removed this form elements to make sure
 // Form works in AMP
 // 'form',
 // 'label',
 // 'input',
 // 'textarea',
 // 'select',
 // 'option',
 'link',
 'picture',

// Sanitizers run after embed handlers, so if anything wasn't matched, it needs to be removed.
 'embed',
 'embedvideo',

// Other weird ones
 'comments-count',

// These are converted into amp-* versions
 //'img',
 //'video',
 //'audio',
 //'iframe',
 ) );
 }
 private function get_blacklisted_attributes() {
 return $this->merge_defaults_with_args( 'add_blacklisted_attributes', array(
 'style',
 'size',
 'clear',
 'align',
 'valign',
 ) );
 }
 }
}

Una vez creados estos dos archivos, comprimiremos en zip el directorio que los contiene, obteniendo un fichero que se llamará AMPforWP-form-enabler.zip. Este fichero lo debemos instalar en nuestro WordPress a través de la opción Plugins>Añadir Nuevo>Subir plugin. Sólo queda activarlo y tendremos habilitados los formularios.

Nota: Esta solución debería funcionar en la mayoría de instalaciones WordPress que estén actualizadas, pero no podemos garantizar su compatibilidad con todas las instalaciones.

Descargar el plugin

Si prefieres descargarte e instalar el plugin directamente, puedes hacerlo simplemente dándote de alta en nuestra lista de correo. Además te enviaremos más información de interés con una frecuencia no mayor de un envío a la semana.

Crear un formulario para AMP

Una vez habilitados los formularios, ha llegado el momento de crear el nuestro. Para ello hacen falta dos pasos. El código del formulario que incluiremos en nuestra página y el archivo php que se encargará de procesar la información recogida y enviarla a nuestro correo.

Lo primero que tenemos que hacer es habilitar las librerías encargadas de procesar los formularios en AMP. Para ello, dentro de las opciones de AMP for WP iremos a AMP>Settings>Advance Settings y en el apartado Enter HTML in Head introduciremos el siguiente código:

<script async custom-element="amp-form" src="https://cdn.ampproject.org/v0/amp-form-0.1.js"></script>
<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.1.js"></script>

Este código carga las librerías amp-form y amp-mustache que se encargan de habilitar los formularios en AMP.

Código HTML para incluir el formulario en nuestra página AMP

El código que debemos introducir en el HTML de nuestra página (en el editor de AMP, a través de la opción HTML) puede ser, por ejemplo, el siguiente (para un formulario de contacto):

<form name="contactoamp" method="POST" target="_top" action-xhr="https://dominio-ejemplo/formamp.php">
<input type="text"
name="nombre"
 placeholder="Nombre"
 required>
<input type="text"
name="email"
 placeholder="Email"
 required>
<input type="text"
 name="telefono"
 placeholder="Teléfono"
 required>
<input type="textarea"
 name="mensaje"
 placeholder="Mensaje"
 maxlength="10000"
 required>
<input type="submit" class="boton" value="Enviar">
 <div submit-success>
 <template type="amp-mustache">
 Mensaje enviado, {{name}}.En breve nos pondremos en contacto contigo.
 </template>
 </div>
 <div submit-error>
 <template type="amp-mustache">
 ¡Envío fallido!
 </template>
 </div>
</form>

No hemos introducido en este código clases CSS ni ningún otro elemento para darle formato. Se limita a presentar el formulario en la página AMP.

En la línea 2 debemos sustituir “dominio-ejemplo” por la dirección de nuestro dominio.

Nota: Para que esto funcione debemos usar un dominio con SSL activado (es decir, con protocolo https).

El archivo php que se encargará de enviar el correo

Para concluir nuestro tutorial, sólo tenemos que crear el archivo php que se encargará de enviar el correo. PAra ello, una vez más con Notepad++ o cualquier otro editor de texto, creamos un archivo que llamaremos formamp.php con el siguiente contenido:

<?php
if(!empty($_POST)){
$name=$_POST['nombre'];
$email = $_POST['email'];
$phone = $_POST['telefono'];
$mensaje = $_POST['mensaje'];
$formcontent=" De: $name \n Teléfono: $phone \n Correo: $email \n Mensaje: $mensaje "; 
$recipient = "dirección-de-correo";
$subject = "Formulario - AMP";
$mailheader = "From: $email \r\n";
mail($recipient, $subject, $formcontent, $mailheader) or die("¡Error!");

$domain_url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]";
 header("Content-type: application/json");
 header("Access-Control-Allow-Credentials: true");
 header("Access-Control-Allow-Origin: ". str_replace('.', '-','https://dominio-ejemplo') .".cdn.ampproject.org");
 header("AMP-Access-Control-Allow-Source-Origin: https://dominio-ejemplo");
 header("Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin");
 header("AMP-Redirect-To: https://dirección-de-reenvío");
 header("Access-Control-Expose-Headers: AMP-Redirect-To, AMP-Access-Control-Allow-Source-Origin"); 
 echo json_encode(array('name' => $name));
 exit;
}
?>

Una vez más, tenemos que sustituir “dominio-ejemplo” por nuestro dominio, “dirección-de-reenvío” por la página a la que queremos que nos lleve tras el envío del formulario y “dirección-de-correo” por la dirección a la que queremos que se nos envíen los datos.

Sólo quedará subir el archivo al directorio principal de nuestra web (o a un subdirectorio modificando consecuentemente el código html) y tendremos nuestro formulario funcionando.

Para cualquier duda sobre crear un formulario para AMP podéis dejarnos un comentario.

Si prefieres que nos encarguemos de crear tus páginas AMP llámanos al 91 218 13 79