npm + Nunjucks + Stylus で静的サイト制作を効率的にする

静的サイト制作時におすすめのタスク自動化ツール構成 npm+Nunjucks+Stylus を紹介します。

各ツールの簡単な紹介

[npm]
Node.jsのパッケージマネージャー。Node.jsをインストールすると自動的にインストールされる。

[Nunjucks]
javascripで使用するテンプレートエンジン。Mozillaが開発している。

[Stylus]
Node.js製のCSSプリプロセッサ。

前提条件があります

満たしていなければ用意してください

〇nodeインストール済み
〇[Windowsのみ]Linuxツールインストール済み(※WSLが使用可、gitbashでも可)

最終的にどう動くか

この構成ではsrcディレクトリ内のファイルを更新すると各タスクが処理されdistフォルダに完成品が吐き出されます。

javascriptはsrcフォルダのjsファイルをそのまま吐き出します。
TypescriptやWebpackで処理を追加するルートもありますがこれらを使用しなければいけないほどのボリュームがあるのなら他の構成が良いと思うので今回は簡易な処理のみにします。

手順

Linuxツールを開いてプロジェクトディレクトリを作成

mkdir project_name

cd project_name

npmで各パッケージをインストールする

npm init -y

npm i -D nunjucks-cli stylus

nunjucksとstylusをインストールします。
他にもタスク処理に便利なパッケージをインストールします。

npm i -D npm-run-all rimraf chokidar-cli cpx browser-sync

各パッケージの簡単な説明です

  1. [npm-run-all] npm-script内で並列処理、直列処理を設定できる
  2. [rimraf] 指定したディレクトリを削除することができる
  3. [chokidar-cli] 指定したディレクト内のファイルが変更されたら処理を発動することができる
  4. [cpx] 指定したファイルを簡単に複製することができる
  5. [browser-sync] 生成されたWEBページを確認できる。更新時に自動リロードされる

各ディレクトリ、ファイルを用意する

mkdir -p src/{tpl,stylus,js}

touch src/tpl/index.njk

touch src/stylus/style.styl

touch src/js/script.js

最初の段階は各1ファイルずつの構成で進めます。

package.jsonにタスク処理を記入する

npm init -y コマンドで生成されたpackage.jsonにscriptsという項目がありますがここがタスク処理を記入する場所です。

このscriptsに以下の行を追加していきます

"clean": "rimraf dist"

distフォルダを削除します。コマンド名通り生成したものを一旦クリーンにします。

"build:html": "nunjucks /**/*.njk -p src/tpl -o dist"

nunjucks-cliを使用してsrc/tplの中にある拡張子njkのファイルをHTML化しdistフォルダに吐き出します。

"build:stylus": "stylus src/stylus/style.styl -o dist/css/style.css"

stylusを使用してstylus/style.stylファイルをdist/css/style.cssとして吐き出します。

"build:js": "cpx \"src/js/**/*.js\" dist/js/"

cpxを使用してsrc/jsフォルダ内にある拡張子jsのファイルをdist/jsフォルダにコピーします。

"build": "run-s build:html build:stylus build:js"

npm-run-allのrun-sコマンドを使用して今まで設定した生成用コマンドを順番に処理していきます。
※並列に処理をおこなうrun-pというコマンドもありますが並列に処理するとディレクトリ作成処理で躓くこともあるので直列処理の方がエラー回避できます。

jsonファイルなので行末にはかならずカンマ(,)を記入してください。これを忘れると動きません。

ここまででdistフォルダ内にindex.html、css/style.css、js/script.jsが吐き出されるようになりました。

次は各ファイルの変更を監視できるように設定していきます。

次回につづく

WordPressでSNS関連の独自オプションを管理画面に制作する

管理画面にSNSの独自オプションを置きたい場合は次のようなコード追加で実現できます。

<?php
/**
 * SnsOptionsクラス
 *
 * SNS関連の設定を保存、表示用に使用
 *
 * ▼WordPressCodex日本語版の設定ページ作成参考
 * https://wpdocs.osdn.jp/%E8%A8%AD%E5%AE%9A%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E4%BD%9C%E6%88%90
 */
class SnsOptions {

	private $options;

	public function __construct() {
		add_action( 'admin_menu', array( $this, 'add_option_page' ) );
		add_action( 'admin_init', array( $this, 'page_init' ) );
	}

	public function add_option_page() {
		add_options_page(
			'SNS',
			'SNS設定',
			'manage_options',
			'my-opitons-sns.php',
			array( $this, 'create_option_page' )
		);
	}

