<?php
/*
 * まるごとホームページCMS
 * Copyright (c) 2010-2025 by Crytus All rights reserved.
 *
 * 【注意】このファイルの修正は充分ご注意ください。
 * 修正する前には、必ずバックアップを保存してください。
 * 動作がおかしくなったら元のファイルに戻してください。
 * このファイルの漢字コードはHTMLと同じにしてください。
 *
 * 2024/08/12 まるごとホームページCMS新規作成
 * 2025/04/19 ブロック入れ替えのバグ修正
 *
 */
ini_set("short_open_tag", "0");
ini_set("mbstring.encoding_translation", "0");

define("TEST", "test");

if (!defined("LOGPREFIX")) {
	define("LOGPREFIX", "log/");
}
// エラー出力の指定
error_reporting (E_ALL & ~E_NOTICE & ~E_WARNING & ~E_STRICT & ~E_DEPRECATED);

// セッションの開始
session_start();

// データベースファイルの場所と名前
$DB_URI = "data/cms.sqlite";

define("SCRIPT_ENCODING", "UTF-8");
$script_encoding = SCRIPT_ENCODING;

// データベースの漢字コード
define("DB_ENCODING", "UTF-8");

if ($_SERVER["HTTP_HOST"] == "localhost") {
define("DOCUMENT1", "http://localhost/cms_2024/newcms/document");
} else {
define("DOCUMENT1", "http://q19cms.com/marugoto/document");
}
//----------------------------------------------
// ブロック定義
$block_no = 1;
//
$block = array(
	// partsディレクトリのファイルを読み込み時に追加されます。
);
// ブロック定義の読み込み
$block_dir = "./parts/*.php";

$files = glob($block_dir);
foreach ($files as $file) {
	include($file);
}

// ブロックリスト
$block_list = array("" => "");
foreach ($block as $key => $val) {
	$block_list[$key] = $val["title"];
}

// 管理処理に渡すデータ
$cms_setup = array(
	"block" => $block,
	"block_list" => $block_list,
	"common_items" => $common_items,
	"kiji_list" => $kiji_list,
);
//
include("demo.php");
//----------------------------------------------
// CMSの共通処理
$dbh = new PDO("sqlite:" . $DB_URI);
// ----------------------------------
$act = $_REQUEST["act"];
if (!$act) {
	$act = "index";
}
// バイナリー処理のファイル
$binary = array(
	"png",
	"jpg",
	"gif",
);

if ($act == "file") {
	$file = $_REQUEST["f"];
	if ($file) {
		if (file_exists($file)) {
			$contents = file_get_contents($file);
		} else if (file_exists("_files/" . $file)) {
			$contents = file_get_contents("_files/" . $file);
		} else {
			$contents = load_blob($file);
			$ary = explode(".", $file);
			if (count($ary) > 1) {	// 拡張子あり？
				if (in_array($ary[count($ary)-1], $binary)) {
					$contents = base64_decode($contents);
				}
			}
		}
	}
	header('Content-Type: ' . get_mime_types($file));
	echo $contents;
	exit;
}
// --------------------------------
exec_file("sqlite_db.inc");
exec_file("dbaccess.inc");

exec_file("cms.inc");
exec_file("info.inc");
exec_file("info_item.inc");
exec_file("image.inc");

exec_file("htmltemplate.inc");
exec_file("cms_core.php");
exec_file("markdown.php");
// --------------------------------

