ブログカードにアフィリエイトのリンクを付けられる「サムネイルリンクv2」

プロフィール
著者

映像系が本業のフリーランス・3児のパパブロガー。
車関連、Wordpress関連の事をよく記事にします。
イラストは下手なりに頑張って描いてますが、全く上手くなりません(笑)

ライトニングをフォローする
“サムネイルリンク(Thumbnail Link) ” by Lightning2014 is licensed under the Apache License, Version2.0

最近 WordPressのテーマをCocoonに移行したライトニングです。

もともと私は「サムネイルリンク」というPostSnippetsコードを公開していたのですが、最近のWordPressテーマは最初から、もっと高性能なブログカード機能がついているものが多くお役御免かなぁと思っていたところ、やはり標準の機能では物足らず、悪い癖で(たいして専門家でもないのに)イチから作ってしまったので、殆どニーズはないとは思いますが(笑)公開します!

「for Cocoon」と書いてはいますが、一部修正すれば、どのWordPressテーマでも使用可能な筈です。ブログカード機能がついていないテーマとかならば少し修正すれば使えます。

書き始めたら結構長いコードになってしまったので、オープンソースライセンスを Apache v2 と(あまりよく理解出来ていませんが)しました。使用はもとより署名を残せば再利用公開なんかもOKってやつ(という認識)です。

SPONSORED LINK

テーマのブログカードで良いんじゃない?

私も実は最初そう思っていました。

が、(無駄に)長くブログをやっているライトニングは言います!

テーマに依存した機能はあまり使うな!

ずっとCocoonを使い続けるならば良いんです。

ライトニングがWordPressを使い始めた頃「Stinger3」というテーマが流行っておりましたが、今そのテーマ使っている人います?(ライトニングは最近までStinger5使ってましたが……)

現代では「Cocoon」は優秀なテーマだと思いますが、3年後果たして人気を維持できているか……

勿論 Simplicity から Cocoon に移行したように後継テーマを使っていれば、もしかしたら気にならないかも知れませんが、他のテーマにしたくなった場合、テーマ独自使用のコードを治すのは結構手間になるんです。

ビジュアルエディターを使っている方はあまり気付かないかも知れませんが

例えば Cocoon のブログカードは

HTML
クリックすると内容全部が選択状態
<a href="https://lightning2014.ensyutsubu.com/blog/">https://lightning2014.ensyutsubu.com/blog/</a>

と、1行にリンクだけを書き込みます。

これって、かなり私にとっては、分かりにくい仕様なんですが、はてなブログのシステムを踏襲しているんですね(多分)

次に同じようにブログカード機能を持つテーマ「Luxeritas」の場合……

HTML
クリックすると内容全部が選択状態
<a href="https://lightning2014.ensyutsubu.com/blog/" data-blogcard="1" >勝手にライトニング! | それは人生をレポートするブログ@多摩センター</a>

のようにaタグに”data-blogcard”という属性を付け加えるとブログカードになる仕組みです。

さて、例えば Cocoon で合計すると1000個ほどあるブログカードを使った複数記事を書いたあと、Luxeritasに移行する場合どうしますか?

手書きで直していたら途方に暮れます(笑)

勿論、Search Regexを使えば、一気に変換する事は出来なくはないです。(しかし正規表現を熟知する必要あり!)

という事で、この解決はショートコード化しておき、テーマを移行して不都合が出たら、その中身を変えるって言うのがライトニング的なベストアンサーかと思います。(後述)

主な特徴

以後は公開当時のver2.0.1をベースに書いています。

外観

正直言ってCocoonのブログカード一緒です(笑)
(ただし SKINでカスタマイズはされている)

そもそもCocoonのブログカードを使っていて、うまく表示出来ないところだけ違う方法で表示するコードを考えていたので、Cocoonと全く同じクラスで括っています。

ただし 結局はソースをイチから書いた為、表示するまでのアプローチが内部で異なっています。

内部リンクもキャッシュする

これが目的で自分で作っちゃったんですが、Cocoonのブログカードは外部リンクの場合はアイキャッチ画像を自分のサーバーに保存してキャッシュとして出力しますが、内部リンクは記事IDからアイキャッチ画像URLを取得しているようです(1.0.0現在)

このおかげで内部記事のアイキャッチやタイトル等の情報は、変更したら即座にブログカードにも反映します(外部リンクは数日間は変わりません)

ただし、想定外のプラグインで作ったページなんかは、対応できず

フォーラムページでOGPが拾えない

間違ったタイトルになっていたりします。

そこでサムネイルリンクでは、内部リンクもキャッシュする仕様にしました。(そのかわり変更には即座に反映しません)
※強制的にOGP取得してキャッシュに反映する方法はあります。

タイトルが付けられる

サムネイルリンクのタイトル表示機能

ひと言コメントみたいなのが付けれます。

本当はもう少しかっこよく付けたいのですが、テーマのブログカード仕様に合わせていると(私の腕では)上手くいきませんでした。

アフィリエイトリンクに変えられる

※注意!上のリンクはアフィリエイトコードが含まれています

カード全体のリンクをアフィリエイトコードにする事ができます。

右上の文字がアフィリエイトコードの文字で、そのリンクを拡張しています。

これはCSSで隠しても良かったのですが、偽装リンク扱いになるのも嫌なので、控えめに表示しています。

その他

細かいところがCocoonブログカードと違います。下記はその一部です。

  1. Cocoonブログカードの場合、外部Imageは生のサイズを縮小して表示していますが、サムネイルリンクはリサイズしているので容量が少ない(表示スピード向上)
  2. アイキャッチを設定でいろんなサイズに変更できます
  3. ratina対応で指定サイズの2倍などでも設定可能
  4. リサイズは縦横比率的に短い方に合わせて、はみ出したところをカットします
  5. リサンプリングしているのでサムネイルの画像が綺麗
  6. キャッシュの有効期限は60日をデフォルト(変更可能)
  7. 少し知識があれば、簡単に出力HTMLコードを変更することが出来ます。(CSSではカスタマイズしきれない場合)
  8. タイトルのサイト名表示がダブらないように調整しています

注意!

GDモジュールが必要

サムネイルリンクはPHP GDモジュールというのを利用しています。

