Archive for ‘ July, 2007

画像アップロード処理のセキュリティホールをふさぐ(CakePHP修行 #37)


さてさてCakePHP修行。青い人に指摘されたセキュリティホールを防ぎます。

そのエントリーでも言及されている『T.Teradaの日記』も読みましたが、どうやら完璧な防ぎ方はないような・・・しかし青い人の提案どおり、できるところまでやってみます。

簡単にいうと「画像形式を調べて、画像変換をかましつつ、拡張子を変えてアップ」です。

画像形式を調べるのはgetimagesizeでいいのかな。JPG、GIF、PNG以外ははじくように設定し、次のようなコードにします。

// file upload
if (!empty($this->data['User']['pic']['name'])) {
  // tmp file info
  $tmp_file = $this->data['User']['pic']['tmp_name'];
  $imginfo = getimagesize($tmp_file);
  // file error handling (file size / JPG,GIF,PNG)
  clearstatcache();
  if (filesize($tmp_file)>300000 || ($imginfo[2] < 1 || $imginfo[2] > 3)) { $this->set('file_error',true); return false; }
  // set file name
  $upload_path = WWW_ROOT . "pics" . DS;
  // set new width, height
  $width_old  = $imginfo[0];
  $height_old = $imginfo[1];
  $width_new  = PIC_WIDTH;
  $height_new = $height_old * ($width_new / $width_old);
  // create new file
  switch ($imginfo[2]) {
    case 2: // jpeg
      $filename = sprintf("%05d.jpg",$me['User']['id']);
      $jpeg = imagecreatefromjpeg($tmp_file);
      $jpeg_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($jpeg_new,$jpeg,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagejpeg($jpeg_new, $upload_path . $filename, 100);
      break;
    case 1: // gif
      $filename = sprintf("%05d.gif",$me['User']['id']);
      $gif = imagecreatefromgif($tmp_file);
      $gif_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($gif_new,$gif,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagegif($gif_new, $upload_path . $filename, 100);
      break;
    case 3: // png
      $filename = sprintf("%05d.png",$me['User']['id']);
      $png = imagecreatefrompng($tmp_file);
      $png_new = imagecreatetruecolor($width_new, $height_new);
      imagecopyresampled($png_new,$png,0,0,0,0,$width_new,$height_new,$width_old,$height_old);
      imagepng($png_new, $upload_path . $filename, 100);
      break;
    Default:
      break;
  }
  $this->data['User']['pic'] = $filename;
} else {
  $this->data['User']['pic'] = $me['User']['pic'];
}

これで一応大丈夫かな・・・拡張子も変えたし。さて、では次にいよいよお友達っぽいSNS処理に移ります。複雑なアソシエーションをつくらないといけないですね。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

いろいろ細かい調整やら修正やら(CakePHP修行 #36)


さてCakePHP修行。ひさびさに青い人に添削してもらいました。うれしす。ありがとう!

さて、それもこれも含め、次のステップに進む前に気になる部分が出てきたので細かいところを直しちゃいます。

■ Password生成関数について

「パスワードを忘れた方は」を実装したい(CakePHP修行 #22)』のコメント欄にて高田さんに次のようなコメントをいただきました。

初期パスワードを作るときに、「1」と「l(小文字のL)」や「o(小文字のO)」と「O(大文字のO)」と「0(数字の0)」のような似てる文字(フォントによって判別が難しい・・・)は適用しないようにしておくと少し幸せかもです。

おお・・・なるほどです。早速パスワード生成関数を修正しました。確かに重要ですね。ありがとうございました!

■ DBに依存しないフィールドはHTMLヘルパーなしで

青い人にずっと前に指摘された「DBに依存しないフィールドは手打ちで」という教え(詳細はこちら)をすっかり失念していました・・・。普通に書いて$this->params['form']['なんたら']で取得できるのでしたね。

ログイン画面での「save my info?」のチェックボックス、それからプロフィール変更画面での画像削除チェックボックスは手打ちし、該当するコントローラーを修正しました。

■ Viewのテンプレート化をもちょっとすすめる

ページのテンプレートは現在/views/layout/default.thtmlでまかなっていますが、ログイン画面にあわせたレイアウトなので、ログイン画面以外では冗長なコードになってきました(メニューを何回も書いているとか)。そこでログイン系とそれ以外のテンプレートを分けて管理することにします。

なお、管理する際には下記のjakkさんのコメントを参考にさせていただきました。

もうご存知かもしれませんが
レイアウトは
$this->layout = “default”;
でかえれます。
挫折しないことをいのります(・人・

コントローラー内のlogin()、resetPwd()については以下の一行を追加。別のテンプレートを使うように指示します。

$this->layout = 'login_default';

そしてページ数の多いほうで使うdefault.thtmlはelementなども使い下記のように修正。その際にはHTMLヘルパーが良くまとまっているこちらのページも参考にしました(docTypeとか一部動かなかったけど)。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php echo $html->charset('utf-8'); ?>
<meta name="keywords" content="">
<meta name="description" content="">
<script type="text/javascript" src="/js/jquery.js"></script>
<?php echo $html->css('core'); ?>
<link rel="shortcut icon" href="/favicon.ico" />
<title><?php echo $title_for_layout; ?></title>
</head>
<body>
<div id="container" class="clear"> 
<?php echo $this->renderElement('header'); ?>
<div id="my_page" class="clear">
<h2><a href="/users/home/"><?= h($me['User']['name']); ?></a></h2>
<div id="contents">
<?php if (isset($sys_msg)) { ?>
<div id="sys_msg"><?= $sys_msg; ?></div>
<script>
$(document).ready(function() { $("#sys_msg").fadeOut(2000); });
</script>
<?php } ?>
<?php echo $this->renderElement('menu'); ?>
<?php echo $content_for_layout ?>
</div><!-- /contents -->
</div><!-- /my_page -->
</div><!-- end of container -->
<?php echo $this->renderElement('footer'); ?>
</body>
</html>

■ モデルのafterFind()を使う

プロフィール画像の幅とか高さとかファイル名とか表示するたびにいちいち計算するのが面倒なのでafterFind()に突っ込んでみます。

次のコードでうまくいったぽい。これでいいのかな>青い人?

まずは/models/user.php。afterFind()を追加。

function afterFind($results) {
  foreach ($results as $key => $val) {
    if (!empty($val['User']['pic'])  && file_exists(WWW_ROOT."pics".DS.$val['User']['pic'])) {
      $imginfo = getimagesize(WWW_ROOT."pics".DS.$val['User']['pic']);
      $results[$key]['User']['pic_loc'] = DS."pics".DS.$val['User']['pic'];
      $results[$key]['User']['pic_width'] = $imginfo[0];
      $results[$key]['User']['pic_height'] = $imginfo[1];
    } else {
      $results[$key]['User']['pic_loc'] = DS."pics".DS."uploadyourphoto.gif";
      $results[$key]['User']['pic_width'] = 50;
      $results[$key]['User']['pic_height'] = 50;
    }
  }
  return $results;
}

次にControllers&Views。コントローラーから画像に関するコードを削除、Viewsの方は次のように変更。これですっきり。

<img src="<?= $me['User']['pic_loc']; ?>" width="<?= $me['User']['pic_width']; ?>" height="<?= $me['User']['pic_height']; ?>" border="0" alt="" />

モデルの中でこうやるの便利だな・・・コードがもうちょっと複雑になってきたら積極的に使ってみよう。

■ 重大なセキュリティホールをふさぐ

青い人に指摘されたセキュリティホール、ふさがなくちゃですね。ちょっと長くなったのであとでやる。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

日記投稿系の処理をガツンとつくる(CakePHP修行 #35)


さて続けてCakePHP修行。今度は日記投稿系の処理をつくっちゃいます。とりあえずSNSっぽい友達の日記はおいておいて、自分で投稿したり削除したりできるようにします。

pic_top.gif

↑ 今回はここのメニューをガツンと。

■ モデルの設定

まずはモデルから。今回設定しなくてはいけないのはUserとPostの紐付け。UserとPostは1:Nの関係なので$hasManyなどでアソシエーションを作ります。

まずは/models/user.phpの方。

class User extends AppModel {
  var $name = 'User';
  var $validate = array (
    'email' => VALID_EMAIL,
  );
  var $hasMany = array ('Post');
}

次にPostの方。

class Post extends AppModel {
  var $name = 'Post';
  var $belongsTo = array('User');
}

アソシエーションはもっと細かく設定できるはずですが、今回はざくっとこんな感じで。

■ コントローラー&Viewを作りこんでいく

今回は日記一覧を/posts/(index)、追加・更新系を/posts/add、/posts/edit、/posts/delで作っていきます。簡単、簡単!と思ったのですが最初にはまったのはposts_controller.phpからUserのデータが見えない問題。あれれ、と思いちょっと調べてギブアップ(=青い人と世間話するふりしてちょっとヒントもらう)、結局これが足りませんでした。

  var $uses = array('User','Post');

$usesで設定しておけば、そのコントローラー内から紐づいた形でモデルが参照できるようです。そのあとは普通にロジックを書いていきます。コードは以下。

function index() {
  // check login
  $this->_checkLogin();
  // sys message
  $this->set('sys_msg', $this->Session->read('sys_msg'));
  $this->Session->delete('sys_msg');
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  $this->pageTitle = 'My Diaries';
  // set list
  $this->set('posts', $this->Post->findAllByUser_id($me['User']['id']));
}
function view($id) {
  // check login
  $this->_checkLogin();
  // sys message
  $this->set('sys_msg', $this->Session->read('sys_msg'));
  $this->Session->delete('sys_msg');
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  // set list
  $post = $this->Post->findById($id);
  $this->set('post', $post);
  $this->pageTitle = $post['Post']['title'];
  $this->set('owner',$this->User->findById($post['Post']['user_id']));
}
function add() {
  // check login
  $this->_checkLogin();
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  // main
  $this->set('error', false);
  if (!empty($this->data)) {
    $this->data['Post']['user_id'] = $me['User']['id'];
    if ($this->Post->save($this->data['Post'])) {
      $this->Session->write('sys_msg', 'Your post has been added.');
      $this->redirect('/posts/');
    }
  }
}
function edit($id = null) {
  // check login
  $this->_checkLogin();
  // get "my info"
  $me = $this->User->findById_hash($_COOKIE['my_id']);
  $this->set('me', $me);
  // main
  $this->set('error', false);
  if (empty($this->data)) {
    $this->Post->id = $id;
    $this->data = $this->Post->read();
  } else {
    $this->data['Post']['user_id'] = $me['User']['id'];
    if (empty($this->data['Post']['title']) || empty($this->data['Post']['body'])) { $this->set('error', true); return false; }
    if ($this->Post->save($this->data['Post'])) {
      $this->Session->write('sys_msg', 'Your post has been updated.');
      $this->redirect('/posts/view/'.$this->data['Post']['id']);
    }
  }
}
function del($id) {
  // check login
  $this->_checkLogin();
  $this->Post->del($id);
  $this->Session->write('sys_msg', 'Your post has been deleted.');
  $this->redirect('/posts/');
}

なんかちょっとリファクタリングしたくなってきましたが、まぁ、こんな感じです。ちなみに自分の日記じゃないと削除や変更できないように権限チェックなどもするべきだと思いますが、まぁ、今はこんな感じです。

■ 動作テスト

テストも完了、ちゃんと動きます。

post_1.gif

↑ リスト画面。一覧が出てきます。

post_2.gif

↑ 変更もできます。

post_3.gif

↑ 削除もできます。

削除の際はhtmlヘルパーを使えば簡単に「Are you sure?」みたいな確認ダイアログを出せて便利。やるなぁ、CakePHP・・・。

さて次は何をするべきか・・・青い人が突っ込みを入れてくれたのでちょっとバグFixというか、細かいところを直したいと思います。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

「情報を保存する」オプションを有効にする(CakePHP修行 #34)


さて続けてCakePHP修行。今度はログイン画面での「情報を保存する」オプションを有効にしてみます。

save_info.gif

↑ 今回はこれがターゲット!

淡々と作業を続けます。

■ クッキーを使う

今までログイン情報の保持にはセッションを使っていましたが、それだとブラウザを閉じた後にも情報を保持してくれません。というわけでクッキーを導入。で、調べてみるとどうやらセッションのようにComponentはない模様(1.1系には)。そこでそのままsetcookieやら$_COOKIEやらを使っていきます。

なお、セッションから完全移行というわけではなく、簡単なメッセージは相変わらずセッションを使って表示することにします。

■ データベーススキームの変更

次にユーザー個別のCookieを保持するためのUsersテーブルを変更します。id_hashというカラムを追加します。なお、この変数には「適当な文字列」「ID」「パスワード」を連結してハッシュ化したものを使います。そしてこのid_hashをCookieとしてブラウザに保持、ログインしているかどうかのチェックに使います。

結局、Usersテーブルはこんな感じ。

CREATE TABLE users (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  id_hash VARCHAR(50),
  name VARCHAR(50),
  email VARCHAR(255) BINARY NOT NULL UNIQUE,
  pwd VARCHAR(255),
  pic VARCHAR(255),
  profile TEXT,
  created DATETIME DEFAULT NULL,
  modified DATETIME DEFAULT NULL
);

なお、適当な文字列にはパスワードを暗号化するために使ったPWD_KEYを使いまわします。よく考えたら使いまわすのよくないか、まぁ、いいや・・。

■ ログインチェックのロジックを変更

いままでログインチェックにはapp_controller.phpのcheckSession()を使っていましたが、セッションではなくなるので_checkLogin()を新しく作ることにします。変数の前には_をつけましたが、これでいいんすよね?

実際のコードはこんな感じ。

function _checkLogin() {
  // if cookie exists ...
  if (!isset($_COOKIE['my_id']) || !$this->User->FindById_hash($_COOKIE['my_id'])) {
    $this->Session->write('login_back_url',((getenv('SERVER_PORT')==443)?'https://':'http://').getenv('HTTP_HOST').getenv('REQUEST_URI'));
    $this->redirect('/users/login');
    exit();
  }
}

そしてusers_controller.php中のcheckSession()を_checkLogin()に変更します。

■ ログイン画面&ロジックの変更

まずはView(login.thtml)の変更。「情報を保存する」チェックボックスを有効にします。HTMLヘルパーを使って以下のように記述。

<tr><td width="40%">&nbsp;</td><td><?php echo $html->checkbox('User/save'); ?> save my info?</td></tr>

Usersのスキーマにないsaveってのを勝手に作ってしまったがいいのだろうか。ま、どちらにしろこれでコントローラー側で$this->data['save']から受け取れるようになりました。

それでもってlogin()も次のように変更。データが入ってきたときに以下のように処理します。

$someone = $this->User->findByEmail($this->data['User']['email']);
if(!empty($someone['User']['pwd']) && $someone['User']['pwd'] == sha1(PWD_KEY.$this->data['User']['pwd']))
{
  if ($this->data['User']['save']) {
    setcookie('my_id', $someone['User']['id_hash'], time()+60*60*24*365*5, '/'); // save it for 5 years
  } else {
    setcookie('my_id', $someone['User']['id_hash'], NULL, '/');
  }
  if (isset($login_back_url))
  {
    $this->redirect($login_back_url);
    $this->Session->delete('login_back_url');
  } else {
    $this->redirect('/users/home/');
  }
}

これによってブラウザ側にCookieが保存されますが、チェックボックスを入れていると5年間、問答無用で保存します。まぁ、これぐらいあればいいですかね。チェックを入れていない場合はブラウザが終了するまで保持することとします。

それからsetcookie中でパス指定(今回は”/”)をしたのですが、これってなんかセキュリティ上、やばいんじゃなかったかな?でもこれを指定しないと”/users”のみで有効になってしまうという結果に・・。これでよかったのかな?

■ コントローラー中の処理

今回の処理で「セッション中にUser IDを保持」から「Cookie中にid_hashを保持」に変わったのでコントローラーの各処理を次のような感じにしておきます。

$me = $this->User->findById_hash($_COOKIE['my_id']);

さて、ここまでで「情報を保存する」オプションがきちんと有効になるようになりました・・・。

cookie_view.gif

↑ クッキーを確認。ちゃんと有効期限が5年後になっています。

■ ログアウト処理

おっと、忘れるところだった。ログイン処理を変えたのでログアウト処理も変更します。コードは以下のとおり。

function logout()
{
  setcookie('my_id','',time()-60*60,'/');
  $this->redirect('/users/home/');
}

やれやれ(JOJO)、ログイン画面だけでだいぶ時間を使ってしまった。次はいよいよ日記の投稿へ行きましょう。アソシエーションとか早くやってみたい。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

デフォルト画像を用意する(CakePHP修行 #33)


さてCakePHP修行。いろいろやってますよ。

そういえば最近青い人がからんでくれない。どうしたのだろうか。一人異国の地でがんばっているのになにか寂しいっす・・・。

さて、気を取り直して。

■ デフォルトの写真を設定

細かいですが、写真がアップされていないときにデフォルトの画像を表示するようにしました。

uploadyourphoto.gif

↑ まずはこんな画像を用意。

pic_2.gif

↑ 画像が登録されていないときにはこんな感じに表示されるように。

コードは簡単。$me['User']['pic']がemptyだったら、デフォルト画像のURLを渡すようにしているだけです。

users_controller.phpのコードは以下のとおり。

if (empty($me['User']['pic'])) { $me['User']['pic'] = 'uploadyourphoto.gif'; $this->set('me', $me); }

なんか$meをまるごと再セットしているのが富豪的だな・・・$me['User']['pic']だけに新しい値をセットするのってどうすればいいのだろう。まぁ、細かいところはあとで悩むとしてとりあえず動くようにしちゃいます。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

バンコクの食費事情


本日のランチ。近くにあるカールフール(スーパー)のフードコートにてw。

thai_food.jpg

↑ チャーハンが40B、水が10B。合計50B(約200円)。

毎食、こんな感じ。ビールをつけるとちょっとあがるけど何百円レベル。滞在費ほとんどかからないですな・・。

日本でかかっている家賃もありますが、日本にいたら費やすであろう食費を考えるとこっちのほうが安いんじゃないか、という気がしてきた・・・。

アドレス帳の力(バンコク携帯事情)


バンコクで携帯を買ったのですが、「これ、いいな」と思った機能があるのでちょっと書いてみます。

携帯を買ってまず最初に行うのが電話番号の登録だと思いますが、アドレス帳を見ていると登録した覚えのない番号がたくさん入っています。

mobile_1.jpg

↑ こんな感じ。

「AIS Call Center」からはじまり、「.Book」「.Joke」なども登録されています。どうやらこうした番号に電話するといろいろなサービスが受けられるようです。

mobile_2.jpg

↑ 試しに「.Balance」に電話。するとこのように残高が返ってきました。

こうした機能はもちろん日本の携帯にもありますが、こうして電話帳に最初から登録されていると便利かな、と思いました(電話帳に最初から登録されていて気持ち悪い、という意見もあるでしょうが)。

メニューを辿って・・・という操作性よりは、アドレス帳から友達を探すように操作出来たほうが便利な人には便利なのではないか、と思ったり思わなかったり。

個人的にはこれはナイスアイディア、と思ったのでメモ程度にここに記しておきます。

突然ですがバンコク出張所を開設しました


最近、日本脱出が流行しているので(はてなとか、あの人とか)、僕もふらりと脱出してみました。行き先はタイ。理由は友達が滞在場所を貸してくれたから。かなりの格安旅行です。なお、期間は2週間ほどです。

bk_dish.jpg

↑ 食費が激安なのも魅力。1日1,000円あれば満腹です。

今回の目的は基本的にひとり合宿。さくさくCakePHPでいろいろ作れるようになる、というのが最大の目的です。最近ブログの更新頻度が落ちていますがw、これはもう、落ちたままにしておきます。開発スキルの向上が最優先です。

昨日タイについて今日は作業環境やらをセットアップしました。いくつかメモも兼ねてタイのお国事情をば。

■ まずは回線!

まずは回線です。これがないとお話になりません。今回借りた場所は無線LANが来ているのでとりあえずテスト。さくっとつながりましたが、日本に比べると速度はちょっと遅いですね。これは様子を見ながら必要ならなにか考えます。

stest.gif

↑ なんかタイのローカルサイトで速度診断。こんぐらいですかね。

■ 次は携帯!

次はなにかと必要なので携帯を買いに。タイ在住の友人に手伝ってもらってNOKIAの端末を購入。そのあとSIMカードを買ってプリペイドで600バーツ分(約2,500円)チャージします。これでしばらくは大丈夫そう。ちなみに端末とSIMカードで4000バーツぐらい(約16,000円)。

bk_mobile.jpg

↑ 赤とオレンジがちょっと可愛い(でもない?)

こちらの携帯はSIMカードをつっこめばすぐに使えるのがいいですね。SIMカードは番号付で売っているので気に入った番号を購入します。またプリペイドのチャージも駅の売店で買えば端末から*120*(チャージ番号)#ですぐにできます(*121#でチャージ金額もすぐに確認可能)。

■ 次は定期!

バンコクではスカイトレインという電車が走っています(地下鉄も走っていますが、あまり使わない)。今後お世話になるだろうから600バーツ分の定期を買います。定期というか、PASMOみたいなものですね。改札は非接触型なので日本と同じ感覚で使えます。これも便利。

bk_card.jpg

↑ バンコクは渋滞がひどいので電車の利用が便利。

■ 次はモニター!

開発といえばデュアルモニター。さすがに日本から持っていくのはどうか、と思ったので現地のセール品を購入。サムソンのワイド液晶を8,000バーツほど(約32,000円)で購入。

bk_monitor.jpg

↑ これがないとお話にならないっす(個人的に)。

さてさて、これで一通り環境が揃いました。もうあとは作業するだけです・・・と思ったら金曜日の夜ですなぁ。ちょっと本を持って呑みにでも行っちゃいそうな気がしないでもない。

またバンコク事情などなど、開発の合間にレポートしますね。

Subversion+Tracデビュー!(CakePHP修行 #32)


さてちょっと間があきました、CakePHP修行。さぼっていたわけではなくて苦戦していましたよ・・・。

青い人から「Subversion+Tracがないとよくわからない」と言われたのでなんとかセットアップしました。あいかわらず青い人に教わりながらでしたが・・・。

なお、subversionは昔から使っていますが、Tracは初めて。どきどきです。

それから青い人からは「うちのサーバーで動いているTracで管理しましょーかー?」といわれましたが、これも修行のうちなので丁重にお断りしました。すっかりCakePHPじゃない連載になっています。

ちなみに使っているOSはCentOS4.4で、僕がもっている自宅サーバーにインストールしました。なお、subversionに関してはapache経由は面倒なので、svnserve経由です。クライアントからはTortoiseSVNでアクセスします。

さて、以下に簡単に作業手順をご紹介。全部書くと大変なので簡単に。需要あったらそのうち書きます。

  • subversionのインストール。yum installを使っても良いのですが、最新版じゃないのでソースから。apr、apr-utilも一緒にいれちゃいます。BerkeleyDBではなくてfsfsを使います。
  • リポジトリの作成、データのImportをします。
  • svnserveを起動。クライアントからアクセスをテストします。OKでした。ちょっと怖いので認証もかけます。
  • 次にTrac。ここで青い人に「yumからtracをインストールする方法」を教わります(ここらへん)。ひょっとして、これでsubversionもインストールできた(ry
  • 最初にtracdを試しましたが、認証かからないので(という理由だっけ?)、cgi-binでやります。httpd.confなんかをごにょごにょして基本認証をかまして起動。
  • とりあえず最初のチェックアウト。一時ファイル系にはigonore属性を付与。その後、ちょっと修正してコミット。無事に反映されました。ちなみにローカルではVMWare+CentOS4.4で動いています。ふー・・・
  • trac_top.gif

    ↑ 初Trac!

さて、簡単に書きましたが、かなり疲れました・・・すごい作業だったなぁ・・・。一人だと絶対!無理でした。

それにしてもソースからインストールとか昔こわくてできなかったけどもう慣れっこになりましたね。ちょっとうれしい。

つうわけで、あとはもう突っ走るだけですね。青い人も隅々までソースを見れるようになったので突っ込みいれてくれるはず!

さー・・・やるぞー・・・(眠い・・・)。

※ CakePHP修業は百式管理人がSNSっぽいものをCakePHPで作ろうとして挫折するまでの日記です。前回までのあらすじはこちらへ。

『実践 Web 2.0 BOOK』でcheck*padが紹介されました!


人気ブロガー直伝!の「実践 Web 2.0 BOOK」にてcheck*padが紹介されました。紹介してくれたあきやん、ありがとう!

web20_1.jpg

↑ パステル系でかわいい表紙っす。

web20_2.jpg

↑ check*padはP175から・・・なんと7Pにわたって解説!

他にもウェブで手に入る無料ツールが満載です。読みながらしみじみいい時代になったなぁ、と思ったり。

詳細&ご購入は以下からどうぞ。

実践Web2.0 BOOK 人気ブロガー直伝! 一歩先行くWeb2.0的ワーキングスタイル