画像やリンクをクリックしてファイル選択できるようにするjQueryプラグインをつくった

通常、fileタイプのinput要素をクリックしたときのみファイルを選択することができますが、
画像や文字をクリックしたときにもファイル選択ダイアログを表示し、ファイルをアップロードできるようにします。


簡単にいうと、AjaxUploadのファイル選択部分と同様の処理をプラグイン化したもの。
要素の上に透明なinput要素を作成することで、擬似的にファイル選択を行います。

サンプル

画像や文字列・リンクなどの好きな要素を指定しfileメソッドをコール。
引数の一番目にinput要素のname属性の値、2番目にファイルが選択された際のコールバック関数を指定可能。
コールバック関数はEventオブジェクトとinput要素を引数にとります。

$('#id').file('name', function(event, input) {
    // ファイルが選択されたときの処理
    // event: Eventオブジェクト
    // input: Input要素

    alert($(input).val()); // ファイル名を表示
});

ソース

(function($) {
    $.fn.file = function(name, callback) {
        var self = this,
      // 透明なinput要素を作成
            input = $('<input type="file" name="' + name + '" style="margin:-5px 0 0 -160px; padding:0; position:absolute; z-index:99999;"/>').css('opacity', 0);
            
        this.append(input).mouseenter(function(event) {
      // input要素を対象要素の上に移動
            move(event.pageX, event.pageY);
      // 表示(透明なまま)
            input.show();
        });

        input.mousemove(function(event) {
            var position =  self.position();

            move(event.pageX, event.pageY);

            if (event.pageY < position.top || 
                event.pageY > position.top + self.height() || 
                event.pageX < position.left ||
                event.pageX > position.left + self.width()) {
        // マウスが対象要素から外れたら非表示
                $(this).hide();
            }
        }).change(function(event) {
            if (callback) {
        // changeイベントのコールバック関数呼び出し
                callback.call(self.get(0), event, this);
            }
        });

        function move(x, y) {
            input.css({left:x, top:y});
        }
    };
})(jQuery);

シンプルにAjaxファイルアップロードできるjQueryプラグイン

Ajaxライクに非同期でファイルをアップロードできるjQueryプラグインつくりました。
同様のプラグインやライブラリはいくつかありますが、Flashを使用しないシンプルな実装で
jQueryのスタイルを踏襲しているものがなかった(要は好みじゃなかった)ので再実装しました。


デモ
ダウンロード

サンプル

ファイルの選択と同時にそのファイルをアップロードする場合は下記のようになります。

$('input[type=file]').change(function() {
    $(this).upload('/action/to/upload', function(res) {
        // アップロード完了時の処理
    }, 'json');
});

upload(url, [data], [callback], [type])

  • url: アップロード先のURL
  • data: 追加で送信するパラメータ
  • callback: アップロード完了時に実行されるコールバック関数
  • type: コールバック関数で受け取るレスポンスのデータタイプ


dataが必要ない場合は、upload(url, callback, type)という形式で引数をとることもできます。


詳しくは、http://lagoscript.org/jquery/uploadを参照してください。

jQuery.autopager1.0.0をリリースしました

スクロールで次ページを自動読み込みするjQueryプラグイン v1.0.0をリリースしました。
全バージョンから全面的に書き直し、クリック等をトリガーにした読込みにも対応させました。


デモ

サンプル

自動ページング機能を有効にするには、次ページへのリンクが存在するページに以下のようなコードを追加します。

$.autopager({
    link   : '#next',   // 次ページリンクのセレクタ
    content: '.content' // コンテンツ部分のセレクタ 
});


ページ区切りは、次ページのコンテンツ読み込み完了直後に発生するloadイベントのコールバック関数で挿入できます。

$.autopager({
    load: function() {
        // "this"は読み込んだコンテンツ要素を指す
        $(this).before('<hr/>');
    }
});


詳しくは、http://lagoscript.org/jquery/autopagerを参照してください。

CakePHPが起動されてからdispatchするまで

app/webroot/.htaccess

リクエストされたURLをクエリに付与してindex.phpを呼び出す形式に書き換え

RewriteCond %{REQUEST_FILENAME} !-d         # ディレクトリが存在しない場合
RewriteCond %{REQUEST_FILENAME} !-f         # ファイルが存在しない場合
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] # URLをクエリに付与してindex.phpを実行
  • QSA=リクエストされたクエリ文字列も付与する

app/webroot/index.php

基本的なパス等の定数やインクルードパス等の設定を行い、Dispatcher::dispatch()をコール。
コアの設定はcake/bootstrap.phpをincludeすることで行われる。

