Como cargar plantillas desde un plugin

Los archivos de plantillas (templates) se usan comunmente en themes y child themes, pero también puedes cargar estos archivos desde un plugin.

No es demasiado complicado crear tu propio cargador de plantillas, pero puedes encontrar una clase en GitHub creada por Gary Jones, Gamajo_Template_Loader

Para usar esta clase:
1. Crea un archivo dentro de tu plugin y copia la clase Gamajo_Template_Loader.
2. Crea una subclase de Gamajo_Template_Loader. Aquí tienes un ejemplo de esta subclase
3. Sobreescribe las propiedades de la clase para que se ajusten a tu plugin, dichas propiedades son : $filter_prefix, $theme_template_directory, $plugin_directory.

También puedes encontrar un ejemplo de un plugin completo utilizando Gamajo_Template_Loader creado por Pippin Williamson de pippinsplugins.com

Seguridad y consejos para instalar WordPress

Debido a la popularidad de WordPress, los sitios web que utilizan esta plataforma se convierten en objetivo de muchos hackers. Es nuestro deber hacer uso de WordPress con cierta responsabilidad y precaución ya que un sitio web vulnerable puede perjudicar a miles de usuarios que lo visitan.

He publicado un artículo en el blog de digiworks.es donde explico algunas de las buenas prácticas para instalar WordPress de una forma más segura.
También doy algunos consejos básicos para la seguridad y para entender mejor la importancia de la misma encontrarás un breve repaso de los ataques más habituales cuando hablamos de WordPress.

Puedes ver el artículo completo desde el siguiente enlace:
http://www.digiworks.es/blog/2015/01/15/seguridad-en-wordpress-y-algunos-consejos-para-la-instalacion/

Plugins gratuitos, dónde buscar y cómo elegir

Podemos estar casi seguros de que cualquier plugin que descarguemos del repositorio de wordpress.org estará libre de código malicioso y malas intenciones, ya que todos los plugins subidos a este repositorio son probados y revisados por un equipo de expertos que realizan esta tarea de forma voluntaria. Pero no siempre podemos fiarnos de la calidad del código.

Recientemente he publicado un artículo en el blog de digiworks.es donde doy unos consejos y recomendaciones sobre los criterios a seguir para elegir correctamente un plugin gratuito.

Si te interesa este tema puedes visitar el artículo completo en el siguiente enlace:
http://www.digiworks.es/blog/2015/01/13/plugins-gratuitos-para-wordpress-donde-buscar-y-como-elegir/

Añadir meta box a un “custom post type”

Antes de añadir un meta box a un “custom post type” necesitamos crear un custom post type.
Echa un vistazo a esta clase completa para crear un custom post type desde un plugin.

Así que siguinedo con este ejemplo nuestro custom post type es cpt_examples

En la función constructora __construct de nuestra clase para crear custom post type añade las siguientes líneas:

1
2
add_action( 'add_meta_boxes', array( $this, 'add_meta_box') );
add_action( 'save_post',      array( $this, 'save_meta_box_data' ) );

Y después crea las funciones en la clase.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * add_meta_box
 */
public function add_meta_box(){
 
	add_meta_box( 'cpt_examples_meta', 'CPT Examples Extra Info', array( $this, 'display_meta_form' ), 'cpt_examples', 'advanced', 'high' );
}
 
/**
 * display_meta_form	
 */
 