	public function create_option_page() {
		$this->options = get_option( 'my_sns_options' );
?>
<div class="wrap">
	<h1>SNS設定</h1>
	<form action="options.php" method="post">
		<?php
		settings_fields( 'sns_settings_group' );
		do_settings_sections( 'my-options-sns.php' );
		submit_button();
		?>
	</form>
</div>
<?php
	}

	public function page_init() {

		register_setting(
			'sns_settings_group',
			'my_sns_options',
			array( $this, 'sanitize' )
		);

		add_settings_section(
			'settings_section_sns',
			'SNS関連オプション',
			array( $this, 'print_section_info' ),
			'my-options-sns.php'
		);

		add_settings_field(
			'fb_app_id',
			'facebookアプリID',
			array( $this, 'print_fb_app_id' ),
			'my-options-sns.php',
			'settings_section_sns'
		);

	}

	/**
	 * データの無害化
	 */
	public function sanitize( $input ) {
		//
	}

	/**
	 * セクションの説明文
	 */
	public function print_section_info() {
		//
	}

	/**
	 * facebook アプリIDの入力エリア表示
	 */
	public function print_fb_app_id() {
		printf(
			'<input type="text" id="fb_app_id" name="my_sns_options[fb_app_id]" value="%s" />',
			isset( $this->options['fb_app_id'] ) ? esc_attr( $this->options['fb_app_id'] ) : ''
		);
	}
}

WordPressでカスタムポストの複数記事の特定文字を一括変更するPHPコード

何十、何百と記事を投稿したあとにある言葉が日本語が間違っていたなんてことはないでしょうか?

ひとつひとつの記事を管理画面から修正するなんて考えたら気が重くなります。

効率よく複数記事の内容を変更する方法は何個かあると思いますが今回はPHPプログラムを書いて変更する処理を書いていきます。

間違っている状況例

  • 本文中の「(仮)」が不要になったので一括で削除したい
  • カスタム投稿のpost_typeは「cp」

前提条件

  • htaccessが操作できる環境である
  • phpMyAdminなどは使えない

最初にサーバーのアクセスできる場所にフォルダを作ります。

そのフォルダにhtaccessファイルを作成しIPアドレスでアクセス制限をかけます

mkdir ./tmp

cd ./tmp

touch .htacces
# AccessControl IP/HOST
order deny,allow
deny from all
allow from 127.0.0.1 # ←ここにIPアドレスを指定

このフォルダにPHPファイルを作成します

// wp-load.phpの読み込み
require_once( dirname( __FILE__ ) . '/../wp-load.php' );

function get_edit_cs_id() {
    // wpdbクラスの読み込み
    global $wpdb;

    // 対象の行データを取得する
    $sql = <<< __EOS__
SELECT * FROM wp_posts
WHERE 1=1
AND post_type = 'cs'
AND post_status = 'publish'
LIMIT 1
__EOS__;

    $res = $wpdb->get_results( $wpdb->prepare( $sql ) );

    return $res;
}

function update_edit_cs( $post_id, $content ) {
    global $wpdb;

    $wpdb->update(
        'wp_posts',
        array(
            'post_content' => $content
        ),
        array( 'ID' => $post_id )
    );
}

$pattern = "/(仮)/";
$replace = "";

$cnt = 0;
$max = 1000; // 1000以上の可能性もあるが負荷を考えとりあえずの上限をつけている

for ( $i = 0; $i <= $max; $i++ ) {

    echo "{$i}の処理を開始します<br>";

    $res = array();
    $res = get_edit_cs_id();

    if ( $res ) {
        $content = $res[0]->post_content;

        if ( preg_match ( $pattern, $content ) === 1) {
            $after = preg_replace ( $pattern, $replace, $content, 1 );
            // IDを指定してテーブルをUPDATE
            update_edit_cs( $res[0]->ID, $after );
            echo "{$i}の変換を行いました<br>";
            $cnt ++;
        }
    }

    echo "{$i}の処理を終了しました<br>";
}

echo '全ての処理が終了しました。<br>';
echo "◎処理件数:{$cnt}件<br>";

PHPコードができたらIPアドレスで制限されたフォルダ「tmp」にPHPファイルをアップします。

ブラウザでそのPHPファイルにアクセスしたら処理のスタートです。

うまくいけばカスタムポスト「cs」の公開中の記事から文字列「(仮)」が消えます。

もしphpMyAdminが操作可能ならばPHPを使用しないでMySQLのREPLACEを使う可能性もあります。

WordPressテーマ制作時に指定すべきネイティブスタイル

WordPressのテーマを制作時のcssに次の指定をしておけば自動で付け加えるクラスになんとなく正解なスタイルがあたっていることになります。

/* エディター用 */
p {
    display: block;
    margin: 1em 0;
}