GDモジュールがサーバーにインストールされていないとエラーになります。

うちで使っている では標準でインストールされていましたが、特に自前サーバーの方、要注意です!

良くわからない時はコードをいじくらない!

LIB_パソコン故障

PHPとは言えPostSnippetsを使用しているので、サイト自体が吹っ飛ぶことはないとは思いますが、下手をすると記事データが損傷する可能性もないとは言い切れません。正直ライトニングもテストしている最中にサーバーに(プログラミングミスで)高負荷がかかり503エラー続出なんてことも経験しました(今は改善されています)

冒頭のライセンス表記は消さないで

サムネイルリンクApache

正直本当はよく分かっていませんが、動きに影響しないところだと言って上記のライセンス表記は、ライセンスに関して熟知している方以外触らないでください。

動作条件

 下記を導入していることが前提です。解説は省略しますが、関連する記事がある場合はリンクを貼っておきます。

WORDPRESS

説明不要だと思いますが……

PostSnippets

WordPressのプラグインです。

PHPを扱う時にライトニングは使っています。

WordPressはそのままだとPHP(プログラム言語)を直接書くことが出来ませんが、このプラグインを使うことによって比較的安全にPHPを使うことが出来ます。

Google Chrome

下の「Lightning Blog Tool」を使うために必要。と言っても「Lightning Blog Tool」を使わなくても使えるので、絶対Google Chromeじゃないといいけないと言うわけではない。

Lightning Blog Tool

ライトニングが開発したGoogle Chromeの拡張機能。

あると便利で一瞬でリンク作れます。

インストール

Lightning Blog Toolの設定

サムネイルリンク_LightningBlogToolsの設定

公開version: 1.5.0
公開日: 2018/7/1

Chromeアドオンの「Lightning Blog Tools」のセッティングです。

正直今の仕様だとURLコピペでも使えるので必要ないかも知れませんが……

右クリック→「ライトニングツール」→「Options」からコピペして設定してください。設定ファイルを取り込むと、より簡単に取り込みもできます。

OPTION設定

TYPE:クリップボード

メニュー名
クリックすると内容全部が選択状態
サムネイルリンク ver1.5.0
クリックすると内容全部が選択状態
[サムネイルリンク ogp="%metaimg%" url="%url%" title="%ht:%title%:%" boxtitle="" af_link='']

ADD設定ファイルダウンロード

Post Snippets設定

プラグイン「PostSnippets」の設定です。

知識があれば、このプラグインなしでfunction.phpとかに書き込んでショートコードを作ることも出来ますが、管理が大変になってくるので、ここはプラグイン使った方が楽です。

よく分からないうちは、下のコードをそれぞれの項目にコピペすれば間違いないです。

ちなみに ”Snippet” の部分は結構長いコードなので御注意!

Title
クリックすると内容全部が選択状態
サムネイルリンク

Variables
クリックすると内容全部が選択状態
ogp,url,title,blogcard,boxtitle,af_photo_link,af_link

Shortcode
PHP Code
wptexturize

Description:
クリックすると内容全部が選択状態
サムネイル付リンク生成<br />ogp:(省略可)アイキャッチ画像URL<br />url:ページのURL<br />title:ページのタイトル<br />boxtitle:(省略可)ボックス左上のタイトル<br />af_link: (省略可)アフィリエイトリンク