<?php
    // 略(アプリ別の基本的なパス等の定数定義)

    if (!defined('CORE_PATH')) {
        // ini_setが使用できる場合は、include_pathを設定する
        if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) {
            // 相対パスでincludeできるのでnull
            define('APP_PATH', null);
            define('CORE_PATH', null);
        } else {
            // 絶対パスでファイルをincludeできるようにパスを定義
            define('APP_PATH', ROOT . DS . APP_DIR . DS);
            define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
        }
    }
    // cake/bootstrap.phpを読み込むことでCakephpのコアの設定を行う
    if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
        trigger_error("CakePHP core could not be found.  Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php.  It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
    }
    if (isset($_GET['url']) && $_GET['url'] === 'favicon.ico') {
        return;
    } else {
        // dispatchする
        $Dispatcher = new Dispatcher();
        $Dispatcher->dispatch($url);
    }
    if (Configure::read() > 0) {
        // debugレベルが1以上の場合にアプリ実行にかかった時間を表示する
        echo "<!-- " . round(getMicrotime() - $TIME_START, 4) . "s -->";
    }

cake/bootstrap.php

コアファイルを読み込み、Configure::getInstance()をコール。

<?php
    // $bootstrapはmod_rewriteや.htaccessが使えない場合に、
    // cakeディレクトリと並列のindex.phpで定義される
    if (!isset($bootstrap)) {
        // a()、h()、env()などのグローバルな関数定義の読込み
        require CORE_PATH . 'cake' . DS . 'basics.php';
        // 起動開始時間を保持
        $TIME_START = getMicrotime();
        // 各種パスの定数定義読込み
        require CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php';
        // 各種クラスの読込み
        require LIBS . 'object.php';
        require LIBS . 'inflector.php';
        require LIBS . 'configure.php';
    }
    require LIBS . 'cache.php';

    Configure::getInstance(); // コアの設定を行う 

    $url = null; // index.phpでDispatcher::dispatch()に渡す引数

    App::import('Core', array('Dispatcher'));

Configure::getInstance()

getInstance()は名前通り、唯一のインスタンスを生成・保持して返すメソッド。
__loadBootstrap()でapp以下のbootstrapの読込みと各種設定を行う。
なぜ配列にインスタンスを格納しているかはわかりません。

<?php
class Configure extends Object {
    function &getInstance($boot = true) {
        static $instance = array();
        if (!$instance) {
            $instance[0] =& new Configure(); // 唯一のインスタンス 
            $instance[0]->__loadBootstrap($boot);
        }
        return $instance[0];
    }
}

Configure::__loadBootstrap()

bootstrap.phpやcore.phpの読込みとキャッシュ、タイプ別のパス設定を行う

<?php
class Configure extends Object {
    function __loadBootstrap($boot) {
        // 下記のbootstrap.php読込みで上書きできる値
        $modelPaths = $behaviorPaths = $controllerPaths = $componentPaths = $viewPaths = $helperPaths = $pluginPaths = $vendorPaths = $localePaths = $shellPaths = null;

        if ($boot) {
            Configure::write('App', array('base' => false, 'baseUrl' => false, 'dir' => APP_DIR, 'webroot' => WEBROOT_DIR));

            // app/config/core.php読込み
            // セキュリティ、セッション、キャッシュなどの設定
            if (!include(CONFIGS . 'core.php')) {
                trigger_error(sprintf(__("Can't find application core file. Please create %score.php, and make sure it is readable by PHP.", true), CONFIGS), E_USER_ERROR);
            }

            // app/config/bootstrap.php読込み
            if (!include(CONFIGS . 'bootstrap.php')) {
		trigger_error(sprintf(__("Can't find application bootstrap file. Please create %sbootstrap.php, and make sure it is readable by PHP.", true), CONFIGS), E_USER_ERROR);
            }

            if (Configure::read('Cache.disable') !== true) {
                // 略 (cache設定)
            }
            // model、view、controllerなど、タイプ別にそれぞれのincludeディレクトリを設定。
            // app/config/bootstrap.phpで、ユーザが追加できるパス設定もここで読込み
            Configure::buildPaths(compact(
                'modelPaths', 'viewPaths', 'controllerPaths', 'helperPaths', 'componentPaths',
                'behaviorPaths', 'pluginPaths', 'vendorPaths', 'localePaths', 'shellPaths'
            ));
        }
    }
}

CakePHPでsmartyを使う際、メソッドの不要な戻り値を表示しない方法

メソッドに表示したくない戻り値がある場合、それを出力しないようにするバッドノウハウ

{$session->flash()}

上記はメソッドがtrueを返すため"1"と表示されてしまう

対処方法

helperに何もしないメソッドを作って戻り値を捨てる

<?php
class AppHelper extends Helper {
    function void() {}
}
{$html->void($session->flash())}

CakePHPでユーザーIDかメールアドレスどちらでもログインできるようにする方法

