Author Archives: neobit

Smartyのtruncateをマルチバイト対応で

Smartyのtruncate修飾子はマルチバイト対応されていないので文字化けしちゃう。
独自にtruncateの拡張をするまでもなく、PHP自身が持っているmb_strimwidthを使えばOK

でも、すぐに忘れちゃうのでメモ
{$contents|mb_strimwidth:0:80:"..."}

変数にHTMLタグが入ってるなら、それも除去
{$contents|strip_tags|mb_strimwidth:0:80:"..."}

PEAR Mail_mimeDecodeでヘッダー文字化け

PHPで送信されてきたメールを解析して処理を行うため、ネット情報を参考に以下のようにやってみた。

PEAR::Mail_mimeDecodeをインストールして以下のように処理したのだが、$mail_body->bodyの処理は良かったとして、ヘッダーの文字列が内部エンコードに変換されず元のままのエンコードで入ってきている。UTF-8だったりJISだったり。。困った。

ネット情報を参考に書いた元ソースがこれ

require_once 'Mail/mimeDecode.php';

$rawdata = file_get_contents("php://stdin"); // 標準入力から

// メールParse
$params = array();
$params['include_bodies'] = true;
$params['decode_bodies']  = true;
$params['decode_headers'] = true';
$params['crlf'] = "\r\n";
$Mail = new Mail_mimeDecode($rawdata);
$mail_data = $Mail->decode($params);

$form = array();
$form['from'] = preg_replace('/^.*<([^<>]+?)>.*$/', '$1', $mail_data->headers['from']);
$form['to'] = preg_replace('/^.*<([^<>]+?)>.*$/', '$1', $mail_data->headers['to']);
$form['subject'] = $mail_data->headers['subject'];

// 本文の取得
$encode = "ISO-2022-JP";
if ($mail_data->ctype_primary == 'multipart') {
	foreach ($mail_data->parts as $parts) {
		if ($parts->ctype_primary == 'text' and $parts->ctype_secondary == 'plain') {
			$MailBody = $parts->body;
			if ($parts->ctype_parameters['charset']) {
				$encode = $parts->ctype_parameters['charset'];
			}
			break;
		}
	}
} else {
	if ($mail_data->ctype_parameters['charset']) {
		$encode = $mail_data->ctype_parameters['charset'];
	}
	$MailBody = $mail_data->body;
}
$form['body'] = mb_convert_encoding($MailBody, "UTF-8", $encode);

print_r($form);

$mail_dataを見てもヘッダーの素のエンコードが何だったかパラメータに入ってこないから、後処理しようがないじゃん!

とmimeDecodeのソースを覗いてみたらこう書かれてるじゃない。

     * @param array An array of various parameters that determine
*              various things:
*              include_bodies - Whether to include the body in the returned
*                               object.
*              decode_bodies  - Whether to decode the bodies
*                               of the parts. (Transfer encoding)
*              decode_headers - Whether to decode headers,
*                             - use "UTF8//IGNORE" to convert charset.
*
*              input          - If called statically, this will be treated
*                               as the input
* @return object Decoded results
* @access public
*/
function decode($params = null)

つまり、decode_bodiesはtrue/falseで良いけど、decode_headresはtrue/falseじゃなくて期待する変換後エンコードを文字で指定するのが正解。

ただ、これも間違ってる。iconvで処理してるから、UTF8じゃなくてUTF-8ね。

それにIGNOREだと不正文字をカットされちゃって不正文字が混ざってた事が分からなくなるので、不正文字マークにしてくれるTRANSLITの方が良いと思われ。

$params['decode_headers'] = 'UTF-8//TRANSLIT';

これで解決!ネット情報もソースのコメントさえもあやふや。

疑って掛かる事をしばらく忘れていましたが、何もかも確認は必要ですね。

Category: PHP

gmoserverでKCFinderが変なパスを返す不具合

CKEditorと組み合わせてKCFinderを画像アップローダーとして使う事が良くあるのですが、gmoserverのSDプランのサイトに設置したときに、画像はKCFinder上でちゃんとアップロードできているのに、選択してCKEditorに戻ると画像のパスがおかしくなっていて、画像が壊れて表示されるという現象にあいました。

正常に動作する他のサーバーと色々と比較してみたところ、$_SERVER[‘DOCUMENT_ROOT’] に返されるパスが、自サイトのサイトTOPと全く異なるパスを返していることが判明。

そのため、KCFinderが相対パス→絶対パスに変換しようとDOCUMENT_ROOTと比較したところで失敗していた。(つまりuploadURLやuploadDirに相対パスを指定したときだけ発生する問題)

