何か創ろう

2011/3/27
昔は、よく見かけた「足跡を残す」を作成してみます。
完成イメージは、「足跡」を参照願います。足跡を残して頂けると、幸いです。

足跡を残すシステムの作成!設計メモ


<論理設計>
  1. 環境設定
    • WEBサーバ:Apache2
    • DBサーバ:MySQL5
    • 開発言語:PHP5、 JavaScript(JQUERY)
    • 文字コード:UTF8
    • 動作対象:パソコンブラウザ

  2. システム構成図


  3. 使用ライブラリ
      クライアント
    1. jquery-1.4.4.min.js
    2. ここ
    3. jquery-ui-1.8.10.custom.min.js
    4. ここ
    5. exvalidation.js
    6. exchecker-ja.js
    7. ここ
  4. 情報検討
    • 投稿No
    • 投稿者名
    • 投稿日時
    • 投稿内容
    • 返信内容
    • 禁止IPアドレス

  5. 画面検討
    • 投稿画面
    • 返信画面

  6. 機能検討
    • 投稿登録
    • 返信登録
    • 投稿一覧表示
    • 投稿内容チェック機能
    • 投稿禁止機能
    • 投稿削除機能

<物理設計>
  1. DB
    • 名称:足跡
    • ID:footprint
    • SQL:CREATE DATABASE `footprint` ;

  2. テーブル
    • 名称:足跡
    • ID:footstamp
      1. 投稿No:cont_no INT 10 A_I
      2. 投稿者名:cont_name VARCHAR 40
      3. 投稿日時:cont_date TIMESTAMP 10
      4. 投稿内容:cont_body VARCHAR 200
      5. 返信内容:cont_res VARCHAR 200
      6. IPアドレス:ip_address VARCHAR 20
    • SQL
      CREATE TABLE `footprint`.`footprint` (
      	`cont_no` INT( 10 ) NOT NULL AUTO_INCREMENT COMMENT '投稿No',
      	`cont_name` VARCHAR( 40 ) NOT NULL COMMENT '投稿者名',
      	`cont_date` TIMESTAMP NOT NULL COMMENT '投稿日時',
      	`cont_body` VARCHAR( 200 ) NOT NULL COMMENT '投稿内容',
      	`cont_res` VARCHAR( 200 ) NULL COMMENT '返信内容',
      	`ip_address` VARCHAR( 20 ) NOT NULL COMMENT 'IPアドレス',
      	PRIMARY KEY ( `cont_no` ) 
      ) ENGINE = InnoDB;
      		
    • 名称:禁止IP
    • ID:badip
      1. IPNo:ip_no INT 11 A_I
      2. IPアドレス:ip_address VARCHAR 20
    • SQL
      CREATE TABLE `footprint`.`badip` (
      `ip_no` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
      `ip_address` VARCHAR( 20 ) NOT NULL 
      ) ENGINE = InnoDB COMMENT = '禁止IPアドレス';
      

  3. インターフェース
    サーバ、クライアントのインターフェースは、JSONで行う。
    	[{"cont_no":"62",
    	"cont_name":"xxxxxx",
    	"cont_date":"xxxxxx",
    	"cont_body":"xxxxxx",
    	"cont_res":"xxxxx",
    	"ip_address":"xxxxx"
    	}]
    
  4. 画面
    <投稿画面>
    お名前 さん
    足跡
    名前:40文字以下、禁止文字チェック
    足跡:3文字以上200文字以下、必須入力、禁止文字チェック
    
    足跡を残すボタン押下で、入力内容チェック後サーバに送信する。
    	
    <返信画面>
    お名前 XXXXXXXXX さん
    内容
    内容:3文字以上200文字以下、必須入力、禁止文字チェック
    
    返信するボタン押下で、入力内容チェック後サーバに送信する。
    	
  5. 機能詳細
      投稿登録
    • 投稿登録ダイアログ表示
      ダイアログID:cdialog1
      
      $(function(){
      	//ダイアログ定義
      	$('#cdialog1').dialog({
      		autoOpen: false,
      		modal: true,
      		width: 400,
      		//show:"fadeIn(5000)",
      		buttons: {
      			"足跡を残す": function() { 
      				$("#fm1").submit();
      			}, 
      			"キャンセル": function() { 
      				$(this).dialog("close"); 
      			}
      		}
      	});
      	//入力内容チェック
      	$("#fm1").exValidation({
      	rules: {
      		cont_name: "min0 max40 kin",
      		cont_body: "required min0 max200 kin"
      	},
      		errInsertPos: 'after',
      		errPosition: 'fixed',
      		customSubmit:fm1submit
      	});
      	//ダイアログ表示
      	$('#push_footprint').click(function() {
      		//入力エリア初期化
      		$('#cont_name').attr('value','');
      		$('#cont_body').attr('value','');
      		//ダイアログオープン
      		$('#cdialog1').dialog('open');
      	});
      	
      });
      
      function fm1submit(){
      	//入力データ送信
      	$.post("setfootprint.php",{
      			cont_name:$('#cont_name').attr('value'),
      			cont_body:$('#cont_body').attr('value')
      		},
      		function(ret){
      			if(ret){
      				alert(ret);
      			}
      		}
      	);
      	
      	$('#cdialog1').dialog("close");
      
      }
      
      //HTML
      <DIV id="cdialog1" title="足跡投稿" style="display:none">
      <form id="fm1">
        <div style="border: 1px #FF6600 solid;padding:10px 10px 10px 10px;width:380px">
        <table>
        <tbody>
          <tr>
          <td>お名前</td>
          <td><input type="text" value="" name="cont_name" id="cont_name" size=20>さん</td>
          </tr>
          <tr>
          <td>足跡</td>
          <td><TEXTAREA rows="5" cols="40" name="cont_body" id="cont_body"></TEXTAREA></td>
        </tbody>
        </table>
        </div>
      </form>
      </DIV>
      		

    • ポイント:
      exValidationに都合の良い禁止文字チェックが無いので、
      無ければ、作れば良い!!
      exchecker-ja.jsに下記のチェックを追加
      kin: [
      	'記号([][&;`,\\\'"|*?~<>^(){}.$_:)以外の文字を入力してください',
      	function(txt, t) {
      		if ( txt && txt.length>0 ) {
      			if ( /[&;`',\\\"|*?~<>^(){}.$_:]/.test(txt) ) {
      				return false;
      			} else {
      				return true;
      			}
      		} else {
      			return true;
      		}
      	}
      ],
      


    • 足跡登録
      POSTパラメータで渡された、名前、足跡を足跡テーブルに登録する。
      <サーバ処理>
      //共通環境設定
      require_once("config.php");
      
      try{
      	//DB接続
          $db = new PDO($dsn,$uid,$passwd);
      
      	$nowencoding = "utf8";
      	
      	$sqlstr="SET NAMES ".$nowencoding;
      	$db->query($sqlstr);
      	mb_language("JA");
      
      //ポストパラメータ取得
      	if(isset($_POST['cont_name'])){
      		$cont_name=$_POST['cont_name'];
      		if($cont_name==""){
      			$cont_name="匿名希望";
      		}
      	}else{
          	die("SYSTEM ERROR WRONG PARAMETER cont_name");
      	}
      	if(isset($_POST['cont_body'])){
      		$cont_body=$_POST['cont_body'];
      	}else{
          	die("SYSTEM ERROR WRONG PARAMETER cont_body");
      	}
      	//IPアドレスを取得
      	$ipAddress = $_SERVER["REMOTE_ADDR"];
      	
      	//メイン処理
      	//連続投稿チェック
      	if(check_ip($ipAddress,$db)){
      		die("連続投稿されました。");
      	}
      	//書き込
      	if(insert_footprint($cont_name,$cont_body,$ipAddress,$db)){
      		echo "足跡を頂きました。ありがとうございます。";
      	}
      	$db = null;
      } catch(PDOException $e){
      	die("SYSTEM ERROR " . $e->getMessage());
      }
      
      exit;
      
      /**
       * IPアドレスチェック
       *
       * @access public
       * @return true:不正アクセス false:正常
       */
      function check_ip($ipAddress,$db){
      	try{
      		$sqlstr="";
      		$sqlstr.="select count(*) as cnt from badip ";
      		$sqlstr.="where ip_address='" . $ipAddress . "'";
      		$rs=$db->query($sqlstr);
      		if($rs){
      			$row=$rs->fetch(PDO::FETCH_ASSOC);
      			$cnt=$row['cnt'];
      			$rs=null;
      			if($cnt>0){
      				return true;
      			}
      			
      			$sqlstr="";
      			$sqlstr.="select cont_date,count(*) as cnt from footprint ";
      			$sqlstr.="where ip_address='" . $ipAddress . "'";
      			$rs=$db->query($sqlstr);
      		
      			$row=$rs->fetch(PDO::FETCH_ASSOC);
      			$cnt=$row['cnt'];
      			if($cnt>0){
      				$cont_date=$row['cont_date'];
      			}else{
      				$cont_date="";
      			}
      			$rs=null;
      		
      			if($cont_date==""){
      				return false;
      			}
      			
      			//現在日付を取得しタイムスタンプに変換
      			$today1 = getdate();
      			$timestamp=chg_timestamp($today1);
      			//取得日付をタイムスタンプに変換
      			$today2 = date_parse($cont_date);
      			$get_timestamp=chg_timestamp($today2);
      			
      			//3秒以内で投稿されている場合、連続投稿と判定
      			$get_timestamp=$get_timestamp+3;
      			if($timestamp<=$get_timestamp){
      				//連続投稿
      				return true;
      			}else{
      				return false;
      			}
      		}else{
      			return false;
      		}
      	} catch(PDOException $e){
      		die("SYSTEM ERROR " . $e->getMessage());
      	}
      }
      function chg_timestamp($today){
      	if(isset($today['mon'])){
      		$today_month = $today['mon'];
      		$today_day = $today['mday'];
      		$today_hours = $today['hours'];
      		$today_minutes = $today['minutes'];
      		$today_second = $today['seconds'];
      	}else{
      		$today_month = $today['month'];
      		$today_day = $today['day'];
      		$today_hours = $today['hour'];
      		$today_minutes = $today['minute'];
      		$today_second = $today['second'];
      	}
      	$today_year = $today['year'];
      	
      	$timestamp = mktime($today_hours, $today_minutes, $today_second, 
      		$today_month, $today_day, $today_year);
      
      	return $timestamp;
      
      }
      
      /**
       * 足跡作成
       *
       * @access public
       * @return true:書き込み成功
       */
      function insert_footprint($cont_name,$cont_body,$ipAddress,$db){
      	try{
      		$sts=false;
      		$sqlstr="insert into footprint ";
      		$sqlstr.="(";
      		$sqlstr.="cont_name,";
      		$sqlstr.="cont_body,";
      		$sqlstr.="ip_address";
      		$sqlstr.=")";
      		$sqlstr.=" values ";
      		$sqlstr.="(";
      		$sqlstr.="'" . $cont_name . "'";
      		$sqlstr.=",";
      		$sqlstr.="'" . $cont_body . "'";
      		$sqlstr.=",";
      		$sqlstr.="'" . $ipAddress . "'";
      		$sqlstr.=")";
      	//トランザクション開始
      	
      		$ret = $db->beginTransaction();
      	
      		$ret = $db->query($sqlstr);
      	//コミット
      		$ret = $db->commit();
      		$sts=true;
      		return $sts;
      	} catch(PDOException $e){
      		$ret = $db->rollback();
      		die("SYSTEM ERROR " . $e->getMessage());
      	}
      }
      
    • 足跡一覧表示
      足跡テーブルより、データを取得して一覧表示を行う。
      <クライアント処理>
      function get_footprint(){
      	url="getfootprint.php";
      	$.getJSON(url,{
      		dmy:Math.floor(Math.random()*1000),
      		pageline:$('#inpageline').attr('value'),
      		nowp:$('#nowp').text()
      	},showdata);
      }
      
      function showdata(lddat){
      	if(lddat){
      		$('#list_area').empty();
      		var tlist="";
      		for (i = 0; i < lddat.length; i++) {
      			tlist="<tr>";
      			tlist+="<td>" + lddat[i].cont_no + "</td>";
      			tlist+="<td>" + lddat[i].cont_date + "</td>";
      			tlist+="<td>" + lddat[i].cont_name + "</td>";
      			tlist+="<td>" + lddat[i].cont_body + "</td>";
      			if(lddat[i].cont_res){
      				tlist+="</tr>";
      				tlist+="<tr>";
      				tlist+="<td colspan=4>";
      				tlist+=lddat[i].cont_res;
      				tlist+="</td>";
      			}
      			tlist+="</tr>";
      			$('#list_area').append(tlist);
      		}
      	}else{
      		var nowp=($('#nowp').text() - 0);
      		nowp=nowp-1;
      		if(nowp&l;t1){
      			nowp=1;
      		}
      		$('#nowp').text(nowp);
      		return;
      	}
      }
      
      
      ポイント:
      $('#nowp').text() - 0
      -0することで、数値として扱う。

      $('#list_area').empty();
      子要素を空にする。



    • 足跡一覧表示
      <サーバ処理>
      //共通環境設定
      require_once("config.php");
      
      try{
      	//DB接続
      	$db = new PDO($dsn,$uid,$passwd);
      
      	$nowencoding = "utf8";
      	
      	$sqlstr="SET NAMES ".$nowencoding;
      	$db->query($sqlstr);
      
      	mb_language("JA");
      
      	$start=0;
      	$pagecnt=30;
      	
      	
      	//GETパラメータ取得
      	if(isset($_GET['pageline'])){
      		$pagecnt=$_GET['pageline'];
      	}
      	if(isset($_GET['nowp'])){
      		$nowp=$_GET['nowp'];
      		$start=($nowp - 1)*$pagecnt;
      	}
      
      	//IPアドレスを取得
      	$ipAddress = $_SERVER["REMOTE_ADDR"];
      
      	$sqlstr="";
      	$sqlstr.="SELECT * from footprint ";
      	$sqlstr.="ORDER BY cont_no DESC ";
      	$sqlstr.="LIMIT " . $start . "," . $pagecnt;
      	$rs=$db->query($sqlstr);
      	while( $row=$rs->fetch(PDO::FETCH_ASSOC) ){
      		$list[]=$row;
      	}
      	$rs=null;
      	if(isset($list)){
      		$hash  = json_encode($list);
      	}else{
      		$hash = "";
      	}
      	$db = null;
      	echo $hash;
      } catch(PDOException $e){
      	die("SYSTEM ERROR " . $e->getMessage());
      }
      exit;
      
      ポイント:
      json_encode($list);
      配列をJSONに変換する。

  6. その他の処理 ページング処理:
    一ページあたりの表示件数:リストボックスで切り替え
    前ページ:前ページの表示
    次ページ:次ページの表示
    足跡返信画面は、Noクリックで表示
    //返信ダイアログオープン
    $('#list_area th').live("click",function() {
    	$('#cont_no').attr('value',$(this).text());
    	$('#disp_no').text($(this).text());
    	
    	$('#cont_name2').text($(this).next().next().text());
    	$('#cont_body2').attr('value','');
    	$('#cont_password').attr('value','');
    	//ダイアログオープン
    	$('#cdialog2').dialog('open');
    });	
    
    パスワードで認証して、書き込み可能にする。
    パスワードは、内緒