// 動作環境チェック
if (!function_exists("mb_convert_encoding")) {
	error_exit("日本語変換処理が使えません。ご確認をお願い致します。");
}
if (!class_exists("PDO")) {
	error_exit("簡易データベース(SQLite3)が使えません。ご確認をお願い致します。");
} else {
	$ary = PDO::getAvailableDrivers();
	if (!in_array("sqlite", $ary)) {
		error_exit("簡易データベース(SQLite3)が使えません。ご確認をお願い致します。");
	}
}
if (!file_exists($DB_URI)) {
	error_exit("データファイル（{$DB_URI}）がみつかりません。ご確認をお願い致します。");
}
if (!function_exists("ImageCreateFromString")) {
	error_exit("画像処理が組み込まれていません。ご確認をお願い致します。");
}
if (substr(phpversion(), 0, 1) == "4") {
	error_exit("このシステムはPHP5 が必要です。ご確認をお願い致します。");
}
// 異常終了
function error_exit($msg)
{
	echo '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body>';
	echo $msg;
	echo "</body></html>";
	exit;
}
//-------------------------------------------------------------
// XSS対策
if (is_array($_REQUEST) && (!$_SESSION["LOGIN"])) {
	foreach ($_REQUEST as $k => $v) {
		if (is_array($v)) {
			foreach ($v as $k2 => $v2) {
				$_REQUEST[$k][$k2] = mb_ereg_replace("<.*>", "", $v2);
			}
		} else {
			$_REQUEST[$k] = mb_ereg_replace("<.*>", "", $_REQUEST[$k]);
		}
	}
}
// --------------------------------
$siteinfo = get_setup();
$data["siteinfo"] = $siteinfo;
// --------------------------------
// ページ設定
$cond = array(
	"kind" => INFO_PROPATY,
);
$page_index = array();
$list = Info::findData($cond);
foreach ($list as $key => $val) {
	$info = get_setup(INFO_PROPATY, $val["info_id"]);
	$info["info_id"] = $val["info_id"];
	$page_index[$info["name"]] = $info;
}
// --------------------------------
// ページデータ更新・ブロック入れ替え
if ($act == "@exchange") {
	//logdump($_REQUEST);
	$cond["kind"] = INFO_ITEM + $page_index[$_REQUEST["p"]]["info_id"];
	$info = Info::findData($cond);
	$page = get_infoitem($info[0]["info_id"]);	// ページ内容
	//logdump($page);
	// 入れ替え
	$i1 = $_REQUEST["i1"];
	$i2 = $_REQUEST["i2"];
	$page = item_change($page, $i1, "item99");
	$page = item_change($page, $i2, $i1);
	$page = item_change($page, "item99", $i2);
	//logdump($page);
	// 保存
	save_infoitem($info[0]["info_id"], $page);
	exit;
}
// ページ定義更新
if ($act == "@pagesave") {
	//logdump($_REQUEST);
	//logdump($_FILES);
	$item = $_REQUEST;
	unset($item["id"]);
	unset($item["mode"]);
	unset($item["act"]);
	// ファイル処理
	foreach ($_FILES as $key => $val) {
		if ($val["name"]) {
			if ($item[$key . "_old"]) {
				delete_image($item[$key . "_old"]);
			}
			$img_id = save_image($_FILES[$key], "");
			if ($img_id) {
				$item[$key] = $img_id;
			} else {
				$msg[$key] = $_FILES[$key]["name"] . "は保存できませんでした。画像は" . ini_get("upload_max_filesize") . "バイト以内にしてください。";
				if ($item[$key . "_old"]) {
					$item[$key] = $item[$key . "_old"];
				}
			}
		} else if ($item[$key . "_old"]) {
			if ($item[$key . "_del"]) {
				delete_image($item[$key . "_old"]);	// 画像は削除
				$item[$key] = array();
			} else if ($item[$key . "_old"]) {
				$item[$key] = $item[$key . "_old"];
			}
		}
	}
	//logdump($item);
	save_infoitem($_REQUEST["id"], $item);
	exit;
}
// --------------------------------
// ページ出力
// 共通データ
$info = get_setup(INFO_COMMON);
if ($common_items["func"]) {
	$data["common"] = call_user_func($common_items["func"], $common_items, $info, $act);
} else {
	$data["common"] = $info;
}
// 追加スタイルシート
if ($info["stylesheet"]) {
	$data["head_tag"] = $info["stylesheet"];
}
// 追加スクリプト
if ($info["javascript"]) {
	$data["body_tag"] = $info["javascript"];
}
// ページ内容
if ($page_index[$act]) {	// 設定データ
	unset($cond);
	$cond["kind"] = INFO_ITEM + $page_index[$act]["info_id"];
	$info = Info::findData($cond);
	$page = get_infoitem($info[0]["info_id"]);	// ページ内容
} else if ($act == "@preview") {	// プレビュー
	$page = get_infoitem($_REQUEST["id"]);	// ページのデータ
	$info = Info::getData($_REQUEST["id"]);	// ページの基本情報
	$info = get_setup(INFO_PROPATY, $info["kind"] - INFO_ITEM);		// ページの設定
	$act = $info["name"];	// ページの名称（＝URL）
} else {
	//
}
//
$data["act" . $act] = $act;		// 現在の処理
$data["act"] = $act;		// 現在の処理
//
$js_file = array();		// 読み込むJavascriptファイル
$css_file = array();		// 読み込むCSSファイル
if ($page) {
	$data["page"] = $page;
	// ブロックごとの処理
	$data["contents"] = "";
	$inst = new htmltemplate();
	for ($i = 1; $i <= 20; $i++) {	// 最大20ブロック
		$info = "info" . $i;	// ブロックデータの接頭辞
		$title = $page_index[$act][$info . "_title"];	// ブロックのタイトル
		$type = $page_index[$act][$info . "_type"];		// ブロックの種類
		if ($title && $type) {		// 使用しているブロック
			$item = info_page($page, $info . "_", $block[$type]);	// ブロックデータの共通変換
			if ($block[$type]["func"]) {					// ブロックの個別処理関数があれば
				$item = call_user_func($block[$type]["func"], $block[$type], $item, $act);	// 個別処理を行う
			}
			$value["item"] = $item;	// ブロックデータ
			$value["id"] = $info;	// ブロックID
			$value["act"] = $act;
			if ($block[$type]["output"]) {	// 指定位置へ埋め込み出力
				$data[$block[$type]["output"]] .= htmltemplate::buffer($block[$type]["template"], $value);
			} else {	// 通常位置へ埋め込み出力
				$data["contents"] .= "<a id=\"{$act}_{$info}\"></a>\n";
				$data["contents"] .= htmltemplate::buffer($block[$type]["template"], $value);
			}
		}
		// 追加スタイルシート
		if ($block[$type]["stylesheet"]) {
			$data["head_tag"] .= $block[$type]["stylesheet"];
		}
		if ($block[$type]["css_file"]) {
			// 同じファイルを2回読み込まないための仕組み
			$css_file[$block[$type]["css_file"]] = $block[$type]["css_file"];
		}
		// 追加スクリプト
		if ($block[$type]["javascript"]) {
			$data["body_tag"] .= $block[$type]["javascript"];
		}
		if ($block[$type]["js_file"]) {
			// 同じファイルを2回読み込まないための仕組み
			$js_file[$block[$type]["js_file"]] = $block[$type]["js_file"];
		}
	}
	if (!(substr($act, 0, 1) == "@")) {
		// ログ出力
		logwritecsv(array($act, $_SERVER["REMOTE_ADDR"],$_SERVER['PHP_AUTH_USER']), "page");
	}
} else {
	// ページデータなしの場合
	if ($page_index["404"]) {
		header("location: ./?act=404");
		exit;
	} else {
		$data["contents"] .= "<h3>該当するページは存在しません。URLをご確認ください。</h3>";
	}
}
// サイドコンテンツの処理
if ($page_index["@side"]) {
	unset($cond);
	$cond["kind"] = INFO_ITEM + $page_index["@side"]["info_id"];
	$info = Info::findData($cond);
	$page = get_infoitem($info[0]["info_id"]);	// ページ内容
	$inst = new htmltemplate();
	for ($i = 1; $i <= 10; $i++) {	// 最大10ブロック
		$info = "info" . $i;	// ブロックデータの接頭辞
		$title = $page_index["@side"][$info . "_title"];	// ブロックのタイトル
		$type = $page_index["@side"][$info . "_type"];		// ブロックの種類
		if ($title && $type) {		// 使用しているブロック
			$item = info_page($page, $info . "_", $block[$type]);	// ブロックデータの共通変換
			if ($block[$type]["func"]) {					// ブロックの個別処理関数があれば
				$item = call_user_func($block[$type]["func"], $block[$type], $item, $act);	// 個別処理を行う
			}
			$value["item"] = $item;	// ブロックデータ
			$value["id"] = $info;	// ブロックID
			$value["act"] = $act;
			if ($block[$type]["output"]) {	// 指定位置へ埋め込み出力
				$data[$block[$type]["output"]] .= htmltemplate::buffer($block[$type]["template"], $value);
			}
		}
		// 追加スタイルシート
		if ($block[$type]["stylesheet"]) {
			$data["head_tag"] .= $block[$type]["stylesheet"];
		}
		if ($block[$type]["css_file"]) {
			// 同じファイルを2回読み込まないための仕組み
			$css_file[$block[$type]["css_file"]] = $block[$type]["css_file"];
		}
		// 追加スクリプト
		if ($block[$type]["javascript"]) {
			$data["body_tag"] .= $block[$type]["javascript"];
		}
		if ($block[$type]["js_file"]) {
			// 同じファイルを2回読み込まないための仕組み
			$js_file[$block[$type]["js_file"]] = $block[$type]["js_file"];
		}
	}
}