public function display_meta_form( $post ) {
 
	wp_nonce_field( 'cptexamples_meta_box', 'cptexamples_meta_box_nonce' );
 
	$my_first_field  = get_post_meta( $post->ID, 'cpt_first_meta_field', true );
	$my_second_field = get_post_meta( $post->ID, 'cpt_second_meta_field', true );
 
		echo '<div class="wrap">';
		echo '<label for="cpt_first_meta_field">' . _e( 'First Meta Field', 'cpt_domain' ) . '</label> <br/>';
		echo '<input class="text" type="text" id="cpt_first_meta_field" name="cpt_first_meta_field" value="' . esc_attr( $my_first_field ) . '"   />';
		echo '</div>';
 
		echo '<div class="wrap">';
		echo '<label for="cpt_second_meta_field">' . _e( 'Second Meta Field', 'cpt_domain' ) . '</label>  <br/>';
		echo '<input class="text" type="text" id="cpt_second_meta_field" name="cpt_second_meta_field" value="' . esc_attr( $my_second_field ) . '"   />';
		echo '</div>';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * save_meta_box_data
 * función llamada en el hook save_post para validar y guardar los datos.
 */
 
public function save_meta_box_data( $post_id ){
 
  // Verificar que se ha declarado el nonce.
    if ( ! isset( $_POST['cptexamples_meta_box_nonce'] ) ) {
	  return;
    }
 
  // Verificar que el nonce es válido.
    if ( ! wp_verify_nonce( $_POST['cptexamples_meta_box_nonce'], 'cptexamples_meta_box' ) ) {
	   return;
    }
 
  // Si es un autoguardado no debe hacer nada.
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
	  return;
    }
 
  // Verificar los permisos del usuario.
    if ( isset( $_POST['post_type'] ) && $_POST['post_type'] == 'cpt_examples' ) {
            if ( ! current_user_can( 'edit_page', $post_id ) ) {
		     return;
	    }
 
    } else {
            if ( ! current_user_can( 'edit_post', $post_id ) ) {
		     return;
	    }
    }
 
   // Guardar la información en la base de datos
       if ( isset( $_POST['cpt_first_meta_field'] ) ) {
 
             $my_first_meta_field = sanitize_text_field( $_POST['cpt_first_meta_field'] );
	     update_post_meta( $post_id, 'cpt_first_meta_field', $my_first_meta_field );
	}
 
       if ( isset( $_POST['cpt_second_meta_field'] ) ) {
 
             $my_second_meta_field = sanitize_text_field( $_POST['cpt_second_meta_field'] );
	     update_post_meta( $post_id, 'cpt_second_meta_field', $my_second_meta_field );
	}
 
 
}

También visita este otro snippet para posicionar el metabox justo después del título dentro del formulario.

Crear “custom post type” desde un plugin

La clase.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<?php 
/** 
 * @package    WPtips
 * @subpackage Ejemplo Custom Post Type 
 * @author     Lucy Tomás
 * @since      1.0
 */
 
