WordPress: implementare un framework per i temi: le basi

WordPress: implementare un framework per i temi: le basi

In questo articolo vedremo come implementare un framework di base per operare sui temi di WordPress.

Struttura del framework

Il nostro framework avrà delle classi base che verranno estese dalle classi che le useranno. Le classi base hanno il compito di automatizzare il più possibile dei task che in WordPress richiedono sempre le stesse routine.

AJAX

In WordPress AJAX viene implementato usando questa procedura:

  1. Viene registrata una action tramite i prefissi wp_ajax (per utenti loggati) e wp_ajax_nopriv (per tutti i visitatori).
  2. Viene associata alla action una funzione o un metodo di una classe; nel secondo caso la parola chiave $this fa riferimento alla classe che definisce il metodo

WordPress imposta il Content Type della risposta su text/html, quindi occorre cambiare questa impostazione prima di restituire l'output al client.


class My_Ajax_Wrapper {

	protected $_actions = array();
	protected $_types;

	public function __construct( $contentTypes ) {
		$this->_types = $contentTypes; // es. application/json, text/xml ecc.
	}

	public function addAction( $name, $visibility, $method ) {
		$this->_actions[] = array(
			'name' => $name, // Nome action
			'visibility' => $visibility, // all, public, private
			'method' => $method // Metodo della classe discendente
		);
	}
	
	// $reference è l'istanza della classe discendente

	public function registerActions( $reference ) {
		$actions = $this->_actions;
		if( count( $actions ) == 0 ) {
			return;
		}
		foreach( $actions as $action ) {
			$visibility = $action['visibility'];
			if( $visibility == 'all' ) {
				add_action( 'wp_ajax_' . $action['name'], array( $reference, $action['method'] ) );
				add_action( 'wp_ajax_nopriv_' . $action['name'], array( $reference, $action['method'] ) );
			} else if( $visibility == 'public' ) {
				add_action( 'wp_ajax_nopriv_' . $action['name'], array( $reference, $action['method'] ) );	
			} else {
				add_action( 'wp_ajax_' . $action['name'], array( $reference, $action['method'] ) );	
			}
		}
	}

	public function setContentType( $type = 'application/json' ) {
		$allowed = $this->_types;
		if( !in_array( $type, $allowed ) ) {
			return;
		}
		header( 'Content-Type: ' . $type );
	}


}

Esempio d'uso:


require_once( 'My_Ajax_Wrapper.php' );

class My_Ajax {
	protected $_ajax;

	public function __construct() {
		$this->_ajax = new My_Ajax_Wrapper( array( 'application/json', 'text/xml' ) );
		$this->actions();
		$this->_ajax->registerActions( $this );
	}

	public function actions() {
		$this->_ajax->addAction( 'test', 'all', 'foo' );
		$this->_ajax->addAction( 'bar', 'all', 'bar' );
	}

	public function foo() {
		$this->_ajax->setContentType( 'text/xml' );
		echo '<foo>ok</foo>';
		exit();
	}

	public function bar() {
		$this->_ajax->setContentType( 'application/json' );	
		echo json_encode( array( 'ok' => 'bar' ) );
		exit();
	}
}

$myA = new My_Ajax();

L'action test restituisce il suo output in XML, mentre la seconda action lo restituisce in JSON. $this nel metodo registerActions() punta alla classe corrente, ossia My_Ajax.

Custom fields

I custom fields in WordPress non sono altro che campi aggiunti ai post e alle pagine. Ciascun campo è un elemento di un normale form HTML che viene visualizzato da WordPress all'interno di un box HTML detto metabox (il nome deriva dal fatto che questi campi sono campi meta).

Quando si crea un nuovo campo bisogna associarlo a un metabox con dei parametri:

  1. ID (stringa identificativa)
  2. Titolo
  3. Funzione o metodo di una classe che genera l'HTML del campo
  4. Screen, ossia se il campo va associato a un post, a una pagina o a un custom post type
  5. Contesto, ossia la posizione nell'editor di WordPress
  6. Priorità