gmoserverでは実際のサイトROOTパスは、$_SERVER[‘HOME’] にセットされているようなので、KCFinderのプログラムファイルを全検索して$_SERVER[‘DOCUMENT_ROOT’] → $_SERVER[‘HOME’] に書き換えてOK

時間の無いところで、思わぬワナに嵌ってしまった。

他のプログラムでもDOCUMENT_ROOTに依存しているコーディングは要注意ですよ。

Linuxで最近更新されたファイルをfindしてtar圧縮

サーバーから最近更新されたファイルだけをコピーして来たいという場面があったのですが、sshはできるけど、rsyncは使えない。FTPで覗こうにも同一ディレクトリにファイルが多すぎて処理できない。

そこでfindコマンドを使って更新日でファイルを洗い出し、それをtarで圧縮したので、また使う機会のためにコマンドをメモしておきます。

●カレントフォルダ配下の過去12日間に更新されたファイル一覧
find . -type f -mtime -12 -ls
過去12日間に更新されたファイルだけをtarで圧縮
find . -type f -mtime -12 | xargs tar zcvf 20160622.tar.gz

Excel 2013が重い

最近、エクセルで文字を入れ始めた瞬間にHDDがガリガリとアクセスしたまま暫くパソコンが固まって進まないという現象に悩まされていた。
急いでるときに非常に困る!!

ネットで調べてみると、どうもエクセルのキャッシュファイルの破損らしい。

エクセルを一旦終わらせておいて、
C:\Users\(ユーザ名)\AppData\Roaming\Microsoft\Excel\Excel15.xlb
というファイルを別名にリネームしてからエクセルを起動すれば、サクサクになった。

記事を書かれた人に感謝!!

参考:
http://d.hatena.ne.jp/scientre/20131011/make_excel_faster

JQueryで、cloneしたinputにdatepickerを設定する

jqueryのclone関数でinputフィールドを増やしてから、datepickerを設定しようとしても新しく追加したフィールドで日付入力が機能しない。(もちろんIDはcloneしたときに一意になるように設定済み)