strong {
    font-weight: bold;
}

em {
    font-style: italic;
}

blockquote {
    display: block;
}

/* 画像用 */
.aligncenter {
    display: block;
    margin-right: auto;
    margin-left: auto;
}

.alignright {
    float: right;
}

.alignleft {
    float: left;
}

img[class*="wp-image-"],
img[class*="attachment-"] {
    max-width: 100%;
    height: auto;
}

/* clearfix */
.clearfix {
    overflow: hidden;
    zoom: 1;
}
.clearfix:after {
    content: "";
    display: block;
    clear: both;
}

Stylusでメディアクエリ用のmixinを作っておき使いまわす

最近のサイト制作で一番記述量が多いのはCSSです。cssでできることが増えてきていることも関係しています。

何年も前からcssも生のcssは書かずscssなどのより管理しやすく記述量を短縮できるプリプロセッサを使用した作り方に変わりました。

以前はscssで書いていましたがstylusというプリプロセッサが気にいったのでこちらを使用しています。

Stylusとは何か

NODE製のプリプロセッサです。もしくはSASSのNODE版ともいえます。


CSS記法、SCSS記法、SASS記法で混在で書いても怒られないおおらかな後発ツールの良さがあります。


また独自記法だと「{}」も「:;」もいらなくなるため文字入力数を大幅に減らすことができます。

このstylusのmixin機能をつかってメディアクエリ記述を簡単にするmixinを作ります。

メディアクエリ用mixinをつくろう

stylusの使いかたは別の記事に書くとしてメディアクエリ用のmixinは次のように設定します

mq(num)
    // ここの値はお好みで
  // 今回はTwitterBootstrapのbreakpointsに合わせて…。
    breakpoints = {
        sm: 576, 
        md: 768, 
        lg: 992, 
        xl: 1200 
    }

    // ※基本、モバイルファースト
    for key, val in breakpoints
        if (num == key)
            num = val

    @media only screen and (min-width unit(num, px))
        {block}


// @USAGE 使い方
+mq(sm)
    background-color pink

これでメディアクエリの記述をだいぶ省略することができるようになりました。

WordPressのテーマ制作の際に使用する自作ショートコードのひな形

WordPressで固定ページ内で記事の一覧を取得したいなどの関数を使用する際はショートコードを自作し、管理画面の固定ページ編集画面にショートコードを挿入すると運用的に楽になります。

なぜ楽になるのか?

  • 管理画面からコンテンツの並び変えができる
  • 非表示にすぐできる
  • PHPファイルを直接触らないのでヘマしてもエラー画面にならない

自作ショートコードのひな形は以下のようになります。
ショートコードに引数をもてるようにするとさらに便利になります。

if ( ! function_exists( 'hogeFunc' ) ) :
function hogeFunc( $atts ) {
    extract( shortcode_atts( array (
        // ここに引数のキーとデフォル値をセット
        'num' => 0,
    ), $atts ) );

    return $num * 2;
}
add_shortcode( 'hoge', 'hogeFunc' );
endif;

↓使いかた

編集画面でショートコードをこのように入れると引数の10に2をかけた20が表示されます

[hoge num=10]

ショートコードからテンプレートを呼び出したいときは以下のようにします。

if ( ! function_exists( 'fugaFunc' ) ) :
function fugaFunc( $atts ) {
    extract( shortcode_atts( array (
        // ここに引数のキーとデフォル値をセット
        'num' => 0,
    ), $atts ) );

    ob_start();
    get_template_part( 'foo', 'bar' );
    $template = ob_get_contents();
    ob_end_clean();
    return $template;
}
add_shortcode( 'fuga', 'fugaFunc' );
endif;

↓使いかた

編集画面でショートコードをこのように入れるとテンプレートファイルの内容が表示されます

[fuga num=10]

ショートコードを活用して運用時のサイト変更時にテーマファイルをなるべく触らなくてよい方向にもっていきたいものです。

WordPressで管理画面にログインしていても非公開記事を表示させない方法

WordPressは記事のステータスを「非公開」にすると一般ユーザーには見ることができません。

しかし、管理画面にログインしているとその記事は表示されます。

その際、頭に「非公開:」という文字が入ります。

慣れている人が管理画面に入ってみている分には問題ないのですが頻繁に担当が変わるようなお客様とか「そんな機能必要ない」というお客様は記事取得のコードを下記のように変更してログイン時でも非公開記事は表示されないようにしましょう。

▽index.php

$args = array(
    'post_type' => 'post', // カスタム投稿なら適宜変更 
    'post_status' => 'publish', // ★ここが重要!
    'posts_per_page' => 10,
    // ...
)
$my_query = new WP_Query( $args );

