shikoan’s memo

プログラミング初心者のチラ裏

ぷろぐらみんぐ帳

Vultr(CentOS7)でSSHのポートを変更したあとにTeraTermでログインできなくなった

環境:Vultr、CentOS7 64bit

こちらを参考に設定をしてた(Time4Vpsじゃないけど)

time4vps.ysklog.net

SSHのポートを変更して秘密鍵認証に変えて、iptablesも変えていざTeraTermを再起動!としたらいきなりログインできなくなったときのメモ(これにハマってOSを2回インストールし直したのは秘密)。

公式に書いてあった。

www.vultr.com

CentOS 7 users, run these commands instead:
firewall-cmd --add-port 2124/tcp --permanent
firewall-cmd --add-port 2124/tcp
The configuration changes are now finished. Restart the SSH server (SSHD)...
service sshd restart

firewall-cmdのほうを使えと。これをブラウザコンソールから入力したらいけた。

ちなみにiptablesもはいっていないからsystemctlを使えと怒られた。これはyumからインストールすればいける。

service iptables save がCentOS 7で出来ない( ゚Д゚) | 技術室(Technical Room)

ログインできなくなったときは結構びっくりした。

PostgreSQLでテーブル、インデックスの合計容量を取得する

探してたら古い情報しかなかったのでメモ。最新バージョンのドキュメントだと、結構明瞭に記述できるようになっている。

SELECT relname,
pg_size_pretty(pg_total_relation_size(relid)) AS totalsize,
pg_size_pretty(pg_table_size(relid)) AS tablesize,
pg_size_pretty(pg_indexes_size(relid)) AS indexsize 
FROM pg_stat_user_tables ORDER BY relid DESC LIMIT 3;

 relname | totalsize | tablesize | indexsize
---------+-----------+-----------+-----------
 five    | 8029 MB   | 7814 MB   | 214 MB
 three   | 5098 MB   | 4884 MB   | 214 MB
 one     | 2582 MB   | 2368 MB   | 214 MB
(3 rows)

ORDERとかLIMITとかは必要に応じていじってちょ。参考↓

9.26. システム管理関数

PostgreSQLでISO8601形式の日付を扱う(タイムゾーン付き)

PostgreSQLでタイムスタンプの文字列から、インポートするときにちょっと困ったのでメモ。

例えばC#でいうところの

Console.WriteLine(DateTimeOffset.Now.ToString("o"));
//2018-02-17T23:50:31.5652713+09:00

こういうISO8601形式のタイムスタンプをパースしたい。PostgreSQLではこうやるらしい。

postgres=# SELECT to_timestamp('2018-02-17T23:50:31.5652713+09:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"');
      to_timestamp
------------------------
 2018-02-17 23:50:31+09
(1 row)

これはタイムゾーンありのtimestamptz型になるので、タイムゾーンを落としたければtimestamp型でキャストすればよい。

postgres=# SELECT to_timestamp('2018-02-17T23:50:31.5652713+09:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')::timestamp;
    to_timestamp
---------------------
 2018-02-17 23:50:31
(1 row)

逆に文字列にする場合、いちいちto_charするのではなく、PostgreSQL9.4以降ではto_json()をかませるのがスマートらしい。StackOverFlowに載っていた。

postgres=# SELECT to_json(now());
              to_json
------------------------------------
 "2018-02-18T00:04:14.328248+09:00"
(1 row)

なるほど~~~これはイケメンやな~~と思った。

stackoverflow.com

PostgreSQLでデータディレクトリを変更して起動する方法

PostgreSQLをローカルで使っていると、通常と別のデータディレクトリから読み込ませたり、データディレクトリを切り替えながら使用したいことがある。その方法のメモ。

環境:PostgreSQL 10.2 64bit

1. サービス登録の解除

インストールするとデフォルトでサービスに登録されているので、サーバーの停止と自動起動の解除を行う。

