MOMOちゃんねるは掲示板です。クライアント側の機能は単純で、書き込み、読み込み、Re:と、書き込み数制限などです。サーバー側では個別削除、全削除、削除用パスワード設定、ログインフォームなどです。
パスワード設定 | |
![]() 設定順番① | ![]() ② |
![]() ③ | |
1.初期設定は管理者パスワード設定フォーム 2.設定するとadmin_password.txtのファイルがハッシュ化されて自動生成 3.管理者パスワード設定された後は、管理者パスワード設定フォームのアドレスを入力しても管理者専用メッセージ削除フォームに移動 4.管理者パスワード変更は、管理者専用メッセージ削除フォームでログイン後でなければ、"権限がありません"を表示 5.管理者パスワード変更は現在のパスワード、新パスワードを入力して変更ボタン 6.万が一パスワードを忘れた場合はadmin_password.txtを直接削除後に再設定可能(テキスト自動再生成) 7.1000件を超えた場合に新スレッド表示、旧スレッドリンクボタン ※詳細は最後の方に記述 |


ご利用について |
掲示板コードは仕様の変更・修正・削除・追加・デザイン変更等、全て制限がなくご自由にアレンジしても差し支えありません。また、コード全体をコピー&ペーストして、動作不具合が発生しても作成者は責任を負う事は致しません。 ※全体を変更なく使用された場合は、どこか片隅に小さくMOMOPLANと書いて頂くと嬉しいです。 |
ファイル名・階層 |
chat/ ├── index.html←クライアント側フォーム ├── script.js←フォーム入力処理 ├── send.php←書き込み・読み込み処理 ├── style.css←デザイン ├── new_script.js←オプション ├── new_send.php←オプション ├── admin/ ← 🔒 管理ページ専用フォルダ(アクセス制限) ├── admin_delete.html←管理者削除フォーム ├── admin_delete.php←===↓==削除・読み込み処理 ├── admin_messages.php ├── admin_script.js←====↑== ├── admin_setup.php←パスワード登録 ├── admin_auth.php←認証 ├── admin_change_password.php←パスワード変更 ├── style.css ├── admin_password.txt←パスワード ├── messages.txt←書き込み内容 フォルダーはお好きな名前で※各ファイルの相対・絶対pathの変更 |
【ファイルジャンプ】 |
index.html script.js send.php style.css admin_delete.html admin_script.js admin_delete.php admin_messages.php admin_setup.php admin_auth.php style.css admin_change_password.php new_script.js new_send.php 1000件を超える場合の処理 |
クライアント側 |
HTML |
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>チャットルーム</title>
<link rel="stylesheet" href=" client_style.css">
</head>
<body>
<center><h2>MOMOちゃんねる</h2></center>
<div class="scroller" id="chat-box"></div>
<h2><div><input type="text" id="username" placeholder="ユーザー名を入力..."><font color="#6633ff"> 【コード公開用のサンプルチャットです・・・】</font></div></h2>
<textarea class="tarea" input type="text" id="message" placeholder="メッセージを入力..."></textarea>
<input type="hidden" id="reply_to" value="0"><br>
<button class="bchat" id="send-button" onclick="sendMessage()" disabled>送信</button>
<script src="script.js"></script>
</body>
</html>
JS |
document.addEventListener("DOMContentLoaded", function () {
let messageInput = document.getElementById("message");
let sendButton = document.getElementById("send-button");
function checkMessage() {
// メッセージが空欄のときだけ送信ボタンを無効化
sendButton.disabled = messageInput.value.trim() === "";
}
// 入力が変更されたらリアルタイムでチェック
messageInput.addEventListener("input", checkMessage);
// 初回チェック(リロード後も適用)
checkMessage();
});
// メッセージ送信
function sendMessage() {
let username = document.getElementById("username").value;
let message = document.getElementById("message").value;
let reply_to = document.getElementById("reply_to").value;
fetch("send.php", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "username=" + encodeURIComponent(username) +
"&message=" + encodeURIComponent(message) +
"&reply_to=" + encodeURIComponent(reply_to)
}).then(() => {
document.getElementById("message").value = "";
document.getElementById("reply_to").value = "0";
});
}
// 返信ボタン処理
function replyToMessage(id) {
document.getElementById("reply_to").value = id;
document.getElementById("message").focus();
}
// メッセージの読み込み
function loadMessages() {
fetch("messages.txt")
.then(response => response.text())
.then(text => {
let chatBox = document.getElementById("chat-box");
chatBox.innerHTML = "";
text.split("\n").forEach(line => {
if (line.trim()) {
let match = line.match(/^(\d+) \| \[(\d{2}-\d{2} \d{2}:\d{2})\] (.+): (.+)$/);
if (!match) {
console.warn("不正データのためスキップ:", line);
return;
}
let msgId = match[1];
let date = match[2];
let username = match[3];
let message = match[4];
chatBox.innerHTML += `<div><strong>${msgId}</strong> | ${date} | ${username}: ${message}
<button onclick="replyToMessage(${msgId})">Re:</button></div>`;
}
});
})
.catch(error => console.error("メッセージ読み込みエラー:", error));
}
// メッセージの定期更新
setInterval(loadMessages, 1000);
send.php |
<?php
session_start();
$file_path = "messages.txt";
// メッセージ送信データ取得
$username = isset($_POST["username"]) && trim($_POST["username"]) !== "" ? htmlspecialchars($_POST["username"]) : "名無しさん";
/*$username = trim($_POST["username"] ?? "");*/
$message = trim($_POST["message"] ?? "");
$reply_to = intval($_POST["reply_to"] ?? 0); // 数値IDで取得
// メッセージファイルの読み込み
$messages = file($file_path, FILE_IGNORE_NEW_LINES);
$new_id = count($messages) + 1;
if ($new_id > 1000) {
die("エラー: メッセージの数が上限に達しました。新規登録はできません。");
}
$reply_user = "";
// 返信元のユーザー名を取得
foreach ($messages as $line) {
preg_match("/^$reply_to \| \[\d{2}-\d{2} \d{2}:\d{2}\] (\S+):/", $line, $matches);
if (!empty($matches[1])) {
$reply_user = $matches[1]; // ユーザー名を取得
break;
}
}
// 新しいIDを設定
$new_id = count($messages) + 1;
// メッセージのフォーマット
$new_message = "$new_id | [" . date("m-d H:i") . "] $username: $message";
if ($reply_to > 0 && !empty($reply_user)) {
$new_message .= " (Re: $reply_user)"; // ユーザー名を返信参照に追加
}
// メッセージファイルに保存
$result = file_put_contents($file_path, $new_message . "\n", FILE_APPEND);
if ($result === false) {
die("エラー: ファイルに書き込めませんでした!");
}
/*session_start();
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = isset($_POST["username"]) && trim($_POST["username"]) !== "" ? htmlspecialchars($_POST["username"]) : "名無しさん";
$message = htmlspecialchars($_POST["message"]);
$reply_to = isset($_POST["reply_to"]) ? intval($_POST["reply_to"]) : 0;
// IDを生成(簡易的な連番方式)
$messages = file("messages.txt");
$new_id = count($messages) + 1;
// メッセージのフォーマット
$log = "$new_id | [" . date("m-d H:i") . "] " . $username . ": " . $message;
if ($reply_to > 0) {
$log .= " (Re: $reply_to)";
}
$log .= "\n";
file_put_contents("messages.txt", $log, FILE_APPEND);
}*/
?>
CSS |
body {
background-image: url("chat.png");
color: #131314; /* 文字色を見やすく */
font-family: Arial, sans-serif;
font-weight: bold;
margin: 2em;
}
.scroller {
margin: 0 auto;
width: auto;
height: 360px;
margin : 30px ;
padding : 20px ;
color: #2e4f97; /* 文字色を見やすく */
overflow-y: scroll;
background-color: #fcf3f8;
border: solid 4px #DE94BF;
border-radius: 6px;
scrollbar-color: #FAF8D8 #DE94BF;
scrollbar-width: thin;
line-height: 1.8em
}
#message {
width: 50%;
}
.message-box {
border: 2px solid #a292fd;
padding: 10px;
background-color: #b7a7fc; /* 白背景で読みやすく */
width: auto;
border-radius: 12px;
}
button {
background-color: #ffe48c; /* 緑のボタンで優しい印象に */
color: rgb(63, 61, 61);
border: none;
padding: 2px 8px;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background-color: #caa53d;
}
.bchat{
background-color: #2e7ff8;
color: #ebf6ff;
padding: 12px 24px;
}
.tarea{
display: inline-block;
width: 10%;
padding: 10px;
border-radius:20px;
box-sizing: border-box;
background: #d7d4f5;
margin: 0.5em 0;
line-height: 1.5;
height: 4em;
}
#username{
display: inline-block;
width: 20%;
padding: 4px;
border-radius:6px;
box-sizing: border-box;
background: #e7e6f8;
color: #4034e4;
font-weight: bold;
margin: 0.5em 0;
line-height: 1;
height: 2em;
}
サーバー側 |
HTML |
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>管理者専用メッセージ削除</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h2>管理者専用メッセージ削除</h2>
<!-- 管理者ログインフォーム -->
<form id="login-form">
<input type="text" name="username" autocomplete="username" style="display:none;">
<input type="password" id="admin_password" placeholder="管理者パスワード" autocomplete="new-password">
<button type="button" onclick="adminLogin()">ログイン</button>
</form>
<!-- メッセージ一覧と削除ボタン(ログイン後に表示) -->
<div id="delete-section" style="display: none;">
<h3>メッセージ一覧</h3>
<div id="message-list" class="message-box">
<!-- メッセージ一覧を動的に追加 -->
</div>
</div>
<button onclick="deleteAllMessages()">全削除</button>
<script src="admin_script.js"></script>
</body>
</html>
JS |
function adminLogin() {
let password = document.getElementById("admin_password").value;
fetch("admin_auth.php", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "password=" + encodeURIComponent(password)
})
.then(response => response.text())
.then(result => {
if (result === "success") {
document.getElementById("login-form").style.display = "none";
document.getElementById("delete-section").style.display = "block";
fetchMessages(); // メッセージ一覧を取得
} else {
alert("認証失敗:パスワードが間違っています");
}
});
}
function fetchMessages() {
fetch("admin_messages.php")
.then(response => response.json())
.then(messages => {
let messageList = document.getElementById("message-list");
messageList.innerHTML = "";
messages.forEach(msg => {
let msgElement = document.createElement("div");
msgElement.innerHTML = `${msg.id} | ${msg.username}: ${msg.message}
<button onclick="deleteMessage(${msg.id})">削除</button>`;
messageList.appendChild(msgElement);
});
});
}
function deleteMessage(messageId) {
fetch("admin_delete.php", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "delete_id=" + encodeURIComponent(messageId)
})
.then(response => response.text())
.then(result => {
console.log("削除レスポンス:", result); // 削除結果をコンソールに表示
fetchMessages(); // メッセージ一覧を更新
});
}
function deleteAllMessages() {
if (!confirm("本当にすべてのメッセージを削除しますか?")) {
return; // ユーザー確認
}
fetch("admin_delete.php", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "delete_all=true"
})
.then(() => fetchMessages()); // 削除後に一覧を更新
}
admin_delete.php |
<?php
session_start();
if (!isset($_SESSION["admin"]) || $_SESSION["admin"] !== true) {
die("権限がありません");
}
$file_path = "messages.txt";
// ファイルが存在しない場合は作成
if (!file_exists($file_path)) {
file_put_contents($file_path, ""); // 空ファイルを作成
}
// ファイルを読み込む
$messages = file($file_path, FILE_IGNORE_NEW_LINES);
if ($messages === false) {
$messages = [];
}
// IDの取得(文字+数値を許可&空白削除)
$delete_id = $_POST["delete_id"] ?? "";
error_log("削除リクエストID: " . $delete_id);
$filtered_messages = array_filter($messages, function($line) use ($delete_id) {
return !preg_match("/^$delete_id \| /", $line); // 正確なIDが一致する行のみ削除
});
file_put_contents($file_path, implode("\n", $filtered_messages) . "\n");
echo "メッセージID '" . htmlspecialchars($delete_id) . "' を削除しました";
if (isset($_POST["delete_all"]) && $_POST["delete_all"] === "true") {
file_put_contents($file_path, ""); // ファイルを空にする
echo "すべてのメッセージを削除しました";
exit;
}
?>
admin_messages.php |
<?php
session_start();
if (!isset($_SESSION["admin"]) || $_SESSION["admin"] !== true) {
die(json_encode([])); // 認証されていない場合は空リスト
}
$file_path = "messages.txt";
$messages = file($file_path, FILE_IGNORE_NEW_LINES);
$response = [];
//=============================================================
/*foreach ($messages as &$line) {
if (preg_match("/\(Re: $delete_id\)/", $line)) {
$line = preg_replace("/\(Re: $delete_id\)/", "(Re: 管理者の都合で削除されました)", $line);
}
}
file_put_contents($file_path, implode("\n", $messages) . "\n");*/
foreach ($messages as $line) {
preg_match("/^(\d+) \| \[\d{2}-\d{2} \d{2}:\d{2}\] (\S+): (.*)/", $line, $matches);
if (!empty($matches)) {
$response[] = ["id" => $matches[1], "username" => $matches[2], "message" => $matches[3]];
}
}
echo json_encode($response);
?>
admin_setup.php |
<?php
session_start();
$file_path = "admin_password.txt";
// すでにパスワードが設定されている場合、ログイン画面へリダイレクト
if (file_exists($file_path)) {
header("Location: admin_delete.html");
exit();
}
// 初回パスワード設定処理
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$new_password = password_hash($_POST["new_password"], PASSWORD_DEFAULT); // ハッシュ化
file_put_contents($file_path, $new_password);
echo "パスワードが設定されました!管理画面へ移動してください。";
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>管理者パスワード設定</title>
</head>
<body>
<h2>管理者パスワード設定</h2>
<form method="POST">
<input type="password" name="new_password" placeholder="管理者パスワードを入力">
<button type="submit">設定</button>
</form>
</body>
</html>
admin_auth.php |
<?php
session_start();
$file_path = "admin_password.txt";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (!file_exists($file_path)) {
die("管理者パスワードが設定されていません。");
}
$saved_password = file_get_contents($file_path);
$input_password = $_POST["password"] ?? "";
if (password_verify($input_password, $saved_password)) {
$_SESSION["admin"] = true;
echo "success";
} else {
echo "error";
}
}
?>
CSS |
body {
background-color: #ccc;
color: #333; /* 文字色を見やすく */
font-family: Arial, sans-serif;
}
#chat-box {
height: 300px;
overflow-y: scroll;
border: 1px solid #918afa;
padding: 10px;
width: auto;
}
#message {
width: 100%;
}
.message-box {
border: 2px solid #333;
padding: 10px;
margin: 10px 0;
background-color: #b5dbff;
width: auto;
border-radius: 8px;
}
.message-box div {
padding: 5px;
margin: 5px 0;
border-bottom: 1px solid #ccc;
}
button {
background-color: #4d26db;
color: white;
border: none;
padding: 4px 8px;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background-color: #cc0000;
}
admin_change_password.php |
<?php
session_start();
$file_path = "admin_password.txt";
// 認証チェック
if (!isset($_SESSION["admin"]) || $_SESSION["admin"] !== true) {
die("権限がありません");
}
// 現在のパスワード取得
$saved_password = file_exists($file_path) ? file_get_contents($file_path) : "";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$current_password = $_POST["current_password"] ?? "";
$new_password = $_POST["new_password"] ?? "";
// 現在のパスワードが正しいか確認
if (!password_verify($current_password, $saved_password)) {
die("エラー: 現在のパスワードが正しくありません");
}
// 新しいパスワードの設定
$hashed_new_password = password_hash($new_password, PASSWORD_DEFAULT);
file_put_contents($file_path, $hashed_new_password);
echo "パスワードが変更されました!";
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>管理者パスワード変更</title>
</head>
<body>
<h2>管理者パスワード変更</h2>
<form method="POST">
<input type="password" name="current_password" placeholder="現在のパスワードを入力">
<input type="password" name="new_password" placeholder="新しいパスワードを入力">
<button type="submit">変更</button>
</form>
</body>
</html>
1000件を超える場合の処置 |
現状の仕様は1000件を超えるとif ($new_id > 1000) {die("エラー: メッセージの数が上限に達しました。新規登録はできません。");}で終了しますが、オプションとして1000件を超えると新スレッドに切り替わり、過去ログはリンクボタンが表示されて、クリックすると閲覧できる仕様となります。オプションのコードを下記に書きましたので参考にして下さい。 |
new_script.js |
document.addEventListener("DOMContentLoaded", function () {
let messageInput = document.getElementById("message");
let sendButton = document.getElementById("send-button");
function checkMessage() {
sendButton.disabled = messageInput.value.trim() === "";
}
messageInput.addEventListener("input", checkMessage);
checkMessage();
let threadList = document.getElementById("thread-list"); // ✅ スレッド一覧の要素を取得
let files = ["messages1.txt", "messages2.txt", "messages3.txt"]; // ✅ 過去スレッドリスト
files.forEach(file => {
let threadLink = document.createElement("button"); // ✅ ボタンを作成
threadLink.textContent = `📂 過去ログ: ${file}`;
threadLink.dataset.file = file;
threadLink.addEventListener("click", function () {
loadMessages(file); // ✅ 指定ファイルのメッセージをロード
});
threadList.appendChild(threadLink);
threadList.appendChild(document.createElement("br"));
});
});
function sendMessage() {
let username = document.getElementById("username").value;
let message = document.getElementById("message").value;
let reply_to = document.getElementById("reply_to").value;
fetch("send.php", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "username=" + encodeURIComponent(username) +
"&message=" + encodeURIComponent(message) +
"&reply_to=" + encodeURIComponent(reply_to)
}).then(() => {
document.getElementById("message").value = "";
document.getElementById("reply_to").value = "0";
});
}
function replyToMessage(id) {
document.getElementById("reply_to").value = id;
document.getElementById("message").focus();
}
// ✅ 指定ファイルのメッセージをロードできるよう修正
function loadMessages(filePath = "messages.txt") {
fetch(filePath)
.then(response => response.text())
.then(text => {
let chatBox = document.getElementById("chat-box");
chatBox.innerHTML = "";
text.split("\n").forEach(line => {
if (line.trim()) {
let match = line.match(/^(\d+) \| \[(\d{2}-\d{2} \d{2}:\d{2})\] (.+): (.+)$/);
if (!match) return;
let msgId = match[1];
let date = match[2];
let username = match[3];
let message = match[4];
chatBox.innerHTML += `<div><strong>${msgId}</strong> | ${date} | ${username}: ${message}
<button onclick="replyToMessage(${msgId})">Re:</button></div>`;
}
});
})
.catch(error => console.error(`エラー: ${filePath} の読み込みに失敗`, error));
}
// ✅ 1秒ごとに最新の messages.txt をロード(リアルタイム更新)
setInterval(() => loadMessages("messages.txt"), 1000);
new_send.php |
<?php
session_start();
$base_file = "messages"; // 基本ファイル名
$extension = ".txt"; // 拡張子
$file_index = 1;
// **最新のファイルを決定**
while (file_exists($base_file . $file_index . $extension)) {
$file_index++;
}
// 最新ファイルのパス(直前のファイルを取得)
$file_path = $base_file . ($file_index - 1) . $extension;
if (!file_exists($file_path)) {
file_put_contents($file_path, ""); // 新規作成
}
// メッセージ送信データ取得
$username = isset($_POST["username"]) && trim($_POST["username"]) !== "" ? htmlspecialchars($_POST["username"]) : "名無しさん";
$message = trim($_POST["message"] ?? "");
$reply_to = intval($_POST["reply_to"] ?? 0); // 数値IDで取得
// **現在のメッセージ数を取得**
$messages = file($file_path, FILE_IGNORE_NEW_LINES);
$new_id = count($messages) + 1;
// **1000件を超えたら新しいファイルへ切り替え**
if ($new_id > 1000) {
$file_index++; // 新しいファイル番号へ
$file_path = $base_file . $file_index . $extension;
file_put_contents($file_path, ""); // 空の新規ファイルを作成
$new_id = 1; // **新しいファイルではIDをリセット**
}
$reply_user = "";
// **返信元のユーザー名を取得**
foreach ($messages as $line) {
preg_match("/^$reply_to \| \[\d{2}-\d{2} \d{2}:\d{2}\] (\S+):/", $line, $matches);
if (!empty($matches[1])) {
$reply_user = $matches[1]; // ユーザー名を取得
break;
}
}
// **メッセージのフォーマット**
$new_message = "$new_id | [" . date("m-d H:i") . "] $username: $message";
if ($reply_to > 0 && !empty($reply_user)) {
$new_message .= " (Re: $reply_user)"; // **返信元ユーザー名を追加**
}
// **メッセージファイルに保存**
$result = file_put_contents($file_path, $new_message . "\n", FILE_APPEND);
if ($result === false) {
die("エラー: ファイルに書き込めませんでした!");
}
echo "メッセージが正常に保存されました!(ファイル: $file_path)";
?>
以上です・・・・・・・ 実装サンプル