WordPress: creare un plugin per gestire un portfolio con jQuery ed Isotope

Short link

I layout di tipo masonry vengono gestiti in jQuery tramite svariati plugin. Il più popolare di questi plugin è certamente Isotope. Creare un plugin di WordPress che gestisca un portfolio tramite Isotope è relativamente semplice, a patto che si conosca il modo con cui Isotope gestisce tag e categorie.

Creiamo una directory per il nostro plugin in cui inseriremo le sottodirectory includes e templates. Vogliamo infatti che l'utente possa scegliere tra un tema classico ed uno in stile masonry.

Quindi definiamo i metadati per WordPress:


/*
  Plugin Name: Portfolio
  Plugin URI: http://tuosito/
  Description: Portfolio
  Version: 1.0.0
  Author: Nome Cognome
  Author URI: http://tuosito/
  License: GPLv2
 */

Passiamo poi a creare delle costanti per rendere più semplice l'inclusione delle risorse:


define('PORTFOLIO_DIR', dirname(__FILE__));
define('PORTFOLIO_TEMPLATES_DIR', PORTFOLIO_DIR . "/templates");
define('PORTFOLIO_INCLUDES_DIR', PORTFOLIO_DIR . "/includes");
define('PORTFOLIO_URL', WP_PLUGIN_URL . "/" . basename(PORTFOLIO_DIR));
define('PORTFOLIO_TEMPLATES_URL', PORTFOLIO_URL . "/templates");
define('PORTFOLIO_INCLUDES_URL', PORTFOLIO_URL . "/includes");

Aggiungiamo il supporto alle immagini in evidenza per il custom post type che andremo a creare:


add_theme_support('post-thumbnails', array('portfolio'));

Ora inizializziamo i custom post type e le loro tassonomie:


register_activation_hook(__FILE__, 'portfolio_activate');

function portfolio_activate() {
    portfolio_portfolio_register();
    flush_rewrite_rules();
}

register_deactivation_hook(__FILE__, 'portfolio_deactivate');

function portfolio_deactivate() {
    flush_rewrite_rules();
}


add_action('init', 'portfolio_register');
add_action('init', 'portfolio_register_taxonomy');

function portfolio_register() {

    $labels = array(
        'name' => __('Portfolio'),
        'singular_name' => __('Portfolio'),
        'add_new' => __('Aggiungi portfolio'),
        'add_new_item' => __('Aggiungi portfolio'),
        'edit_item' => __('Modifica portfolio'),
        'new_item' => __('Nuovo portfolio'),
        'view_item' => __('Visualizza portfolio'),
        'search_items' => __('Cerca portfolio'),
        'not_found' => __('Portfolio non trovato'),
        'not_found_in_trash' => __('Nessun portfolio trovato nel Cestino'),
        'parent_item_colon' => '',
        'menu_name' => __('Portfolio')
    );
    $args = array(
        'labels' => $labels,
        'public' => true,
        'show_ui' => true,
        'capability_type' => 'post',
        'hierarchical' => true,
        'rewrite' => true,
        'supports' => array(
            'title',
            'thumbnail',
            'editor',
            'excerpt'
        ),
        'menu_position' => 23,
        'menu_icon' => PORTFOLIO_URL.'/icon.png', // la vostra icona
        'taxonomies' => array('categorie')
    );

    register_post_type('portfolio', $args);
}

function portfolio_register_taxonomy() {
    register_taxonomy(
        'categorie',
        'portfolio',
        array(
            'hierarchical' => true,
            'label' => 'Categorie',
            'query_var' => true,
            'rewrite' => array('slug' => 'categorie')
        )
    );
}

Ciascun portfolio farà parte di una categoria che definiremo noi, esattamente come avviene per i normali post. Questa suddivisione ci permetterà di creare un filtro visivo con jQuery.

Una volta creata la struttura occorre aggiungere i file JavaScript e CSS:


add_action('wp_enqueue_scripts', 'portfolio_enqueue_scripts');

function portfolio_enqueue_scripts() {

    if(!is_admin()) {

    wp_register_script('prettyphoto', PORTFOLIO_INCLUDES_URL . '/prettyphoto/jquery.prettyPhoto.js', 'jquery');
    wp_register_script('isotope', PORTFOLIO_INCLUDES_URL . '/isotope/jquery.isotope.js', 'jquery');
    wp_register_script('portfolio_scripts', PORTFOLIO_INCLUDES_URL . '/scripts.js', 'jquery');
    wp_register_script('isotope_scripts', PORTFOLIO_INCLUDES_URL . '/isotope.js', 'jquery');

if(!wp_script_is('jquery')) { // solo se jQuery NON è già presente
    wp_enqueue_script('jquery');
}
    wp_enqueue_script('prettyphoto');

    $tpl = get_option('portfolio_template');

    if($tpl == 'masonry') {
        wp_enqueue_script('isotope');
        wp_enqueue_script('isotope_scripts');
    } else {

         wp_enqueue_script('portfolio_scripts');

    }
}
}