コントロールパネルを「サービス」で検索し、サービス(ローカル)の管理画面を開く。postgresqlを探し、状態を「停止」、スタートアップの種類を「手動」にする。

これでサーバーの起動、終了を基本的にコマンドラインから行うことになる。

2. データベースクラスタの作成

PostgreSQLはMongoDBのように空のディレクトリを指定して起動すると勝手に初期化してくれないので、データベースクラスタを作成する必要がある。

デフォルトのデータディレクトリをコピー(おすすめ)

ちょっと邪道だが、デフォルトのデータディレクトリを特に使っていない場合、デフォルトのデータディレクトリ(初期設定では「C:\Program Files\PostgreSQL\10\data」フォルダ)をそのままエクスプローラーでコピーするのが速い。コピーすればそのまま起動オプションに指定して起動できる。

initdbコマンドでデータベースクラスタの初期化

こっちが正攻法。テキスト検索設定がよくわからなかったがとりあえずこのコマンドでよさそう。この例では「D:\psql\newdb」フォルダに作るものとする。

initdb --encoding=UTF8 --no-locale --username=postgres --pwprompt -D D:\psql\newdb

encodingとlocale(no-localeの場合はロケールCに設定される)とusername=postgresは設定したほうがよさそう。pwpromptオプションを入れるとパスワード認証ありになるが、パスワード認証が不要なら特に必要はない。

3. データベースの起動

コマンドラインからデータベース・サーバーを起動する。

> pg_ctl start -D D:\psql\newdb

そのままコンソールでクライアントとしてログイン、ログアウトする場合は、

> psql -U postgres #ログイン
postgres=# \q #ログアウト

サーバーの終了は以下の通り。

> pg_ctl stop -D D:\psql\newdb

PostgreSQLでもデータディレクトリを切り替えながら使うことができるらしい。

Goutteでhtmlファイルからスクレイピングする方法

Goutteにはダウンロード用のクライアントがついているが、ファイルからHTMLの構造解析をしたいことがある。その方法のメモ。

事前準備FriendsOfPHPから「>If you need support for PHP 5.3 or Guzzle 3, use Goutte 1.x (latest phar).」にある goutte-v1.0.7.phar をダウンロード。実行するphpと同じフォルダに置く。

goutteのバージョンは2以降もあるが、ここではv1.0.7を使用するものとする。

<?php
require_once "./goutte-v1.0.7.phar";
use Symfony\Component\DomCrawler\Crawler;

$filestr = file_get_contents("hogehoge.html");
$crawler = new Crawler($filestr);

$hoge = $crawler->filter("div.hoge")->eq(0);
/*<div class="hoge">の1つ目 以下、通常の場合と同様*/

useをこのようにして、ダイレクトに文字列をCrawlerに突っ込むのがポイント。

PHPのZipArchiveの日本語を含むディレクトリでハマった話

PHPディレクトリ内のファイルを1つのZipファイルに圧縮しようとしたらハマったときの話。

以下のようなファイル構成である。
-test
└index.php
└data
 -001.txt
 -: :

ここでdataフォルダ以下をtest直下のdata.zipに圧縮したい。index.phpのソースは以下の通り(適当に探して拝借しました)。

<?php
//index.php
$files = glob("data/*.*");

//Zipに圧縮
$zip = new ZipArchive;
$res = $zip->open("data.zip", ZipArchive::CREATE | ZipArchive::OVERWRITE);

if($res === true)
{
    foreach($files as $f)
    {
        $zip->addFile($f, basename($f));
    }
    $zip->close();
}
else
{
    echo "Error Code".$res;
}

シェルを起動し、cdでtestフォルダをルートとして「php -S localhost:8000」でPHPを起動する。ブラウザで「localhost:8000」でindex.phpを読み込ませる。PHPのバージョンは7.0.9、Windows上で実行させた。

