useMemory(false); $cache->setContent(db()->fetchAll(" SELECT `id`, `text`, `time`, `report_id`, `status` FROM `znote_changelog` ORDER BY `id` DESC; ") ?: []); $cache->save(); } // The public page writes 35 for entries created by hand; reports.php writes the // report status. Kept as-is so both keep meaning the same thing. const ACP_CHANGELOG_MANUAL_STATUS = 35; // --------------------------------------------------------------------------- // Mutations // --------------------------------------------------------------------------- if ($_SERVER['REQUEST_METHOD'] === 'POST') { $do = (string)($_POST['do'] ?? ''); $id = intv($_POST['id'] ?? 0); if ($do === 'delete' && $id > 0) { db()->execute("DELETE FROM `znote_changelog` WHERE `id` = ? LIMIT 1;", [$id]); acp_changelog_rebuild_cache(); acp_log('changelog.delete', '#' . $id); acp_flash_success(t('acp.chg.deleted')); acp_redirect('changelog'); } $text = trim((string)($_POST['text'] ?? '')); if ($text === '') { acp_flash_error(t('acp.chg.empty')); acp_redirect('changelog', $id > 0 ? ['action' => 'edit', 'id' => $id] : []); } // The column is varchar(255). Cutting is no longer safe now that the text can // carry BBCode - substr() would happily slice [b]word[/b] into [b]word[/ and // leave the tag dangling on the public page. Refuse instead and say why. if (strlen($text) > 254) { acp_flash_error(t('acp.chg.too_long', ['n' => strlen($text)])); acp_redirect('changelog', $id > 0 ? ['action' => 'edit', 'id' => $id] : []); } if ($do === 'update' && $id > 0) { db()->execute(" UPDATE `znote_changelog` SET `text` = ? WHERE `id` = ? LIMIT 1; ", [$text, $id]); acp_changelog_rebuild_cache(); acp_log('changelog.update', '#' . $id, ['text' => substr($text, 0, 60)]); acp_flash_success(t('acp.chg.updated')); acp_redirect('changelog'); } if ($do === 'create') { $when = intv($_POST['time'] ?? 0); if ($when <= 0) { $when = time(); } db()->execute(" INSERT INTO `znote_changelog` (`text`, `time`, `report_id`, `status`) VALUES (?, ?, 0, ?); ", [$text, $when, ACP_CHANGELOG_MANUAL_STATUS]); acp_changelog_rebuild_cache(); acp_log('changelog.create', '', ['text' => substr($text, 0, 60)]); acp_flash_success(t('acp.chg.published')); acp_redirect('changelog'); } acp_flash_error(t('acp.chg.unknown_action')); acp_redirect('changelog'); } // --------------------------------------------------------------------------- // View state // --------------------------------------------------------------------------- $entries = db()->fetchAll(" SELECT `id`, `text`, `time`, `report_id`, `status` FROM `znote_changelog` ORDER BY `id` DESC; "); $entries = is_array($entries) ? $entries : []; $editing = null; if (($_GET['action'] ?? '') === 'edit') { $editId = intv($_GET['id'] ?? 0); foreach ($entries as $entry) { if ((int)$entry['id'] === $editId) { $editing = $entry; break; } } if ($editing === null) { acp_flash_error(t('acp.chg.not_found')); acp_redirect('changelog'); } } $fromReports = 0; foreach ($entries as $entry) { if ((int)$entry['report_id'] > 0) { $fromReports++; } } ?>

(int)$editing['id']]) : t('acp.chg.new_entry') ?>

150, 'maxlength' => 254, // A changelog line is one sentence in a varchar(255), so the // toolbar stays small - lists and images would not fit. 'toolbar' => 'bold,italic,underline,strike|color,removeformat|link,unlink|undo,redo,source', ]); ?>