$(‘#datepickup’+count).removeClass(“hasDatepicker”);
$(‘#datepickup’+count).datepicker();
とやればOK

どうやら、hasDatepicker というクラスが追加されたままだと既にdatepickerをbind済みのフィールドとみなされて、新しいdatepickerがbindされないぽい。

EC-CUBE 2系の「もっと見る」を簡潔に

今更ながら、EC-CUBE 2系のスマホ版のデザイン変更で苦労しました。

EC-CUBEに組み込まれている「もっと見る」ボタンですが、Ajaxで次ページのjsonデータを取得してきて画像、商品名、価格等のspanやimgタグの中身を1個ずつコピーしている。。。(3系でも同じなのかな??)

これでは、つぶしが気かないったらありゃしないので、デザイン変更しても動作するJavaScriptに書き換えてみました。jsonではなく2ページ目のHTMLをそのままGETしてきて商品一覧部分のタグをそのまま現在の一覧の下に差し込むだけ。

・総HIT数が<span id=”productscount”>XXX</span>に書かれていること。
・もっと見るボタンにid=”btn_more_product”が付いていること。
・商品リストの個別商品が class=”list_area” に入っていて、各商品ブロックが<form>で囲まれていること。
(<form>で囲まれていないなら、2ヶ所の .closest(‘form’) は不要)

の3点だけ守られていれば、デザイン変更しても大丈夫です。

<script>
    var pageNo = 2;
    var url = "<!--{$smarty.const.P_DETAIL_URLPATH}-->";
    var imagePath = "<!--{$smarty.const.IMAGE_SAVE_URLPATH|sfTrimURL}-->/";
    var statusImagePath = "<!--{$TPL_URLPATH}-->";
	
    function getProducts(limit) {
        $.mobile.showPageLoadingMsg();
        var i = limit;
        //送信データを準備
        var postData = {};
        $('#form1').find(':input').each(function(){
            postData[$(this).attr('name')] = $(this).val();
        });
        postData["mode"] = "html";
        postData["pageno"] = pageNo;

        // デザイン変更に左右されない「もっと見る」処理に書き換えました。
        $.ajax({
            type: "POST",
            data: postData,
            url: "<!--{$smarty.const.ROOT_URLPATH}-->products/list.php",
            cache: false,
            dataType: "html",
            error: function(XMLHttpRequest, textStatus, errorThrown){
                alert(textStatus);
                $.mobile.hidePageLoadingMsg();
            },
            success: function(result){
                $('.list_area:last').closest('form').after($(result).find('.list_area').closest('form').clone(true));
                pageNo++;

                //すべての商品を表示したか判定
                if (parseInt($("#productscount").text()) <= $(".list_area").length) {
                    $("#btn_more_product").hide();
                }
                $.mobile.hidePageLoadingMsg();
            }
        });
    }
</script>

Windows7でフォントが消えた

WindowsUpdateを繰り返した結果か、それともいろいろなアプリをインストール&削除を繰り返した結果か、ある朝気がつくと、いつも使っているメーラー画面の表示フォントが文字化けしてしまっていました。

???

化けているフォントは何だ?と思い、メーラーのフォント設定画面を開いてみると、選択されているフォントは「メイリオ」
Windows7の標準搭載のフォントです。それならば、再度指定しなおしてみたら治るかもしれないと思い、フォント選択ボックスを開いてみましたが、MSゴシック、MS明朝 ・・などはあるけどフォント一覧に「メイリオ」が無い!!え?!
念のため、コントロールパネルからフォント一覧を見てもやっぱり無い。

困った!!

何かの拍子に間違ってフォント自体が削除されてしまったのか?!と思い、ネット検索した情報をもとに、マイクロソフトの技術情報からメイリオフォントをダウンロードしてみるが、インストールしてみようとすると「すでにメイリオはインストールされています」と出るだけで復活できない。

うーん、物理的なファイルは存在してるけど、見えてないだけかも知れない。

じゃあフォントキャッシュを削除すれば良いのか?と、セーフモードで立ち上げて、コマンドプロンプトから
「DEL %windir%\system32\FNTCACHE.DAT」と入力して再起動・・・それでも出てこない。

こまった。見えない物を相手に出来ない。。

そうだ!こういうときはコマンドプロンプトで実ファイルの存在を確認しよう!
「DIR C:\Windows\fonts\ME*」とやると meiryo.ttc meiryob.ttc は有るじゃない!(メイリオって明瞭の意味だったのか?)

じゃあ、壊れてないか表示してみよう。エクスプローラー画面からは見えないのだから、直接シェルコマンドでファイルを開いてみる。
「START C:\Windows\fonts\meiryo.ttc」

。。。おおお!サンプル文字一覧はちゃんときれいに出るじゃないですか。
よしよし。。。ん?「インストール」ボタンがここにもある。。。

ポチッ。。。。「インストールされました」やった!復活!長かったー(笑)

二度押しで選択解除できるラジオボタン

解除できるラジオボタンを設置したいときもあります。

解除ボタンを追加して押させるのもスマートじゃないので、チェック済のラジオボタンを押したら選択解除できるようにしてみます。

ネットで情報を探すと苦戦している人も多いけど、チェックされてる値を記憶しておいて、同じラジオボタンが押されたら解除するだけ、意外と簡単です。
JQueryで書いてみましたが、別に素のJavaScriptでも簡単だと思います。






<script>
$(function(){
    var nowchecked = $('input[name=xxxx]:checked').val();
    $('input[name=xxxx]').click(function(){
        if($(this).val() == nowchecked) {
            $(this).prop('checked', false);
            nowchecked = false;
        } else {
            nowchecked = $(this).val();
        }
    });
});
</script>

JQueryで「もっと見る」を簡単に実装

「もっと見る」を実現するjavascriptコードはいくつかありますが、一番使えると思ったのが、JQuery.autopagerです。

導入方法など、詳しくはこちらの記事を参考にしてください。
http://wispyon.com/jquery-autopager/

このプラグインの何が良いかというと、
「次ページへ」というリンクでページングしているプログラムなら、抽出編集プログラム側を全く手直しせずに、次ページへのリンクタグを置き換える形でAjaxの「もっと見る」を簡単に実装する事ができるのです。

仕組みはメインとなる一覧部分全体をdivで囲ってどこが一覧データなのかを明確にしておき、「次ページへ」のリンクタグをパラメータに指定してあげると、次ページへのAタグのhrefを抜き出して、次ページのHTMLをAjaxで読み込んできて、上記の一覧部分のDIVタグの中身を現在表示している一覧の下に追加します。

既に動作している「次ページへ」のアンカーをそのまま利用して、既存の次ページへタグ自体は非表示にして置き換えるという至れり尽くせりな仕様です。

スクリプト自体は配布終了しているようですが、プログラム側の処理ロジックを変えずインターフェースを差替えられる物は他に見当たらないので是非今後とも使っていきたいです。

最近は jquery.infinitescroll というのが人気なようですが、そちらはうまく動かせない人も要るようなので要注意ですね。