From 68a04ab18dad76001b2a81757f3a9d6928ab1f93 Mon Sep 17 00:00:00 2001 From: "E. S." Date: Sat, 9 Mar 2024 03:50:33 +0000 Subject: [PATCH] fix pages editing --- engine/mysql.php | 4 + engine/skin.php | 2 +- handler/AdminHandler.php | 134 ++++++++++++++----------------- htdocs/js/admin/11-write-form.js | 66 +++++++++------ lib/pages.php | 6 +- skin/admin.phps | 31 +++++-- skin/base.phps | 6 +- strings/main.yaml | 1 + 8 files changed, 138 insertions(+), 112 deletions(-) diff --git a/engine/mysql.php b/engine/mysql.php index 9d3a54c..0978fdd 100644 --- a/engine/mysql.php +++ b/engine/mysql.php @@ -131,6 +131,10 @@ class mysql { return $q; } + function error() { + return $this->link?->error; + } + function fetch($q): ?array { $row = $q->fetch_assoc(); if (!$row) { diff --git a/engine/skin.php b/engine/skin.php index 45f0bd0..2bb0394 100644 --- a/engine/skin.php +++ b/engine/skin.php @@ -39,7 +39,7 @@ function render($f, ...$vars): void { $lang = []; foreach ($SkinState->lang as $key) $lang[$key] = lang($key); - $lang = !empty($lang) ? json_encode($lang, JSON_UNESCAPED_UNICODE) : ''; + $lang = !empty($lang) ? jsonEncode($lang, JSON_UNESCAPED_UNICODE) : ''; $title = $SkinState->title; if (!$SkinState->options['is_index']) diff --git a/handler/AdminHandler.php b/handler/AdminHandler.php index f3d842c..bf44bfd 100644 --- a/handler/AdminHandler.php +++ b/handler/AdminHandler.php @@ -122,6 +122,8 @@ class AdminHandler extends request_handler { } function POST_ajax_md_preview() { + if (!is_xhr_request()) + forbidden(); list($md, $title, $use_image_previews) = input('md, title, b:use_image_previews'); $html = markup::markdownToHtml($md, $use_image_previews); $ctx = new SkinContext('\\skin\\admin'); @@ -136,8 +138,16 @@ class AdminHandler extends request_handler { list($name) = input('short_name'); $page = pages::getByName($name); if ($page) - not_found(); - return $this->_get_pageAdd($name); + redirect($page->getUrl(), code: HTTPCode::Found); + add_skin_strings_re('/^(err_)?pages_/'); + add_skin_strings_re('/^(err_)?blog_/'); + set_title(lang('pages_create_title', $name)); + static::make_wide(); + render('admin/pageForm', + short_name: $name, + title: '', + text: '', + langs: PostLanguage::cases()); } function POST_page_add() { @@ -168,7 +178,7 @@ class AdminHandler extends request_handler { } $page = pages::getByName($name); - redirect($page->getUrl()); + ajax_ok(['url' => $page->getUrl()]); } function GET_post_add() { @@ -265,9 +275,11 @@ class AdminHandler extends request_handler { if (!$page) not_found(); + $url = $page->getUrl(); + csrf_check('delpage'.$page->shortName); pages::delete($page); - redirect('/'); + redirect($url, code: HTTPCode::Found); } function GET_post_delete() { @@ -279,7 +291,7 @@ class AdminHandler extends request_handler { csrf_check('delpost'.$post->id); posts::delete($post); - redirect('/articles/'); + redirect('/articles/', code: HTTPCode::Found); } function GET_post_edit() { @@ -400,55 +412,63 @@ class AdminHandler extends request_handler { if (!$page) not_found(); - return $this->_get_pageEdit($page, + add_skin_strings_re('/^(err_)?pages_/'); + add_skin_strings_re('/^(err_)?blog_/'); + set_title(lang('pages_page_edit_title', $page->shortName.'.html')); + static::make_wide(); + $js_text = [ + 'text' => $page->md, + 'title' => $page->title, + ]; + render('admin/pageForm', + is_edit: true, + short_name: $page->shortName, title: $page->title, text: $page->md, - saved: $saved, visible: $page->visible, - ); + saved: $saved, + langs: PostLanguage::cases(), + js_text: $js_text); } - function POST_auto_edit() { + function POST_page_edit() { + if (!is_xhr_request()) + forbidden(); + list($short_name) = input('short_name'); $page = pages::getByName($short_name); - if ($page) { - csrf_check('editpage'.$page->shortName); + if (!$page) + not_found(); - list($text, $title, $visible, $short_name) - = input('text, title, b:visible, new_short_name'); + csrf_check('editpage'.$page->shortName); - $text = trim($text); - $title = trim($title); - $error_code = null; + list($text, $title, $visible, $short_name) + = input('text, title, b:visible, new_short_name'); - if (!$title) { - $error_code = 'no_title'; - } else if (!$text) { - $error_code = 'no_text'; - } else if (!$short_name) { - $error_code = 'no_short_name'; - } + $text = trim($text); + $title = trim($title); + $error_code = null; - if ($error_code) { - return $this->_get_pageEdit($page, - title: $title, - text: $text, - visible: $visible, - ); - } - - $page->edit([ - 'title' => $title, - 'md' => $text, - 'visible' => (int)$visible, - 'short_name' => $short_name, - ]); - - redirect($page->getUrl().'edit/?saved=1'); + if (!$title) { + $error_code = 'no_title'; + } else if (!$text) { + $error_code = 'no_text'; + } else if (!$short_name) { + $error_code = 'no_short_name'; } - not_found(); + if ($error_code) + ajax_error(['code' => $error_code]); + + $page->edit([ + 'title' => $title, + 'md' => $text, + 'visible' => (int)$visible, + 'short_name' => $short_name, + ]); + + ajax_ok(['url' => $page->getUrl().'edit/?saved=1']); } protected static function make_wide() { @@ -458,38 +478,4 @@ class AdminHandler extends request_handler { ]); } - protected function _get_pageAdd( - string $name, - string $title = '', - string $text = '' - ) { - add_skin_strings_re('/^(err_)?pages_/'); - set_title(lang('pages_create_title', $name)); - static::make_wide(); - render('admin/pageForm', - short_name: $name, - title: $title, - text: $text); - } - - protected function _get_pageEdit( - Page $page, - string $title = '', - string $text = '', - bool $saved = false, - bool $visible = false, - ?string $error_code = null - ) { - add_skin_strings_re('/^(err_)?pages_/'); - set_title(lang('pages_page_edit_title', $page->shortName.'.html')); - static::make_wide(); - render('admin/pageForm', - is_edit: true, - short_name: $page->shortName, - title: $title, - text: $text, - visible: $visible, - saved: $saved); - } - } \ No newline at end of file diff --git a/htdocs/js/admin/11-write-form.js b/htdocs/js/admin/11-write-form.js index ebdbd9a..f5d465a 100644 --- a/htdocs/js/admin/11-write-form.js +++ b/htdocs/js/admin/11-write-form.js @@ -7,16 +7,14 @@ class AdminWriteEditForm { this.previewRequest = null this.tocByLang = {} - if (!this.isEditing()) { + if (!this.isEditing() && this.isPost()) { for (const l of opts.langs) { this.tocByLang[l] = false } } this.form.addEventListener('submit', this.onSubmit) - if (this.isPost()) - this.form.title.addEventListener('input', this.onInput) - + this.form.title.addEventListener('input', this.onInput) this.form.text.addEventListener('input', this.onInput) ge('toggle_wrap').addEventListener('click', this.onToggleWrapClick) @@ -46,13 +44,18 @@ class AdminWriteEditForm { this.draft = new Draft(draftId, lang); if (this.isEditing()) { - for (let l in opts.texts) { - this.draft.setLang(l) - this.draft.title = opts.texts[l].title - this.draft.text = opts.texts[l].md - this.tocByLang[l] = opts.texts[l].toc + if (this.isPost()) { + for (let l in opts.texts) { + this.draft.setLang(l) + this.draft.title = opts.texts[l].title + this.draft.text = opts.texts[l].md + this.tocByLang[l] = opts.texts[l].toc + } + this.draft.setLang(lang) + } else { + this.draft.title = opts.text.title + this.draft.text = opts.text.text } - this.draft.setLang(lang) this.showPreview() } else { this.fillFromDraft() @@ -131,21 +134,32 @@ class AdminWriteEditForm { } // fd.append('lang', this.getCurrentLang()) - fd.append('visible', ge('visible_cb').checked ? 1 : 0) + if (this.isPost()) + fd.append('visible', ge('visible_cb').checked ? 1 : 0) - // language-specific fields + // text-specific fields let atLeastOneLangIsWritten = false const writtenLangs = [] - for (const l of this.opts.langs) { - let title = this.draft.getForLang(l, 'title') - let text = this.draft.getForLang(l, 'text') - console.log(`lang: ${l}`, title, text) - if (title !== '' && text !== '') + if (this.isPost()) { + for (const l of this.opts.langs) { + const title = this.draft.getForLang(l, 'title') + const text = this.draft.getForLang(l, 'text') + console.log(`lang: ${l}`, title, text) + if (title !== '' && text !== '') + atLeastOneLangIsWritten = true + fd.append(`title:${l}`, title) + fd.append(`text:${l}`, text) + fd.append(`toc:${l}`, this.tocByLang[l] ? 1 : 0) + writtenLangs.push(l) + } + } else { + const title = this.draft.title + const text = this.draft.text + if (title !== '' && text !== '') { atLeastOneLangIsWritten = true - fd.append(`title:${l}`, title) - fd.append(`text:${l}`, text) - fd.append(`toc:${l}`, this.tocByLang[l] ? 1 : 0) - writtenLangs.push(l) + fd.append('title', title) + fd.append('text', text) + } } if (!atLeastOneLangIsWritten) throw 'no_text' @@ -153,10 +167,12 @@ class AdminWriteEditForm { fd.append('langs', writtenLangs.join(',')) // date field - const dateInput = evt.target.elements.date; - if (!dateInput.value) - throw 'no_date' - fd.append('date', dateInput.value) + if (this.isPost()) { + const dateInput = evt.target.elements.date; + if (!dateInput.value) + throw 'no_date' + fd.append('date', dateInput.value) + } fd.append('token', this.opts.token) cancelEvent(evt) diff --git a/lib/pages.php b/lib/pages.php index 8524cf3..a883483 100644 --- a/lib/pages.php +++ b/lib/pages.php @@ -44,13 +44,13 @@ class Page extends model { class pages { - static function add(array $data): ?int { + static function add(array $data): bool { $db = DB(); $data['ts'] = time(); $data['html'] = markup::markdownToHtml($data['md']); if (!$db->insert('pages', $data)) - return null; - return $db->insertId(); + return false; + return true; } static function delete(Page $page): void { diff --git a/skin/admin.phps b/skin/admin.phps index 2db7329..45fde40 100644 --- a/skin/admin.phps +++ b/skin/admin.phps @@ -269,20 +269,30 @@ function pageForm($ctx, string|Stringable $title, string|Stringable $text, string|Stringable $short_name, + array $langs, bool $is_edit = false, - $error_code = null, ?bool $saved = null, - bool $visible = false): array { + bool $visible = false, + ?array $js_text = null): array { $form_url = '/'.$short_name.'/'.($is_edit ? 'edit' : 'create').'/'; + +// breadcrumbs +if ($is_edit) { + $bc_html = $ctx->bc([ + ['url' => '/'.$short_name.'/', 'text' => $ctx->lang('view_page')] + ], 'padding-bottom: 20px'); +} else { + $bc_html = ''; +} + $html = <<if_true($error_code, fn() => '
'.$ctx->lang('err_pages_'.$error_code).'
')} + {$ctx->if_true($saved, fn() => '
'.$ctx->lang('info_saved').'
')} +{$bc_html}
- -
{$ctx->lang('pages_write_form_title')}
@@ -312,7 +322,16 @@ $html = << HTML; -$js_params = json_encode(['pages' => true, 'edit' => $is_edit]); +$js_params = [ + 'pages' => true, + 'edit' => $is_edit, + 'token' => $is_edit ? $ctx->csrf('editpage'.$short_name) : $ctx->csrf('addpage'), + 'langs' => array_map(fn($lang) => $lang->value, $langs), // still needed for draft erasing +]; +if ($js_text !== null) + $js_params['text'] = $js_text; +$js_params = jsonEncode($js_params); + $js = << $config['domain'], 'devMode' => $config['is_dev'], 'cookieHost' => $config['cookie_host'], @@ -64,7 +64,7 @@ HTML; function renderScript($ctx, $unsafe_js, $unsafe_lang) { global $config; -$styles = json_encode($ctx->styleNames); +$styles = jsonEncode($ctx->styleNames); if ($config['is_dev']) $versions = '{}'; else { @@ -73,7 +73,7 @@ else { list($type, $bname) = getStaticNameParts($name); $versions[$type][$bname] = $v; } - $versions = json_encode($versions); + $versions = jsonEncode($versions); } return <<