add_action('wp_enqueue_scripts', 'portfolio_enqueue_styles');

function portfolio_enqueue_styles() {
    if(!is_admin()) {
        wp_enqueue_style('prettyphoto_style', PORTFOLIO_INCLUDES_URL . "/prettyphoto/prettyPhoto.css", null, null, "screen");
        $tpl = get_option('portfolio_template');
        if($tpl == 'masonry') {
            wp_enqueue_style('isotope-style', PORTFOLIO_INCLUDES_URL . '/isotope/isotope.css', null, null, 'screen');
            wp_enqueue_style('portfolio_style', PORTFOLIO_TEMPLATES_URL . "/masonry/template.css", null, null, 'screen');
        } else {
            wp_enqueue_style('portfolio_style', PORTFOLIO_TEMPLATES_URL . "/3colround/template.css", null, null, 'screen');
        }
    }
}

Come si noterà l'inclusione degli script e degli stili è condizionata dalla scelta di avere un layout di tipo masonry o meno. Abbiamo due script di inizializzazione. Il primo gestisce il filtro delle categorie per il layout predefinito:


jQuery('document').ready(function() {
	// Filtro
	jQuery('div.portfolio-filter ul li a').click(function() {
		jQuery(this).css('outline','none');
		jQuery('div.portfolio-filter ul .current').removeClass('current');
		jQuery(this).parent().addClass('current');
		var filterVal = jQuery(this).attr('rel');
		if(filterVal == 'all') {
			jQuery('div.portfolio ul li.hidden').fadeIn('normal').removeClass('hidden');
		} else {
			jQuery('div.portfolio ul li').each(function() {
				if(!jQuery(this).hasClass(filterVal)) {
					jQuery(this).fadeOut('normal').addClass('hidden');
    
				} else {
					jQuery(this).fadeIn('normal').removeClass('hidden');
				}
			});
		}
                jQuery("a[rel^='lightbox'], a[rel^='youtube'], a[rel^='fancybox']", "div.portfolio ul li:not(.hidden)" ).prettyPhoto();
		return false;
	});
        // PrettyPhoto 
	jQuery("a[rel^='lightbox'], a[rel^='youtube'], a[rel^='fancybox']").prettyPhoto();
});

In pratica ciascun link del menu che gestisce il filtraggio e l'ordinamento ha un attributo rel che corrisponde alla classe di una o più voci della lista del portfolio. Così facendo si seleziona un gruppo di elementi con un effetto di assolvenza o dissolvenza a seconda dei casi.

Con Isotope (secondo script) il discorso cambia e guadagna in semplicità:


(function($) {
	$(function() {

		var $container = $('div.portfolio-ul-div');
    	$container.isotope({
        filter: '*',
        animationOptions: {
            duration: 750,
            easing: 'linear',
            queue: false
        }
    });

		$('div.portfolio-filter a').click(function(){
    var selector = $(this).attr('data-filter');
    $container.isotope({
        filter: selector,
        animationOptions: {
            duration: 750,
            easing: 'linear',
            queue: false
        }
    });
  return false;
});

      	$("a[rel^='lightbox'], a[rel^='youtube'], a[rel^='fancybox']").prettyPhoto();

	});


})(jQuery);

Quello che nel primo script veniva gestito tramite l'attributo rel qui viene gestito tramite l'attributo HTML5 data-filter. Il risultato è lo stesso, ma l'effetto visivo finale è di maggiore impatto.

Il problema ora è assegnare ai link del menu e agli elementi del portfolio lo stesso riferimento, ossia una classe. Possiamo usare le categorie del portfolio stesso:


function portfolio_list_categories() {
    $_categories = get_categories('taxonomy=categorie'); // tassonomia del portfolio
    $content = '';
    foreach ($_categories as $_cat) {
        $type = get_option('portfolio_template');
        if($type == 'masonry') {
            $content .= '<li>';   
        } else {
            $content .= '<li class="cat-item cat-item-' . $_cat->term_id . '">';
        }
        if($type == 'masonry') {
            $content .= '<a title="Visualizza tutti i post di ' . $_cat->name . '" href="' . get_term_link($_cat->slug, 'categorie') . '" data-filter=".' . $_cat->slug . '">' . $_cat->name . '</a></li>';
        } else {
            $content .= '<a title="Visualizza tutti i post di ' . $_cat->name . '" href="' . get_term_link($_cat->slug, 'categorie') . '" rel="' . $_cat->slug . '">' . $_cat->name . '</a></li>';    
        }

        
    }

    return $content;
}