Poi bisogna salvare il valore del campo aggiungendo la action save_post: infatti i custom field non vengono salvati in automatico.

Definiamo la seguente classe base:


class My_CF {

	public $parameters;
	public $fields;

	public function __construct( $params = array(), $fiels = array() ) {
		$this->parameters = $params; // parametri metabox
		$this->fields = $fiels; // campi
		add_action( 'add_meta_boxes', array( &$this, 'register' ) ); // crea la metabox
		add_action( 'save_post', array( &$this, 'save' ) ); // salva i campi
		
		// aggiungiamo uno script per i campi immagine
		wp_register_script( 'cf', plugins_url() . '/my-plugin/framework/cf/cf.js', array( 'jquery' ), '1.0', true );
		wp_enqueue_script( 'cf' );
	}

	// crea la metabox
	public function register() {
		$config = $this->parameters;
		add_meta_box( $config['id'], $config['title'], array( &$this, 'output' ), $config['screen'], $config['context'], $config['priority'] ); 
	}
	
	// crea il codice HTML dei campi

	public function output( $post ) {
		$form_fields = $this->fields;
		$post_id = $post->ID;
		$html = '';
		$output = array();
		foreach( $form_fields as $form_field ) {
			$output[] = $this->_display( $form_field, $post_id );
		}
		$html = implode( "\n", $output );
		echo $html;
	}
	
	// Salva il campo

	public function save( $post_id ) {
		$form_fields = $this->fields;
		$config = $this->parameters;
		if ( $config['screen'] != $_POST['post_type'] ) {
        	return $post_id;
    	}
    	if ( !current_user_can( 'edit_post', $post_id ) ) {
        	return $post_id;
    	}
    	foreach( $form_fields as $form_field ) {
    		$name = $form_field['name'];
    		update_post_meta( $post_id, $name, sanitize_text_field( $_POST[$name] ) );
    	}	
	}
	
	// Crea una stringa HTML a seconda del tipo di campo

	private function _display( $field, $id ) {
		$field_html = '';
		if( !is_array( $field ) ) {
			return '';
		}
		switch( $field['type'] ) {
			case 'text':
				$field_html = '<p><label style="display: block; font-weight: bold; margin-bottom: 0.6em" for="' . $field['name'] . '">' . $field['label'] . '</label><input type="' . $field['type'] . '" style="display: block; width: 100%;" name="' . $field['name'] . '" placeholder="' . $field['label'] . '" value="' . get_post_meta( $id, $field['name'], true ) . '" /></p>';
				break;
			case 'checkbox':
			case 'radio':
				$checked = ( get_post_meta( $id, $field['name'], true ) == $field['value'] ) ? ' checked="checked"' : '';
				$field_html = '<p><input type="' . $field['type'] . '" name="' . $field['name'] . '" value="' . $field['value'] . '"' . $checked . ' /><label style="font-weight: bold; margin-left: 0.5em" for="' . $field['name'] . '">' . $field['label'] . '</label></p>';
				break;
			case 'textarea':
				$field_html = '<p><label style="display: block; font-weight: bold; margin-bottom: 0.6em" for="' . $field['name'] . '">' . $field['label'] . '</label><textarea cols="20" rows="10" style="display: block; width: 100%;" name="' . $field['name'] . '" placeholder="' . $field['label'] . '">';
				$field_html .= esc_html( get_post_meta( $id, $field['name'], true ) );
				$field_html .= '</textarea></p>';
				break;
			case 'select':
				$field_html = '<p><label style="display: block; font-weight: bold; margin-bottom: 0.6em" for="' . $field['name'] . '">' . $field['label'] . '</label>';
				$field_html .= '<select name="' . $field['name'] . '" style="display: block; width: 100%;">';
				$field_html .= '<option value=""></option>';

				foreach( $field['options'] as $opt ) {
					$selected = ( get_post_meta( $id, $field['name'], true ) == $opt['value'] ) ? ' selected="selected"' : '';
					$field_html .= '<option value="' . $opt['value'] . '"' . $selected . '>' . $opt['label'] . '</option>';
				}
				$field_html .= '</select></p>';
				break;
			case 'image':
				$field_html = '<div><label style="display: block; font-weight: bold; margin-bottom: 0.6em" for="' . $field['name'] . '">' . $field['label'] . '</label>';
				$image = ( is_numeric( get_post_meta( $id, $field['name'], true ) ) ) ? wp_get_attachment_image_src( get_post_meta( $id, $field['name'], true ), 'thumbnail' ) : '';
				$field_html .= '<div id="cf-image-' . $field['name'] . '" style="background-color: #ccc; background-size: cover; background-repeat: no-repeat; width: 150px; height: 150px;">';
				if( $image !== '' ) {
					$field_html .= '<img src="' . $image[0] . '" alt="" width="150" height="150" />';
				} else {
					$field_html .= '<img />';	
				}
				$field_html .= '</div>';
				$field_html .= '<input type="hidden" name="' . $field['name'] . '" value="' . get_post_meta( $id, $field['name'], true ) . '" />';
				$field_html .= '<p><button type="button" class="cf-image button-primary">' . __( 'Seleziona', 'my-plugin' ) . '</button></p>';
				$field_html .= '</div>';
				break;

			default:
				break;
		}

		return $field_html;
	}

}

