<?php
/*
	Copyright(c) 2010 Shift Tech Inc. All Rights Reserved.
 	http://www.shift-tech.jp/

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 DROP TABLE IF EXISTS tbl_extmodule CASCADE;
CREATE TABLE tbl_extmodule (
	extmodule_code		serial,			--モジュールコード(mysql: int auto_increment)
	extmodule_active		smallint default 1,	--状態フラグ 0:削除 1:有効
	extmodule_type		smallint default 1,	--モジュールタイプ　0:標準（プレインストールモジュール） 1:拡張モジュール（追加・削除可能）extmodule_code > 5000となります
	extmodule_admin_path	text,				--管理画面のパス（モジュールフォルダ名/○○○の○○○の部分）
	extmodule_name		text not null,		--モジュール名
	extmodule_summary	text,				--モジュール説明、コメント等
	extmodule_ver		text,				--モジュールバージョン
	extmodule_path		text,				--モジュール本体
	extmodule_cdate		timestamptz,		--インストール日
	primary key (extmodule_code)
);
 */
class moduleDB extends Model
{

	function moduleDB()
	{
		$this->__construct();
	}
	function __construct()
	{
		Model::__construct();
		$this->db = new DataBase();
	}
	
	function getList()
	{
		$sql = "select * from tbl_extmodule where extmodule_active = 1 ";
		$order = " order by extmodule_code desc";
		$result = $this->db->query($sql.$order);
		$rtn=null;
		if($result != null) {
			while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
				$rtn[$row["extmodule_code"]] = $row;
			}
		}
		return $rtn;
	}
	
	function getMenulist()
	{
		$sql = "select extmodule_code, extmodule_path, extmodule_name, extmodule_admin_path from tbl_extmodule where extmodule_active = 1 and extmodule_admin_path is not null ";
		$order = " order by extmodule_code desc";
		$result = $this->db->query($sql.$order);
		$rtn=null;
		if($result != null) {
			while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
				$rtn[$row["extmodule_code"]] = $row;
			}
		}
		return $rtn;
	}
	
	/*
	 * モジュールマスタコード、またはフォルダ名より
	 * モジュールマスタ情報の取得
	 */
	function getData($extmodule_code=null,$extmodule_path=null) {
		$data = null;
		$sql = "select * from tbl_extmodule ";
		if(is_numeric($extmodule_code)){
			 $whrsql[] = " extmodule_code = ?";
			 $whrdata[] = $extmodule_code;
		}
		if($extmodule_path!=null) {
			$whrsql[] = " extmodule_path like ?";
			$whrdata[] = $extmodule_path;
		}
		if(count($whrdata) > 0) {
			$sql .= "where ".implode(" and ", $whrsql);
			$result = $this->db->prepare($sql,$whrdata);
		} else {
			$result = $this->db->query($sql);
		}
		if($result != null) {
			return $result->fetchRow(DB_FETCHMODE_ASSOC);
		}
		return $data;
	}
	
	
	/*
		モジュール専用ユニットが利用されているページを取得
	*/
	function getMdlUnitUsingPages($module_code_ar) {
		if(!is_array($module_code_ar) || count($module_code_ar)<1) {
			return null;
		}
		$rtn = null;
		$str = implode(", ", $module_code_ar);
		//編集中ページ
		$sql = "SELECT sidx_title,cat_name,pidx_title,pidx_code,unit_extmodule_code
FROM tbl_unit
INNER JOIN tbl_page_unit_tmp ON pageu_unit_code = unit_code
INNER JOIN tbl_page_idx_tmp ON pidx_code = pageu_pidx_code
INNER JOIN tbl_section_idx_tmp ON pidx_sidx_code = sidx_code
INNER JOIN tbl_category_tmp ON pidx_cat_code = cat_code
WHERE sidx_active = 1
AND cat_active = 1
AND pidx_active =1
AND unit_active =1
AND unit_extmodule_code in (".$str.")
ORDER BY unit_extmodule_code DESC , pidx_code";
		$result = $this->db->query($sql);
		if($result != null) {
			while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
				$rtn["tmp"][$row["unit_extmodule_code"]][$row["pidx_code"]] = $row["sidx_title"]."&gt;".$row["cat_name"]."&gt;".$row["pidx_title"];
			}
		}
		//公開ページ
		$sql = "SELECT sidx_title,cat_name,pidx_title,pidx_code,unit_extmodule_code
