WordPress ギャラリーショートコードのカスタマイズ（サムネイル＋大きい画像＋画像が表示されたらGoogleアナリティクスに送信）

タイトル長いですが。

# 仕様

- 大きい画像＋サムネイル
- 左右にスライドさせるボタンあり
- サムネイルクリックで大きい画像が変わる
- レスポンシブでスマホでも対応（スワイプ）
- 通常のギャラリーショートコードで入力は簡単に！
- テーマの切り替えや他のギャラリー系プラグインに差し替えても影響がないように。
- 大きい画像が表示されたらGoogleアナリティクスのトラッキングイベントを発火させて表示数取りたい
    - 対象画像のアタッチメントページのURLを Googleアナリティクスに送る

実は最後のをPJAXとかでやるのがめんどかったのでjsで実装してます。そのほうが楽。

# 完成形イメージ

![スクリーンショット 2016-07-01 19.45.25.png](https://qiita-image-store.s3.amazonaws.com/0/18348/9e4a57fa-90fb-7c5a-13bd-668993ea953c.png)

# ソース

- ギャラリー部分は [bxSlider](http://bxslider.com/) 使う
- ギャラリーショートコードをオーバーライド
- テーマに埋め込んでおけばテーマ変えても通常のギャラリーに戻る
- 1ページ内に複数のギャラリーがあることを考慮しておく
- Googleアナリティクスを使ってなくてもエラーを出さないように

## PHP

bxSliderのjsとcssの埋め込みは割愛。よしなに処理してください。
基本コアのコードと同じなので、「// ここからが本番」以降が大事。

```php:my-gallery-shortcode.php
<?php
/**
 * Remove default gallery shortcode function.
 * Add custom gallery shortcode function.
 */
remove_shortcode( 'gallery', 'gallery_shortcode' );
add_shortcode( 'gallery', 'my_gallery_shortcode' );
function my_gallery_shortcode( $attr ) {
	$post = get_post();
	
	static $instance = 0;
	$instance++;
	
	if ( ! empty( $attr['ids'] ) ) {
		// 'ids' is explicitly ordered, unless you specify otherwise.
		if ( empty( $attr['orderby'] ) ) {
			$attr['orderby'] = 'post__in';
		}
		$attr['include'] = $attr['ids'];
	}
	
	/**
	 * Filter the default gallery shortcode output.
	 *
	 * If the filtered output isn't empty, it will be used instead of generating
	 * the default gallery template.
	 *
	 * @since 2.5.0
	 * @since 4.2.0 The `$instance` parameter was added.
	 *
	 * @see gallery_shortcode()
	 *
	 * @param string $output   The gallery output. Default empty.
	 * @param array  $attr     Attributes of the gallery shortcode.
	 * @param int    $instance Unique numeric ID of this gallery shortcode instance.
	 */
	$output = apply_filters( 'post_gallery', '', $attr, $instance );
	if ( $output != '' ) {
		return $output;
	}
	
	$html5 = current_theme_supports( 'html5', 'gallery' );
	$atts = shortcode_atts( array(
		'order'      => 'ASC',
		'orderby'    => 'menu_order ID',
		'id'         => $post ? $post->ID : 0,
		'itemtag'    => $html5 ? 'figure'     : 'dl',
		'icontag'    => $html5 ? 'div'        : 'dt',
		'captiontag' => $html5 ? 'figcaption' : 'dd',
		'columns'    => 3,
		'size'       => 'thumbnail',
		'include'    => '',
		'exclude'    => '',
		'link'       => ''
	), $attr, 'gallery' );
	
	$id = intval( $atts['id'] );
	
	if ( ! empty( $atts['include'] ) ) {
		$_attachments = get_posts( array( 'include' => $atts['include'], 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'] ) );
	
		$attachments = array();
		foreach ( $_attachments as $key => $val ) {
			$attachments[$val->ID] = $_attachments[$key];
		}
	} elseif ( ! empty( $atts['exclude'] ) ) {
		$attachments = get_children( array( 'post_parent' => $id, 'exclude' => $atts['exclude'], 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'] ) );
	} else {
		$attachments = get_children( array( 'post_parent' => $id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'] ) );
	}
	
	if ( empty( $attachments ) ) {
		return '';
	}
	
	if ( is_feed() ) {
		$output = "\n";
		foreach ( $attachments as $att_id => $attachment ) {
			$output .= wp_get_attachment_link( $att_id, $atts['size'], true ) . "\n";
		}
		return $output;
	}
	
	$itemtag = tag_escape( $atts['itemtag'] );
	$captiontag = tag_escape( $atts['captiontag'] );
	$icontag = tag_escape( $atts['icontag'] );
	$valid_tags = wp_kses_allowed_html( 'post' );
	if ( ! isset( $valid_tags[ $itemtag ] ) ) {
		$itemtag = 'dl';
	}
	if ( ! isset( $valid_tags[ $captiontag ] ) ) {
		$captiontag = 'dd';
	}
	if ( ! isset( $valid_tags[ $icontag ] ) ) {
		$icontag = 'dt';
	}
	
	$columns = intval( $atts['columns'] );
	$itemwidth = $columns > 0 ? floor(100/$columns) : 100;
	$float = is_rtl() ? 'right' : 'left';
	
	$selector = "gallery-{$instance}";
	
	$gallery_style = '';
	
	/**
	 * Filter whether to print default gallery styles.
	 *
	 * @since 3.1.0
	 *
	 * @param bool $print Whether to print default gallery styles.
	 *                    Defaults to false if the theme supports HTML5 galleries.
	 *                    Otherwise, defaults to true.
	 */
	if ( apply_filters( 'use_default_gallery_style', ! $html5 ) ) {
		$gallery_style = "
		<style type='text/css'>
			#{$selector} {
				margin: auto;
			}
			#{$selector} .gallery-item {
				float: {$float};
				margin-top: 10px;
				text-align: center;
				width: {$itemwidth}%;
			}
			#{$selector} img {
				border: 2px solid #cfcfcf;
			}
			#{$selector} .gallery-caption {
				margin-left: 0;
			}
			/* see gallery_shortcode() in wp-includes/media.php */
		</style>\n\t\t";
	}
	
	$size_class = sanitize_html_class( $atts['size'] );
	$gallery_div = "<div id='$selector' class='gallery galleryid-{$id} gallery-columns-{$columns} gallery-size-{$size_class}'>";
	
	/**
	 * Filter the default gallery shortcode CSS styles.
	 *
	 * @since 2.5.0
	 *
	 * @param string $gallery_style Default CSS styles and opening HTML div container
	 *                              for the gallery shortcode output.
	 */
	$output = apply_filters( 'gallery_style', $gallery_style . $gallery_div );

	// ここからが本番
	$slider = '';
	$pager  = '';
	$i = 0;

	foreach ( $attachments as $id => $attachment ) {
	
		$attr = ( trim( $attachment->post_excerpt ) ) ? array( 'aria-describedby' => "$selector-$id" ) : '';

		$image_output = wp_get_attachment_image( $id, 'full', false, $attr );
		$image_uri    = get_attachment_link( $id );
		$image_path   = str_replace( home_url(), '', $image_uri ); // GAに送るアタッチメントページのURL（処理済）

		$pager_output = wp_get_attachment_image( $id, 'thumbnail', false, $attr );
		$image_meta   = wp_get_attachment_metadata( $id );
	
		$orientation = '';
		if ( isset( $image_meta['height'], $image_meta['width'] ) ) {
			$orientation = ( $image_meta['height'] > $image_meta['width'] ) ? 'portrait' : 'landscape';
		}
		$slider .= "<{$itemtag} class='slider-item' data-href='{$image_path}'>";
		$slider .= "
			<{$icontag} class='slider-icon {$orientation}'><div class='slider-icon-img'>
				$image_output
			</div></{$icontag}>";
		if ( $captiontag && trim($attachment->post_excerpt) ) {
			$slider .= "
				<{$captiontag} class='slider-caption-text slider-caption' id='$selector-$id'>
				" . wptexturize($attachment->post_excerpt) . "
				</{$captiontag}>";
		}
		$slider .= "</{$itemtag}>\n";

		$pager .= '<a data-slide-index="' . $i . '" href="#" class="slider-pager-icon">';
		$pager .= $pager_output;
		$pager .= "</a>\n";
		
		$i ++;
	}

	$output .= '<div class="gallery-bxslider">' . "\n";
	$output .= $slider;
	$output .= "</div>\n";

	$output .= '<div id="bx-pager-' . $instance . '" class="slider-pager">' . "\n";
	$output .= $pager;
	$output .= "</div>\n";

	$output .= "</div>\n";
	
	return $output;
}
```

で、これが出力されるとHTMLソースはこんな感じ（一部省略）

```html
<div id='gallery-1' class='gallery galleryid-555 gallery-columns-3 gallery-size-thumbnail'>
	<div class="gallery-bxslider">
		<figure class='slider-item' data-href='/archives/555/canola2'>
			<div class='slider-icon landscape'>
				<img width="640" height="480" src="http://example.com/wp-content/uploads/2011/01/canola2.jpg" />
			</div>
			<figcaption class='slider-caption-text slider-caption' id='gallery-1-611'>
				caption text.
			</figcaption>
		</figure>
		(.slider-item の繰り返し)
	</div>
	<div id="bx-pager-1" class="slider-pager">
		<a data-slide-index="0" href="#" class="slider-pager-icon"><img width="150" height="150" src="http://example.com/wp-content/uploads/2011/01/canola2-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="canola" aria-describedby="gallery-1-611" /></a>
		(.slider-pager-icon の繰り返し)
	</div>
</div>		
```

## JacaScript

- `onSliderLoad`（スライダー読み込み時）と `onSlideBefore`（スライダーが表示される直前） に `data-href` の値をGoogleアナリティクスに送信
    - この部分はイベントトラッキングでもいいと思われ
    - bxSliderのコールバックで追加処理を行うようにしているので、詳しくはドキュメント見て  
http://bxslider.com/options

```js:slider.js
(function($){
	// BxSlider gallery
	var gallerybox = $('.gallery');
	var gallery, pagerObj, pagerID, slideurl;
	if ( gallerybox.length > 0 ) {
		gallerybox.each(function(){
			gallery        = $(this).children('.gallery-bxslider');
			pagerObj       = $(this).children('.slider-pager');
			pagerID        = pagerObj.attr('id');

			gallery.bxSlider({
				pagerCustom:  '#' + pagerID,
				onSliderLoad: function( currentIndex ) {
					slideurl = gallery.children().eq( currentIndex + 1 ).attr( 'data-href' );
					console.log(slideurl);
					if ( typeof ga == 'function' ) { 
						ga( 'send', 'pageview', slideurl );
					}
				},
				onSlideBefore: function($slideElement, oldIndex, newIndex) {
					slideurl = $slideElement.attr( 'data-href' );
					console.log(slideurl);
					if ( typeof ga == 'function' ) { 
						ga( 'send', 'pageview', slideurl );
					}
				}
			});
		});
	}
})(jQuery);
```


## CSS

お好みで

```css:gallery-style.css
.slider-pager .slider-pager-icon {
	display: inline-block;
	width: 50px;
	height: 50px;
    border: 3px solid #ffffff;
}
.slider-pager .slider-pager-icon.active,
.slider-pager .slider-pager-icon:hover,
.slider-pager .slider-pager-icon:focus,
.slider-pager .slider-pager-icon:active {
    border: 3px solid #e60012;
}
```