if ( $my_query->have_posts() ) :
    while( $my_query->have_posts() ) : $my_query->the_post();
        get_template_part( 'loop', 'hoge' );
    endwhile;
endif;

▽loop-hoge.php

<article id="post-<?php the_ID(); ?>" class="post">
    <h1 class="post-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
</article>

↑あくまでもサンプルです。ファイル名とHTML構造も適宜変更してください。

jQueryでリンク先が別ドメインのURL(外部サイト)かどうか判定して自動的にtarget=”_blank” rel=”noreferrer noopener”を加えるスクリプト

完全に備忘録です。

前提条件

  • jqueryをheadタグ内で読み込んでいる
  • ES2015(ただlet使用しているだけ…)を使用しても問題ない状況である
$(function() {
    $('a').each(function() {
        let href = $(this).attr('href');
        let target = $(this).attr('target');

        // href属性がhttp(s)で始まっているか
        let link = href.match(/^https?:\/\//);
        if ( link == null ) return;

        // target属性に先客がいないか
        if ( typeof target === 'undefined' || target === false ) return;

        // ドメインが閲覧中ページと同じか
        if ( href.indexOf(location.host) != -1) return;

        $(this).attr('target', '_blank');
        $(this).attr('rel', 'noreferrer noopener');
    }
});

メモ

  • aタグにさらにクラスを加えても良し
  • jQuery非依存のコードもあとで残しておく
  • aタグすべてを対象にするのではなく指定クラスを持ったaタグだけに処理対象をしぼった方がブラウザに優しい
  • クラスをつけれないようなユーザーが操作する状況があるのならaタグ全般に処理した方がユーザーに優しい

WordPressで特定の投稿や固定ページにスタイルやスクリプトを挿入するカスタマイズ

WordPressのテーマ制作時に絶対といってよいほど加えるべき機能がこれです。

運用が始まる前はコンテンツ内容は固まっていますが運用段階が始まってしばらくたつと例外処理が発生します。

そんなときこの機能を用意しておくとテーマのcssファイルやjsファイルを変更する必要がなく、かつ本番系、開発系で処理をわけることが簡単になります。

内容的にはカスタムフィールドを使います。

今回のコードではカスタムフィールド「custom_header」を作成してコードを書くとheadタグ内にここに書いたコードが吐き出されます。

また、カスタムフィールド「custom_footer」を作成してコードを書くとbodyタグの一番下のほうにここに書いたコードが吐き出されます。

カスタムフィールドを設定したらfunctions.phpに以下のコードを記入します。

if ( ! function_exists( 'custom_header_css' ) ) :
function custom_header_css() {
    global $post;
    $field = 'custom_header';
    if ( is_singular() ) {
        $custom_block = get_post_meta( $post->ID, $field, true );
        if ( $custom_block ) {
            echo $custom_block;
        }
    }
}
add_action( 'wp_head', 'custom_header_css', $priority = 100 );
endif;

if ( ! function_exists( 'custom_footer_script' ) ) :
function custom_footer_script() {
    global $post;
    $field = 'custom_footer';
    if ( is_singular() ) {
        $custom_block = get_post_meta( $post->ID, $field, true );
        if ( $custom_block ) {
            echo $custom_block;
        }
    }
}
add_action( 'wp_head', 'custom_header_css', $priority = 100 );
endif;

これでいざとなった時に即座にスタイルとスクリプトを挿入できるようになります。

WordPressで直近1年分の記事を取得する方法

WordPressで過去1年分の記事を取得したい場合、テンプレートで以下のコードを描くと取得できます。

<?php 

$args = array(
    'post_type' => 'custom_post_type', // ここは取得したい投稿タイプを指定します
    'posts_per_page' => -1,
    'orderby' => 'date',
    'order' => 'DESC',
    'date_query' => array(
        array(
            'before' => 'now',
            'after' => '1 year ago',
            'inclusive' => true, // その日を含めるか真偽値で設定します
        )
    )
);
$my_query = new WP_Query( $args );

if ( $my_query->have_posts() ) :
    while ( $my_query->have_posts() ) : $my_query->the_post; ?>

        <!-- ここのコードはお好みで -->
        <article>
            <h1><?php the_title(); ?></h1>
            <?php the_content(); ?>
        </article>

    <?php
    endwhile; 
endif;
wp_reset_postdata();

ポイントはWP_Queryの引数内のdate_queryです。

date_query内のbeforeは戻る前の基準となる日付です。

コードではnowという文字列を記入することで現在を基準としています。

afterの値は過去のある時点をさします。

コードでは「1 year ago」という文字列を記入しています。1年前の今日を指定しています

これで直近1年分の投稿を取得することができました。