この例はこれは普通に成功し、zipファイルができる。addFileで第2引数以下でbasenameとしないと、data.zipの中にさらにdataフォルダができる(要はルートディレクトリ以下のパスが維持される)。

ところがルートを「test」から「テスト」に変えてパスに日本語を含む形にすると上手くは行かない

同様に実行させると、「Error Code18」と表示されzipファイルが生成されない。エラーはZipArchive::openで発生しており、globは正しく取得できている。エラーコードを逆引きすると、「Invalid argument(引数がおかしい)」とのこと。どこがいけないのか数時間ハマった。

PHP: ZipArchive::open - Manual

日本語をパスに含む環境でZipArchiveを使う際は、用心したほうがよさそうだ。

Tumblrのテキスト投稿でLightBoxを独自導入して使う

Tumblrにはbuilt-inのLightBoxがあり、カスタムHTML編集で簡単に導入することができる。以下のサイトで紹介されているのがこれを用いた方法。

f-u.seesaa.net

しかし、これはどうも写真投稿限定のようで、テキスト投稿になると画像のURLやサイズをbuilt-inのLightBoxに引き渡すことができなかった。似たような質問が英語記事だが紹介されている。

css-tricks.com

Unfortunately, if you’re using the built-in Tumblr lightbox for photosets there’s currently no way to apply it to single photos as well. What you can do is apply your own lightbox (eg. View.js) to single photos.

テキスト投稿はカスタム編集を見ていると、入力したHTMLソースをそのまま記事に渡しているようで、読み込んだあとにDOM操作してLightBoxのタグを付与しようとしても、画像のサイズや解像度を{block:Posts}の外側から{PhotoWidth-HighRes}の変数で取得することができず、built-inのLightBoxのポップアップが立ち上がらなかった。カスタムテーマ用の変数は以下を参照。

Tumblrカスタムテーマの作り方:日本語訳 (Creating a custom HTML theme | Tumblr)

{block:Posts}に入れ子になっている{block:Photo}や{block:Photos}も写真投稿用のブロックで、例えばブログのようにテキスト投稿で間に画像を挿入しながら書いていくという形式だと{block:Text}で読み込まれるようだ。{block:Text}において挿入されている画像をTumblrが用意した変数で取得することは不可能なので、テキスト投稿で入れた画像にLightBoxのような”味付け”をする場合は、結局のところDOM操作が必要になる。

結論からいうと、テキスト投稿で埋め込まれた画像においてLightBoxを導入することは可能だ。ただし、Tumblr側が用意したbuilt-inのLightBoxではなく、一からLightBoxを導入する必要がある

LightBoxの導入

まずはLightBoxの公式サイトにアクセスし、LightBoxをダウンロードする。

lokeshdhakar.com

英語になるが、公式のGetting Startedに目を通して使い方に慣れるといいだろう。

ダウンロードしたlightbox2-master.zipを解凍する。必要なファイルは、

  1. dist/css/lightbox.css

  2. dist/imagesのフォルダにある画像ファイル(close.png, loading.png, next.png, prev.png)の4つすべて

  3. dist/js/lightbox.js

この3種類だ。LightBoxjQueryを使用し、jQuery同梱版のLightBoxlightbox-plus-jquery.js)もあり公式チュートリアルではそれの使用を推奨する旨もあったが、自分の環境では同梱版を使うとTumblrjQueryを使用するプラグインと衝突するバグが発生したため、CDNからjQueryを読み込ませることにした。そのため使うのは同梱版ではない普通のlightbox.jsでよい。

Tumblrへのアップロード

ここが若干わかりづらいのだが、Tumblrにも静的ファイルをアップロードするスペースはある。

ダッシュボードから設定アイコン→テーマの編集を開き、「HTMLを編集」を開きカスタムHTML編集画面を起動する。そこからさらに左上の設定アイコン→「テーマアセット」を開く。ここが静的ファイルのアップロードスペースとなる。