// 現在のURL
$data["page_url"] = $_SERVER["REQUEST_SCHEME"] . "://" . $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
//
// 追加スタイルシート
if ($css_file) {
	foreach ($css_file as $f) {
		$data["head_tag"] .= "\n<link rel=\"stylesheet\" href=\"parts/{$f}\">";
	}
}
// 追加スクリプト
if ($js_file) {
	foreach ($js_file as $f) {
		$data["body_tag"] .= "\n<script src=\"parts/{$f}\"></script>";
	}
}
//
if ($act == "index") {
	$html = "top.html";	// トップページ専用テンプレート
	if ($page_index["@side"] && file_exists("./templates/top2.html")) {
		$html = "top2.html";	// トップページ専用テンプレート・サイドコンテンツあり
	}
} else {
	$html = "page.html";	// 一般ページ用テンプレート
	if ($page_index["@side"] && file_exists("./templates/page2.html")) {
		$html = "page2.html";	// 一般ページ用テンプレート・サイドコンテンツあり
	}
}
if (file_exists($act . ".html")) {	// 専用テンプレートがあればそれを使う
	$html = $act . ".html";
}
//
$html = filecheck($html);	// 実ファイルチェック
htmltemplate::t_include($html, $data);	// ページ表示
exit;
// --------------------------------
// お知らせ
function get_newinfo($max=10, $kind=INFO_NEWS, $from="", $to="", $genre="")
{
	global $DB_URI;

	$sql = "select * from info where open=1 and kind=" . $kind;
	if ($from) {
		$sql .= " and date>='{$from}'";
	}
	if ($to) {
		$sql .= " and date<='{$to}'";
	}
	if ($genre) {
		$sql .= " and genre='{$genre}'";
	}
	$inst = DBConnection::getConnection($DB_URI);
	$ret = $inst->search_sql($sql);
	$list = array();
	if ($ret["count"]) {
		foreach ($ret["data"] as $val) {
			$item = get_setup($kind, $val["info_id"]);
			$item["info_id"] = $val["info_id"];
			if ($item["open"]) {
				$item["reg_date"] = $val["reg_date"];
				if (newflag(-1, $item["news_date"])) {
					$item["new_flag"] = "1";
				}
				if (!$item["news_date"]) {
					$item["news_date"] = $item["reg_date"];
				}
				$t = strtotime($item["news_date"]);
				while (1) {
					if ($list[$t]) {
						$t++;
					} else {
						$list[$t] = $item;
						break;
					}
				}
			}
		}
	}
	// 並べ替え
	$data = array();
	if ($list) {
		$count = 0;
		krsort($list);
		foreach ($list as $val) {
			$val["news_date"] = substr($val["news_date"], 0, 4) . "/" . substr($val["news_date"], 5, 2) . "/" . substr($val["news_date"], 8, 2);
			$data[] = $val;
			$count++;
			if ($count >= $max) {
				break;
			}
		}
	}
	return $data;
}
function newflag($new, $date)
{
	if (defined("NEW_DAYS") && NEW_DAYS) {
		$t = intval((time() - strtotime($date)) / 86400);
		if ($new < 0) {
			if ($t < NEW_DAYS) return 1;
		} else if (defined("NEW_OR")) {
			// OR(新着または設定日数以内の場合)
			if ($new || ($t < NEW_DAYS)) return 1;
		} else if (defined("NEW_AND")) {
			// AND(新着かつ設定日数以内の場合)
			if ($new && ($t < NEW_DAYS)) return 1;
		} else {
			// 自動判定のみ
			if ($t < NEW_DAYS) return 1;
		}
	} else {
		// 新着設定のみ
		if ($new > 0) return 1;
	}
	return 0;
}
// 特定ブロックのデータを抽出して、マークダウン指定のチェックと、画像とファイルの取得を行う
function info_page($page, $info, $def)
{
	$data = array();
	foreach ($page as $key => $val) {
		if (substr($key, 0, strlen($info)) == $info) {
			if ($def["input"][substr($key, strlen($info))]["type"] == "textarea") {		// markdownのチェック
				$val = md_check($val);
			}
			if (($def["input"][substr($key, strlen($info))]["type"] == "image")||
				($def["input"][substr($key, strlen($info))]["type"] == "file")) {		// 画像またはファイルのチェック
				$f = Image::getData($val);
				if ($f) {
					$data[substr($key, strlen($info)) . "_file"] = $f["save_name"];		// 保存ファイル名
					$data[substr($key, strlen($info)) . "_name"] = $f["name"];			// 元のファイル名
				}
			}
			$data[substr($key, strlen($info))] = $val;
		}
	}
	// 繰り返し要素処理
	if ($def["repeat"]) {	// 繰り返し要素の宣言がある
		$list = array();
		for ($i = 1; $i <= 10; $i++) {
			unset($item);
			foreach ($def["repeat"] as $key) {
				if ($data[$key . $i]) {		// 値がある
					if ($def["input"][$key . $i]["type"] == "image") {
						$item[$key] = $data[$key . $i . "_file"];
					} else {
						$item[$key] = $data[$key . $i];
					}
				}
			}
			if ($item) {
				$list[] = $item;
			}
		}
		if ($list) {
			$data["list"] = $list;
		}
	}
	return $data;
}
// マークダウン変換
function md_check($data)
{
	if (substr($data, 0, 3) == "#md") {
		$data = "<span class=\"markdown-text\">" . markdown(substr($data, 3)) . "</span>";
	} else {
		$data = nl2br($data);
	}
	return $data;
}
// ブロックを追加する
function block_add($item)
{
	global $block;
	global $block_no;

	if ($item["name"]) {
		if ($block[$item["name"]]) {
			echo "ブロック {$item["name"]} が重複しています。";
			return;
		}
		$block[$item["name"]] = $item;
	} else {
		$block[$block_no] = $item;
		$block_no++;
	}
}

