Archive for the ‘PHP’ Category

baserCMSの認証をカスタマイズ(WordPressのアカウントでログイン)

baserCMSのバージョンは2.0.1です。

現在弊社のサイトはbaserCMSとWordPress(スタッフブログ)が同居しています。

アカウントの管理を統一したかったのでbaserCMSにWordPressのアカウントでログイン可能なようにbaserCMSの認証をカスタマイズしました(先にWordPressを運用していたのでスタッフのアカウントがWordPressにありますので)。

次のような仕様にしました。

  • WordPressのアカウントでbaserCMSにログインした場合、事前に作成したstaffアカウントとしての操作になる。
  • 公開ページで表示される名前(氏名)はstaffとする。
  • ログイン中、氏名を「staff(WordPressアカウント)」とする。
  • 誰が操作したかの記録は欲しいのでダッシュボードの「最近の動き」に by 氏名 と付加して記録する。

 

baserCMSの認証はbcAuthComponent(baser/controllers/components/bc_auth.php)に切り出してありますので、このファイルをappディレクトリの同一階層にコピーしてそのファイルを修正します。元のbcAuthComponentは携帯の簡単ログイン対応になっていたのですが携帯でのログインは必要なかったので全部削除して書き直しって感じになりました。
app/controllers/components/bc_auth.php

App::import('Component', 'Auth', 'Sanitize');
class BcAuthComponent extends AuthComponent {
//WordPressアカウントでstaffとしてログイン murave
function login($data = null) {
$this->__setDefaults();
$this->_loggedIn = false;

$wordpress_id = null;

if (empty($data)) {
$data = $this->data;
}

$model =& $this->getModel();
$alias = $model->alias;
$cnt = $model->find('count', 
array('conditions' => array($this->userModel . '.' . $this->fields['username'] => $data['User']['name'])));
if($cnt == 0) {//baserCMSには存在しないユーザー
if($this->_wp_CheckPassword($_POST['data']['User']['name'], $_POST['data']['User']['password'])) {
$wordpress_id = $_POST['data']['User']['name'];
$staff_record = $model->find('first',
array('conditions' => array($this->userModel . '.' . $this->fields['username'] => 'staff')));

$username = $staff_record[$this->userModel][$this->fields['username']];
$password = $staff_record[$this->userModel][$this->fields['password']];
$data = array(
$this->userModel . '.' . $this->fields['username'] => $username,
$this->userModel . '.' . $this->fields['password'] => $password
);
}
}

if ($user = $this->identify($data)) {
if(!is_null($wordpress_id)) {
$user['real_name_2'] = '('.$wordpress_id.')';
}
$this->Session->write($this->sessionKey, $user);
$this->_loggedIn = true;
}
return $this->_loggedIn;

}

/**
 * WordPressユーザー認証を行う。
 */
private function _wp_CheckPassword($wordpress_id, $password) {
//WordPressの関数を使って認証を行ない結果をtrue/falseで返す処理です。
}

}

_wp_CheckPasswordがWordPressに認証させる処理で「肝心な所」ではあるのですが、環境によって異なるはずなので申し訳ありませんが割愛しています。
baserCMSにアカウントが無かったらWordPressに認証させて、WordPressで認証に成功したらstaffというアカウントでbaserCMS側は認証、その場合には $user[‘real_name_2’] = ‘(‘.$wordpress_id.’)’ とreal_name_2に設定してからセッションに書き込んでいます。real_name_1はstaffにしているので操作中の氏名は「staff(WordPressアカウント)」となります。

次に「最近の動き」への記録ですがBaserAppModelのsaveDbLogメソッドで行われていますのでこのメソッドの動作を変更します。baser/models/app_model.phpをappディレクトリの同一階層にコピーしてメソッドを定義すれば上書きできます。

app/models/app_model.php

class AppModel extends BaserAppModel {
function saveDbLog($message) {
// ログを記録する
App::import('Model', 'Dblog');
$Dblog = new Dblog();
$logdata['Dblog']['name'] = $message
.' by '.@$_SESSION['Auth']['User']['real_name_1'].@$_SESSION['Auth']['User']['real_name_2'];
$logdata['Dblog']['user_id'] = @$_SESSION['Auth']['User']['id'];
return $Dblog->save($logdata);
}
}

