fork download
  1. <?php
  2.  
  3. define('CONTACTS_FILE_DIR', __DIR__ . '/data/');
  4. define('CONTACTS_FILE_NAME', 'contacts.tsv');
  5. define('CONTACTS_FILE_PATH', CONTACTS_FILE_DIR . CONTACTS_FILE_NAME);
  6. define('MAX_NAME_LENGTH', 50);
  7. define('MAX_MESSAGE_LENGTH', 1000);
  8.  
  9. define('STATUS_UNCONFIRMED', 0);
  10. define('STATUS_CONFIRMED', 1);
  11.  
  12. if (!is_dir(CONTACTS_FILE_DIR)) {
  13. if (!mkdir(CONTACTS_FILE_DIR, 0755, true)) {
  14. die('連絡事項保存用ディレクトリの作成に失敗しました。パーミッションを確認してください。');
  15. }
  16. }
  17.  
  18. $errors = [];
  19. $form_data = [
  20. 'name' => '',
  21. 'message_text' => ''
  22. ];
  23.  
  24. // 連絡事項の追加処理 (変更なし)
  25. if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'add_contact') {
  26. $form_data['name'] = isset($_POST['name']) ? trim($_POST['name']) : '';
  27. $form_data['message_text'] = isset($_POST['message_text']) ? trim($_POST['message_text']) : '';
  28.  
  29. if (empty($form_data['name'])) $errors[] = "名前を入力してください。";
  30. elseif (mb_strlen($form_data['name']) > MAX_NAME_LENGTH) $errors[] = "名前は" . MAX_NAME_LENGTH . "文字以内で入力してください。";
  31. if (empty($form_data['message_text'])) $errors[] = "連絡事項を入力してください。";
  32. elseif (mb_strlen($form_data['message_text']) > MAX_MESSAGE_LENGTH) $errors[] = "連絡事項は" . MAX_MESSAGE_LENGTH . "文字以内で入力してください。";
  33.  
  34. if (empty($errors)) {
  35. $id = uniqid('contact_', true);
  36. $timestamp = date("Y-m-d H:i:s");
  37. $name_to_save = str_replace(["\t", "\r", "\n"], " ", $form_data['name']);
  38. $message_to_save = str_replace(["\r\n", "\r", "\n"], "<br_temp>", $form_data['message_text']);
  39. $message_to_save = str_replace("\t", " ", $message_to_save);
  40. $status = STATUS_UNCONFIRMED;
  41.  
  42. $data_line = $id . "\t" . $timestamp . "\t" . $name_to_save . "\t" . $message_to_save . "\t" . $status . PHP_EOL;
  43.  
  44. $fp = fopen(CONTACTS_FILE_PATH, 'ab');
  45. if ($fp) {
  46. if (flock($fp, LOCK_EX)) {
  47. if (fwrite($fp, $data_line) === false) {
  48. $_SESSION['message_display'] = "連絡事項の保存に失敗しました。(書き込みエラー)"; $_SESSION['message_type'] = "error";
  49. } else {
  50. $_SESSION['message_display'] = "連絡事項が追加されました!"; $_SESSION['message_type'] = "success";
  51. $form_data = ['name' => '', 'message_text' => ''];
  52. }
  53. flock($fp, LOCK_UN);
  54. } else { $_SESSION['message_display'] = "連絡事項の保存に失敗しました。(ロック取得失敗)"; $_SESSION['message_type'] = "error"; }
  55. fclose($fp);
  56. } else { $_SESSION['message_display'] = "連絡事項の保存に失敗しました。(ファイルオープン失敗)"; $_SESSION['message_type'] = "error"; }
  57. header("Location: " . $_SERVER['PHP_SELF']); exit;
  58. } else {
  59. $_SESSION['message_display'] = implode("<br>", $errors); $_SESSION['message_type'] = "error";
  60. $_SESSION['form_data'] = $form_data;
  61. header("Location: " . $_SERVER['PHP_SELF']); exit;
  62. }
  63. }
  64.  
  65. // 連絡事項の削除処理 (変更なし)
  66. if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'delete_contact' && isset($_POST['contact_id'])) {
  67. $contact_id_to_delete = trim($_POST['contact_id']);
  68. $temp_file_path = CONTACTS_FILE_DIR . 'contacts_temp.tsv';
  69. $deleted = false;
  70. if (file_exists(CONTACTS_FILE_PATH)) {
  71. $fp_read = fopen(CONTACTS_FILE_PATH, 'rb'); $fp_write = fopen($temp_file_path, 'wb');
  72. if ($fp_read && $fp_write) {
  73. if (flock($fp_read, LOCK_SH) && flock($fp_write, LOCK_EX)) {
  74. while (($line = fgets($fp_read)) !== false) {
  75. $parts = explode("\t", $line);
  76. if (isset($parts[0]) && trim($parts[0]) === $contact_id_to_delete) { $deleted = true; }
  77. else { fwrite($fp_write, $line); }
  78. }
  79. flock($fp_write, LOCK_UN); flock($fp_read, LOCK_UN);
  80. } else { $_SESSION['message_display'] = "ファイルのロックに失敗しました(削除時)。"; $_SESSION['message_type'] = "error"; }
  81. fclose($fp_read); fclose($fp_write);
  82. if ($deleted) {
  83. if (rename($temp_file_path, CONTACTS_FILE_PATH)) { $_SESSION['message_display'] = "連絡事項が削除されました。"; $_SESSION['message_type'] = "success"; }
  84. else { $_SESSION['message_display'] = "ファイル更新に失敗しました(削除時)。"; $_SESSION['message_type'] = "error"; @unlink($temp_file_path); }
  85. } else { $_SESSION['message_display'] = "削除対象の連絡事項が見つかりませんでした。"; $_SESSION['message_type'] = "error"; @unlink($temp_file_path); }
  86. } else { if($fp_read) fclose($fp_read); if($fp_write) fclose($fp_write); @unlink($temp_file_path); $_SESSION['message_display'] = "ファイル操作に失敗しました(削除時)。"; $_SESSION['message_type'] = "error"; }
  87. } else { $_SESSION['message_display'] = "連絡事項ファイルが見つかりません。"; $_SESSION['message_type'] = "error"; }
  88. header("Location: " . $_SERVER['PHP_SELF']); exit;
  89. }
  90.  
  91. // 確認状態のトグル処理 (変更なし)
  92. if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'toggle_confirm' && isset($_POST['contact_id'])) {
  93. $contact_id_to_toggle = trim($_POST['contact_id']);
  94. $temp_file_path = CONTACTS_FILE_DIR . 'contacts_temp.tsv';
  95. $toggled = false;
  96. if (file_exists(CONTACTS_FILE_PATH)) {
  97. $fp_read = fopen(CONTACTS_FILE_PATH, 'rb'); $fp_write = fopen($temp_file_path, 'wb');
  98. if ($fp_read && $fp_write) {
  99. if (flock($fp_read, LOCK_SH) && flock($fp_write, LOCK_EX)) {
  100. while (($line = fgets($fp_read)) !== false) {
  101. $parts = explode("\t", rtrim($line, PHP_EOL));
  102. if (isset($parts[0]) && trim($parts[0]) === $contact_id_to_toggle) {
  103. $current_status = isset($parts[4]) ? (int)$parts[4] : STATUS_UNCONFIRMED;
  104. $new_status = ($current_status == STATUS_CONFIRMED) ? STATUS_UNCONFIRMED : STATUS_CONFIRMED;
  105. $parts[4] = $new_status;
  106. $line = implode("\t", $parts) . PHP_EOL;
  107. $toggled = true;
  108. }
  109. fwrite($fp_write, $line);
  110. }
  111. flock($fp_write, LOCK_UN); flock($fp_read, LOCK_UN);
  112. } else { $_SESSION['message_display'] = "ファイルのロックに失敗しました(状態更新時)。"; $_SESSION['message_type'] = "error"; }
  113. fclose($fp_read); fclose($fp_write);
  114. if ($toggled) {
  115. if (rename($temp_file_path, CONTACTS_FILE_PATH)) { $_SESSION['message_display'] = "確認状態が更新されました。"; $_SESSION['message_type'] = "success"; }
  116. else { $_SESSION['message_display'] = "ファイル更新に失敗しました(状態更新時)。"; $_SESSION['message_type'] = "error"; @unlink($temp_file_path); }
  117. } else { $_SESSION['message_display'] = "対象の連絡事項が見つかりませんでした(状態更新時)。"; $_SESSION['message_type'] = "error"; @unlink($temp_file_path); }
  118. } else { if($fp_read) fclose($fp_read); if($fp_write) fclose($fp_write); @unlink($temp_file_path); $_SESSION['message_display'] = "ファイル操作に失敗しました(状態更新時)。"; $_SESSION['message_type'] = "error"; }
  119. } else { $_SESSION['message_display'] = "連絡事項ファイルが見つかりません。"; $_SESSION['message_type'] = "error"; }
  120. header("Location: " . $_SERVER['PHP_SELF']); exit;
  121. }
  122.  
  123. if (isset($_SESSION['message_display'])) { $page_message = $_SESSION['message_display']; $page_message_type = $_SESSION['message_type']; unset($_SESSION['message_display']); unset($_SESSION['message_type']); }
  124. if (isset($_SESSION['form_data'])) { $form_data = $_SESSION['form_data']; unset($_SESSION['form_data']); }
  125.  
  126. function display_contacts() {
  127. if (!file_exists(CONTACTS_FILE_PATH) || filesize(CONTACTS_FILE_PATH) === 0) {
  128. echo "<p>まだ連絡事項はありません。</p>"; return;
  129. }
  130. $lines_array = file(CONTACTS_FILE_PATH, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  131. if ($lines_array === false) { echo "<p>連絡事項ファイルの読み込みに失敗しました。</p>"; return; }
  132. $lines_array = array_reverse($lines_array);
  133.  
  134. $contacts_html = "";
  135. foreach ($lines_array as $line) {
  136. $parts = explode("\t", $line, 5);
  137. if (count($parts) >= 4) {
  138. $id = htmlspecialchars($parts[0], ENT_QUOTES, 'UTF-8');
  139. $timestamp = htmlspecialchars($parts[1], ENT_QUOTES, 'UTF-8');
  140. $name = htmlspecialchars($parts[2], ENT_QUOTES, 'UTF-8');
  141. $message_text_raw = str_replace("<br_temp>", "\n", $parts[3]);
  142. $message_text_display = nl2br(htmlspecialchars($message_text_raw, ENT_QUOTES, 'UTF-8'));
  143. $status = isset($parts[4]) ? (int)$parts[4] : STATUS_UNCONFIRMED;
  144.  
  145. $confirm_button_text = "";
  146. $confirm_dialog_message = "";
  147. $button_confirm_class = "";
  148.  
  149. if ($status == STATUS_CONFIRMED) {
  150. $confirm_button_text = "確認済み";
  151. $confirm_dialog_message = "この連絡事項を未確認に戻しますか?";
  152. $button_confirm_class = "is-confirmed";
  153. } else {
  154. $confirm_button_text = "確認済みにする";
  155. $confirm_dialog_message = "この連絡事項を確認済みにしますか?";
  156. $button_confirm_class = "is-unconfirmed";
  157. }
  158.  
  159. $item_class = ($status == STATUS_CONFIRMED) ? "contact-item confirmed" : "contact-item";
  160.  
  161. $contacts_html .= "<div class='" . $item_class . "'>" .
  162. "<div class='contact-header'>" .
  163. "<strong>" . $name . "</strong>" .
  164. "<small class='contact-date'> (" . $timestamp . ")</small>" .
  165. "<form method='post' action='" . htmlspecialchars($_SERVER["PHP_SELF"], ENT_QUOTES, 'UTF-8') . "' class='confirm-button-form' onsubmit='return confirm(\"" . htmlspecialchars($confirm_dialog_message, ENT_QUOTES, 'UTF-8') . "\");'>" .
  166. "<input type='hidden' name='action' value='toggle_confirm'>" .
  167. "<input type='hidden' name='contact_id' value='" . $id . "'>" .
  168. "<button type='submit' class='button-confirm " . $button_confirm_class . "'>" . $confirm_button_text . "</button>" .
  169. "</form>" .
  170. "<form method='post' action='" . htmlspecialchars($_SERVER["PHP_SELF"], ENT_QUOTES, 'UTF-8') . "' class='delete-button-form' onsubmit='return confirm(\"この連絡事項を削除してもよろしいですか?\");'>" .
  171. "<input type='hidden' name='action' value='delete_contact'>" .
  172. "<input type='hidden' name='contact_id' value='" . $id . "'>" .
  173. "<button type='submit' class='button-delete-item'>削除</button>" .
  174. "</form>" .
  175. "</div>" .
  176. "<p>" . $message_text_display . "</p>" .
  177. "</div>";
  178. }
  179. }
  180. echo $contacts_html;
  181. }
  182. ?>
  183. <!DOCTYPE html>
  184. <html lang="ja">
  185. <head>
  186. <meta charset="UTF-8">
  187. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  188. <title>連絡事項</title>
  189. <style>
  190. body { font-family: sans-serif; line-height: 1.6; margin: 20px; background-color: #f4f4f4; color: #333; }
  191. .container { max-width: 600px; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
  192. h1 { text-align: center; color: #333; }
  193. label { display: block; margin-bottom: 8px; font-weight: bold; }
  194. input[type="text"], textarea {
  195. width: calc(100% - 22px); padding: 10px; margin-bottom: 15px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;
  196. }
  197. textarea { min-height: 60px; resize: vertical; }
  198. button[type="submit"] { background-color: #28a745; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
  199. button[type="submit"]:hover { background-color: #218838; }
  200.  
  201. .message { padding: 10px; margin-bottom: 15px; border-radius: 4px; border-width: 1px; border-style: solid; }
  202. .success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
  203. .error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
  204. .info { background-color: #d1ecf1; color: #0c5460; border-color: #bee5eb; }
  205.  
  206. .contacts-display { margin-top: 30px; border-top: 1px solid #eee; padding-top: 20px; }
  207. .contact-item {
  208. padding: 10px 15px; border-width: 1px 1px 1px 5px; border-style: solid; border-color: #eee #eee #eee; /* 右と下は薄いグレー、左は状態に応じて */
  209. border-radius: 4px; margin-bottom: 10px;
  210. transition: background-color 0.3s ease, border-left-color 0.3s ease;
  211. }
  212. .contact-item.confirmed {
  213. background-color: #e6ffe6; /* 確認済みの背景色 (薄い緑) */
  214. border-left-color: #28a745; /* 緑のアクセントボーダー */
  215. }
  216. .contact-item:not(.confirmed) {
  217. background-color: #e0f7fa; /* 未確認の背景色 (薄い水色) */
  218. border-left-color: #007bff; /* 青のアクセントボーダー */
  219. }
  220.  
  221. .contact-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
  222. .contact-item strong { margin-right: 5px; }
  223. .contact-item .contact-date { font-size: 0.9em; color: #777; flex-grow: 1; min-width: 150px; }
  224. .contact-item p { margin-top: 5px; margin-bottom: 0; word-wrap: break-word; border: 1px solid #ccc; padding: 8px 10px; background-color: #fff; border-radius: 3px; }
  225. .delete-button-form, .confirm-button-form { display: inline-block; margin-left: 5px; }
  226. .button-delete-item, .button-confirm { color: white; padding: 3px 8px; border: none; border-radius: 3px; cursor: pointer; font-size: 0.85em; transition: background-color 0.2s ease; }
  227. .button-delete-item { background-color: #dc3545; }
  228. .button-delete-item:hover { background-color: #c82333; }
  229.  
  230. .button-confirm.is-unconfirmed { background-color: #007bff; }
  231. .button-confirm.is-unconfirmed:hover { background-color: #0056b3; }
  232. .button-confirm.is-confirmed { background-color: #ffc107; color: #212529; }
  233. .button-confirm.is-confirmed:hover { background-color: #e0a800; }
  234. </style>
  235. </head>
  236. <body>
  237. <div class="container">
  238. <h1>連絡事項</h1>
  239. <?php if (isset($page_message) && !empty($page_message)): ?>
  240. <div class="message <?php echo htmlspecialchars($page_message_type, ENT_QUOTES, 'UTF-8'); ?>">
  241. <?php echo $page_message; ?>
  242. </div>
  243. <?php endif; ?>
  244. <form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"], ENT_QUOTES, 'UTF-8'); ?>" method="post">
  245. <input type="hidden" name="action" value="add_contact">
  246. <div>
  247. <label for="name">名前:</label>
  248. <input type="text" id="name" name="name" value="<?php echo htmlspecialchars($form_data['name'], ENT_QUOTES, 'UTF-8'); ?>" required maxlength="<?php echo MAX_NAME_LENGTH; ?>">
  249. </div>
  250. <div>
  251. <label for="message_text">連絡事項:</label>
  252. <textarea id="message_text" name="message_text" rows="3" required maxlength="<?php echo MAX_MESSAGE_LENGTH; ?>"><?php echo htmlspecialchars($form_data['message_text'], ENT_QUOTES, 'UTF-8'); ?></textarea>
  253. </div>
  254. <div>
  255. <button type="submit">追加</button>
  256. </div>
  257. </form>
  258. <div class="contacts-display">
  259. <?php display_contacts(); ?>
  260. </div>
  261. </div>
  262. </body>
  263. </html>
Success #stdin #stdout #stderr 0.02s 26056KB
stdin
Standard input is empty
stdout
連絡事項保存用ディレクトリの作成に失敗しました。パーミッションを確認してください。
stderr
PHP Warning:  mkdir(): Permission denied in /home/B91vBI/prog.php on line 14