FROM tbl_unit
INNER JOIN tbl_page_unit ON pageu_unit_code = unit_code
INNER JOIN tbl_page_idx ON pidx_code = pageu_pidx_code
INNER JOIN tbl_section_idx ON pidx_sidx_code = sidx_code
INNER JOIN tbl_category ON pidx_cat_code = cat_code
WHERE sidx_active = 1
AND cat_active = 1
AND pidx_active =1
AND unit_active =1
AND unit_extmodule_code in (".$str.")
ORDER BY unit_extmodule_code DESC , pidx_code";
		$result = $this->db->query($sql);
		if($result != null) {
			while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
				$rtn["open"][$row["unit_extmodule_code"]][$row["pidx_code"]] = $row["sidx_title"]."&gt;".$row["cat_name"]."&gt;".$row["pidx_title"];
			}
		}
		return $rtn;

	}
	
	
	function ModuleVersionUpdate($code) {
		if(!is_numeric($code)){
			return "モジュールコードが不明です。";
		}
		//モジュールディレクトリ内のupdate.phpにお任せ
		//util/update.phpの読み込み
		$inst_module_path = $this->getModulePathFromCode($code);
		if(file_exists($inst_module_path."/util/update.php")) {
			//util/update.phpの実行
			$this->db->query('BEGIN');
			require_once($inst_module_path."/util/update.php");
			if(function_exists("module_update")) {
				if(!module_update($param=$this->getData($code), &$this->db)) {
					$this->db->query('ROLLBACK');
					return false;
				} else {
					$this->db->query('COMMIT');	
					return true;
				}
			}
		} else {
			return "モジュールアップデート用プログラムファイルがありません。";
		}
	}
	
	
	
	/*
		モジュールマスタ削除処理
		付属のモジュール専用ユニットや関連データの削除
	*/
	function delete($code) {
		if(!is_numeric($code)){
			return "削除コードが不明です。";
		}
		
		$inst_module_path = $this->getModulePathFromCode($code);
		if(file_exists($inst_module_path."/util/delete.php")) {
			require_once($inst_module_path."/util/delete.php");
		}
		$module_data = $this->getData($code);
		
		$this->db->query('BEGIN');
		
		//モジュールマスタの削除
		if(!$this->db->update("tbl_extmodule",array("extmodule_active"=>0),"extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "モジュールマスタレコードの削除に失敗しました。";
		}
		
		//モジュール専用ユニットの削除
		if(!$this->db->update("tbl_unit",array("unit_active"=>0),"unit_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "ユニットマスタレコードの削除に失敗しました。";
		}
		
		//ユニット配置情報の削除
		if(!$this->db->query("delete from tbl_page_unit where pageu_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "ページに配置されたモジュール専用ユニットの削除に失敗しました。";
		}
		if(!$this->db->query("delete from tbl_page_unit_tmp where pageu_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "ページに配置されたモジュール専用ユニット（編集中）の削除に失敗しました。";
		}

		//クリップボードの削除
		if(!$this->db->update("tbl_unitclip",array("unitclip_active"=>0),"pageu_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "クリップボード上のユニットレコードの削除に失敗しました。";
		}
		
		//自動生成用構成保存テーブルの削除
		//tbl_extsection_idx, tbl_extcategory, tbl_extpage_idx, tbl_extpage_container, tbl_extpage_unit
		if(!$this->db->query("delete from tbl_extsection_idx where sidx_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "モジュール自動生成メニュー構成データの削除に失敗しました。";
		}
		if(!$this->db->query("delete from tbl_extcategory where cat_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "モジュール自動生成サブメニュー構成データの削除に失敗しました。";
		}
		if(!$this->db->query("delete from tbl_extpage_idx where pidx_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "モジュール自動生成ページ構成データの削除に失敗しました。";
		}
		if(!$this->db->query("delete from tbl_extpage_container where pagec_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "モジュール自動生成ページ構成データの削除に失敗しました。";
		}
		if(!$this->db->query("delete from tbl_extpage_unit where pageu_extmodule_code=".$code)) {
			$this->db->query('ROLLBACK');
			return "モジュール自動生成ページユニット構成データの削除に失敗しました。";
		}
		$this->db->query('COMMIT');
		//モジュール、およびモジュール専用ユニットが作成したテーブルの削除
		//モジュールディレクトリ内のdelete.phpにお任せ
		//util/install.phpの読み込み
		//$inst_module_path = $this->getModulePathFromCode($code);
		if(file_exists($inst_module_path."/util/delete.php")) {
			if(function_exists("module_delete")) {
				if(!module_delete($param=$module_data, &$this->db)) {
					commonFile::AlldelDir($inst_module_path);
					return "モジュール本体は削除しましたが、モジュールデータベースの削除に失敗しました。";
				}
			}
		}
		//モジュールディレクトリの削除
		commonFile::AlldelDir($inst_module_path);
		return true;
		
	}
	
	function installModule($zipfilepath) {
		require_once("Archive/Zip.php");
		$objZip = new Archive_Zip($zipfilepath);
		//workディレクトリに展開し、path,セッティングファイルがあるか確認
		$tmp_path = str_replace(".zip","",$zipfilepath);
		$p_params = array ('add_path' => MODULES_WORKPATH);
		if(!$objZip->extract($p_params)) {
			@unlink($zipfilepath);
			return "・zipファイルの解凍に失敗しました";
		}
		//moduleCnf.incの確認
		if(!is_file($tmp_path."/moduleCnf.inc")) {
			@unlink($zipfilepath);
			commonFile::AlldelDir($tmp_path);
			return "・モジュール設定ファイル(moduleCnf.inc)がありません。zipファイルの内容を確認してください";
		}
		require_once($tmp_path."/moduleCnf.inc");
		//$VERSION, $NAME, $TYPEを読み込み
		if($VERSION==""||$NAME==""||$TYPE==""||$PATH=="") {
			@unlink($zipfilepath);
			commonFile::AlldelDir($tmp_path);
			return "・モジュール設定ファイル(moduleCnf.inc)の内容が正しくありません。zipファイルの内容を確認してください";
		}
		if($TYPE!=6) {
			@unlink($zipfilepath);
			commonFile::AlldelDir($tmp_path);
			return "・モジュールパッケージファイルではありません。";
		}
		if(!isset($ADMINPATH)) {
			$ADMINPATH = null;
		}

		//インストール先のパス
		$inst_module_path = MODULES_PATH."/".$PATH;
		//同じpathがいないかチェック
		if(is_dir($inst_module_path)){
			@unlink($zipfilepath);
			commonFile::AlldelDir($tmp_path);
			return "・既にインストールされています";
		}
		
		//自動生成するレコードがある場合は、ページ生成時に各ユニットのコードが必要となるため、
		//ここでマスタのリストを取得しておく
		require_once (LIB_PATH.'/common/unitDB.php');
		$unitDB = new unitDB();
		$_m_unit = $unitDB->getlist($unit_type=array(2,3,4,5,6)); //2:デザインユニット, 3:機能ユニットのみ取得しておく 2008.11.12 4:拡張Dユニット、5:拡張機能ユニット 6:拡張モジュールユニット追加
		//ユニットディレクトリ名称からコードが引けるように配列を再構成
		if(is_array($_m_unit)&&count($_m_unit)>0) {
			foreach($_m_unit as $mkey => $mval) {
				$unit_list[$mval["unit_idname"]] = $mval;
			}
		}
		//DB保存　まずはtbl_extmodule
		$record = array("extmodule_code"=>$this->db->getKey("tbl_extmodule_extmodule_code"),
						"extmodule_active"=>1,
						"extmodule_name"=>$NAME,
						"extmodule_summary"=>$SUMMARY,
						"extmodule_ver"=>$VERSION,
						"extmodule_path"=>$PATH,
						"extmodule_admin_path"=>$ADMINPATH,
						"extmodule_cdate"=>"now()"
						);
		$this->db->query('BEGIN');
		if(!$this->db->insert("tbl_extmodule",$record)) {
			$this->db->query('ROLLBACK');
			@unlink($zipfilepath);
			commonFile::AlldelDir($tmp_path);
			return "・モジュールデータの保存に失敗しました";
		}
		//モジュール専用ユニットをtbl_unitへ保存（あれば）
		//moduleCnf.incの読み込みから$installunitを取得する
		if(is_array($installunit)&&count($installunit)>0) {
			foreach($installunit as $key => $val) {
				$installunit[$key]["instcode"] = $this->db->getKey("tbl_unit_unit_code");
				$unitrecord = array("unit_code" => $installunit[$key]["instcode"],
											"unit_type" => 6,
											"unit_name" => $val["unit_name"],
											"unit_tpl_path" => $val["unit_tpl_path"],
											"unit_php_path" => $val["unit_php_path"],
											"unit_order" => $installunit[$key]["instcode"],
											"unit_cache" => $val["unit_cache"],
											"unit_extmodule_code" => $record["extmodule_code"],
											"unit_extmodule_private" => $val["unit_extmodule_private"],
											"unit_cdate" => "now()",
											"unit_ver" => $record["extmodule_ver"]
											);
				if(!$this->db->insert("tbl_unit",$unitrecord)) {
					$this->db->query('ROLLBACK');
					@unlink($zipfilepath);
					commonFile::AlldelDir($tmp_path);
					return "・モジュールユニットの保存に失敗しました";
				}
				
				$unit_list[$key] = $unitrecord;
			}
		}
					
		//自動生成メニュー、ページがあればそれをDBに保存（install.xml）
		if(is_file($tmp_path."/util/install.xml")) {
			require_once("XML/Unserializer.php");
			$xml = file_get_contents($tmp_path."/util/install.xml");
			$options = array('encoding' => 'UTF-8',
										'complexType' => "array",
										'forceEnum' => array('cat','page','container','unit'),
										'parseAttributes'=>true); // or array
			$unserializer =& new XML_Unserializer($options);
			$unserializer->setOption('parseAttributes', TRUE);
			$status = $unserializer->unserialize($xml);
			if (PEAR::isError($status)) {
				$this->db->query('ROLLBACK');
				@unlink($zipfilepath);
				commonFile::AlldelDir($tmp_path);
				return "・自動生成メニュー構成ファイルの読み取りに失敗しました。";
			}
			$data = $unserializer->getUnserializedData();
			
			if(is_array($data)&&count($data)>0) {
				foreach($data as $key=>$val) { //sidx
					if($key=="sidx") {
						//sidxレコードの登録
						$extsidx_code = $this->db->getKey("tbl_extsection_idx_sidx_code");
						$sidx_record = array("sidx_code"=>$extsidx_code,
											 "sidx_extmodule_code"=>$record["extmodule_code"],
											 "sidx_c_date"=>"now()",
											 "sidx_u_date"=>"now()"
											 );
						//$sidx_recordの保存
						if(!$this->db->insert("tbl_extsection_idx",$sidx_record)) {
							$this->db->query('ROLLBACK');
							@unlink($zipfilepath);
							commonFile::AlldelDir($tmp_path);
							return "・自動生成メニューレコード(sidx)の保存に失敗しました。";
						}
						//cat
						if(isset($val["cat"])&&is_array($val["cat"])&&count($val["cat"])>0) {
							foreach($val["cat"] as $catkey => $catval) {
								//$catval["dir"]と$catval["name"]を確認
								//先頭レコードはトップカテゴリとし、保存はしない（既にある）
								if($catkey==0) {
									$extcat_code = 1; //トップカテゴリは共通ID=1
								} else {
									//catレコードの登録
									$extcat_code = $this->db->getKey("tbl_extcategory_cat_code");
									$cat_record = array("cat_code"=>$extcat_code,
																	"cat_extmodule_code"=>$record["extmodule_code"],
																	"cat_type"=>2,
																	"cat_sidx_code"=>$extsidx_code,
																	"cat_name"=>$catval["name"],
																	"cat_order"=>$extcat_code,
																	"cat_dir"=>$catval["dir"],
																	"cat_c_date"=>"now()",
																	"cat_u_date"=>"now()"
																);
									//$cat_recordの保存
									if(!$this->db->insert("tbl_extcategory",$cat_record)) {
										$this->db->query('ROLLBACK');
										@unlink($zipfilepath);
										commonFile::AlldelDir($tmp_path);
										return "・自動生成メニューレコード(cat)の保存に失敗しました。";
									}
								}
								//page
								if(isset($catval["page"])&&is_array($catval["page"])&&count($catval["page"])>0) {
									foreach($catval["page"] as $pagekey => $pageval) {
										//先頭レコードはトップページとする
										if($pagekey==0) {
											$ptype = 2;
											$url = null;
										} else {
											$ptype = 0;
											$url = str_replace(array('.',',',';',':',' ','	','　'),"",$pageval["dir"]);
										}
										//pageレコードの登録
										$extpidx_code = $this->db->getKey("tbl_extpage_idx_pidx_code");
										$pidx_record = array("pidx_code"=>$extpidx_code,
																		"pidx_extmodule_code"=>$record["extmodule_code"],
																		"pidx_sidx_code"=>$extsidx_code,
																		"pidx_cat_code"=>$extcat_code,
																		"pidx_view"=>0,
																		"pidx_ptype"=>$ptype,
																		"pidx_url"=>$url,
																		"pidx_date"=>"now()",
																		"pidx_title"=>$pageval["name"],
																		"pidx_c_date"=>"now()",
																		"pidx_u_date"=>"now()"
																	);
										if(!$this->db->insert("tbl_extpage_idx",$pidx_record)) {
											$this->db->query('ROLLBACK');
											@unlink($zipfilepath);
											commonFile::AlldelDir($tmp_path);
											return "・自動生成メニューレコード(page)の保存に失敗しました。";
										}
										//container
										if(isset($pageval["container"])&&is_array($pageval["container"])&&count($pageval["container"])>0) {
											$container_order = 0;
											foreach($pageval["container"] as $containerkey => $containerval) {
												$extpagec_code = $this->db->getKey("tbl_extpage_container_pagec_code");
												$container_record = array("pagec_code" => $extpagec_code,
																						"pagec_extmodule_code" => $record["extmodule_code"],
																						"pagec_pidx_code" => $extpidx_code,
																						"pagec_type" => empty($containerval["type"]) ? "cunit_1" : $containerval["type"],
																						"pagec_order" => $container_order
																					);
												if(!$this->db->insert("tbl_extpage_container",$container_record)) {
													$this->db->query('ROLLBACK');
													@unlink($zipfilepath);
													commonFile::AlldelDir($tmp_path);
													return "・自動生成メニューレコード(container)の保存に失敗しました。";
												}
												$container_order++;
												
												//unit
												if(isset($containerval["unit"])&&is_array($containerval["unit"])&&count($containerval["unit"])>0) {
													$unit_order = 0;
													foreach($containerval["unit"] as $unitkey => $unitval) {
														if(isset($unitval["name"])&&$unitval["name"]!="") {
															//unit名がないとコードを取得できないので必須とする
															//ユニット名称からユニットマスタコードを取得する
															//$unit_list[$mval["unit_idname"]] = $mval;
															$extpageu_code = $this->db->getKey("tbl_extpage_unit_pageu_code");
															$extpageu_record = array("pageu_code" => $extpageu_code,
																									"pageu_extmodule_code" => $unit_list[$unitval["name"]]["unit_extmodule_code"],
																									"pageu_pidx_code" => $extpidx_code,
																									"pageu_pagec_code" => $extpagec_code,
																									"pageu_column" => empty($unitval["column"]) ? 0 : $unitval["column"],
																									"pageu_unit_code" => $unit_list[$unitval["name"]]["unit_code"],
																									"pageu_order" => $unit_order
																									);
															if(!$this->db->insert("tbl_extpage_unit",$extpageu_record)) {
																$this->db->query('ROLLBACK');
																@unlink($zipfilepath);
																commonFile::AlldelDir($tmp_path);
																return "・自動生成メニューレコード(unit)の保存に失敗しました。";
															}
															$unit_order++;
														}
													} // foreach
												} //end of unit if
											} //end of container foreach
										}
									}
								}
							}
						}
					}
				}
			}
		}
		//インストール先に解凍
		$p_params = array ('add_path' => MODULES_PATH);
		$result = $objZip->extract($p_params);
		
		//util/install.phpの読み込み
		if(!file_exists($inst_module_path."/util/install.php")) {
			$this->db->query('ROLLBACK');
			@unlink($zipfilepath);
			commonFile::AlldelDir($tmp_path);
			commonFile::AlldelDir($inst_module_path);
			return "・取り込んだモジュールのインストール実行ファイルが見つかりません。";
		}
		//util/install.phpの実行
		//モジュールが持つテーブル、ユニットが持つテーブルの生成を行う
		require_once($inst_module_path."/util/install.php");
		if(!function_exists("module_install")) {
			$this->db->query('ROLLBACK');
			@unlink($zipfilepath);
			commonFile::AlldelDir($tmp_path);
			commonFile::AlldelDir($inst_module_path);
			return "・取り込んだモジュールのインストール実行関数が見つかりません。";
		}
		if(!module_install($param=$record, &$this->db)) {
			$this->db->query('ROLLBACK');
			@unlink($zipfilepath);
			commonFile::AlldelDir($tmp_path);
			commonFile::AlldelDir($inst_module_path);
			return "・取り込んだモジュールのテーブル生成に失敗しました。";
		}
		
		//すべてが正常に処理できたらコミット
		$this->db->query('COMMIT');	
		@unlink($zipfilepath);
		commonFile::AlldelDir($tmp_path);
		
		//ユニットCSSの書き出し
		require_once (LIB_PATH.'/common/pageunitDB.php');
		$pageunitDB = new pageunitDB();
		$pageunitDB->putAllUnitsCSS($pageunitDB->makeAllUnitsCSS());
		
		return "・新しいモジュールをインストールしました。";
	}

	
	/*
	 * モジュールコードから保存先のディレクトリ名を取得
	 */
	function getModulePathFromCode($extmodule_code) {
		if(is_numeric($extmodule_code)) {
			$sql = "select extmodule_path from tbl_extmodule where extmodule_code = ".$extmodule_code;
			$result = $this->db->getOne($sql);
			return MODULES_PATH."/".$result;
		}
		return null;
	}
}
?>