tohokuaikiのチラシの裏

技術的ネタとか。

160行+.htaccessで動かすイベント合わせPHPスクリプト

50行PHPスクリプト1枚 だけ で動かす超シンプルチャット - 肉とビールとパンケーキ by @sotarok ってあったので、半年前作って*1忘れてたのをアップ。

ポイントってほどでもないが携帯対応。ただ、mod_rewriteを使うので.htaccessも必要。なんでこんな仕様にしたかは忘れた。

あと、仕様として

  • TSV形式の設定ファイル重要
  • TSV設定ファイルのファイル名を知らないとアクセスできない
  • 逆にURLさえ知ってれば管理者権限(だから、仲間内うちでしかできない)
  • あまりに単機能なので脆弱性とか無いはず(仕様を守ってる限り)*2

.htaccess

http://www.junoe.jp/m/itoh/tasks/.htaccess
において、

RewriteCond %{REQUEST_URI} !^/(m/itoh/tasks/entrypoint.php|.*/static)/
RewriteRule ^(.*)$ /m/itoh/tasks/entrypoint.php/$1 [L]

って書く。

で、PHPファイル

http://www.junoe.jp/m/itoh/tasks/entrypoint.php

<?php
header('Content-Type: text/html; charset=Shift_JIS');
$now = time()  + 86400 * 1;
$root_url  = sprintf('http://%s%s', $_SERVER['SERVER_NAME'], dirname($_SERVER['SCRIPT_NAME']));
$event_url = 
$params = get_params();
if (is_post_method()){
	save_data($params);
}
$data = load_data($params);
?>
<html>
<head>
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Content-Type" content="text/html" charset="Shift_JIS" />
<title>[Chotaro]<?php echo $data['label'][0]?></title>
</head>
<body>
<a name="top"></a>
<a href="#bottom" accesskey="8">[8]bottom</a><br>
<a href="<?php printf('%s/help/%s', $root_url, $params['event_id'])?>">使い方</a><br>
<form action="<?php printf('%s/%s/%s', $root_url, $params['event_id'], $params['user_id'])?>" method="post">
<center><?php printf('<a href="%s/%s">[%s]</a>', $root_url, $params['event_id'], $data['label'][0])?></center>
<?php
if($params['user_id']){
	printf('<input type="submit" value="%sの予定変更"><br>', $params['user_id']);
}
/* print label */
echo str_repeat('&nbsp;', 5);
foreach ($data['label'] as $lk=>$l){
	if ($lk==0) continue;
	printf('&nbsp;<a href="%s/%s/%s">%s</a>',
		   $root_url, 
		   $params['event_id'],
		   $l, substr($l,0,1))."\n";
}
echo "<br>";
foreach ($data['data'] as $day=>$d){
	if ($day < $now){
		continue;
	}
	$w = date('w', $day);
	$format = $w==0 ? "<font color='#ff0000'>%s&nbsp;</font>" : ($w==6 ? "<font color='#0000ff'>%s&nbsp;</font>" : "%s&nbsp;") ;
	$day_str = date('md', $day);
	printf($format, $day_str);
	foreach ($d as $rk=>$r){
		if ($params['user_id'] == $data['label'][$rk+1]){
			printf('<input type="checkbox" name="check[%s]" value="1"%s>&nbsp;',
				   $day_str, $r?' checked':'');
		} else {
			printf('%s&nbsp;', $r ? 'o' : 'x');
		}
	}
	echo "<br>\n";
}

if($params['user_id']){
	printf('<input type="submit" value="%sの予定変更"><br>', $params['user_id']);
}
?>
</form>
<a href="#top" accesskey="2">[2]top</a><br>
<a name="bottom"><?php echo date('Y/n/j H:i')?></a></body></html>
<?php
////////////////////////////////////////////////////////////
function get_params()
{
	$params = array();

	if (isset($_SERVER['PATH_INFO'])){
		$pathinfo = explode('/', trim($_SERVER['PATH_INFO'], '/'));
		if (count($pathinfo)>=1){
			$params['event_id'] = $pathinfo[0];
			$params['user_id']  = isset($pathinfo[1]) ? $pathinfo[1] : '';
		}
	}
	
	if (count($params)==2){
		return $params;
	}
	
	exit('invalid path');
}


function save_data($params)
{
	$file = get_file($params);
	$data = load_data($params);
	if (!isset($_POST['check']) || !is_array($_POST['check'])){
		$_POST['check'] = array();
	}
	$user_key = array_search($params['user_id'], $data['label']);
	if ($user_key!==false && $user_key>0){
		$user_key--;
		foreach ($data['data'] as $dday=>$dd){
			$dday_str = date('md', $dday);
			if (isset($dd[$user_key])){
				$data['data'][$dday][$user_key] = array_key_exists($dday_str , $_POST['check']) ? 1 : '';
			}
		}
		/// write in file
		if ($fp = fopen($file, 'w')){
			fputs($fp, implode("\t", $data['label'])."\n");
			foreach ($data['data'] as $dday=> $dd){
				fputs($fp, sprintf("%s\t%s\n", date('Y/n/j', $dday), implode("\t", $dd)));
			}
			fclose($fp);
		} else {
			exit('cannot write file.');
		}
	}
}



function load_data($params)
{
	$label = array();
	$data  = array();
	$file = get_file($params);
	foreach (file($file) as $k=>$line){
		$d = explode("\t", trim($line, "\r\n"));
		if ($k==0){
			$label = $d;
		} else {
			if (count($d) == count($label)){
				$date = strtotime(array_shift($d));
				$data[$date] = $d;
			}
		}
	}
	$ret = array('label' => $label, 'data' => $data);
	return $ret;
}


function get_file($params)
{
	$file = sprintf('%s/cache/%s.tsv', dirname(__FILE__), $params['event_id']);
	if (file_exists($file)){
		return $file;
	}
	if ($params['event_id']=="help"){
		require dirname(__FILE__)."/static/help.html";
		exit;
	}
	
	exit('cannot exists file.');
}

function is_post_method()
{
	return strcasecmp($_SERVER['REQUEST_METHOD'], 'post')===0;
}

function h($s)
{
	return htmlspecialchars($s, ENT_QUOTES);
}

んで、TSVファイルには

サンプル飲み会 itoh minmin foo bar uki hoge
2009/6/1
2009/6/2
2009/6/3
2009/6/4
2009/6/5
2009/6/6
2009/6/7
2009/6/8

みたいに、一行目にイベント名と参加者一覧を書く。
ファイル権限を666にする。

TSVファイル名が重要

さっきのサンプルの場合はTSVファイル名がsampleevent.tsvなので
http://www.junoe.jp/m/itoh/tasks/sampleevent
にアクセスする。

結論

設置が面倒。環境依存でかいし。この辺をなくそうと思って思ってるうちに忘却してたんだ。

*1:この時の飲み会用に作ったのだった

*2:とか言って、あったら結構ドキドキ