function portfolio_get_item_classes($post_id = null) {
    if ($post_id === null)
        return;
    $_terms = wp_get_post_terms($post_id, 'categorie');
    $t = '';
    
    foreach ($_terms as $_term) {
        $t .= " " . $_term->slug;
    }
    

    return $t;
}

A questo punto ci servono le opzioni del plugin:


class PortfolioSettings {
    public function __construct(){
        if(is_admin())  {
            add_action('admin_menu', array($this, 'addPluginPage'));
            add_action('admin_init', array($this, 'pageInit'));
        }
    }
    
    public function addPluginPage(){
        add_options_page('Settings Admin', 'Opzioni Portfolio', 'manage_options', 'portfolio-setting-admin', array($this, 'createAdminPage'));
    }

    public function createAdminPage(){
        ?>
    <div class="wrap">
        <?php screen_icon(); ?>
        <h2>Opzioni</h2>            
        <form method="post" action="options.php">
            <?php
                    
            settings_fields('portfolio_option_group');   
            do_settings_sections('portfolio-setting-admin');
        ?>
            <?php submit_button(); ?>
        </form>
    </div>
    <?php
    }
    
    public function pageInit(){     
    register_setting('portfolio_option_group', 'array_key', array($this, 'checkOption'));
        
        add_settings_section(
        'portfolio_template_section',
        'Template',
        array($this, 'printSectionInfo'),
        'portfolio-setting-admin'
    );  
        
    add_settings_field(
        'portfolio_template', 
        'Template', 
        array($this, 'createSelectField'), 
        'portfolio-setting-admin',
        'portfolio_template_section'         
    );      
    }
    
    public function checkOption($input){
        $choices = array('default', 'masonry');
        if(in_array($input['portfolio_template'], $choices)){
            $choice = $input['portfolio_template'];          
        if(get_option('portfolio_template') === FALSE){
        add_option('portfolio_template', $choice);
        }else{
        update_option('portfolio_template', $choice);
        }
    }else{
        $choice = '';
    }
    return $choice;
    }
    
    public function printSectionInfo(){
        echo 'Scegli il tuo template:';
    }
    public function createSelectField(){
        ?>
             <select name="array_key[portfolio_template]">
                <option>Seleziona</option>
                <option value="default" <?php if(get_option('portfolio_template') == 'default'):?> selected="selected" <?php endif;?>>Default</option>
                <option value="masonry" <?php if(get_option('portfolio_template') == 'masonry'):?> selected="selected" <?php endif; ?>>Masonry</option>
             </select>
        <?php
    }
}

$portfolio_settings = new PortfolioSettings();

L'ultimo passo è la creazione dello shortcode per la visualizzazione del portfolio:


function portfolio_display( $atts ) {
    
    extract( shortcode_atts( array(
        'items' => 'all',
    ), $atts ) );

    $html = '<div class="content group portfolio-content">';
    $html .= '<div class="portfolio-filter group">';
    $html .= '<ul class="portfolio-ul">';
    if(get_option('portfolio_template') == 'masonry') {
        $html .= '<li class="current"><a href="" data-filter="*">Tutti</a></li>';    
    } else {
        $html .= '<li class="current"><a href="#" rel="all">Tutti</a></li>';
    }
    $html .= portfolio_list_categories();
    $html .= '</ul>';
    $html .= '</div>';

    $html .= '<div class="portfolio three group">';
    $html .= '<ul class="portfolio-ul">';
    $html .= '<div class="portfolio-ul-div">';

    $portfolio = new WP_Query(array('post_type' => 'portfolio', 'posts_per_page' => -1));

    

    while ($portfolio->have_posts()) {
        $portfolio->the_post();

        $html .= '<li class="' . portfolio_get_item_classes(get_the_ID()) . '">';
        $html .= '<h3>' . get_the_title() . '</h3>';
        $html .= '<div class="portfolio-links"><a href="' . get_permalink() . '">Leggi</a></div>';
        $dimensions = 'full';

        $src = wp_get_attachment_image_src(get_post_thumbnail_id(get_the_ID()), $dimensions, false, '');
        $html .=  '<div class="portfolio-item">';
        $html .= '<a href="' . $src[0] . '" rel="lightbox[portfolio_gal]">';
        $html .= '<img src="' . $src[0] . '" />';
        $html .= '</a>';
        $html .= '</div>';
        $html .= '</li>';

        
    }
     wp_reset_query();

     $html .= '</div>';
     $html .= '</ul>';
     $html .= '</div>';
     $html .= '</div>';

     return $html;

}

add_shortcode('portfolio', 'portfolio_display');

Ho volutamente evitato di specificare stili CSS per lasciarvi liberi di sperimentare. Tenete sempre presente che per ottenere buoni risultati con Isotope occorre utilizzare il floating per allineare i blocchi del portfolio.