これで当初予定していたカスタマイズが一通り完了しました。baserCMSはカスタマイズしやすくていいですね。

Facebooktwitterlinkedintumblrmail

baserCMS 2系で追加されたテーマヘルパーが便利(だけど要注意)

本日の「忘れないうちに書いとくぜ!baserCMSネタ」のお時間がやってまいりました。

baserCMS 2系でテーマヘルパーが追加されました。
http://basercms.net/functions/theme_helper

弊社サイトはlancardというテーマを作ってその下で構築しています。

現在、その中で、これから追加や編集をしていく予定があり、書き方が複雑だけど規則性がある、「ホームページのピックアップ(上部の画像切替)」と「ウィジェットのバナー表示」についてテーマヘルパーを利用して更新コストを下げています。

参考に、バナー表示用のヘルパーのコードを抽出して貼ります(ピックアップについては複雑なので)。
app/webroot/themed/lancard/helpers/lancard.php

BcBaser->getUrl('/themed/lancard/img/bunner/'.$imgFile, true)
.')';
$this->BcBaser->link(
$title,
$url,
$linkAttributes
);
}
}
?>

使用方法はこんな感じ。

bunner('OSSダウンサイジング', 'downsizing.jpg', '/it-model') ?>

と書くと

OSSダウンサイジング

と出力されます。便利です。

しかし、このテーマヘルパー、注意して扱わないと大変ヒドイ目にあいます。

テーマヘルパー用のディレクトリに「正しいテーマヘルパー以外のファイル」を置くとサイトのルーティングが全滅します。管理ページもアクセス不能! 初めてこの現象に遭遇したときは超ビビリました。

ヘルパーを読み込もうとしてエラーが発生するわけですが、その影響範囲が想定の範囲外!

私がやってしまったパターン

  • テーマヘルパー開発中に動作確認しようとしたらサイトが全滅してた。
  • テーマヘルパーを更新するときに旧ファイルをlancard.php_oldとリネームしたら全滅。

 
いかがでしょうか。恐ろしいですね。

しかしこの副作用、次のチケットでかなり解決しそうですので一時的な問題だと思います。
TODO #2604 「テーマヘルパの読み込みはテーマ名のヘルパのみにする」

そうそう、ヘルパー名は上記チケットの内容にしたがってつけておきましょうね。

Facebooktwitterlinkedintumblrmail

baserCMSのサイトマップをカスタマイズ

baserCMS のバージョンは 2.0.1 です。

baserCMSにはサイトマップを出力するヘルパーがあって、

sitemap() ?>

と書くと固定ページについてはサイトマップを自動で更新してくれて便利です。

なのですが、いろいろと不満もあるヘルパーです。

  • 固定ページ以外(Blogとかメールフォーム等)はダメ。
  • カテゴリのindexにページ名をつけるとカテゴリとページで同じURLが表示されてダブる。

 

てなわけでカスタマイズしました。

baser/views/helpers/bc_baser.php を読んでヘルパーの仕組みを調べると再帰的にページをたどっていること、テンプレート baser/views/elements/sitemap.php を使用していることが分かりました。

検討した結果、「固定ページ以外はダメ」に関して根本的な解決は出来ないのですがテンプレートのカスタマイズで対応することにしました。
データの取得方法から変更した独自ヘルパーの作成も考えたのですが、結局どこかに「Blogやメールフォームを何処に表示するか」という情報を持たせなければならないので完全自動化はできない。それならデータ取得は既存の方法を使って必要な追加設定はテンプレート内で配列にでももたせるか、と。

まず、baserCMS カスタマイズの常道としてbaserディレクトリのファイルを自分のテーマの同階層にコピー。これでコピーしたファイルが有効になるのでこのファイルを改造していきます。

改造した結果、こうなっちゃいました。

//ここから設定
//表示しないurl
$disables = array(
'/index_test',
);

//$inserts = array(before_url=>array(title, url),,,)
//before_urlの次に挿入url,titleで作成したアイテムを挿入
//ブログ、メールフォームなどpage以外を途中に挿入することが出来る。
$inserts = array(
'/sitemap' => array('新着情報', '/news'),
'/news' => array('お問い合わせ', '/contact'),
'/it-model/faq' => array('ダウンサイジングについてのお問い合わせ', '/contact_itmodel'),
);
//設定ここまで