認証時に、ユーザIDとパスワードなどの組み合わせだけではなく、
メールアドレスとパスワード等、複数のカラムどれでもログインできるようにする方法。

サンプル

Authコンポーネントをそのまま使用。
一方の組み合わせで認証に失敗したら、もう一度、別カラムを参照してログインを試みる。
ビューのinputタグのnameには一組目のカラム名をセットしておく。

<?php
class UsersController extends AppController {
    var $components = array('Auth');

    function beforeFilter() {
        parent::beforeFilter();

    // 一組目の参照カラム設定
        $this->Auth->fields = array(
            'username' => 'username1',
            'password' => 'password',
        );
        $this->Auth->autoRedirect = false; // 自動でリダイレクトしない

        $model = $this->Auth->userModel;
        if ($this->params['action'] === 'login' && isset($this->data[$model][$this->Auth->fields['password']])) {
            // 一度認証を行うとpasswordフィールドにNULLが設定されてしまうので退避させる
      // Configureではなく、コントローラのプロパティなどに保存してもいい
            $password = $this->Auth->password($this->data[$model][$this->Auth->fields['password']]);
            Configure::write($model . '.password', $password);
        }
    }

    function login() {
        if (!empty($this->data) {
            if ($this->Auth->user()) {
                // 一組目の認証に成功した場合はリダイレクト
                $this->redirect($this->Auth->redirect()); 
            } else {
                // 失敗した場合
                $field = 'username2';
                $data  = $this->data;
                $model = $this->Auth->userModel;

                // データを再セット
                $data[$model][$this->Auth->fields['password']] = Configure::read($model . '.password');
                $data[$model][$field] = $data[$model][$this->Auth->fields['username']];
              // 別カラムを参照するように変更
          $this->Auth->fields['username'] = $field;

                // もう一度ログインを試みる
                if ($this->Auth->login($data)) {
                    // sessionのメッセージを削除
                    $this->Session->del('Message.auth');
                    $this->redirect($this->Auth->redirect()); 
                }
            }
        }
    }
}

※8/4 一部修正


SQLが複数呼ばれることになるので、可能ならば下記ページで紹介されてるやり方のほうがいいかも。

http://cakephp.jp/modules/newbb/viewtopic.php?topic_id=1128&forum=8&post_id=2297

UbuntuにRuby、Rails、Passengerをインストール

ruby

$ sudo apt-get install ruby ruby-dev irb libopenssl-ruby

gem

$ sudo apt-get install rubygems
$ vi ~/.bashrc

# gemにパスを通す
export PATH="$PATH":/var/lib/gems/1.8/bin

$ source ~/.bashrc

rails

$ sudo gem install rails

passenger

$ sudo gem install passenger
$ sudo /var/lib/gems/1.8/bin/passenger-install-apache2-module

Checking for required software...

 * GNU C++ compiler... found at /usr/bin/g++
 * Ruby development headers... found
 * OpenSSL support for Ruby... found
 * RubyGems... found
 * Rake... found at /var/lib/gems/1.9.0/bin/rake
 * Apache 2... found at /usr/sbin/apache2
 * Apache 2 development headers... not found
 * Apache Portable Runtime (APR) development headers... not found
 * Apache Portable Runtime Utility (APU) development headers... not found
# 足りないパッケージをインストール
$ sudo apt-get install apache2-prefork-dev libapr1-dev libaprutil1-dev 
# もう一度
$ sudo /var/lib/gems/1.8/bin/passenger-install-apache2-module

# 以下のような設定方法のメッセージが表示される
Please edit your Apache configuration file, and add these lines:

   LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-2.2.4/ext/apache2/mod_passenger.so
   PassengerRoot /var/lib/gems/1.8/gems/passenger-2.2.4
   PassengerRuby /usr/bin/ruby1.8
...
Suppose you have a Ruby on Rails application in /somewhere. Add a virtual host
to your Apache configuration file, and set its DocumentRoot to
/somewhere/public, like this:

   <VirtualHost *:80>
      ServerName www.yourhost.com
      DocumentRoot /somewhere/public    # <-- be sure to point to 'public'!
   </VirtualHost>

passengerとapacheの設定

$ cd /etc/apache2/mods-available
$ sudo vi rails.load

# インストール時に指定されたとおりに書く
LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-2.2.4/ext/apache2/mod_passenger.so
$ sudo vi rails.conf

PassengerRoot /var/lib/gems/1.8/gems/passenger-2.2.4
PassengerRuby /usr/bin/ruby1.8
$ sudo vi /etc/apache2/sites-available/yoursite

<VirtualHost *:80>
   ServerName www.yourhost.com
   DocumentRoot /somewhere/public
...
# モジュールとサイトを有効にする
$ sudo a2enmod rails
$ sudo a2ensite yoursite
$ sudo /etc/init.d/apache2 restart