// Exit si se accede directamente
if ( !defined( 'ABSPATH' ) ) exit;
 
 
if( !class_exists( 'CPT_Example' )){
 
class CPT_Example {
 
	/**
	 * constructor
	 * @since 1.0
	 */
 
	public function __construct() {
 
		add_action( 'init', 		     array( $this, 'register_posttype'));
		add_filter( 'post_updated_messages', array( $this, 'custom_post_type_messages'));
		add_action( 'admin_head',            array( $this, 'custom_post_type_help' ) );
		add_filter( 'template_include',      array( $this, 'post_type_template'));
 
	}	
 
	/**
	 * register_posttype
	 * @since 1.0
	 */
 
	public function register_posttype() {
 
		$labels = array(
					'name'               => __('CPT Examples', 'cpt_domain'),
					'singular_name'      => __('CPT Example', 'cpt_domain'),
					'menu_name'          => __('CPT Examples', 'cpt_domain'),
					'name_admin_bar'     => __('CPT Example', 'cpt_domain'),
					'add_new'            => __('Add new', 'cpt_domain'),
					'add_new_item'       => __('Add new CPT Example', 'cpt_domain'),
					'new_item'           => __('New CPT Example', 'cpt_domain'),
					'edit_item'          => __('Edit CPT Example', 'cpt_domain'),
					'view_item'          => __('View CPT Examples', 'cpt_domain'),
					'all_items'          => __('All CPT Examples', 'cpt_domain'),
					'search_items'       => __('Search CPT Examples', 'cpt_domain'),
					'parent_item_colon'  => __('CPT Example parent', 'cpt_domain'),
					'not_found'          => __('No CPT Example found', 'cpt_domain'),
					'not_found_in_trash' => __('No CPT Example in trash', 'cpt_domain')
		);
 
 
		$slug = get_theme_mod( 'ctp_example_permalink' );
  		$slug = ( empty( $slug ) ) ? 'cpt-example' : $slug;
 
 
 		$args = array( 
			'public'        => true, 
			'labels'        => $labels,
			'description'   => __('CPT Examples', 'cpt_domain'),
			'menu_icon'     => 'dashicons-groups',
			'menu_position' => 5,
			'has_archive' 	=> true,
			'supports'      => array( 'title', 'editor', 'thumbnail'),
			'rewrite'     	=> array( 'slug' =&gt; $slug )
 
		);
 
		register_post_type( 'cpt_examples', $args );
 
	}
 
	/**
	 * custom_post_type_messages
	 * @since 1.0
	 */
 
	public function custom_post_type_messages( $messages ) {
 
		$post = get_post();
 
			$messages['cpt_examples'] = array(
				0  => '',
				1  => __('CTP Example updated', 'cpt_domain'),
				2  => __('Custom field updated', 'cpt_domain'),
				3  => __('Custom field deleted', 'cpt_domain'),
				4  => __('CTP Example updated', 'cpt_domain'),
				5  => isset( $_GET['revision'] ) ? sprintf( __('CTP Example restored to revision', 'cpt_domain') . ' %s',wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
				6  => __('CTP Example published', 'cpt_domain'),
				7  => __('CTP Example saved', 'cpt_domain'),
				8  => __('CTP Example sent', 'cpt_domain'),
				9  => sprintf(
					__('CTP Example programed for', 'cpt_domain') . ': <strong>%1$s</strong>.',
					date_i18n( 'M j, Y @ G:i', strtotime( $post->post_date ) )
				),
				10 => __('Draft CTP Example updated', 'cpt_domain')
			);
 
 
		return $messages;
 
	}
 
	/**
	 * custom_post_type_help
	 * @since 1.0
	 */
 
	public function custom_post_type_help() {
 
 
		$screen = get_current_screen();
 
		if ( $screen->post_type != 'cpt_examples' ) {
			return;
		}
 
		$basics = array(
			'id'      => 'cpt_examples_basics',
			'title'   => 'Ayuda básica para CPT',
			'content' => '<p>Desde esta sección puedes visualizar una lista de CPT Ejemplos.</p>
                                      <p>Haz click en una para Editar o haz click en Añadir Nuevo para crear una nueva.</p>'
		);
 
		$formatting = array(
			'id'      => 'cpt_examples_formating',
			'title'   => 'Creación de un CPT Ejemplo',
			'content' => '<p>Rellena el frmulario para crear un nuevo CPT Ejemplo .</p>'
 
		);
 
		$screen->add_help_tab( $basics );
		$screen->add_help_tab( $formatting );
 
 	}
 
	/**
	 * post_type_template
	 * @since 1.0
	 */
 
	function post_type_template( $original_template ) {
 
 
		  if ( is_post_type_archive('cpt_examples') ) {
 
		    load_template( plugin_dir_path( __FILE__ ) . 'templates/cpt_examples.php');
 
		  } else {
 
		    return $original_template;
		  }
 
	}
 
 
 
}// class
 
}

En esta otra entrada puedes ver como añadir meta box con campos de formulario extra a esta misma clase de “custom post type”.

Eliminar las notificaciones por correo de WooCommerce

Snippet útil por Mike Jolley encontrado en Github con una lista de hooks para las notificaciones de WooCommerce.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
add_action( 'woocommerce_email', 'unhook_those_pesky_emails' );
 
function unhook_those_pesky_emails( $email_class ) {
 
		/**
		 * Hooks for sending emails during store events
		 **/
		remove_action( 'woocommerce_low_stock_notification', array( $email_class, 'low_stock' ) );
		remove_action( 'woocommerce_no_stock_notification', array( $email_class, 'no_stock' ) );
		remove_action( 'woocommerce_product_on_backorder_notification', array( $email_class, 'backorder' ) );
 
		// New order emails
		remove_action( 'woocommerce_order_status_pending_to_processing_notification', array( $email_class-&gt;emails['WC_Email_New_Order'], 'trigger' ) );
		remove_action( 'woocommerce_order_status_pending_to_completed_notification', array( $email_class-&gt;emails['WC_Email_New_Order'], 'trigger' ) );
		remove_action( 'woocommerce_order_status_pending_to_on-hold_notification', array( $email_class-&gt;emails['WC_Email_New_Order'], 'trigger' ) );
		remove_action( 'woocommerce_order_status_failed_to_processing_notification', array( $email_class-&gt;emails['WC_Email_New_Order'], 'trigger' ) );
		remove_action( 'woocommerce_order_status_failed_to_completed_notification', array( $email_class-&gt;emails['WC_Email_New_Order'], 'trigger' ) );
		remove_action( 'woocommerce_order_status_failed_to_on-hold_notification', array( $email_class-&gt;emails['WC_Email_New_Order'], 'trigger' ) );
 
		// Processing order emails
		remove_action( 'woocommerce_order_status_pending_to_processing_notification', array( $email_class-&gt;emails['WC_Email_Customer_Processing_Order'], 'trigger' ) );
		remove_action( 'woocommerce_order_status_pending_to_on-hold_notification', array( $email_class-&gt;emails['WC_Email_Customer_Processing_Order'], 'trigger' ) );
 
		// Completed order emails
		remove_action( 'woocommerce_order_status_completed_notification', array( $email_class-&gt;emails['WC_Email_Customer_Completed_Order'], 'trigger' ) );
 
		// Note emails
		remove_action( 'woocommerce_new_customer_note_notification', array( $email_class-&gt;emails['WC_Email_Customer_Note'], 'trigger' ) );
}

Como obtener el “slug” de un post

basename( get_permalink($id) );

Si tienes disponible los datos del post en la variable global $post puedes simplemente usar $post->post_name;

1
2
3
4
5
6
7
8
9
10
11
12
function get_slug( $id = NULL ) {
 
   if( $id != NULL ) {
     return basename( get_permalink($id) );
   }
 
   global $post;
   if( empty($post) ) return;
 
   return $post->post_name;
 
}

En otros casos puede que no tengas el $id y estes intentando recuperar $post demasiado pronto como por ejemplo en el hook init donde los datos del post aún no estan disponibles. En este caso puedes intentar usar la función url_to_postid para recuperar el ID y luego poder tener el slug.

La función completa quedaría de la siguiente manera:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function get_slug( $id = NULL ) {
 
  if( $id == NULL ){
 
    global $post;
 
   if( !empty( $post )) {
        $id = $post->ID;
    } else {
        $id = url_to_postid( "http://".$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
    }
 
  } // if $id NULL
 
  return basename( get_permalink($id) );
}

Añade Galerias en Mosaico sin instalar Jetpack

tiled-gallery-banner

Existe una gran demanda en tener algunas de las funcionalidades que ofrece Jetpack sin tener que instalar el propio plugin.
Aquí os dejo este plugin, por ThemePacific, para poder añadir una galería en mosaico con carousel. Hace exactamente lo mimo que la Galería en Mosacio de Jetpack.

Las opciones son:

  • Hablitar o deshabilitar la función de carousel.
  • Escoger el color de fondo para el carousel.
  • Mostrar el metadata de la foto en el carousel.
  • Seleccionar el tipo de galería: Grid de miniaturas, Mosaico, Cuadrados o Círculos.
  • Y la opción para indicar  que todas las galerías sean del tipo Mosaíco por defecto.

Puedes descargarlo desde el repositorio de plugins en  wordpress.org.

Cuando usar template_redirect y template_include

¿ Cuando deberíamos usar template_redirect y cuando deberíamos usar template_include ?

Echando un ojo al codex de wordpress.org podemos leer claramente en que situaciones debemos usar una u otra función, pero a pesar de tener esto plasmado en la documentación a veces puede resultar confuso.

template_redirect

Este hook se ejecuta justo antes de que WordPress determine que template debe usar. Es decir,  que cuando se ejecuta WordPress ya ha hecho las consultas principales necesarias e instanciado todos los objetos, todo esta hecho excepto el “output”.

Es el punto ideal para redirigir al usuario a otro lugar queriendo tener conocimiento del contenido.

No es una buena práctica usar este hook para incluir una plantilla distinta.

Supongamos que usamos template_redirect para incluir una plantilla:

1
2
3
4
5
6
7
8
add_action( 'template_redirect', 'switch_template' );
 
function switch_template() {
  if ( is_page('test') ) {
    include( PATH . '/test-file.php' );
    exit();
  }
}

El problema aquí es que cualquier script que venga después de este código no va a ejecutarse.

La manera correcta de hacer esto sería usando template_include.

template_include

Este hook se ejecuta justo antes de que WordPress incluya el archivo del template.

Con el ejemplo de antes, la manera correcta sería:

1
2
3
4
5
6
7
8
9
10
11
12
add_filter( 'template_include', 'switch_template' );
 
function switch_template( $template ) {
  if ( is_page('test') ) {
    $template_test = locate_template( array( 'test-file.php' ) );
    if ( $template_test != '' ) {
	   return $template_test ;
	}
  } else {
    return $template;
  }
}

¿ Entonces cuándo podríamos usar template_redirect ?
Un buen ejemplo és el que esta en el codex de wordpress.org, cuando llegamos a una determinada página y queremos que el usuario este identificado, si no esta identificado se le redirige a la pagina de registro.
El siguiente código es el que se encuentra en ese mismo ejemplo:

function my_page_template_redirect()
{
    if( is_page( 'goodies' ) &amp;&amp; ! is_user_logged_in() )
    {
        wp_redirect( home_url( '/signup/' ) );
        exit();
    }
}
add_action( 'template_redirect', 'my_page_template_redirect' );

Conclusión

Simplemente hay que tener en cuenta que tal y como indican sus respectivos nombres:

Contact Form 7, guardar formulario en la base de datos

Crear la tabla para guardar los datos en la base de datos.

CREATE TABLE `wp_tps_forms` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `form` varchar(100) NOT NULL DEFAULT '',
  `data` text NOT NULL,
  `date` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

Llamar la función justo antes del envío de correo con el hook wpcf7_before_send_mail.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 
add_action('wpcf7_before_send_mail', 'save_form' );
 
function save_form( $wpcf7 ) {
   global $wpdb;
 
   /*
    Nota: desde la version 3.9 Contact Form 7 ha eliminado $wpcf7->posted_data
    y ahora se accede a los datos a través de API.
   */
 
   $submission = WPCF7_Submission::get_instance();
 
   if ( $submission ) {
 
       $submited = array();
       $submited['title'] = $wpcf7->title();
       $submited['posted_data'] = $submission->get_posted_data();
 
    }
 
     $data = array(
   		'name'  => $submited['posted_data']['name'],
   		'email' => $submited['posted_data']['email']
   	     );
 
     $wpdb->insert( $wpdb->prefix . 'tps_forms', 
		    array( 
                          'form'  => $submited['title'], 
			   'data' => serialize( $data ),
			   'date' => date('Y-m-d H:i:s')
			)
		);
}