Il codice jQuery viene usato per selezionare le immagini dalla Media Library:


(function( $ ) {
	var CF = function( element ) {
		this.$el = $( element );
		if( this.$el.length ) {
			this.init();
		}
	};

	CF.prototype = {
		init: function() {
			this.selectImage();
		},
		selectImage: function() {
			this.$el.each(function() {
				var $btn = $( this ),
					$input = $btn.parent().prev(),
					$img = $input.prev().find( "img" );

					$btn.click(function() {
						if( wp.media.frames.gk_frame ) {
                				wp.media.frames.gk_frame.open();
                				
            				} else {
            				wp.media.frames.gk_frame = wp.media({
                				title: "Seleziona immagine",
                				multiple: false,
                				library: {
                    				type: "image"
                				},
                				button: {
                    				text: "Usa immagine"
                				}
            				});
            					var selectMedia = function() {
            						var selection = wp.media.frames.gk_frame.state().get( "selection" );
            						if( !selection ) {
            							return;
            						}
            						selection.each(function( attachment ) {
                    					var attrs = attachment.attributes;
                    					var imageID = attrs.id; // ID immagine
                    					var url = attrs.url;  // URL immagine
                    					$input.val( imageID );
                    					$img.parent().css( "background-image", "url(" + url + ")" );
                					});
            					};
            					wp.media.frames.gk_frame.on( "close", selectMedia );

            					wp.media.frames.gk_frame.open();
            				}
					});
			});
		}
	};

	$(function() {
		var _cf = new CF( ".cf-image" );

	});

})( jQuery );

Esempio d'uso:


require_once( 'My_CF.php' );

class My_Fields {
	
	public $parameters = array();
	public $fields = array();
	protected $_cf;
	
	public function __construct( $params, $flds ) {
		
		$this->parameters = $params;
		$this->fields = $flds;
		
		if ( is_admin() ) {
			// quando viene caricato l'editor
			add_action( 'load-post.php', array( $this, 'loadFields' ) );
			add_action( 'load-post-new.php', array( $this, 'loadFields' ) );
		}

	}
	
	public function loadFields() {
		$this->_cf = new My_CF( $this->parameters, $this->fields );	
	}
}

Quindi instanziamo la classe:


$my_params = array(
	'id' => 'test',
	'title' => 'Test',
	'screen' => 'post',
	'context' => 'side',
	'priority' => 'default'
);
$my_fields = array(
	array(
		'name' => 'foo',
		'type' => 'text',
		'label' => 'Foo'
	),
	array(
		'name' => 'bar',
		'type' => 'checkbox',
		'label' => 'Bar',
		'value' => '1'
	)
);

$cf = new My_Fields( $my_params, $my_fields );

Torna su