テーマアセットの注意書きを見ると「テーマ以外のデータをアップロードした場合は、そのアカウントを随時凍結します」という物々しい注意書きがあるが、プラグイン用の画像やスクリプトファイルを上げるぐらいなら問題はないだろう(確証はないのでBANされても責任は取れません)。要はアップローダーみたいに使うのはNGということだろう。

まず、ここにimage以下の画像4ファイルをアップロードする。ここでのアップロード先のURLをコピーしてメモしておく。

次に、css/lightbox.cssCSSテキストエディタ等で編集する。なんでこんな面倒なことするのかというと、テーマアセットのアップロード先が不定かつ任意のディレクトリを作れないので、画像ファイルを相対パスから絶対パスに書き換えてやる必要があるからだ。LightBoxはMITライセンスなので改変についてのライセンス上の問題はない。

CSSの書き換え箇所は4箇所。例えばローディングの画像では、

.lb-cancel {
  display: block;
  width: 32px;
  height: 32px;
  margin: 0 auto;
  background: url(../images/loading.gif) no-repeat;
  //ここのurl以下を書き換える
}

url()のカッコ内をTumblrにアップロードした対応する画像ファイルのURLに書き換える。ファイル内を「url」で検索すると書き換え箇所が簡単に見つかる。

書き換えが終わったら、編集済みのlightbox.cssを同様にアップロードする。また、lightbox.jsもアップロードする。こちらは編集は特に必要ない。アップロードしたCSSとJSファイルのURLをまたメモしておく。

カスタムHTML編集

<head>~</head>にLightBoxCSS、<body>~</body>にjQueryLightBoxJavaScriptを設置する。

<head>
(中略)
<link rel="stylesheet" href="テーマアセットでアップロードしたURL/lightbox.css">
</head>

<body>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="テーマアセットでアップロードしたURL/lightbox.js"></script>
(中略)
</body>

使用しているテーマにあわせてDOMを操作

ここからは使用しているテーマによってクラス構造が違うので一概には言えないが、DOM編集のスクリプトを追加し、LightBoxでテキスト投稿の画像を表示できるようにする。

例えば自分の使用しているテーマでは、記事は次のような構造になっている(そのままソースを表示すると文字がエンコードされている可能性があるため、画像周辺を開発者ツールで見るとわかりすい)。

<div class="post-body">
  <p>町田は神奈川</p>
  <p>駅前走っているバスは神奈中~~~<img src="~~~.jpg" /></p>
</div>

これをLightBoxで表示するにはimgタグの部分を、

<a href="~~~.jpg" data-lightbox="ぶっちゃけなんでもいい"><img src="~~~.jpg" /></a>

とする。このdata-lightboxが重要でLightBoxで表示するという印である同時に、data-lightboxが同一の画像でグループ化して表示される

HTML編集画面を開き、bodyの最後あたりにDOM操作のスクリプトを追加する({block:Posts}のあとならどこでもよさそう)。

<script>
    var postImgs = document.getElementsByTagName("img");
for (var i = 0; i < postImgs.length; i++) {
    if (!postImgs[i].parentNode || 
        postImgs[i].parentNode.tagName.toLowerCase() !== "p") continue;
    if (!postImgs[i].parentNode.parentNode || 
        postImgs[i].parentNode.parentNode.className.toLowerCase() !== "post-body") continue;
    postImgs[i].outerHTML = "<a href='" + postImgs[i].src + 
        "' data-lightbox='lightbox" + i + "'><img src='" + postImgs[i].src + 
        "' alt='" + postImgs[i].alt + "' /></a>";
}
</script>

これでOKだ。プレビュー画面では反映されないことがあるので、保存してから別ページでホームを開くとLightBoxで画像が表示されるようになっている。せっかくjQuery導入したからここをjQueryで書いてもよかったけど個人的に嫌いなので(

以前投稿した記事をLightBox用に再編集する必要はない。