2017-11-28

Blogger 「自分のビューを追跡しない」が機能しない(保存されない)原因と対策

このブログに自分がアクセスしたときのページビューをカウントしますか?

Blogger の管理画面から 統計 -> 概要 ->自分のページビューの追跡を管理をクリックし、このブログの自分のビューを追跡しないをチェックすると自分がアクセスしたときのページビューをカウントから除外することができる「はず」なのですが、カウントされることがあります。また、チェックはブラウザを起動するたびに外れてしまいます。

原因

「このブログの自分のビューを追跡しない」をチェックすると、以下のJavaScript が動き、Cookie を設定します。

var COOKIE_NAME = '_ns';
var COOKIE_SET_VALUE = '2';
document.cookie = COOKIE_NAME + '=' + COOKIE_SET_VALUE;

しかし、以下の点で不完全なようです。

・domain 属性

domain を指定していませんので、今接続しているサブドメイン blogname.blogspot.com が設定されます。日本からの接続は blogname.blogspot.jp にリダイレクトされますのでドメイン違いでクッキーが送信されずビューがカウントされます。(国別リダイレクトは廃止されました。Official Blogger Blog: It’s spring cleaning time for Blogger

注: domain 属性は、あるドメインに属するサブドメイン全体を Cookie の対象にする場合に指定するものですので、今回のように特定のサブドメインを対象にする場合は指定すべきではありません。また、現在接続しているドメイン以外を指定することはできません。

・path 属性

path を指定していませんので、path には現在のページのパス /b がセットされてしまいます。これだと原則として *blogname*.blogspot.jp/b/ blogname.blogspot.com/b/以外はクッキーが送信されません。

・expires 属性

expires を指定していませんので、ブラウザが閉じるとクッキーは削除されます。

対策

以上の問題を修正したスクリプトは以下のようになります。

document.cookie = "_ns=2; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
  • expires を無期限にすることはできませんので、2038年問題の上限日時(2038年1月19日3時14分7秒)を指定しています。
    max-age 属性は IE11 が非対応とのことなので今回使用しませんでした。

このスクリプトを、設定したい 自分の *blogname*.blogspot.jp blogname.blogspot.comにアクセスしたうえで、 Chrome なら F12 キーを押してデベロッパー ツール を起動して Console に貼り付けて実行すればOKです。

Chrome デベロッパー ツール

デベロッパー ツールでは、Application -> Cookies で登録されているクッキーの内容を直接編集することもできます。

また、ブログ管理画面(blogname.blogspot.com)でも、スクリプトを実行するとブラウザを終了しても このブログの自分のビューを追跡しない のチェックが外れなくなります。


expires の日付を分かりやすくしたバージョンも置いておきます。

var d = new Date("2038-01-19"); document.cookie = "_ns=2; expires=" + d.toGMTString() + "; path=/";

"2038-01-19" の箇所をお好みで(”2038-01-19”を超えない範囲で)修正してください。
過去の日付にすれば、Cookie を削除できます。

スマホでも設定したい。

スマホなどデベロッパー ツールが使えない場合は、設定したいページにアクセスしたうえで、アドレスバーに javascript: と入力した後に、上のスクリプトを貼付けすることで実行できます。

アドレスバーに javascript:document.cookie; (最後のセミコロンはなくてもたぶん大丈夫)と入力することで、そのページの Cookie が表示されます。_ns=2 が表示されれば、設定されています。

2017-11-27

oo4oからADOへの変換 (6) カーソル変数(REF CURSOR)

カーソル変数(REF CURSOR)は、PL/SQL との連携において非常に有用ですが、ADOでは少し扱いづらいものとなります。
サンプルコードはOracle® Objects for OLE開発者ガイドの PL/SQLカーソル変数を戻す方法 の例を書き換えたものです。

ADO

Sub Get_Data()
'  Set OO4OSession = CreateObject("OracleInProcServer.XOraSession")
  Set cnn = CreateObject("ADODB.Connection")

'  Set empDb = OO4OSession.OpenDatabase("ExampleDb", "scott/tiger", 0)
  cnn.Open "Provider=OraOLEDB.Oracle;Data Source=ExampleDb;User ID=scott;Password=tiger"

  Dim cmd As New ADODB.Command
  Set cmd.ActiveConnection = cnn
  cmd.CommandType = adCmdText

'  empDb.Parameters.Add "EMPCUR", 0, ORAPARM_OUTPUT
'  empDb.Parameters("EMPCUR").serverType = ORATYPE_CURSOR
'  empDb.Parameters.Add "DEPTCUR", 0, ORAPARM_OUTPUT
'  empDb.Parameters("DEPTCUR").serverType = ORATYPE_CURSOR
  cmd.Properties("PLSQLRSet") = True

'  Set PlSqlStmt = empDb.CreateSql("Begin EmpAndDept.GetEmpAndDeptData (:EMPCUR, :DEPTCUR); end;", 0)
  cmd.CommandText = "{CALL EmpAndDept.GetEmpAndDeptData()}"

'  Set EmpDynaset = empDb.Parameters("EmpCur").Value
  Set EmpRst = cmd.Execute
  cmd.Properties("PLSQLRSet") = False

'  Set DeptDynaset = empDb.Parameters("DeptCur").Value
  Set DeptRst = EmpRst.NextRecordset

'  MsgBox EmpDynaset.Fields("ENAME").Value
'  MsgBox DeptDynaset.Fields("DNAME").Value
  MsgBox EmpRst.Fields("ENAME").Value
  MsgBox DeptRst.Fields("DNAME").Value
End Sub
  • REF CURSOR型を使う場合、Oracle 固有の "BEGIN END;" 構文が使えません。ODBCプロシージャ・コールの構文{CALL} を使用します。(パラメータのプレースホルダには ? を使います。)
  • ADO では、REF CURSOR 型のパラメータを設定する必要がありません。(REF CURSOR 型以外のパラメータのみ設定します。)

    上の例では、Oracleの構文"Begin EmpAndDept.GetEmpAndDeptData (:EMPCUR, :DEPTCUR); end;" が ODBCの構文では {CALL EmpAndDept.GetEmpAndDeptData(?, ?)} となるところが、 パラメータが二つとも REF CURSOR型のため {CALL EmpAndDept.GetEmpAndDeptData()} となります。

  • Command オブジェクトのカスタムプロパティ PLSQLRSetTrue をセットする必要があります。使用終了後は False をセットします

  • 複数の REF CURSOR 型パラメータを使用した場合は、Recordset オブジェクトの NextRecordset で次のパラメータに対応する Recordset を取得できます。

2017-11-26

oo4oからADOへの変換 (5) Find 系メソッド

oo4o と ADO では Move 系メソッドは大きな違いはありませんが、Find 系メソッドは大きく異なります。
サンプルコードはOracle® Objects for OLE開発者ガイドの FindFirst、FindLast、FindNextおよびFindPreviousメソッド の例を書き換えたものです。

ADO

Sub Form_Load()

'  Dim OraSession As OraSession
'  Dim OraDatabase As OraDatabase
  Dim cnn As ADODB.Connection

'  Dim OraDynaset As OraDynaset
  Dim rst As ADODB.Recordset

'  Dim OraFields As OraFields
  Dim flds As ADODB.Fields
  Dim FindClause As String

'  Set OraSession = CreateObject("OracleInProcServer.XOraSession")
  Set cnn = CreateObject("ADODB.Connection")

'  Set OraDatabase = OraSession.OpenDatabase("ExampleDb", "SCOTT/TIGER", 0&)
  cnn.Open "Provider=OraOLEDB.Oracle;Data Source=ExampleDb;User ID=scott;Password=tiger"

'  Set OraDynaset = OraDatabase.CreateDynaset("select * from emp where empno >= 7654 and empno <= 7844 ", ORADYN_NO_BLANKSTRIP)
  Set rst = CreateObject("ADODB.Recordset")
  rst.CursorLocation = adUseClient
  rst.Open "select * from emp where empno >= 7654 and empno <= 7844 ", cnn, adOpenStatic, adLockOptimistic, adCommandText

'  Set OraFields = OraDynaset.Fields
  Set flds = rst.Fields

'  OraDynaset.MoveFirst
  rst.MoveFirst

  'FindClause for job as MANAGER
'  FindClause = "job LIKE '%GER'" 
   FindClause = "job LIKE '*GER*'"

'  OraDynaset.FindFirst FindClause
  rst.Find FindClause

  'NoMatch property set to true , if no rows found
'  If OraDynaset.NoMatch Then
  If rst.EOF Then
    MsgBox "Couldn't find rows "
  Else
'    MsgBox OraFields("ename").Value  ' Should display BLAKE
    MsgBox flds("ename").Value

'    OraDynaset.FindNext FindClause
    rst.Find FindClause, 1, adSearchForward

'    MsgBox OraFields("ename").Value  ' Should display CLARK
    MsgBox flds("ename").Value

'    OraDynaset.FindPrevious FindClause
    rst.Find FindClause, 1, adSearchBackward

'    MsgBox OraFields("ename").Value  ' Should display BLAKE
    MsgBox flds("ename").Value

  End If

End Sub
  • 本筋ではありませんが、oo4o の CreateDynaset はデフォルトで(ORADYN_NO_BLANKSTRIP を指定しないと)、右側のスペースを削除しますが、ADO のRecordsetでは削除されません。(RTRIM が必要)
  • oo4o の OraDynaset には FindFirstFindLastFindNextFindPrevious の4つのメソッドがありますが、ADO のRecordset では Findメソッドのみでオプションを利用します。
  • ADO Recordset には NOMATCH プロパティがありませんので、EOF (または BOF)プロパティで判定します。
  • ADO では検索文字列のワイルドカード % の代わりに * を使います。また、後方一致検索 LIKE '*GER' は使用できませんので LIKE '*GER*' (中間一致)にする必要があります。
  • IS 演算子も使用できません。IS NULL , IS NOT NULL の代わりに = NULL , <> NULL を使用する必要があります。

2017-11-24

textarea 内を置換する Chrome 拡張機能を作る

注意:

自作の Chrome 拡張機能を組み込むと Chrome 起動時に毎回以下のメッセージが表示されるようになります。
デベロッパー モードの拡張機能を無効にする
拡張機能を有効にするには[キャンセル]ボタンを押してください。

目的

前回(Blogger StackEdit で枠囲みをしたい)前々回(Blogger コードの一部を強調表示したい) の記事で、 Markdown 上でコメントアウトした HTML を HTML編集画面で置換によりコメントインすることに触れました。今回の記事のそもそもの目的は、それをボタン一つで実現しようというものです。

ここでは textarea 内をボタン一つで複数パターンまとめて置換する Chrome 拡張機能を作ってみます。Blogger に関係なく textarea があるページ全般に適用できます。

拡張機能作成の概要

以下の4つのファイルを一つのフォルダ(ここでは C:\BloggerReplace とします)に置き、Chrome 拡張機能に読み込むだけです。

  • manifest.json - 拡張機能の定義
  • background.js - background (イベント処理)スクリプト
  • content.js - content(コンテンツ挿入) スクリプト
  • jquery.min.js - jQuery

ざっくり、拡張機能のアイコンをクリックすると、background.js で定義されたイベントハンドラが、 jquery.js と content.js をページに組み込んで実行という流れです。

ファイルの作成

以下の3つのファイルをエディタで作成して、一つのフォルダ(ここでは C:\BloggerReplace )にUTF-8 で保存します。

manifest.json

{
    "name": "Blogger Replace",
    "version": "0.1",
    "manifest_version": 2,
    "browser_action": {
        "default_title": "Blogger Replace"
    },
    "background": {
        "scripts": ["background.js"],
        "persistent": false
    },
    "permissions": ["activeTab"]
}

background.js

chrome.browserAction.onClicked.addListener(function(tab) {
    chrome.tabs.executeScript(null, { file: "jquery.min.js" }, function() {
        chrome.tabs.executeScript(null, { file: "content.js" });
    });
});

content.js

$(function() {
    $('textarea').each(function(){
        var txt = $(this).val();
            $(this).val(
                txt.replace(/<!-- *div *class *= *&#34;(.+)&#34; *-->/gi, '<div class="$1">')
                    .replace(/<!-- *\/div *-->/gi, '</div>')
                    .replace(/&lt;!-- *(\/?)mark *--&gt;/gi, '<$1mark>')
            );
    });
});

<!-- div class="hoge" --> <!--/div--> (コードブロック中の)<!-- (/)mark-->をコメントイン(アンコメント)する例です。適宜修正してください。

jquery.min.js

http://jquery.com/download/ から Download the compressed, production jQuery x.x.x をダウンロードして、jquery.min.js という名前で同じフォルダに保存しておきます。

拡張機能の登録

あとは、拡張機能 (chrome://extenions) にアクセスして、

  1. [デベロッパーモード] にチェックを入れる。
  2. [パッケージ化されていない拡張機能を読み込む…] で上のファイルを保存したフォルダ(C:\BloggerReplace)を指定する。
  3. [有効] にチェックを入れる。

と拡張機能が組み込まれ、アドレスバーの右に B というアイコンが現れます。

Chrome拡張機能画面

使用方法

  1. content.js の replace メソッド(メソッドチェーン)の内容を適宜修正します。
  2. 修正したら、拡張機能の管理画面(chrome://extensions)でリロード(Ctrl+R)を実行します。
  3. 置換したい文字がある textarea のあるページ(ブログ編集画面等)でアイコン B をクリックします。

textarea 内の文字が置換されます。

Chrome 起動時の警告は消せないのか?

『Chrome 警告「デベロッパー モードの拡張機能を無効にする」を消す』にまとめました。

2017-11-23

Blogger コードの一部を強調表示したい

HTML では

コードの任意の一部のみを強調表示したい場合は、<mark> タグを使えば良いです。

例えば

HTML
<code>
document.write(<mark>"Hello World!!"</mark>);
</code>

と書けば、

document.write("Hello World!!");

と表示されます。

Markdown では

これを Markdown だけで実現する方法はなく、コードブロック自体を Markdown を使わず HTML で記述すれば実現できますが面倒です。そこで、強引にMarkdown のコードブロック中に <mark> タグを挿入しておき、変換された &lt;mark&gt; をHTML編集画面でタグ<mark>に戻すことにしました。

ただし、コードブロック中に <mark> が元々存在する場合には、それと区別する必要がありますし、StackEdit を使用していると <mark> がキーワードと認識されて余計なタグが追加されることがあります。そこで、Markdown では <mark> タグをそのまま使わずコメントアウトした<!--mark--> で挿入することにします。その上で、最終的にHTMLで <!--mark--> <!--/mark--> をコメントインすることにしました。

具体例を書くと、Markdownで

Markdown
 ```
 document.write(<!--mark-->"Hello World!!"<!--/mark-->);
 ```

と記載すると、HTMLでは

HTML
<pre><code>
document.write(&lt;--mark--&gt;"Hello World!!"&lt;--/mark--&gt;);
</code></pre>

と変換されます。
この &lt;--mark--&gt;&lt;--/mark--&gt;<mark> </mark>に戻せば良いことになります。

Blogger での HTML 編集

Blogger の場合、HTML 編集画面で Chrome 拡張機能の Search and Replace を使えば、正規表現を使って Serach For&lt;!-- *(\/?)mark *--&gt;Replace With<$1mark> にセットすことでまとめて置換できます。

ただ、Search and Replace は1パターンしか記憶できませんので、前回の記事のようなパターンの置換も同時に行いたい場合は、打ち変える必要があり少し面倒です。

続く。

2017-11-21

Blogger StackEdit で枠囲みをしたい

ここで言う枠囲みとは、HTML で

HTML
<div class="box">
inside box 
</div>

と記述して

inside box

このように出力することです。

私は StackEdit を使っているのですが、Markdown では枠囲みのような細かな装飾をすることができません。Markdown で対応できない場合は HTML で記述すれば良いのですが、StackEdit は PHP Markdown Extra の “Markdown Inside HTML Blocks” には対応しておらず、Markdown で

Markdown
<div markdown="1">
This is *true* markdown text.
</div>

と記述しても、Markdown の部分が HTML に反映されません。This is true (←強調表示) markdown text. となって欲しいところが、This is *true* markdown text. とそのまま表示されてしまいます。

こういう場合は、HTML ブロック内はすべて Markdown を使わず HTML で記述すれば良いわけですが、

Markdown
*outer html*
<div>
This is <em>true</em> markdown text. 
</div>

と記述すると、今度は HTML ブロックの外にある *outer html*outer html (←強調表示)にならず、*outer html* とそのまま表示されてしまいます。

こういう場合は、

Markdown
*outer html*
<div>This is <em>true</em> markdown text. </div>

と一行で書くとうまくいったりします。

改行を入れたい。HTML は面倒。

HTML の中が短い文章の場合は上述の方法が取れるのですが、長い文章の場合は、改行を入れたいし、そもそも HTML で記述するのは面倒です。仕方ないので、Markdown の中では HTML 部分を一旦コメントアウトして、

Markdown
<!-- div class="box" -->
This is *true* markdown text. 
<!-- /div -->

Markdown を有効にしておき、コメントアウトした HTML を Blogger のHTML編集画面でコメントインすることにしました。

(StackEdit の Extension を使って HTML への変換時にコメントを外してみようとしましたが、終了タグが別の場所に動いたりする(多分 DOM での処理の影響)ので挫折しました。また、StackEdit は開発がストップしていることもあり、StackEdit に依存した処理にはこだわりたくありませんでした。)

コメントインが面倒

Markdown (StackEdit) ではコメントアウト、HTML (Blogger) ではコメントインすることで一応対応可能にはなったのですが、コメントアウトはともかくコメントインは手作業では面倒です。

また、Blogger の編集画面には置換機能がありません。
Chrome 拡張機能の Search and Replace を使えば置換可能ですが、置換対象が複数パターンある場合は打ち変える必要があり、やっぱり面倒でした。

Search and Replace の利用画面

Search and Replace の利用画面

(StackEdit で <!-- div class="hoge" --> と入力すると、サニタイジングされて HTML では<!-- div class=&H34;hoge&H34; --> になります。)

続く。

2017-11-19

oo4oからADOへの変換 (4) AddNewメソッドとデフォルト列値

Edit メソッドに引続き AddNew メソッドです。文法的には大きな違いはないものの細かな部分で違いがあります。

ADO

Sub Form_Load()

 'Declare variables
' Dim OraSession As OraSession
' Dim OraDatabase As OraDatabase
  Dim cnn As ADODB.Connection

' Dim OraDynaset As OraDynaset
  Dim rst As ADODB.Recordset

 'Create the OraSession Object.
' Set OraSession = CreateObject("OracleInProcServer.XOraSession")
  Set cnn = CreateObject("ADODB.Connection")

 'Create the OraDatabase Object by opening a connection to Oracle. 
' Set OraDatabase = OraSession.OpenDatabase("ExampleDb", "scott/tiger", 0&)
  cnn.Open "Provider=OraOLEDB.Oracle;Data Source=ExampleDb;User ID=scott;Password=tiger"

 'Create the OraDynaset Object.
' Set OraDynaset = OraDatabase.CreateDynaset("select * from emp", 0&)
  Dim cmd As New ADODB.Command
  cmd.ActiveConnection = cnn
  cmd.CommandText = "select * from emp"
  Set rst = CreateObject("ADODB.Recordset")
  rst.CursorLocation = adUseClient
  rst.Open cmd, , adOpenStatic, adLockOptimistic

 'Begin an AddNew.
' OraDynaset.AddNew
 rst.AddNew

 'Set the field(column) values.
' OraDynaset.Fields("EMPNO").Value = "1000"
' OraDynaset.Fields("ENAME").Value = "WILSON"
' OraDynaset.Fields("JOB").Value = "SALESMAN"
' OraDynaset.Fields("MGR").Value = "7698"
' OraDynaset.Fields("HIREDATE").Value = "19-SEP-92"
' OraDynaset.Fields("SAL").Value = 2000
' OraDynaset.Fields("COMM").Value = 500
' OraDynaset.Fields("DEPTNO").Value = 30
 rst.Fields("EMPNO").Value = "1000"
 rst.Fields("ENAME").Value = "WILSON"
 rst.Fields("JOB").Value = "SALESMAN"
 rst.Fields("MGR").Value = "7698"
 rst.Fields("HIREDATE").Value = "19-SEP-92"
 rst.Fields("SAL").Value = 2000
 rst.Fields("COMM").Value = 500
 rst.Fields("DEPTNO").Value = 30

 'End the AddNew and Update the dynaset.
' OraDynaset.Update
 rst.Update

' OraDynaset.Bookmark = OraDynaset.LastModified

' Debug.Print OraDynaset.Fields("EMPNO").Value
 Debug.Print Cstr(rst.Fields("EMPNO").Value)

 MsgBox "Added one new employee."

End Sub

文法的には oo4o と ADO は大きな違いはありませんが、oo4o は AddNew メソッドを実行してもレコードの現在位置が変わりませんが、ADO では新しく追加されたレコードが現在位置になります。oo4o の OraDynaset.Bookmark = OraDynaset.LastModified に相当するコードが ADO にはありません。

もう一つの違いは、ADO の方は、Debug.Print の出力時にStringへの型変換 (CStr) を行っていることです。CStr がなければ ␣1000 (先頭にスペースが入ります)となります。これはoo4oは OraDynaset.Fields(index).Value のデータ型が String (本来は Integer にマッピングされるはず)であるのに対して、ADOの Recordset.Fields(index).ValueDecimal 型になっているためです。

Field.Type のマッピングについては別途整理しようと思います。

デフォルト列値の違い

次に、表 EMP にデフォルト値 0 を持つ列 DELETE_FLAG を追加(alter table EMP add DELETE_FLAG NUMBER DEFAULT 0; )して、AddNew メソッド実行後のDELETE_FLAG の値を見てみます。

実行結果は、oo4o、ADO とも DELETE_FLAGDebug.Print の値は Null となりますが、データベースに書き込まれた値は、oo4o は Null、ADO は 0 となります。
ADO のこの振る舞いは、oo4o の CreateDynaset のオプションに ORADYN_ORAMODE + ORADYN_NO_REFETCH を指定した場合と一致します。

2017-11-16

Blogger パンくずリスト導入の注意点

内容が古いため更新しました。最新記事はこちらです。

パンくずリストを導入しました。

導入方法は他のブログ等で記載されているので、ここではあまり触れられていないことについて記載しておきます。

多くのブログで参照されている元記事はこちらです。

Breadcrumb for Blogger - Blogger Widgets

元記事は2009年に作成され、その後2012年にアップデートされているのですが、当時とは環境が変わっており、とくにBlogger の「HTMLの編集」画面の Formatter と Sanitizer により元記事のやり方では 勝手に 自動的にコードが変換されてしまうことに注意する必要があります。(エラーになるわけではないので大きな問題ではありませんが。)

b:includable の挿入場所

元記事の順番と前後しますが、最初にウィジェットタグ b:includableの挿入場所について説明します。
元記事では、<b:includable id='main' var='top'> の前に<b:includable id='breadcrumb' var='posts'> のコード を挿入するとありますが、Formatter が <b:includable id='xxx'> </b:includable>を id 順(アルファベット順)にソートしてしまいますので、<b:includable id='comment-form' var='post'> の前に挿入した方が良いです。

つまり、 正しくは『「Blog1」ウィジェットの <b:includable> の定義場所にidがアルファベット順になるように挿入する』ということになります。

検索を使う前に「ウィジェットへ移動」で「Blog1」を選択すると該当箇所を探しやすいと思います。

Blogger の HTML編集画面での検索機能の使い方

編集画面で Ctrl + F キーを押すと、右肩に Search ボックスが現れます。検索文字を入力後、Enter キーを押すたびに見つかった文字にカーソルが移動します。

HTML サニタイジングによる変換

「テーマを保存」ボタンを押すと、b:includable のスニペットは、Sanitizer により "&quote; に、»&#187; に自動的に変換されます。

変換後のコードは以下のとおりです。最初から変換後のコードを挿入する方が望ましいと思います。

              <b:includable id='breadcrumb' var='posts'>
              <!-- breadcrumb start -->
              <b:if cond='data:blog.homepageUrl != data:blog.url'>
              <b:if cond='data:blog.pageType == &quot;static_page&quot;'>
              <div class='breadcrumbs'><span><a expr:href='data:blog.homepageUrl' rel='tag'>Home</a></span> &#187; <span><data:blog.pageName/></span></div>
              <b:else/>
              <b:if cond='data:blog.pageType == &quot;item&quot;'>
              <!-- breadcrumb for the post page -->
              <b:loop values='data:posts' var='post'>
              <b:if cond='data:post.labels'>
              <div class='breadcrumbs' xmlns:v='http://rdf.data-vocabulary.org/#'>
              <span typeof='v:Breadcrumb'><a expr:href='data:blog.homepageUrl' property='v:title' rel='v:url'>Home</a></span>
              <b:loop values='data:post.labels' var='label'>
              <!-- b:if cond='data:label.isLast == &quot;true&quot;' -->
               &#187; <span typeof='v:Breadcrumb'><a expr:href='data:label.url' property='v:title' rel='v:url'><data:label.name/></a></span>
              <!-- /b:if -->
              </b:loop>
               &#187; <span><data:post.title/></span>
              </div>
              <b:else/>
              <div class='breadcrumbs'><span><a expr:href='data:blog.homepageUrl' rel='tag'>Home</a></span> &#187; <span>Unlabelled</span> &#187; <span><data:post.title/></span></div>
              </b:if>
              </b:loop>
              <b:else/>
              <b:if cond='data:blog.pageType == &quot;archive&quot;'>
              <!-- breadcrumb for the label archive page and search pages.. -->
              <div class='breadcrumbs'>
              <span><a expr:href='data:blog.homepageUrl'>Home</a></span> &#187; <span>Archives for <data:blog.pageName/></span>
              </div>
              <b:else/>
              <b:if cond='data:blog.pageType == &quot;index&quot;'>
              <div class='breadcrumbs'>
              <b:if cond='data:blog.pageName == &quot;&quot;'>
              <span><a expr:href='data:blog.homepageUrl'>Home</a></span> &#187; <span>All posts</span>
              <b:else/>
              <span><a expr:href='data:blog.homepageUrl'>Home</a></span> &#187; <span>Posts filed under <data:blog.pageName/></span>
              </b:if>
              </div>
              </b:if>
              </b:if>
              </b:if>
              </b:if>
              </b:if>
              <!-- breadcrumb end -->
              </b:includable>
  • Formatter が(先頭行のみ)行頭にスペース12文字のインデントを挿入するので、行頭に12文字スペースを入れています。
  • <b:includable> </b:includable> の外にコメントしても Formatter が消してしまうので内側にコメントを記載しています。
  • 複数ラベルに対応するため、<!-- b:if cond='data:label.isLast == &quot;true&quot;' --><!-- /b:if --> の箇所をコメントアウトしています。
    最後のラベルのみをパンくずリストに表示する場合はコメントインしてください。
    複数ラベルを使うと、ラベルが階層化されて表示されます。
    ただし、順序はアルファベット順・あいうえお順となります。
  • <b:if cond='data:blog.homepageUrl != data:blog.url'> と 対応する</b:div> をコメントアウトすればインデックスページに表示することができます。
  • パンくずリストのつなぎ文字をGoogleの検索で表示されるのと同じ「 › 」にする場合は、&#187;&#8250; に変更してください。実体参照 &rsaquo; だと

The entity “rsaquo” was referenced, but not declared.

とのエラーが出ました。

b:include の挿入場所

表示用ウィジェットタグ <b:include data='posts' name='breadcrumb'/> は、<b:include data='top' name='status-message'/> の前に置くとありますが、
<div class='blog-posts hfeed'> の後と記載しているサイト(海外)が多いようです。<div class='blog-posts hfeed'> が閉じる </div> の前に置くとページ下部に表示できます。(両方置くことも可能です。)

さらに <b:if cond='!data:mobile'> の前に置けば、モバイル版でも表示されるようにできます。

Google 構造化データテストツール

元記事にはその後の記事(Google Breadcrumb for Blogger - Blogger Widgets)があり、そこではGoogleの構造化データテストツールでパンくずリストのテストが可能であることに触れられています。(この記事を掲載したときに元記事もアップデートしたようです。)

Google 構造化データテストツール

パンくずリストはトップページには表示されない仕組みのため、構造化データテストツールの URL には、パンくずリストが表示される記事ページを指定する必要があります。

構造化データテストを実施すると、BlogPosting に大量のエラーが見つかるかもしれません。Google プロダクト フォーラム によるとそれほど気にしなくて良いようです。

2017-11-10

oo4oからADOへの変換 (3) Editメソッドとトランザクション

今回はトランザクションと行セット更新の例です。
サンプルコードは、Oracle® Objects for OLE開発者ガイドのBeginTransメソッド の例を書き換えたものです。

ADO
Sub Form_Load()

  'Declare variables
'  Dim OraSession As OraSession
'  Dim OraDatabase As OraDatabase
   Dim cnn As ADODB.Connection

'  Dim OraDynaset As OraDynaset
   Dim rst As ADODB.Recordset

'  Dim fld As OraField
  Dim fld As ADODB.Field

 'Create the OraSession Object.
' Set OraSession = CreateObject("OracleInProcServer.XOraSession")
  Set cnn = CreateObject("ADODB.Connection")

  'Create the OraDatabase Object by opening a connection to Oracle.
'  Set OraDatabase = OraSession.OpenDatabase("ExampleDb", "scott/tiger", 0&)
  cnn.Open "Provider=OraOLEDB.Oracle;Data Source=ExampleDb;User ID=scott;Password=tiger"

  Dim cmd As New ADODB.Command
  cmd.ActiveConnection = cnn
  cmd.CommandType = adCmdText
  cmd.CommandText = "select * from emp"

  'Create the OraDynaset Object.
'  Set OraDynaset = OraDatabase.CreateDynaset("select * from emp", 0&)
  Set rst = CreateObject("ADODB.Recordset")
  rst.CursorLocation = adUseClient
  rst.Open cmd, , adOpenStatic, adLockOptimistic

  'Start Transaction processing.
'  OraSession.BeginTrans
  cnn.BeginTrans

  'Setup a field object to save object references.
'  Set fld = OraDynaset.Fields("sal")
  Set fld = rst.Fields("sal")

  'Traverse until EOF is reached, setting each employees salary to zero
'  Do Until OraDynaset.EOF = True
  Do Until rst.EOF = True
'    OraDynaset.Edit
'    fld.Value = 0
'    OraDynaset.Update
'    OraDynaset.MoveNext
    fld.Value = 0
    rst.Update
    rst.MoveNext
'  Loop
  Loop

  MsgBox "All salaries set to ZERO."

  'Currently, the changes have NOT been committed to the database.
  'End Transaction processing. Using RollbackTrans
  'means the rollback can be canceled in the Validate event.
' OraSession.Rollback
  cnn.RollbackTrans

  'MsgBox "Salary changes rolled back."

End Sub
  • パラメータにも対応できるように、あえて Command オブジェクトを使用しています。
  • ADO では、編集開始の Edit メソッドが不要です。また、Update メソッドがなくてもMoveNext で更新されます。
  • oo4o の Rollback は、ADOでは RollbackTrans にメソッド名が変わります。BeginTrans, CommitTransはメソッド名も同じです。
  • ADO には、ResetTrans メソッドはないため、
On Error Resume Next
cn.RollbackTnans
On Error Goto 0

で代用することになります。

UpdateBatch メソッド(ADO)

ADO の場合、

  rst.Open cmd, , adOpenStatic, adLockBatchOptimistic

  Do Until rst.EOF = True
    fld.Value = 0
    rs.MoveNext
  Loop
  rst.UpdateBatch

Update をループの外に出し、UpdateBatch メソッドにすることにより、まとめて変更処理を行い、ラウンドトリップを削減できます。
MSDAORA で UpdateBatch メソッドを使う場合、Recordset の LockType に adLockBatchOptimistic を指定しなければ、

現在の Recordset は更新をサポートしていません。プロバイダーか、選択されたロックタイプの限界の可能性があります。

とのエラーが発生します。(OraOLEDB ではなぜか出ません。)

Execute メソッドによる更新可能行セットの取得(OraOLEDB)

OraOLEDBの場合、Command.Executeで更新可能な行セットを取得できます。

  Set rst = CreateObject("ADODB.Recordset")
  rst.Open cmd, , adOpenStatic, adLockOptimistic

の箇所は

  cmd.Properties("IRowsetChange") = True
  cmd.Properties("Updatability") = 7
  Set rst = cmd.Execute

に書き換え可能です。
但し、AddNew メソッドを実行すると、

ORA-01400: (“SCOTT”.”EMP”.”EMPNO”)にはNULLは挿入できません。

が発生しました。

2017-11-03

Blogger highlight.js に行番号(highlightjs-line-numbers.js)

highlightjs-line-numbers.js の導入

前回導入した highlight.js に行番号を表示するため highlightjs-line-numbers.js を導入する。
highlightjs-line-numbers.js の GitHub はこちら

highlight.js の読み込みの後に、

<script src="//cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.1.0/highlightjs-line-numbers.min.js"></script>

を読み込みし、
hljs.initHighlightingOnLoad(); の後に、hljs.lineNumbersBlock();を呼べばよい。(簡単!)

前回のhighlight.jsの設定とまとめると、<head> への記載は以下のようになる。

    <!-- hilight.js -->
    <link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css' rel='stylesheet'/>
    <script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js'/>
    <script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/vbnet.min.js'/>
    <script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/vbscript.min.js'/>
    <script src='//cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.1.0/highlightjs-line-numbers.min.js'/>
    <script>hljs.initHighlightingOnLoad();</script>
    <script>hljs.initLineNumbersOnLoad();</script>

CSS の調整

見栄えをよくするため、以下をCSSに追加する。

/* for block of numbers */
td.hljs-ln-numbers {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;

    text-align: center;
    color: #ccc;
    border-right: 1px solid #CCC;
    vertical-align: top;
    padding-right: 5px;

    /* your custom style here */
}

/* for block of code */
td.hljs-ln-code {
    padding-left: 10px !important;
}

Blogger テーマデザイナーの「CSS の追加」では、!important がないと有効にならなかった(行番号とコードの間隔が詰まったままになる)。

2017-11-02

Blogger シンタックスハイライト(highlight.js)の導入

導入方針

導入方針は以下のとおり。

  • StackEdit + Blogger の環境に対応
  • VB 系言語に対応
  • 手間がかからない。

StackEdit では、Google Code Prettify と highlight.js に対応している。
Settings -> Extensions -> MarkdownExtra -> Syntax Hilighter で

  • None
  • Prettify
  • Highlight.js

が選択可能になっている。

なお、ここで Highlight.js を選択しても、<pre class="prettyprint"> と出力されるので、Google Code Prettify でも使えそうなんだけど。(バージョンアップしたときに過去バージョンとの互換性のために残したのかな。)

Google Code Prettify は、VB については

It works passably on Ruby, PHP, VB, and Awk…

なんとか動くっぽいけど、VB の自動判定に難があったので、とりあえず highlight.js を採用する。

highlight.jsの導入

まず、Getting Started を読む。

<link rel="stylesheet" href="/path/to/styles/default.css">
<script src="/path/to/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

CSS読み込んで、JS読み込んで、initHighlightingOnLoad() を呼べばいい。

次に、同じページの Getting Libraly を読む。

CDN が使えるけど、CDN は Getting highlight.js の Common にある23言語のみ対応しており、その中に VB 系は含まれていない。
23言語に含まれていない場合は、

<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/languages/go.min.js"></script> 

のように手で追加する必要がある。
VB.NET と VBScript 用のファイル名が分からなかったので、
GitHubのSourceで vbnet.js 、vbscipt.js であることを確認。( minify 化したファイル名 xxx.min.js で指定する。)

最終的に

    <!-- hilight.js -->
    <link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css' rel='stylesheet'/>
    <script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js'/>
    <script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/vbnet.min.js'/>
    <script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/vbscript.min.js'/>
    <script>hljs.initHighlightingOnLoad();</script>

</head>の上に追加する。
Blogger の「 HTML の編集」で保存しようとすると <link> が閉じていないと怒られたので / を追加する。(Blogger の「 HTML の編集」は HTML5 に対応していないもよう)

CSSの微調整

コードブロックの文字サイズが本文と一緒なので一回り小さくし、行間も詰める。
また、インラインコードにも背景色も付けたい。
以下を CSS に追加する。

pre code.hljs {
    font-size: 13px;
    line-height: 1.2;
}
code {
    background:#F0F0F0;
}

font-family を Consolas (Windows標準) 等にするのはとりあえず見送った。

行番号

highlit.js は行番号表示には対応していないし、する気もない。作者のご意見はこちら
行番号を表示するには、highlightjs-line-numbers.js などを使う必要がある。

続く

2017-11-01

Blogger 最初のカスタマイズ

Bloggerのデフォルト設定では問題があると感じたところを変更したのでメモしておく。なお、テーマはAwesome.incを使用している。

横幅の修正

デフォルトでは960pxで記事の幅が狭い。デザインは8の倍数でできている を参考に一回り大きい1040pxに変更する。

文字の大きさ

ページのフォントはデフォルトで14pxになっている。一回り大きい15pxに変更する。

また、デフォルトのCSSでは投稿タイトルとh4の文字が以下のように同じ扱いになっている。

h3.post-title, h4 {
    font: normal bold 22px Arial, Tahoma, Helvetica, FreeSans, sans-serif;
    color: #444444;
}

これだとh3はデフォルトで1.17em (本文16pxの場合、18.72px) なので、h4 (22px) よりh3の方が文字が小さくなってしまう。
Bloggerの投稿編集画面では、見出し h2 、小見出し h3 、準見出し h4 の3つが定義され、それぞれの文字の大きさはデフォルトCSSとなっている(つまり編集画面と実際の画面の文字の大きさが異なる)。編集画面に近い見た目になるように以下のCSSを追加。

.post-body.entry-content h2 {
    font-size: 1.5em;
    margin-top: 1.5em;
}
.post-body h3 {
    font-size: 1.3em;
    margin-top: 1.3em;
}
h4:not([class]) {
    font-size: 1.1em;
}

h2h3はウィジェットのタイトルにも使われているので投稿ページに限定する。h4はページ下部のコメント欄タイトルに使われておりデフォルトの状態ではむやみに大きくなっている。そこにも適用するため、:note([class]) でクラス指定なしを対象にする。
クラスの指定は .post-body だけだと、main-inner が優先されることがあるので、その場合は h2 の例のように .post-body.entry-content とする。

行間

デフォルトの1.4emでは狭すぎるので、1.6emに変更する。
以下のCSSを追加。

.post-body {
 line-height: 1.6;
}

ヘッダー空白

ヘッダーと本文間の余白が目立つので padding-top を10pxで上書きする。(デフォルト30px)

.main-inner {
    padding-top: 10px;
}

引用

デフォルトだと引用が分かりづらい。
元のCSSを確認すると、こうなっていた。

blockquote {
    display: block;
    -webkit-margin-before: 1em;
    -webkit-margin-after: 1em;
    -webkit-margin-start: 40px;
    -webkit-margin-end: 40px;
}

字を一回り小さくし、枠を追加する。
以下のCSSを追加。

blockquote {
    padding: 0 20px;
    border: 1px solid #dddddd;
    border-left-width: 5px;
    background-color: #f6f6f6;
} 

レイアウト

デフォルトで

メイン
ブログの投稿
注目の投稿
人気の投稿
右サイドバー
ブログ検索
ページ
自己紹介
不正行為を報告
ラベル
ブログアーカイブ

になっているのを

メイン
ブログの投稿
右サイドバー
ブログ検索
自己紹介
ページ
ラベル
ブログアーカイブ
人気の投稿
不正行為を報告

に変更。

「ブログアーカイブ」はスタイルを「階層」に、「人気の投稿」は表示を「投稿のタイトル」のみに変更。