Snippet
クリックすると内容全部が選択状態
/* Copyright 2018 Lightning2014(https://lightning2014.ensyutsubu.com/blog/) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ if(!defined('LTG_THUMBLINK_VER')) define(LTG_THUMBLINK_VER , '2.0.3'); //バージョン情報(変更不可) /* サムネイルリンク 要 PHP 5 以上 WordPress 2.8以上 要 GDモジュール powered by 勝手にライトニング! https://lightning2014.ensyutsubu.com/blog/ */ /* 流用設定 */ $arg = array( 'cash_reflesh_mode' => false, //キャッシュを強制的に更新したい時はtrueにする 'no_image' => '', //サイトが存在しない時に出すサムネイル画像のURL(省略可)※画像サイズはサムネイルサイズに合わせてください 'no_site_str' => 'ページがなくなった可能性があります。', //metaデータが取得出来なかった時にdiscriptionに表示する文言 'cash_period' => 60, //キャッシュの有効期限(日) 'image_mag' => 2, //画像取得時の倍率 ratina用に2倍が現在のところ基本 'thumbnail_width' => 160, //スナップショットの横幅 及び サムネイルの横幅設定 'thumbnail_height' => 90, //スナップショットの縦幅 'separation' => ' - ', //タイトルとサイト名の間を区切る文字(ただしOGPタイトルがない場合は対象サイトに合わせる) 'mysite_name' => false, //タイトル欄に自分のサイト名を付け加えるかどうか 'theme' => 'Cocoon', //WordPressのテーマを指定(今の所Cocoonのみ対応) /* PostSnippetsのVariablesを取得 */ 'ogpimg' => '{ogp}', 'siteurl' => '{url}', 'sitetitle' => '{title}', 'boxtitle' => '{boxtitle}', 'aflink' => '{af_link}', 'card_str' => '{blogcard}', ); /* テーマのブログカードに合わせた出力関数 */ /* lightning_theme_card_XXX (XXXは小文字で) で登録すれば追加できます*/ if(!function_exists('lightning_theme_card_cocoon')){ function lightning_theme_card_cocoon($url,$title=''){ /* Cocoon仕様 */ $tag = ''; $tag .= "\\n"; $tag .= '<a href="'.$url.'">'; // このリンクはCocoonでは意味がないようです $tag .= $url; $tag .= "</a>\\n"; return $tag; } } /* テーマのブログカードのフォーマットを出力する関数 */ /* lightning_tagbox_XXX (XXXは小文字で) で登録すれば追加できます*/ if(!function_exists('lightning_tagbox_cocoon')){ function lightning_tagbox_cocoon($cash,$config,$inner_check = false){ /* ----------- 使える変数 ----------- $cash['url'] - ターゲットのURL $cash['name'] - ターゲットのサイト名 $cash'[title'] - タイトル $cash['desc'] - discription $cash['image'] - OGP image URL(オリジナル) $cash['width'] - OGP image の横幅(オリジナル) $cash['height'] - OGP image の縦幅(オリジナル $cash['domain'] - ターゲットのドメイン $cash['cash_img'] - キャッシュされた画像のURL $cash['cash_imgw'] - キャッシュされた画像の横幅 $cash['cash_imgh'] - キャッシュされた画像の縦幅 $cash['date'] - 投稿日 ※全てのデータが取得できているとは限りません --------------------------------- */ /* Cocoon 仕様 */ $tag_img = '<figure class="blogcard-thumbnail '.($inner_check ? 'internal-blogcard-thumbnail' : 'external-blogcard-thumbnail').'">'; $tag_img .= '<img src="'.$cash['cash_img'].'" alt="" '; $tag_img .= 'class="blogcard-thumb-image '.($inner_check ? 'internal' : 'external').'-blogcard-thumb-image" width="'.$config['thumbnail_width'].'" height="'.$config['thumbnail_height'].'" />'; $tag_img .= '</figure>'; $tag_title = '<div class="blogcard-title '.($inner_check ? 'internal' : 'external').'-blogcard-title">'.$cash['title'].'</div>'; $tag_snipet = '<div class="blogcard-snipet '.($inner_check ? 'internal' : 'external').'-blogcard-snipet">'.$cash['desc'].'</div>'; $tag_favicon = '<div class="blogcard-favicon '.($inner_check ? 'internal' : 'external').'-blogcard-favicon">'; $tag_favicon .= '<img src="//www.google.com/s2/favicons?domain='.$cash['domain'].'" class="blogcard-favicon-image'; $tag_favicon .= ($inner_check ? ' internal-blogcard-favicon-image' : '').'" alt="" width="16" height="16" />'; $tag_favicon .= '</div>'; $tag_domain = '<div class="blogcard-domain '.($inner_check ? 'internal' : 'external').'-blogcard-domain">'.$cash['domain'].'</div>'; $tag_date = '<div class="blogcard-post-date internal-blogcard-post-date">'.$cash['date'].'</div>'; $tag_container = '<a href="'.$cash['url'].'" title="'.$cash['title'].'" class="blogcard-wrap '.($inner_check ? 'internal' : 'external').'-blogcard-wrap a-wrap cf" target="_blank">'; $tag_container .= '<div class="blogcard '.($inner_check ? 'internal' : 'external').'-blogcard ib-left cf">'; $tag_container .= $tag_img; $tag_container .= '<div class="blogcard-content '.($inner_check ? 'internal' : 'external').'-blogcard-content">'; $tag_container .= $tag_title; $tag_container .= $tag_snipet; $tag_container .= '</div>'; $tag_container .= '<div class="blogcard-footer '.($inner_check ? 'internal' : 'external').'-blogcard-footer cf">'; $tag_container .= '<div class="blogcard-site '.($inner_check ? 'internal' : 'external').'-blogcard-site">'; $tag_container .= $tag_favicon; $tag_container .= $tag_domain; $tag_container .= '</div>'; if($inner_check){ $tag_container .= '<div class="blogcard-date internal-blogcard-date">'; $tag_container .= $tag_date; $tag_container .= '</div>'; } $tag_container .= '</div></div></a>'; return $tag_container; } } //サムネイルリンク全体のラッピング関数 if(!function_exists('lightning_thumblink_container')){ function lightning_thumblink_container($tag_box, $config){ $cont = ''; $cont .= "\\n<!-- サムネイルリンク (for Theme ".$config['theme'].") ver.".$config['ver']." -->\\n"; $cont .= '<div class="ltng_thumlinkbox_container"><div class="ltng_thumlinkbox"><div class="ltng_afbox">'; if($config['boxtitle']){ $cont .= '<div class ="thumlinkbox_title">'.$config['boxtitle']; if($config['aflink']){ $cont .= '<div class="ltng_aflink">'.$config['aflink'].'</div>'; } $cont .= '</div>'; }elseif($config['aflink']){ $cont .= '<div class ="thumlinkbox_title"><div class="ltng_aflink">'.$config['aflink'].'</div></div>'; } $cont .= $tag_box; $cont .= '</div><div class="ltng_signature"><a href="https://lightning2014.ensyutsubu.com/blog/page-8469/" target="_blank" rel="noopener">powered by サムネイルリンク ver.'.$config['ver'].'</a></div></div></div>'; return $cont; } } /* サムネイルリンクの機能をまとめたクラス */ if(!class_exists('Ltg_Thumblink')): class Ltg_Thumblink { public $url = NULL; //対象URL public $domain = NULL; //対象サイトのドメイン public $inner_check = NULL; //内部リンクかどうかのチェック public $id = NULL; //内部リンクの場合の記事ID public $html = NULL; //$urlから取得したHTML格納 public $meta = NULL; //メタ情報格納 public $config = NULL; //設定情報 public $hash = NULL; //URLから作られるハッシュ public $cash = NULL; //書き込まれたキャッシュ /* HTML取得メソッド */ public function get_html($url = NULL, $args = NULL){ if($url === NULL){//引数省略されている場合プロパティを使う if($this->url === NULL) return false; $url = $this->url; } $ua = $_SERVER['HTTP_USER_AGENT']; //ユーザーエージェントを取得 if(strpos($ua,$this->config['user_agent']) !== false) return false; global $wp_version; $http = (is_ssl() ? 'https' : 'http') . '://'; $this_url = $http . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]; $tmpargs = array( //$argsが省略されている場合 'timeout' => 10, 'redirection' => 5, 'sslverify' => false, ); if(!$args) $args = $tmpargs; $args['user-agent'] = 'WordPress/' . $wp_version . '; ' . this_url.'; '.$this->config['user_agent']; $target = wp_remote_get( $url, $args ); //サイトを取得 $response_code = wp_remote_retrieve_response_code($target); if(!is_wp_error($target) && $response_code === 200 ) { if(array_key_exists('body',$target)){ $this->html = $target['body']; return $this->html; } } //失敗した場合cURLを試してみる if($response_code === 503) return false; //アクセス集中を防止 $cu = curl_init(); curl_setopt($cu, CURLOPT_URL, $url); curl_setopt($cu, 'WordPress/' . $wp_version . '; ' . this_url.'; '.$this->config['user_agent'] ); curl_setopt($cu, CURLOPT_RETURNTRANSFER, true); $target_str = curl_exec($cu); curl_close($cu); if($target_str == false){ $this->config['no_site_str'] .= '<br>response_code : '.$response_code; return false; //取得失敗の時はfalseを返す } $this->html = $target_str; return $this->html; } /* ハッシュ取得メソッド */ public function get_hash($url = NULL){ if($url === NULL){//引数省略されている場合プロパティを使う if($this->url === NULL) return false; $url = $this->url; } $hash = 'ltgcash_'.md5($url); //URLからハッシュ値を算出 $this->hash = $hash; return $hash; } /* ドメイン取得メソッド */ public function get_domain($url = NULL){ if($url === NULL){//引数省略されている場合プロパティを使う if($this->url === NULL) return false; $url = $this->url; } return parse_url($url,PHP_URL_HOST); } /* 内部リンクかどうか確認するメソッド */ public function check_inout($url = NULL){ if($url === NULL){//引数省略されている場合プロパティを使う if($this->url === NULL) return NULL; $url = $this->url; } if(!$this->domain) $this->domain = $this->get_domain($url); //対象のドメイン $this->inner_check = ($this->domain == $_SERVER["HTTP_HOST"]) ? true : false; //内部リンクかどうかのチェック return $this->inner_check; } /* meta情報取得メソッド */ public function get_metas($html = NULL){ /* ------------------------------------------------ html文字列からmeta情報を全て抜き取り meta['(nameなど)__(og:imageなど)'] = (contentの値) の形式を戻り値として返す関数。 ※属性文字は小文字に統一されます。(値は区別) ex. 例えばOGPをイメージ取得したい場合 戻り値をmetaという配列に入れたい場合 meta['property__og:image'] から取り出すことができる。 ------------------------------------------------ */ if($html === NULL) $html = $this->html ? $this->html : $this->get_html($this->url); if($html == false) return false; $html = mb_convert_encoding($html,'UTF-8', 'auto'); //UTF-8以外の文字をUTF-8に変換 preg_match_all('/<meta\\s+([^\\/>\\s=]+)\\s*=\\s*(?:\\"|\\')([^\\"\\']*)(?:\\"|\\')(?:\\s+([^\\/>\\s=]+)\\s*=\\s*(?:\\"|\\')([^\\"\\']*)(?:\\"|\\')\\s*[^\\/>\\s=]*)*\\s*\\/*>/ui',$html,$row,PREG_SET_ORDER); $meta = array(); for( $i = 0, $max = count($row); $i < $max ; $i++){ if(count($row[$i]) == 3){ //contentのないmeta $meta[mb_strtolower($row[$i][1])] = $row[$i][2]; } else { //contentがあるmeta if(mb_strtolower($row[$i][3]) == 'content'){ $meta[mb_strtolower($row[$i][1]).'__'.mb_strtolower($row[$i][2])] = $row[$i][4]; } else { $meta[mb_strtolower($row[$i][3]).'__'.mb_strtolower($row[$i][4])] = $row[$i][2]; } } } $this->meta = $meta; return $meta; } /* OGP情報取得して配列で返す関数 */ public function get_ogp($url = NULL,$inner_check = false){ if($url === NULL){//引数省略されている場合プロパティを使う if($this->url === NULL) return false; $url = $this->url; } $target = $this->get_html($url); //body要素取得 if($target === false) return false; $info = array( //情報格納配列の初期化 'title' => null, //通常のタイトル 'desc' => null, //通常のdescription 'og_type' => null, //OGPのページタイプ 'og_url' => null, //OGP URL 'og_sitename' => null, //OGP サイトネーム 'og_locale' => null, //OGP サイトの国 'og_title' => null, //OGPタイトル 'og_desc' => null, //OGPのdescription 'og_image' => null, //OGPのimageURL 'tw_url' => null, //Twitterカード URL 'tw_domain' => null, //Twitterカード ドメイン 'tw_card' => null, //Twitterカードの種類 'tw_title' => null, //Twitterカードのタイトル 'tw_desc' => null, //Twitterカードのdescription 'tw_image' => null, //TwitterカードのimageURL 'tw_creator' => null, //Twitterカード アカウントのURL 'tw_site' => null, //Twitterカード サイトのURL 'date' => null, //ページ作成日(あまり設定されている事はない) ); /* 通常タイトルの取得 */ preg_match('/<title.*?>(.*?)<\\/title.*?>/ui', $target, $matches); $info['title'] = $matches ? $matches[1] : null; $meta = $this->get_metas($target); //メタ情報を全部取得 /* 通常のdescription取得 */ if(array_key_exists('name__description',$meta)) $info['desc'] = $meta['name__description']; /* OGP ページタイプの取得 */ if(array_key_exists('property__og:type',$meta)) $info['og_type'] = $meta['property__og:type']; /* OGP URLの取得(削除しても良いかも) */ if(array_key_exists('property__og:url',$meta)) $info['og_url'] = $meta['property__og:url']; /* OGP サイトネームの取得 */ if(array_key_exists('property__og:site_name',$meta)) $info['og_sitename'] = $meta['property__og:site_name']; /* OGP サイト国の取得 */ if(array_key_exists('property__og:locale',$meta)) $info['og_locale'] = $meta['property__og:locale']; /* OGPタイトルの取得 */ if(array_key_exists('property__og:title',$meta)) $info['og_title'] = $meta['property__og:title']; /* OGP descriptionの取得 */ if(array_key_exists('property__og:description',$meta)) $info['og_desc'] = $meta['property__og:description']; /* OGP imageURLの取得 */ if(array_key_exists('property__og:image',$meta)) $info['og_image'] = $meta['property__og:image']; /* Twitterカード URLの取得(削除しても良いかも) */ if(array_key_exists('name__twitter:url',$meta)) $info['tw_url'] = $meta['name__twitter:url']; /* Twitterカード ドメインの取得(削除しても良いかも) */ if(array_key_exists('name__twitter:domain',$meta)) $info['tw_domain'] = $meta['name__twitter:domain']; /* Twitterカードの種類の取得 */ if(array_key_exists('name__twitter:card',$meta)) $info['tw_card'] = $meta['name__twitter:card']; /* Twitterカード タイトルの取得 */ if(array_key_exists('name__twitter:title',$meta)) $info['tw_title'] = $meta['name__twitter:title']; /* Twitterカード descriptionの取得 */ if(array_key_exists('name__twitter:description',$meta)) $info['tw_desc'] = $meta['name__twitter:description']; /* Twitterカード imageの取得 */ if(array_key_exists('name__twitter:image',$meta)) $info['tw_image'] = $meta['name__twitter:image']; /* Twitterカード アカウントURLの取得 */ if(array_key_exists('name__twitter:creator',$meta)) $info['tw_creator'] = $meta['name__twitter:creator']; /* Twitterカード サイトURLの取得 */ if(array_key_exists('name__twitter:site',$meta)) $info['tw_site'] = $meta['name__twitter:site']; /* 作成日の取得 */ $this->id = $inner_check ? url_to_postid( $url ) : NULL; //WordPressの記事IDを取得(TOPページやカテゴリページはIDがない) if($inner_check && $this->id){ //内部リンクかつIDが取得できる場合 if($this->id) $info['date'] = get_the_date('Y.n.j',$this->id); } else { //外部リンクまたはIDが取得できなかった場合 if(array_key_exists('name__creation_date',$meta)) $info['date'] = $meta['name__creation_date']; if(array_key_exists('name__creation date',$meta)) $info['date'] = $meta['name__creation date']; if(array_key_exists('name__creation-date',$meta)) $info['date'] = $meta['name__creation-date']; if(array_key_exists('property__article:published_time',$meta)) $info['date'] = $meta['property__article:published_time'];//「2018-06-18T13:34:16+00:00」のような形式? if($info['date']) $info['date'] = date('Y.n.j',strtotime($info['date'])); //一度timestampに変換して整形する } return $info; } /* オブジェクト初期化メソッド */ public function init($url, $arg = NULL){ if(!$url) return false; /* 設定の初期値 */ $this->config = array( 'cash_reflesh_mode' => false, //キャッシュを強制的に更新したい時はtrueにする 'cash_period' => 30, //キャッシュの有効期限(日) 'image_mag' => 2, //画像取得時の倍率 ratina用に2倍が現在のところ基本 'dir_cash' => '/uploads/lightning-system/image-cash/', //画像cashの保存場所(基本的に変更しないで) 'wp_snapshot_url' => 'https://s0.wordpress.com/mshots/v1/', //WordPressのSNAPSHOT APIアドレス 'thumbnail_width' => 160, //スナップショットの横幅 及び サムネイルの横幅設定 'thumbnail_height' => 90, //スナップショットの縦幅 'no_image' => '', //サイトが存在しない時に出すサムネイル画像のURL(省略可)※画像サイズはサムネイルサイズに合わせてください 'no_site_str' => 'ページがなくなった可能性があります。', //metaデータが取得出来なかった時にdiscriptionに表示する文言 'separation' => ' - ', //タイトルとサイト名の間を区切る文字(ただしOGPタイトルがない場合は対象サイトに合わせる) 'mysite_name' => false, //タイトル欄に自分のサイト名を付け加えるかどうか 'ver' => LTG_THUMBLINK_VER, //バージョン情報 'ogpimg' => '', //ショートコードのプロパティ 'siteurl' => $url, //ショートコードのプロパティ 'sitetitle' => '', //ショートコードのプロパティ 'sitedesc' => '', //ショートコードのプロパティ 'boxtitle' => '', //ショートコードのプロパティ 'afphoto' => '', //ショートコードのプロパティ 'aflink' => '', //ショートコードのプロパティ 'card_str' => '0', //ショートコードのプロパティ 'user_agent' => 'PHP-Lightning-Thumbnail-Link', //繰り返し防止判定に使用 'reflesh_parameter' => 'ltg-thumb-reflesh', //この名前のパラメーターが付いていると強制的にキャッシュをリフレッシュします 'theme' => 'Cocoon', //WordPressのテーマを指定(今の所Cocoonのみ対応) ); /* 引数argをconfigに上書き */ if(is_array($arg)) $this->config = array_merge($this->config,$arg); /* 変数変換処理 */ $this->config['card'] = (trim($this->config['card_str']) == "0" || trim($this->config['card_str']) == '') ? false : true; //boolean型に変更 $this->config['aflink'] = str_replace(array("\\r", "\\n","<br>","<br />"), '', $this->config['aflink']);//アフィリエイトリンクの改行を削除(自動変換された<br>も削除) ※規約違反の可能性あり注意 $this->config['upload_dir'] = WP_CONTENT_DIR.$this->config['dir_cash']; //画像保存場所パス $this->config['upload_url'] = WP_CONTENT_URL.$this->config['dir_cash']; //画像保存場所URL $this->config['wp_snapshot_prop'] = '?w='.$this->config['thumbnail_width']*$this->config['image_mag'].'&h='.$this->config['thumbnail_height']*$this->config['image_mag']; //スナップショットのサイズ指定 $this->domain = $this->get_domain($url);//ドメイン取得 $this->inner_check = $this->check_inout($url); //対象が内部リンクかどうかチェック } /* 画像をリサイズするメソッド(GDモジュール使用) */ public function resize_imagejpg($img, $w, $h, $mag = 1){ /* 画像のサイズを取得 */ $imgformat = imagecreatefromstring($img); $org_w = imagesx($imgformat); $org_h = imagesy($imgformat); $asp_target = $org_w / $org_h; //アスペクト比 $asp_thum = $w / $h; //サムネイルのアスペクト比 /* 画像をリサイズ */ if($asp_target > $asp_thum){ $diffW = $org_h * $asp_thum; $diffH = $org_h; $diffX = ($org_w - $diffW) * 0.5; $diffY = 0; }elseif($asp_target < $asp_thum){ $diffW = $org_w; $diffH = $org_w / $asp_thum; $diffX = 0; $diffY = ($org_h - $diffH) * 0.5; }elseif($asp_target === $asp_thum){ $diffW = $org_w; $diffH = $org_h; $diffX = 0; $diffY = 0; } $thumbnail = imagecreatetruecolor($w * $mag, $h * $mag); //リサイズ先格納場所 imagefill($thumbnail,0,0,0xFFFFFF); $er_image = imagecopyresampled($thumbnail, $imgformat, 0, 0, $diffX, $diffY, $w * $mag, $h * $mag, $diffW, $diffH); //リサイズ&トリミング ob_start(); imagejpeg($thumbnail, null, 60); //バッファに画像出力 $image_resized = ob_get_clean(); //リサイズ画像を取得 $res = array( 'org_image' => $img, //オリジナルImage 'org_width' => $org_w, //オリジナルImageの横幅 'org_height' => $org_h, //オリジナルImageの縦幅 'resized_image' => $image_resized, //リサイズ画像Image 'resized_width' => $w * $mag, //リサイズ画像Imageの縦幅 'resized_height' => $h * $mag, //リサイズ画像Imageの縦幅 ); return $res; } /* キャッシュ書き込みメソッド */ public function write_cash($url = NULL,$hash = NULL){ /* 引数が省略されている場合の処理 */ if($url === NULL){ if($this->url === NULL) return false; $url = $this->url; } if($hash === NULL) $hash = $this->get_hash($url); $ua = $_SERVER['HTTP_USER_AGENT']; //ユーザーエージェントを取得 if(strpos($ua,$this->config['user_agent']) !== false) return false; if($this->config === NULL){ //initされているか判定 return false; } else { $config = $this->config; } $inner_check = $this->inner_check !== NULL ? $this->inner_check : $this->check_inout($url); require_once(ABSPATH . 'wp-admin/includes/file.php'); //FILE API読み込み /* キャッシュするデータ配列初期化(メモの役割も) */ $cash = array( 'url' => $url, //ターゲットのURL 'name' => null, //ターゲットのサイト名 'title' => null, //タイトル 'desc' => null, //discription 'image' => null, //OGP image URL(オリジナル) 'width' => null, //OGP image の横幅(オリジナル) 'height' => null, //OGP image の縦幅(オリジナル 'domain' => $this->domain, //ターゲットのドメイン 'cash_img' => null, //キャッシュされた画像のURL 'cash_imgw' => $config['thumbnail_width'] * $config['image_mag'], //キャッシュされた画像の横幅 'cash_imgh' => $config['thumbnail_height'] * $config['image_mag'],//キャッシュされた画像の縦幅 'date' => null, //投稿日 ); $ogp = $this->get_ogp($url,$inner_check); //OGP情報取得 if($ogp){ $cash['image'] = $ogp['og_image'] ? $ogp['og_image'] : $ogp['tw_image']; if($cash['image'] && ($cash['image'] != '')){ $img = NULL; //画像格納場所の初期化 /* 画像読み込み */ if( WP_Filesystem()){ global $wp_filesystem; $img = $wp_filesystem->get_contents($cash['image']); } $resized = $this->resize_imagejpg($img,$config['thumbnail_width'],$config['thumbnail_height'],$config['image_mag']); //画像をリサイズ $cash['width'] = $resized['org_width']; $cash['height'] = $resized['org_height']; $image_resized = $resized['resized_image']; /* 画像を格納 */ if (!file_exists($config['upload_dir'])){ //保存ディレクトリが存在しない場合は作成 mkdir($config['upload_dir'], 0777, true); } if(WP_Filesystem($image_resized)){ global $wp_filesystem; $wp_filesystem->put_contents($config['upload_dir'].$hash.'.jpg',$image_resized); $cash['cash_img'] = $config['upload_url'].$hash.'.jpg'; } else { //なんらかの理由で格納できない場合はWPスナップショットにする $cash['cash_img'] = $config['wp_snapshot_url'].urlencode($url).$config['wp_snapshot_prop']; } } else { //OGP imageが設定されていなかった場合 $cash['cash_img'] = $config['wp_snapshot_url'].urlencode($url).$config['wp_snapshot_prop']; } /* 各種データ代入 */ $cash['name'] = $ogp['og_sitename']; if($inner_check){ $cash['title'] = $ogp['og_title'] ? $ogp['og_title'].($config['mysite_name'] ? $config['separation'].$ogp['og_sitename'] : '') : ($ogp['tw_title'] ? $ogp['tw_title'].($config['mysite_name'] ? $config['separation'].bloginfo('name') : '') : $ogp['title']); } else { $cash['title'] = !$ogp['og_title'] ? $ogp['title'] : ((strpos($ogp['og_title'],$ogp['og_sitename']) === false) ? $ogp['og_title'].$config['separation'].$ogp['og_sitename'] : $ogp['og_title']); //Twitterカードのタイトル情報はサイト名が入らないので使用しなくした } $cash['desc'] = $ogp['og_desc'] ? $ogp['og_desc'] : ($ogp['tw_desc'] ? $ogp['tw_desc'] : $ogp['desc']); $cash['date'] = $ogp['date']; } else { //OGPが取得できなかった場合 $cash['title'] = $config['sitetitle']; $cash['cash_img'] = !empty($config['no_image']) ? $config['no_image'] : $config['wp_snapshot_url'].urlencode($url).$config['wp_snapshot_prop']; $cash['desc'] = $config['no_site_str']; //descriptionに注意書きを入れる } /* キャッシュデータ書き込み */ set_transient($hash,$cash, $config['cash_period'] * DAY_IN_SECONDS); $this->cash = $cash; return $cash; } /* キャッシュ読み込みメソッド */ public function read_cash($url){ $hash = $this->get_hash($url); //サイトURLからハッシュ値を算出 $ogp_cash = get_transient( $hash ); //cashを読み込む if($this->config === NULL) $this->init($url); //初期化されていないようならばarg要素なしで初期化 $config = $this->config; if($ogp_cash === false || $ogp_cash['title'] == NULL || ((bool)$_GET[$config['reflesh_parameter']] && current_user_can('edit_user'))||($config['cash_reflesh_mode']) && current_user_can('edit_user')){ //cashが見つからない場合(新規)またはキャッシュ更新モードがtrueの場合 $ogp_cash = $this->write_cash($url,$hash); //キャッシュ書き込みと情報取得 if(!$ogp_cash['title']) $ogp_cash['title'] = $config['sitetitle']; //タイトルが取得できなかった場合(ページ消滅?) } return $ogp_cash; } /* コンストラクタ */ public function __construct($url = NULL){ if($url) $this->url = $url; } } endif; /* オブジェクト初期化 */ $target = new Ltg_Thumblink(); $target->init($arg['siteurl'],$arg); $config = $target->config; //argが追加されたconfig全体を取得 $url = $config['siteurl']; if($config['card']){ //Cocoonのブログカード機能を使用(ブログカードのフォーマットで出力) $func_name = 'lightning_theme_card_'.mb_strtolower(trim($config['theme'])); if(!function_exists($func_name)) $func_name = 'lightning_theme_card_cocoon';//関数が存在しない場合はCocoon仕様を使う $tag_box = $func_name($url); } else { //サムネイルリンク独自機能使用の場合 $domain = $target->domain; //ドメイン取得 $inner_check = $target->inner_check; //内部リンクチェック取得 $ogp_cash = $target->read_cash($url); //キャッシュ読み込み $func_name = 'lightning_tagbox_'.mb_strtolower(trim($config['theme'])); if(!function_exists($func_name)) $func_name = 'lightning_tagbox_cocoon';//関数が存在しない場合はCocoon仕様を使う $tag_box = $func_name($ogp_cash, $config, $inner_check);//タグを作成 } echo lightning_thumblink_container($tag_box,$config);

CSS追記

これは人それぞれでカスタマイズして良い所ではありますが、サンプルとして書いておきます。下のCSSをコピーして(Coconnの場合)子テーマの style.css に追記します(一番最後でOK)

CSS
クリックすると内容全部が選択状態
/*======================================================= サムネイルリンク for Cocoonブログカード CSS ver0.51 =======================================================*/ .ltng_thumlinkbox_container { margin: 20px auto; text-align: left; } .ltng_thumlinkbox_container .ltng_thumlinkbox { display: block; margin: 0 auto; width: 100%; max-width: 600px; } .ltng_thumlinkbox_container .ltng_thumlinkbox .thumlinkbox_title{ font-size: 14px; margin-bottom: -4px; padding-left: 5px; display: block; min-height: 24px; } .ltng_thumlinkbox_container .ltng_thumlinkbox .ltng_afbox{ position: relative; } .ltng_thumlinkbox_container .ltng_thumlinkbox .ltng_afbox .ltng_aflink { display: inline; text-align: right } .ltng_thumlinkbox_container .ltng_thumlinkbox .ltng_afbox .ltng_aflink img{ display: inline !important; } .ltng_thumlinkbox_container .ltng_thumlinkbox .ltng_afbox .ltng_aflink a{ text-decoration: none; color: #808080; position: absolute; top: 4px; left: 0; width: 100%; height: calc(100% - 4px); font-size: 10px; } .ltng_thumlinkbox_container .ltng_thumlinkbox .ltng_afbox:hover > .blogcard-wrap{ transition: all 0.5s ease ; background-color: #f5f8fa; } .ltng_thumlinkbox .ltng_signature { font-size: 7px; text-align: right; padding-right: 15px; } .ltng_thumlinkbox .ltng_signature a{ font-size: 7px; color: #d4d4d4; text-decoration: none; } .ltng_thumlinkbox_container .blogcard-wrap{ margin: 0; width: 100%; } .ltng_thumlinkbox_container .a-wrap{ display: inline-block; }

ライトニングと同じようにしたい場合、(Cocoon)SKINの「ほんわかライトニング」を使って貰えば良いですが、他のSKINを使っている場合は下記も追記して頂けると、サンプルと同じ形になると思います(使っているSKINによっては多少修正しないとイケないかも)

CSS追記
クリックすると内容全部が選択状態
/* ブログカード */ .blogcard-wrap { max-width: 600px; width: 100%; } .blogcard-title { margin-bottom: .5em; background-color: #f8f8ef; padding: .5em; } .a-wrap:hover .blogcard-title{ background-color: transparent; transition: all .5s ease; } .blogcard-snipet { color: #808080; font-size: 12px; } .blogcard-domain { color: #808080; } @media screen and (max-width: 768px){ .blogcard-content{ max-height: 110px; } }

使い方

普通の使い方

ショートコードをペースト

まずは一般的な使い方から

リンクを作りたいページで

右クリック→「ライトニングツール」→「サムネイルリンクverX.X.X」

をクリックします。

LightningBlogToolsの選択画面で出ている「サムネイルリンクverX.X.X」のバージョン情報はサムネイルリンクコード自体のverではなくLightningBlogToolsの設定コードのバージョンになっていますので、「バージョンが違う!」と思っても気にしないでください。

って言うか統一バージョンにしておけば紛らわしくなかったかも……(今度の時に修正します)

サムネイルリンクコードをペースト

あとはWordPressの投稿画面でペーストするだけ。

写真ではHTMLエディター(それもプラグインでハイライトしてますが)になっていますが、ビジュアルエディターでも全く同じです。
※ビジュアルエディターでもショートコードが表示されるだけで、カード自体は表示されません

これでプレビューしてみると あら不思議(でもないかw)サムネイルリンクが表示されています。

タイトルの付け方

サムネイルリンクのタイトルの付け方

先程生成したショートコードのプロパティ「boxtitle=""」の ” と ” の間にタイトルに入れたい文言を入れます。

サムネイルリンク-タイトルの表示

ちと控えめですがこのように左上にタイトルが付け加えられます。

カスタマイズする時は ”.ltng_thumlinkbox_container .ltng_thumlinkbox .thumlinkbox_title” というCSSクラスを修正してみてください

アフィリエイトタグの付け方

サムネイルリンクのアフィリエイトコードの付け方01

先程生成したショートコードのプロパティ「af_link=''」の ’ と ’ の間にテキストタイプのアフィリエイトコードを(ビジュアルエディターの場合)「HTML挿入」で挿入します。

サムネイルリンクのアフィリエイトコードの付け方02

すると投稿画面ではこんな表示になるかと

シングルクォート(’)である事に御注意ください。

ほとんどのアフィリエイトコードにはダブルクォート(”)が含まれているのでシングルクォートで括ります。

ただし、たまにアフィリエイトコード自体にシングルクォート(’)が含まれている場合があるので、その場合は全体をシングルクォートではなく、逆にダブルクォートで括ってください

サムネイルリンク-アフィリエイトコード

プレビューしてみるとこんな感じ。

アフィリエイトのテキストが右上に表示され、カード自体のリンクがアフィリエイトリンクに変わります。

アフィリエイトのテキストは消せる?

正直言うとCSSをいじれば消すことは可能です。
が、アフィリエイト的に規約違反になると思いますし、Googleさんに偽装リンクと判断される可能性が高いので、あえて表示するようにしました。

とは言え、これ自体が偽装リンクと判断される可能性もあるので、使用に関しては自己判断でお願いします。

ショートコードのプロパティに関して

ショートコードは下記のフォーマット形式になっています。

[サムネイルリンク ogp="(OGP image)" url="(URL)" title="(対象記事のタイトル)" boxtitle="(タイトル)" af_link='(アフィリエイトコード)']

プロパティの説明

ogp 対象サイトのOGP ImageのURLを入れます。
基本的に自動で取得するので入力しなくて大丈夫です。
と言うか現在のところ、このプロパティを参照している部分はありません。
url 対象のURLを入力。
正直これだけ記載していればサムネイルリンクを表示できます
title 対象記事のタイトルを入力。
基本的に自動で取得するので入力しなくて大丈夫です。
対象ページが消滅した時とかに、使用されます。
boxtitle 左上にちょっとしたタイトルを足します
af_link テキストタイプのアフィリエイトコードを入れるとリンクがアフィリエイトリンクになります。
blogcard これを例えば「blogcard=”1″」とすると、内部処理がテーマ独自のものになります(デフォルトではCocoon)
つまり、テーマ独自のブログカードをタイトルなどの機能を付け加えたラッピングだけになります。

キャッシュし直す方法

サムネイルリンクはデフォルトだと一度キャッシュすると60日間(変更可能)は一度取得した情報を表示するようになります。

自分のサイトの記事修正や対象サイトの情報が変わって即座にキャッシュし直したい場合は

サムネイルリンクが貼っているページURLのパラメータに「ltg-thumb-reflesh=true」と足すと、サムネイルリンクが起動時に強制的にキャッシュし直します。

例えばサムネイルリンクを貼ったページのURLが

https://lightning2014.ensyutsubu.com/blog/skin-honwaka-lightning/

であった場合はパラメータを表す「?」を付け加え

https://lightning2014.ensyutsubu.com/blog/skin-honwaka-lightning/?ltg-thumb-reflesh=true

で読み込むと、そのページで使われているサムネイルリンクのリンクキャッシュが更新されます。

すでにURLに「?」があるような場合は、URLの最後に「&」をつけて

https://lightning2014.ensyutsubu.com/blog/?p=13772&ltg-thumb-reflesh=true

としてください。

この機能はそのWordPressの「編集権限」があるユーザーでログインしていないと動作しません

設定

サムネイルリンクはユーザーに合わせて色々とカスタマイズできるようにしてあります。

ここからはコード自体を触らないといけないので、自信がなければ諦めてください(笑)

「流用設定」と書いてあるところの「$arg」という配列の中身を弄るとカスタマイズできます。

key
cash_reflesh_mode(true or false) デフォルト:false
trueにすると対象とするキャッシュを強制的に全て更新します。
前述の「ltg-thumb-reflesh=true」を使用した方が安全で楽だと思います
no_image(URL)
サイトが消滅した時などにアイキャッチが取得できない時に表示する画像を指定
no_site_str(STRING)
metaデータが取得出来なかった時にdiscriptionに表示する文言
cash_periodキャッシュの有効期限日数(デフォルトでは60日)
image_magデフォルト:2
アイキャッチサイズの倍数
ratina対応にするために例えば表示サイズ160×90だけど倍の320x180で画像取得しておきたいと言う時はこの値を2としておきます
thumbnail_widthデフォルト:160
アイキャッチの表示横幅
thumbnail_heightデフォルト:90
アイキャッチの表示縦幅
separationデフォルト:’ – ‘
タイトルとサイト名の間を区切る文字(ただしOGPタイトルがない場合は対象サイトに合わせる)
mysite_name(true or false) デフォルト:false
タイトル欄に自分のサイト名を付け加えるかどうか
themeデフォルト: Cocoon
サムネイルリンクの出力形式(テーマ)を指定
今の所Cocoonしかないので、他を書いても変わりません。

実はもっと設定項目は存在しているのですが、あまり変える需要がなさそうなところなので、そこは解説しません。コードが読める人は結構、注釈で書いていますので覗いてみてください。

PHPソース」ファイル

PostSnipppetsのコードはエスケープ文字がダブらせないといけないので、サムネイルリンクをプラグインなどに組み込みたい方や研究したい方は、素のPHPコードを下記からダウンロードしてお使いください。

更新履歴

2.0.3公開

コメント

  1. ふじちゃん より:

    ワクワクするような素晴らしい内容のコードを、ありがとうございます!
    Lightning Blog Tool も導入させていただきました!
    感謝感謝です。
    子テーマの functions.php が、空っぽなので、コードを貼り付けたいのですが
    残念ながら、そこまでの知識がなく(;´д`)トホホ…
    貼り付けできるコードを、公開するご予定は、ありませんでしょうか。
    くだらない質問で、申し訳ありません。

    • ふじちゃんさん
      ありがとうございます。
      function.phpに入れる方法は、勿論あり、コードを少しいじらないといけないのですが(と言ってもショートコード登録するだけ)、正直、テーマを変えたらまた、書き込まないといけないなどメンテナンス面でも、私は記事にあるpost snippetsをお勧めします。
      あくまで、ソース公開はテーマ作成者様などで利用する価値があるかと思っている程度で、そう言う方は、簡単に組み込めると思っていると思うので……

      とは言えニーズがあるようならば、少し考えても良いかと思ってはいます。(すみません、今本業が忙しくすぐには出来そうもありません……)

      もう少しお時間頂ければ……

  2. ふじちゃん より:

    早速のお返事、恐縮です。ありがとうございます!
    ご検討は、いつでも構いませんので、気長にお待ちしています。
    こんなくだらない質問に、ていねいに、お返事いただいただけでも
    ものすごく、うれしいです!!
    猛暑が続いていますが、ご本業、がんばってください。
    ありがとうございました。