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.