//関数
$outputPageItem = function ($recursive, $title, $url) use (&$bcBaser) {
?>
  • link($title, $url) ?>
  • link($title, $url); else: echo $category_title; endif; echo PHP_EOL; }; $outputCategoryItemTail = function () { ?>
    • element('sitemap', array( 'pageList' => $pageCategories['children'], 'category_title' => $category_title, 'category_url' => $category_url, 'recursive' => $recursive+1 )); endif; if($outputed_category_li): $outputCategoryItemTail(); endif; endforeach; endif; ?>

    「カテゴリのindexにページ名をつけるとカテゴリとページで同じURLが表示されてダブる」のは解消。
    追加設定に特定ページを表示したくない時のための$disables配列、Blogやメールフォームを思い通りの場所に挿入するための$inserts配列を用意することで希望する表示ができるようになりました。

    しかし、元テンプレートからかけ離れているのはともかくとして、



    てなことをやってるので万人にはオススメできない。無名関数を使っているのでPHP5.3以降限定ですし。

    関数を定義したのは「処理をパーツ化しないとコードがあまりにもカオス」「処理を再帰呼び出しする必要があった」からです。
    無名関数を使ったのは「関数の影響範囲を限定したかった」から。テンプレートが複数回呼び出しされるので関数の再定義エラーがでるという問題もありましたが、これは無名関数にしなくても未定義の場合のみ関数を定義するようにすれば回避できると思います(試してませんが)。

    Facebooktwitterlinkedintumblrmail

    baserCMSのfeedプラグインで既存フィードを合成したフィードを作成

    baserCMSでリニューアルした弊社サイトですが、このスタッフブログはWordPressのまま残しました。

    そうなるとサイトのフィードはbaserCMSで運用している新着情報とWordPressで運用しているスタッフブログの両方の情報が入っていて欲しい。「両方のデータベースからデータさらってきてフィードを作るプログラムでも書くかなー」などと調査したりしていたのですが、気づきました。

    「feedプラグインで合成表示は出来るんだからRSSのフォーマットで出せばいいんじゃね?」

    てなわけで、現在弊社のフィード(RSS 2.0) http://www.lancard.com/rss はfeedプラグインでbaserCMSのBlogのフィードとWordPressのフィードを合成して作成しています。

    どうやったかといいますと、
    feedプラグインで合成したフィードを作成。テンプレートについては後ほど。

    フィードのキャッシュ期間ですが、baserCMSのBlogについては更新時にフィードのキャッシュはクリアされるはずなのでとりあえず1日(※2013.7.27追記:公開日時の予約をすると公開時にはキャッシュクリアされないので1時間に変更しました)、外部のスタッフブログについては長くキャッシュされるとまずいので1分にしています。

    RSS 2.0 用 のレイアウト作成(ただし、WordPressに合わせて古い項目を追加してます)。
    $this->cacheAction = false とすることでサーバーキャッシュは無効化。

    テーマ名/layouts/rss2.php

    cacheAction = false;
    echo "\n";
    ?>
    
    
    Lancard.com
    http://www.lancard.com/
    Lancard.com 新着情報とスタッフブログのRSS(RSS 2.0)
    feed(3); ?>
    
    
    

    ITEM作成用に作成したfeedのテンプレートはこんな感じ。
    テーマ名/feed/rss2item.php

    
    
    
     $item): ?>
    
    <![CDATA[<?php echo $item['title']['value']; ?>]]>
    
    
    name; ?>]]>
    
    ]]>
    ]]>
    
    
    
    

    そして固定ページ管理でフィード用のページを作成。

    layout = 'rss2'; ?>
    

    以上です。もっといい方法がある気も。

    Facebooktwitterlinkedintumblrmail

    baserCMSで作成したサイトの公開時にルーティングでハマった話

    ぶっちゃけ、弊社サイトリニューアルでの話です(baserCMS 2.0.1)。

    baserCMSが2系にバージョンアップしたので「まってました!」とサイトをリニューアルしました。
    さすがは「コーポレイトサイトにちょうどいい」CMS。作成はサクサクでした。

    なのですが公開時に謎のルーティング不具合でハマって「ギニャー!」

    どんな症状かといいますと、作成中は表示出来ていたページがなぜか

    404 NOT FOUND
    
    Error : The requested address '/recruit' was not found on this server.
    

    app/config/routes.php に直接

    Router::connect('/recruit', array('controller' => 'pages', 'action' => 'display', 'recruit'));
    

    と追加すると表示される。どう見てもルーティングがおかしい。

    baser/config/routes.php の処理を追っかけてみたりして判明したのはrecruitと渡ってきてほしいurlがrecruit/と末尾に/がついて渡って来ているということ。

    公開前までは大丈夫だったのになんでだー?

    結論は「apacheのwebrootに旧コンテンツのrecruitディレクトリを残していたためにbaserCMSに渡される前に/をつけられていた」のでした。

    .htaccessでbaserCMSに制御をすべて渡すように設定したので、影響でるとは思いもよらず、旧コンテンツを残していたのが不幸の始まり。みなさんもお気をつけください。

    ボクはこれで6時間程ハマってました。ネムイヨ。

    Facebooktwitterlinkedintumblrmail

    31歳でPHPUnitを使った僕

    31歳でPHPUnitを使った僕

    プロローグ
    これまでPHPUnitを使ってこなかったけど、少し心配になる部分があったので
    PHPUnitを使ってみることにした。
    ——————————————————————
    第一章『インストール』
    インストールは
    ここ
    を読んでやりました。

    ——————————————————————
    第二章『テスト対象のファイル』
    Calc.php

    
    

    ------------------------------------------------------------------
    第三章『コマンドの実行』
    このコマンドを実行する。
    「phpunit --skeleton-test Calc」

    ------------------------------------------------------------------
    第四章『テストファイル』
    このファイルは第三章のコマンドを実行すると自動的に作られます。
    (よく出来てるな~。)
    CalcTest.php

    class CalcTest extends PHPUnit_Framework_TestCase
    {
        /**
         * @var Calc
         */
        protected $object;
    
        /**
         * Sets up the fixture, for example, opens a network connection.
         * This method is called before a test is executed.
         */
        protected function setUp()
        {
            $this->object = new Calc;
        }
    
        /**
         * Tears down the fixture, for example, closes a network connection.
         * This method is called after a test is executed.
         */
        protected function tearDown()
        {
        }
    
        /**
         * @covers Calc::add
         * @todo Implement testAdd().
         */
        public function testAdd()
        {
            // Remove the following lines when you implement this test.
            $this->markTestIncomplete(
              'This test has not been implemented yet.'
            );
        }
    }
    

    ------------------------------------------------------------------
    第五章『書き換える』
    testAddメソッドを次のように書き換える。

    /**
     * @covers Calc::add
     * @todo Implement testAdd().
     */
    public function testAdd()
    {
        $this->assertEquals(3, $this->object->add(1, 2));
    }
    

    ------------------------------------------------------------------
    第六章『実行する』
    このコマンドを実行する。
    「phpunit CalcTest」
    PHPUnit 3.6.10 by Sebastian Bergmann.

    .

    Time: 1 second, Memory: 4.00Mb

    OK (1 test, 1 assertion)

    ------------------------------------------------------------------
    第七章『Eclipseから実行したい。』
    MakeGoodというのを使えばいいらしいです。

    ------------------------------------------------------------------
    第八章『MakeGoodのインストール』
    ①ヘルプ→新規ソフトウェアのインストール

    ②追加ボタンを押します。

    ③次のように入力します。
    名前:「MakeGood」
    ロケーション:「http://eclipse.piece-framework.com/」

    ④以下のようにMakeGoodにチェックをいれます。

    ⑤あとは使用条件に同意出来ればOKを押してインストール完了です。

    ------------------------------------------------------------------
    第九章『MakeGoodの設定』
    ①Eclipseの環境設定を開く。

    ②PHPの実行可能ファイルの設定を開き、追加ボタンをクリックします。

    ③赤枠部分を環境に応じて入力します。

    ④PHPのデバッグの設定をします。
    以下の赤枠部分を環境に応じて選択します。

    ------------------------------------------------------------------
    第十章『MakeGoodのプロジェクトの設定』
    ①プロジェクトを右クリックしてプロパティをクリックします。
    ②インクルードパスを設定します。
     ライブラリーの追加をクリックします。

    ③次へをクリック

    ④新規ボタンからダイアログをだして「PHPUnit」と入力

    ⑤PHPUnitが選択されている状態で外部フォルダーの追加をクリックし、
    PHPUnitがインストールされているフォルダを選択

    このような状態になります。

    ⑦PHPUnitにチェックをつけて完了をクリック

    ⑧「ソース」の項目をクリックして「システムインクルードパスの追加ボタン」をクリックします。

    ⑨プロパティの「MakeGood」の項目を開き、下にある「追加ボタン」をクリックし、テスト対象のフォルダを追加します。

    このようにチェックします。

    ------------------------------------------------------------------
    最終章『実際に使ってみる』
    ①テストプログラムを右クリックしてメニューを出し、「テストの実行」メニューをクリックします。

    ②ビューにこのように結果が表示されます。

    ------------------------------------------------------------------
    エピローグ
    物語風なのにまったく物語がなかったけど。
    やっぱりテストを書く気になれる環境があるのはいいことかな~と思う。

    Facebooktwitterlinkedintumblrmail

    メモ:CS-Cart勉強会@福岡 〜デザインされたHTMLをテンプレートにする方法〜

    CS-Cart勉強会@福岡 〜デザインされたHTMLをテンプレートにする方法〜 に来てます。

    デザインしてもらったファイルに合わせてCS-Cartをカスタマイズする方法の勉強会。
    落ちこぼれ気味でしたが、理解できた範囲でメモを残しておきます。

    編集するファイルの場所を局所化するためにデザイナーさんから受け取ったCSS等を

    cs-cart/skins/basic/customer/addons/localization_jp/

    にコピーしてその下で作業するのがお勧めとのこと(cs-cartは設置したディレクトリ)。

    買い物画面のCSSは

    cs-cart/skins/basic/customer/styles.css

    別名でバックアップした後に、これも編集ファイルを局所化するために

    @import url(‘styles.base.css’);

    よりしたのインポート文以外のコードを

    cs-cart/skins/basic/customer/addons/localization_jp/styles-css.css

    などの別ファイルに分離して、cs-cart/skins/basic/customer/styles.css にて

    @import url(‘addons/localization_jp/styles-css.css’);

    とインポートするようにする。

    以降、CS-CartのCSSの編集はstyles-css.cssに対して行う。

    デザイナーが作ってくれたCSSファイルを追加するにはフックの

    cs-cart/skins/basic/customer/addons/localization_jp/hooks/index/styles.post.tpl

    にて

    のように追加して読み込むようにする。

    フックを修正したらキャッシュをクリアする。
    方法は下記のように&ccを付けて管理画面にアクセス。

    http://localhost:8888/cs-cart/admin.php?cc

    編集したのに反映されないなぁって時はまずはキャッシュをクリアしてみる。

    以降はテンプレート(拡張子tpl)やCSSを地道に編集して合わせていく。

    以前は「テンプレートはなるべく編集しないほうがいいですよ」って方針だった
    そうですがガシガシ編集していっても結構大丈夫とのことでした。

    あと、Smartyの知識はあったほうがいいとのこと。

    CS-Cart、素性はよさそうです。

    Facebooktwitterlinkedintumblrmail

    恥ずかしながら PHP + MySQL + シフトJIS でハマりました

    「今更こんなことやっちゃうとはなぁ」というたぐいの話です。

    mysql_query(“SET NAMES ‘CP932′”, $conn);

    とかやっちゃうとMySQLには文字コードを教えてるけどPHPには伝わらないので

    mysql_set_charset(‘CP932’);

    としてPHPにも教えてあげなさいとバグるぜって話。セキュリティホールにもなります。

    シフトJISには文字コード中に\(5C)が含まれる文字があるのでPHPさんがUTF8気分で mysql_real_escape_string すると\が挿入されてバグるわけです。

    昔、Rails関係でも SET NAMES ダメ絶対って話を読んだような。UTF8ばかり使っててボケたかね。

    詳しくは『SET NAMESが危険な理由のおさらい』に簡潔にまとめてくださってるのでそちらをどうぞ。

    Facebooktwitterlinkedintumblrmail

    PHPでXSLTを使ってみる

    今回はPHPでXSLTを使ってみました。

    1)XMLファイル「list.xml」

    
    
    
    2
    山田
    沖縄
    
    
    3
    田中
    長崎
    
    
    5
    佐藤
    北海道
    
    
    1
    斉藤
    東京
    
    
    

    2)XSLTファイル「table.xsl」

    
    
    
    
    
    list
    
    

    3)PHPファイル「xlst_process.php」

    load('table.xsl');
    $processor->importStylesheet($xsl);
    
    $xml = new DOMDocument;
    $xml->load('list.xml');
    
    $result = $processor->transformToXml($xml);
    
    echo $result;
    

    4)結果

    5)感想
    これはすごく簡単に出来ていい!

    Facebooktwitterlinkedintumblrmail

    PHPでbundlerぽいことを行うOnion、Composer

    pear.phpunit.de/PHPUnitを依存関係として設定する前提で、OnionComposerを使ってみました。

    Onion

    PEARモジュールのビルド、コンパイル、そして今回の目的であるbundler的なことを簡単に行えます。

    インストール

    homebrewの場合

    gistにFormulaを置きましたので使ってください。

    # brew install --HEAD https://raw.github.com/gist/1594321/6769fa97864462c4813f06bbb08760630ba7c87f/onion.rb
    brew install --HEAD https://raw.github.com/gist/1594321/onion.rb
    # 2012/02/07 修正 実行ファイルがonion.pharからonionに変更になりましたので、Formulaを修正しました。
    
    直接

    onion.pharをパスの通ったディレクトリに置いて、実行権限を与えるだけ。

    # curl https://github.com/c9s/Onion/raw/master/onion > ~/bin/onion 
    curl -s http://install.onionphp.org/ | sh
    # 2012/02/07 修正 インストール方法が変更されてました。
    

    使用方法

    onion_sample
    |– vendor
    |– package.ini

    プロジェクト直下にpackage.iniを作成し、ここに依存関係などを記述します。
    packageセクションにあるname、desc、version、authorは必須となっています。(そもそもOnionはPEARパッケージを作る際にも使うためだと思われます。) requireセクションに使用したいPEARパッケージを記述します。 上記の状態で下記コマンドを実行することで、vendorディレクトリにPEARパッケージがインストールされます。

    $ onion -d bundle

    いやー、インストールから実行まで非常に手軽。この手軽さが素晴らしいです。

    Composer

    こちらもbundler的な事を行えるのですが、Onionよりもその依存関係部分に注力したパッケージマネージャといった感じです。 PEARだけに限らず、gitのリポジトリを依存関係として指定したりもできます。ただその辺りはあまり調べていないので、今回はPEARパッケージのみ使う前提で試して見ました。

    インストール

    brew install --HEAD https://raw.github.com/gist/1574469/composer.rb

    使用方法

    composer_sample
    |– vendor
    |– composer.json

    プロジェクト直下にcomposer.jsonを作成し、ここに依存関係などを記述します。

    上記の状態で下記コマンドを実行することで、vendorディレクトリにPEARパッケージがインストールされます。

    $ composer.phar install

    ComposerはPEARモジュール以外も扱うためか、PEARモジュールそのものを扱うのに設定ファイルが少々複雑になります。
    今回はpear.phpunit.de/PHPUnitを依存関係として設定していますが、まずそのためにrepositoriesにチャンネルを設定する必要があります。そこまではいいのですが、PHPUnitがpear.symfony-project.com/YAMLに依存しているので、そのチャンネルも記述しておく必要があります。つまり依存関係にある全てのチャンネルを設定しておく必要があるみたいです。これどうにかならないのでしょうか。

    雑感としてはPEARモジュールさえ扱えればいいのであれば、Onionの方が簡単だと思いました。対してComposerはPEARモジュール以外にも使えるので、今後に期待できるのはComposerだと思いますが、現状は用途に合わせてといったところでしょう。また気になる動きとして、OnionのGithubリポジトリにcomposer-supportというブランチがあるのが楽しみです。

    Facebooktwitterlinkedintumblrmail