function exec_file($file)
{
	global $setup;
	global $DB_URI;
	global $script_encoding;
	global $cms_setup;

	if (file_exists("_files/" . $file)) {
		include_once("_files/" . $file);
	} else {
		$php = load_blob($file);
		if ($php) {
			eval("?>" . $php);
		}
	}
}

// 大きなデータを取り出す
function load_blob($key)
{
	global $dbh;

//echo "load:{$key}<br>";
	$sql = "select seq from file_list where name='{$key}'";
	//$result = sqlite_query($dbh, $sql);
	//$val = sqlite_fetch_all($result);
	$ret = $dbh->query($sql);
	$val = $ret->fetchAll();

	if ($val) {
		$val = $val[0];
		$id = $val["seq"];
		$file = load_db_file($id);
		return $file;
	}
	return false;
}

function load_db_file($id)
{
	global $dbh;

	$data = "";
	$sql = "select contents from file_contents where file_num={$id} order by seq";
	$stmt = $dbh->query($sql);
	while ($ret = $stmt->fetch(PDO::FETCH_ASSOC)) {
		$data .= $ret["contents"];
	}
	return $data;
}
// htmlファイル存在チェック
function filecheck($html, $default = "top.html")
{
	if (file_exists($html)) {
		return $html;
	}
	if (file_exists("./templates/" . $html)) {
		return $html;
	}
	return $default;
}
// ファイルの拡張子からMIMEタイプを得る
function get_mime_types($file)
{
	$mime_types = array(
		'txt' => 'text/plain',
		'htm' => 'text/html',
		'html' => 'text/html',
		'php' => 'text/html',
		'css' => 'text/css',
		'js' => 'application/javascript',
		'json' => 'application/json',
		'xml' => 'application/xml',
		'swf' => 'application/x-shockwave-flash',
		'flv' => 'video/x-flv',

		// images
		'png' => 'image/png',
		'jpe' => 'image/jpeg',
		'jpeg' => 'image/jpeg',
		'jpg' => 'image/jpeg',
		'gif' => 'image/gif',
		'bmp' => 'image/bmp',
		'ico' => 'image/vnd.microsoft.icon',
		'tiff' => 'image/tiff',
		'tif' => 'image/tiff',
		'svg' => 'image/svg+xml',
		'svgz' => 'image/svg+xml',

		// archives
		'zip' => 'application/zip',
		'rar' => 'application/x-rar-compressed',
		'exe' => 'application/x-msdownload',
		'msi' => 'application/x-msdownload',
		'cab' => 'application/vnd.ms-cab-compressed',

		// audio/video
		'mp3' => 'audio/mpeg',
		'qt' => 'video/quicktime',
		'mov' => 'video/quicktime',

		// adobe
		'pdf' => 'application/pdf',
		'psd' => 'image/vnd.adobe.photoshop',
		'ai' => 'application/postscript',
		'eps' => 'application/postscript',
		'ps' => 'application/postscript',

		// ms office
		'doc' => 'application/msword',
		'rtf' => 'application/rtf',
		'xls' => 'application/vnd.ms-excel',
		'ppt' => 'application/vnd.ms-powerpoint',

		// open office
		'odt' => 'application/vnd.oasis.opendocument.text',
		'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
	);

	$ary = explode(".", $file);
	if (count($ary) > 1) {	// 拡張子あり？
		if ($mime_types[$ary[count($ary)-1]]) {
			return $mime_types[$ary[count($ary)-1]];
		}
	}
	return "application/octet-stream";	// 不明なファイル
}
// ブロック移動
function item_change($page, $i1, $i2)
{
	//logwrite($i1 . "->" . $i2);
	$page_new = array();
	foreach ($page as $key => $val) {
		$ary = explode("_", $key);
		if ($ary[0] == $i1) {
			array_shift($ary);
			$page_new[$i2 . "_" . implode("_", $ary)] = $val;
		} else {
			$page_new[$key] = $val;
		}
	}
	return $page_new;
}
// ページ切り替え
function pager($cur, $pages)
{
	$page = array();
	$no = 0;
	if ($cur > 0) {
		$page['prev'] = array('no' => $cur - 1, 'name' => '前へ', 'link' => 1);
	}
	for ($i = 0; $i < $pages; $i++) {
		if ($i == $cur) {
			$page['list'][$no] = array('no' => $i, 'name' => $i + 1);
		} else {
			$page['list'][$no] = array('no' => $i, 'name' => $i + 1, 'link' => 1);
		}
		$no++;
	}
	if ($cur < ($pages - 1)) {
		$page['next'] = array('no' => $cur + 1, 'name' => '次へ', 'link' => 1);
	}
	return $page;
}
// ログ関数
function logwrite($str, $file="log")
{
	if (($fp = fopen(LOGPREFIX . $file . date('Ymd') . ".log" , "a+"))) {
		fputs($fp, $str . "\n");
		fclose($fp);
	}
}
function logwritecsv($data, $file="log")
{
	if (($fp = fopen(LOGPREFIX . $file . date('Ymd') . ".csv", "a+"))) {
		array_unshift($data, date('H:i:s'));
		fputs($fp, implode(",", $data) . "\n");
		fclose($fp);
	}
}
function logdump($item, $file="log")
{
	ob_start();
	var_dump($item);
	$str = ob_get_contents();
	ob_end_flush();
	logwrite($str, $file);
}
