803 lines
26 KiB
PHP
803 lines
26 KiB
PHP
<?php
|
|
|
|
class AdminHandler extends request_handler {
|
|
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->skin->addStatic('css/admin.css', 'js/admin.js');
|
|
$this->skin->exportStrings(['error']);
|
|
$this->skin->setRenderOptions(['inside_admin_interface' => true]);
|
|
}
|
|
|
|
public function beforeDispatch(string $http_method, string $action) {
|
|
if ($action != 'login' && !isAdmin())
|
|
self::forbidden();
|
|
}
|
|
|
|
public function GET_index() {
|
|
//$admin_info = admin_current_info();
|
|
$this->skin->setTitle('$admin_title');
|
|
$this->skin->renderPage('admin_index.twig', [
|
|
'admin_login' => admin::getLogin(),
|
|
'logout_token' => self::getCSRF('logout'),
|
|
]);
|
|
}
|
|
|
|
public function GET_login() {
|
|
if (isAdmin())
|
|
self::redirect('/admin/');
|
|
$this->skin->setTitle('$admin_title');
|
|
$this->skin->renderPage('admin_login.twig', [
|
|
'form_token' => self::getCSRF('adminlogin'),
|
|
]);
|
|
}
|
|
|
|
public function POST_login() {
|
|
self::checkCSRF('adminlogin');
|
|
list($login, $password) = $this->input('login, password');
|
|
admin::auth($login, $password)
|
|
? self::redirect('/admin/')
|
|
: self::forbidden();
|
|
}
|
|
|
|
public function GET_logout() {
|
|
self::checkCSRF('logout');
|
|
admin::logout();
|
|
self::redirect('/admin/login/', HTTPCode::Found);
|
|
}
|
|
|
|
public function GET_errors() {
|
|
list($ip, $query, $url_query, $file_query, $line_query, $per_page)
|
|
= $this->input('i:ip, query, url_query, file_query, i:line_query, i:per_page');
|
|
|
|
if (!$per_page)
|
|
$per_page = 100;
|
|
$db = DB();
|
|
|
|
$query = trim($query ?? '');
|
|
$url_query = trim($url_query ?? '');
|
|
|
|
$sql_where = [];
|
|
if ($ip) {
|
|
$sql_where[] = "ip='".$db->escape($ip)."'";
|
|
}
|
|
if ($query) {
|
|
$sql_where[] = "text LIKE '%".$db->escape($query)."%'";
|
|
}
|
|
if ($url_query) {
|
|
$sql_where[] = "url LIKE '%".$db->escape($url_query)."%'";
|
|
}
|
|
if ($file_query) {
|
|
$sql_where[] = "file LIKE '%".$db->escape($file_query)."%'";
|
|
}
|
|
if ($line_query) {
|
|
$sql_where[] = "line='".$db->escape($line_query)."'";
|
|
}
|
|
|
|
if (!empty($sql_where)) {
|
|
$sql_where = " WHERE ".implode(' AND ', $sql_where)." ";
|
|
} else {
|
|
$sql_where = '';
|
|
}
|
|
|
|
$count = (int)$db->result($db->query("SELECT COUNT(*) FROM backend_errors".$sql_where));
|
|
list($page, $pages, $offset) = $this->getPage($per_page, $count);
|
|
|
|
$q = $db->query("SELECT *, INET_NTOA(ip) ip_s FROM backend_errors $sql_where ORDER BY id DESC LIMIT $offset, $per_page");
|
|
|
|
$list = [];
|
|
|
|
while ($row = $db->fetch($q)) {
|
|
$row['date'] = formatTime($row['ts'], [
|
|
'seconds' => true,
|
|
'short_months' => true,
|
|
]);
|
|
$row['full_url'] = !str_starts_with($row['url'], 'https://') ? 'https://'.$row['url'] : $row['url'];
|
|
$error_name = getPHPErrorName((int)$row['errno']);
|
|
if (!is_null($error_name))
|
|
$row['errtype'] = $error_name;
|
|
$list[] = $row;
|
|
}
|
|
|
|
// url for pageNav
|
|
$url = '/admin/errors/';
|
|
$url_params = [];
|
|
if ($ip) {
|
|
$url_params['ip'] = $ip;
|
|
}
|
|
if ($query) {
|
|
$url_params['query'] = $query;
|
|
}
|
|
if ($url_query) {
|
|
$url_params['url_query'] = $url_query;
|
|
}
|
|
if ($file_query) {
|
|
$url_params['file_query'] = $file_query;
|
|
}
|
|
if ($line_query) {
|
|
$url_params['line_query'] = $line_query;
|
|
}
|
|
|
|
if (!empty($url_params)) {
|
|
$url .= '?'.http_build_query($url_params).'&';
|
|
} else {
|
|
$url .= '?';
|
|
}
|
|
|
|
$vars = [
|
|
'list' => $list,
|
|
'count' => $count,
|
|
'pn_page' => $page,
|
|
'pn_pages' => $pages,
|
|
'url' => $url,
|
|
];
|
|
|
|
if ($ip) {
|
|
$vars += [
|
|
'ip_filter' => ulong2ip($ip),
|
|
'ip' => $ip
|
|
];
|
|
}
|
|
|
|
$query_var_names = ['query', 'url_query', 'file_query', 'line_query'];
|
|
foreach ($query_var_names as $query_var_name) {
|
|
if ($$query_var_name)
|
|
$vars += [$query_var_name => $$query_var_name];
|
|
}
|
|
|
|
$this->skin->setRenderOptions(['wide' => true]);
|
|
$this->skin->setTitle('$admin_errors');
|
|
$this->skin->renderPage('admin_errors.twig', $vars);
|
|
}
|
|
|
|
public function GET_auth_log() {
|
|
$db = DB();
|
|
$count = (int)$db->result($db->query("SELECT COUNT(*) FROM admin_log"));
|
|
$per_page = 100;
|
|
list($page, $pages, $offset) = $this->getPage($per_page, $count);
|
|
|
|
$q = $db->query("SELECT *,
|
|
INET_NTOA(ip) AS ip,
|
|
admins.login AS login,
|
|
admins.activity_ts AS activitiy_ts
|
|
FROM admin_log
|
|
LEFT JOIN admins ON admins.id=admin_log.admin_id
|
|
ORDER BY admin_log.id DESC
|
|
LIMIT $offset, ".$per_page);
|
|
$list = $db->fetchAll($q);
|
|
|
|
if (!empty($list)) {
|
|
$list = array_map(function($item) {
|
|
$item['date'] = formatTime($item['ts']);
|
|
$item['activity_ts_s'] = formatTime($item['activity_ts']);
|
|
return $item;
|
|
}, $list);
|
|
}
|
|
|
|
$this->skin->setRenderOptions(['wide' => true]);
|
|
$this->skin->setTitle('$admin_auth_log');
|
|
$this->skin->set([
|
|
'list' => $list,
|
|
'pn_page' => $page,
|
|
'pn_pages' => $pages
|
|
]);
|
|
$this->skin->renderPage('admin_auth_log.twig');
|
|
}
|
|
|
|
public function GET_actions_log() {
|
|
$field_types = \AdminActions\Util\Logger::getFieldTypes();
|
|
foreach ($field_types as $type_prefix => $type_data) {
|
|
for ($i = 1; $i <= $type_data['count']; $i++) {
|
|
$name = $type_prefix.'arg'.$i;
|
|
if (isset($_REQUEST[$name]) && (string)$_REQUEST[$name] !== '')
|
|
$argument_filters[$name] = $type_data['unpacker']((string)$_REQUEST[$name]);
|
|
}
|
|
}
|
|
|
|
$per_page = 100;
|
|
|
|
$count = \AdminActions\Util\Logger::getRecordsCount();
|
|
list($page, $pages, $offset) = $this->getPage($per_page, $count);
|
|
|
|
$admin_ids = [];
|
|
$admin_logins = [];
|
|
|
|
$records = \AdminActions\Util\Logger::getRecords($offset, $per_page);
|
|
foreach ($records as $record) {
|
|
list($admin_id) = $record->getActorInfo();
|
|
if ($admin_id !== null)
|
|
$admin_ids[$admin_id] = true;
|
|
}
|
|
|
|
if (!empty($admin_ids))
|
|
$admin_logins = admin::getLoginsById(array_keys($admin_ids));
|
|
|
|
$url = '/admin/actions-log/?';
|
|
|
|
$filter_fields = [];
|
|
foreach ($field_types as $type_prefix => $type_data) {
|
|
for ($i = 1; $i <= $type_data['count']; $i++) {
|
|
$name = $type_prefix.'arg'.$i;
|
|
$filter_fields[$name] = $argument_filters[$name] ?? '';
|
|
}
|
|
}
|
|
|
|
$this->skin->setRenderOptions(['wide' => true]);
|
|
$this->skin->setTitle('$admin_actions_log');
|
|
$this->skin->renderPage('admin_actions_log.twig', [
|
|
'list' => $records,
|
|
'pn_page' => $page,
|
|
'pn_pages' => $pages,
|
|
'admin_logins' => $admin_logins,
|
|
'url' => $url,
|
|
'action_types' => \AdminActions\Util\Logger::getActions(true),
|
|
]);
|
|
}
|
|
|
|
public function GET_uploads() {
|
|
list($error) = $this->input('error');
|
|
$uploads = uploads::getAllUploads();
|
|
|
|
$this->skin->setTitle('$blog_upload');
|
|
$this->skin->renderPage('admin_uploads.twig', [
|
|
'error' => $error,
|
|
'uploads' => $uploads,
|
|
'langs' => PostLanguage::casesAsStrings(),
|
|
'form_token' => self::getCSRF('add_upload'),
|
|
]);
|
|
}
|
|
|
|
public function POST_uploads() {
|
|
self::checkCSRF('add_upload');
|
|
list($custom_name, $note_en, $note_ru) = $this->input('name, note_en, note_ru');
|
|
|
|
if (!isset($_FILES['files']))
|
|
self::redirect('/admin/uploads/?error='.urlencode('no file'));
|
|
|
|
$files = [];
|
|
for ($i = 0; $i < count($_FILES['files']['name']); $i++) {
|
|
$files[] = [
|
|
'name' => $_FILES['files']['name'][$i],
|
|
'type' => $_FILES['files']['type'][$i],
|
|
'tmp_name' => $_FILES['files']['tmp_name'][$i],
|
|
'error' => $_FILES['files']['error'][$i],
|
|
'size' => $_FILES['files']['size'][$i],
|
|
];
|
|
}
|
|
|
|
if (count($files) > 1) {
|
|
$note_en = '';
|
|
$note_ru = '';
|
|
$custom_name = '';
|
|
}
|
|
|
|
foreach ($files as $f) {
|
|
if ($f['error'])
|
|
self::redirect('/admin/uploads/?error='.urlencode('error code '.$f['error']));
|
|
|
|
if (!$f['size'])
|
|
self::redirect('/admin/uploads/?error='.urlencode('received empty file'));
|
|
|
|
$ext = extension($f['name']);
|
|
if (!uploads::isExtensionAllowed($ext))
|
|
self::redirect('/admin/uploads/?error='.urlencode('extension not allowed'));
|
|
|
|
$name = $custom_name ?: $f['name'];
|
|
$upload_id = uploads::add(
|
|
$f['tmp_name'],
|
|
$name,
|
|
$note_en,
|
|
$note_ru);
|
|
|
|
if (!$upload_id)
|
|
self::redirect('/admin/uploads/?error='.urlencode('failed to create upload'));
|
|
|
|
admin::log(new \AdminActions\UploadsAdd($upload_id, $name, $note_en, $note_ru));
|
|
}
|
|
|
|
self::redirect('/admin/uploads/');
|
|
}
|
|
|
|
public function GET_upload_delete() {
|
|
list($id) = $this->input('i:id');
|
|
$upload = uploads::get($id);
|
|
if (!$upload)
|
|
self::redirect('/admin/uploads/?error='.urlencode('upload not found'));
|
|
self::checkCSRF('delupl'.$id);
|
|
uploads::delete($id);
|
|
admin::log(new \AdminActions\UploadsDelete($id));
|
|
self::redirect('/admin/uploads/');
|
|
}
|
|
|
|
public function POST_upload_edit_note() {
|
|
list($id, $note, $lang) = $this->input('i:id, note, lang');
|
|
$lang = PostLanguage::tryFrom($lang);
|
|
if (!$lang)
|
|
self::notFound();
|
|
|
|
$upload = uploads::get($id);
|
|
if (!$upload)
|
|
self::redirect('/admin/uploads/?error='.urlencode('upload not found'));
|
|
|
|
self::checkCSRF('editupl'.$id);
|
|
|
|
$upload->setNote($lang, $note);
|
|
$texts = posts::getTextsWithUpload($upload);
|
|
if (!empty($texts)) {
|
|
foreach ($texts as $text) {
|
|
$text->updateHtml();
|
|
$text->updateText();
|
|
}
|
|
}
|
|
|
|
admin::log(new \AdminActions\UploadsEditNote($id, $note, $lang->value));
|
|
self::redirect('/admin/uploads/');
|
|
}
|
|
|
|
public function POST_ajax_md_preview() {
|
|
self::ensureXhr();
|
|
list($md, $title, $use_image_previews, $lang, $is_page) = $this->input('md, title, b:use_image_previews, lang, b:is_page');
|
|
$lang = PostLanguage::tryFrom($lang);
|
|
if (!$lang)
|
|
$lang = PostLanguage::getDefault();
|
|
if ($is_page) {
|
|
$md = '# '.$title."\n\n".$md;
|
|
$title = '';
|
|
}
|
|
$html = markup::markdownToHtml($md, $use_image_previews, $lang);
|
|
$html = $this->skin->render('markdown_preview.twig', [
|
|
'unsafe_html' => $html,
|
|
'title' => $title
|
|
]);
|
|
self::ajaxOk(['html' => $html]);
|
|
}
|
|
|
|
public function GET_page_add() {
|
|
list($name) = $this->input('short_name');
|
|
$page = pages::getByName($name);
|
|
if ($page)
|
|
self::redirect($page->getUrl(), code: HTTPCode::Found);
|
|
$this->skin->exportStrings('/^(err_)?pages_/');
|
|
$this->skin->exportStrings('/^(err_)?blog_/');
|
|
$this->skin->setTitle(lang('pages_create_title', $name));
|
|
$this->setWidePageOptions();
|
|
|
|
$js_params = [
|
|
'pages' => true,
|
|
'edit' => false,
|
|
'token' => self::getCSRF('addpage'),
|
|
'langs' => array_map(fn($lang) => $lang->value, PostLanguage::cases()), // still needed for draft erasing
|
|
];
|
|
|
|
$this->skin->renderPage('admin_page_form.twig', [
|
|
'is_edit' => false,
|
|
'short_name' => $name,
|
|
'title' => '',
|
|
'text' => '',
|
|
'langs' => PostLanguage::cases(),
|
|
'js_params' => $js_params,
|
|
'form_url' => '/'.$name.'/create/'
|
|
]);
|
|
}
|
|
|
|
public function POST_page_add() {
|
|
self::checkCSRF('addpage');
|
|
|
|
list($name, $text, $title) = $this->input('short_name, text, title');
|
|
$page = pages::getByName($name);
|
|
if ($page)
|
|
self::notFound();
|
|
|
|
$error_code = null;
|
|
|
|
if (!$title) {
|
|
$error_code = 'no_title';
|
|
} else if (!$text) {
|
|
$error_code = 'no_text';
|
|
}
|
|
|
|
if ($error_code)
|
|
self::ajaxError(['code' => $error_code]);
|
|
|
|
if (!pages::add([
|
|
'short_name' => $name,
|
|
'title' => $title,
|
|
'md' => $text
|
|
])) {
|
|
self::ajaxError(['code' => 'db_err']);
|
|
}
|
|
|
|
admin::log(new \AdminActions\PageCreate($name));
|
|
|
|
$page = pages::getByName($name);
|
|
self::ajaxOk(['url' => $page->getUrl()]);
|
|
}
|
|
|
|
public function GET_page_delete() {
|
|
list($name) = $this->input('short_name');
|
|
|
|
$page = pages::getByName($name);
|
|
if (!$page)
|
|
self::notFound();
|
|
|
|
$url = $page->getUrl();
|
|
|
|
self::checkCSRF('delpage'.$page->shortName);
|
|
pages::delete($page);
|
|
admin::log(new \AdminActions\PageDelete($name));
|
|
self::redirect($url, code: HTTPCode::Found);
|
|
}
|
|
|
|
public function GET_page_edit() {
|
|
list($short_name, $saved) = $this->input('short_name, b:saved');
|
|
|
|
$page = pages::getByName($short_name);
|
|
if (!$page)
|
|
self::notFound();
|
|
|
|
$this->skin->exportStrings('/^(err_)?pages_/');
|
|
$this->skin->exportStrings('/^(err_)?blog_/');
|
|
$this->skin->setTitle(lang('pages_page_edit_title', $page->shortName));
|
|
$this->setWidePageOptions();
|
|
$js_text = [
|
|
'text' => $page->md,
|
|
'title' => $page->title,
|
|
];
|
|
|
|
$parent = '';
|
|
if ($page->parentId) {
|
|
$parent_page = pages::getById($page->parentId);
|
|
if ($parent_page)
|
|
$parent = $parent_page->shortName;
|
|
}
|
|
|
|
$js_params = [
|
|
'pages' => true,
|
|
'edit' => true,
|
|
'token' => self::getCSRF('editpage'.$short_name),
|
|
'langs' => array_map(fn($lang) => $lang->value, PostLanguage::cases()), // still needed for draft erasing
|
|
'text' => [
|
|
'text' => $page->md,
|
|
'title' => $page->title,
|
|
]
|
|
];
|
|
|
|
$this->skin->renderPage('admin_page_form.twig', [
|
|
'is_edit' => true,
|
|
'short_name' => $page->shortName,
|
|
'title' => $page->title,
|
|
'text' => $page->md,
|
|
'visible' => $page->visible,
|
|
'render_title' => $page->renderTitle,
|
|
'parent' => $parent,
|
|
'saved' => $saved,
|
|
'langs' => PostLanguage::cases(),
|
|
'js_params' => $js_params,
|
|
]);
|
|
}
|
|
|
|
public function POST_page_edit() {
|
|
self::ensureXhr();
|
|
list($short_name) = $this->input('short_name');
|
|
|
|
$page = pages::getByName($short_name);
|
|
if (!$page)
|
|
self::notFound();
|
|
|
|
self::checkCSRF('editpage'.$page->shortName);
|
|
|
|
list($text, $title, $visible, $short_name, $parent, $render_title)
|
|
= $this->input('text, title, b:visible, new_short_name, parent, b:render_title');
|
|
|
|
$text = trim($text);
|
|
$title = trim($title);
|
|
$error_code = null;
|
|
|
|
if (!$title) {
|
|
$error_code = 'no_title';
|
|
} else if (!$text) {
|
|
$error_code = 'no_text';
|
|
} else if (!$short_name) {
|
|
$error_code = 'no_short_name';
|
|
}
|
|
|
|
if ($error_code)
|
|
self::ajaxError(['code' => $error_code]);
|
|
|
|
$new_short_name = $page->shortName != $short_name ? $short_name : null;
|
|
$parent_page = pages::getByName($parent);
|
|
$parent_id = $parent_page ? $parent_page->id : 0;
|
|
|
|
previous_texts::add(PreviousText::TYPE_PAGE, $page->get_id(), $page->md, $page->updateTs ?: $page->ts);
|
|
$page->edit([
|
|
'title' => $title,
|
|
'md' => $text,
|
|
'visible' => (bool)$visible,
|
|
'short_name' => $short_name,
|
|
'render_title' => (bool)$render_title,
|
|
'parent_id' => $parent_id
|
|
]);
|
|
|
|
admin::log(new \AdminActions\PageEdit($short_name, $new_short_name));
|
|
self::ajaxOk(['url' => $page->getUrl().'edit/?saved=1']);
|
|
}
|
|
|
|
public function GET_post_add() {
|
|
$this->skin->exportStrings('/^(err_)?blog_/');
|
|
$this->skin->setTitle('$blog_write');
|
|
$this->setWidePageOptions();
|
|
|
|
$js_texts = [];
|
|
foreach (PostLanguage::cases() as $pl) {
|
|
$js_texts[$pl->value] = [
|
|
'title' => '',
|
|
'md' => '',
|
|
'toc' => false,
|
|
'keywords' => '',
|
|
];
|
|
}
|
|
|
|
$js_params = [
|
|
'langs' => array_map(fn($lang) => $lang->value, PostLanguage::cases()),
|
|
'token' => self::getCSRF('post_add')
|
|
];
|
|
$form_url = '/articles/write/';
|
|
|
|
$bc = [
|
|
['url' => '/articles/?lang='.PostLanguage::getDefault()->value, 'text' => lang('articles')],
|
|
['text' => lang('blog_new_post')]
|
|
];
|
|
|
|
$this->skin->renderPage('admin_post_form.twig', [
|
|
// form data
|
|
'title' => '',
|
|
'text' => '',
|
|
'short_name' => '',
|
|
'source_url' => '',
|
|
'keywords' => '',
|
|
'date' => '',
|
|
|
|
'bc' => $bc,
|
|
'js_params' => $js_params,
|
|
'form_url' => $form_url,
|
|
'langs' => PostLanguage::casesAsStrings(),
|
|
'lang' => PostLanguage::getDefault()->value
|
|
]);
|
|
}
|
|
|
|
public function POST_post_add() {
|
|
self::ensureXhr();
|
|
self::checkCSRF('post_add');
|
|
|
|
list($visibility_enabled, $short_name, $langs, $date)
|
|
= $this->input('b:visible, short_name, langs, date');
|
|
|
|
self::_postEditValidateCommonData($date);
|
|
|
|
$lang_data = [];
|
|
$at_least_one_lang_is_written = false;
|
|
foreach (PostLanguage::cases() as $lang) {
|
|
list($title, $text, $keywords, $toc_enabled) = $this->input("title:{$lang->value}, text:{$lang->value}, keywords:{$lang->value}, b:toc:{$lang->value}", ['trim' => true]);
|
|
if ($title !== '' && $text !== '') {
|
|
$lang_data[$lang->value] = [$title, $text, $keywords, $toc_enabled];
|
|
$at_least_one_lang_is_written = true;
|
|
}
|
|
}
|
|
|
|
$error_code = null;
|
|
if (!$at_least_one_lang_is_written) {
|
|
$error_code = 'no_text';
|
|
} else if (empty($short_name)) {
|
|
$error_code = 'no_short_name';
|
|
}
|
|
if ($error_code)
|
|
self::ajaxError(['code' => $error_code]);
|
|
|
|
$post = posts::add([
|
|
'visible' => $visibility_enabled,
|
|
'short_name' => $short_name,
|
|
'date' => $date,
|
|
'source_url' => ''
|
|
]);
|
|
|
|
if (!$post)
|
|
self::ajaxError(['code' => 'db_err', 'message' => 'failed to add post']);
|
|
|
|
// add texts
|
|
$added_texts = []; // for admin actions logging, at the end
|
|
foreach ($lang_data as $lang => $data) {
|
|
list($title, $text, $keywords, $toc_enabled) = $data;
|
|
if (!($new_post_text = $post->addText(
|
|
lang: PostLanguage::from($lang),
|
|
title: $title,
|
|
md: $text,
|
|
keywords: $keywords,
|
|
toc: $toc_enabled))
|
|
) {
|
|
posts::delete($post);
|
|
self::ajaxError(['code' => 'db_err', 'message' => 'failed to add text language '.$lang]);
|
|
} else {
|
|
$added_texts[] = [$new_post_text->id, $lang];
|
|
}
|
|
}
|
|
|
|
admin::log(new \AdminActions\PostCreate($post->id));
|
|
foreach ($added_texts as $added_text) {
|
|
list($id, $lang) = $added_text;
|
|
admin::log(new \AdminActions\PostTextCreate($id, $post->id, $lang));
|
|
}
|
|
|
|
// done
|
|
self::ajaxOk(['url' => $post->getUrl()]);
|
|
}
|
|
|
|
public function GET_post_delete() {
|
|
list($name) = $this->input('short_name');
|
|
|
|
$post = posts::getByName($name);
|
|
if (!$post)
|
|
self::notFound();
|
|
|
|
$id = $post->id;
|
|
self::checkCSRF('delpost'.$id);
|
|
posts::delete($post);
|
|
admin::log(new \AdminActions\PostDelete($id));
|
|
self::redirect('/articles/', code: HTTPCode::Found);
|
|
}
|
|
|
|
public function GET_post_edit() {
|
|
list($short_name, $saved, $lang) = $this->input('short_name, b:saved, lang');
|
|
$lang = PostLanguage::from($lang);
|
|
|
|
$post = posts::getByName($short_name);
|
|
if (!$post)
|
|
self::notFound();
|
|
|
|
$texts = $post->getTexts();
|
|
if (!isset($texts[$lang->value]))
|
|
self::notFound();
|
|
|
|
$js_texts = [];
|
|
foreach (PostLanguage::cases() as $pl) {
|
|
if (isset($texts[$pl->value])) {
|
|
$text = $texts[$pl->value];
|
|
$js_texts[$pl->value] = [
|
|
'title' => $text->title,
|
|
'md' => $text->md,
|
|
'toc' => $text->toc,
|
|
'keywords' => $text->keywords,
|
|
];
|
|
} else {
|
|
$js_texts[$pl->value] = [
|
|
'title' => '',
|
|
'md' => '',
|
|
'toc' => false,
|
|
'keywords' => '',
|
|
];
|
|
}
|
|
}
|
|
|
|
$text = $texts[$lang->value];
|
|
|
|
$this->skin->exportStrings('/^(err_)?blog_/');
|
|
$this->skin->exportStrings(['blog_post_edit_title']);
|
|
$this->skin->setTitle(lang('blog_post_edit_title', $text->title));
|
|
$this->setWidePageOptions();
|
|
|
|
$bc = [
|
|
['url' => '/articles/?lang='.$text->lang->value, 'text' => lang('articles')],
|
|
['url' => $post->getUrl().'?lang='.$text->lang->value, 'text' => lang('blog_view_post')]
|
|
];
|
|
|
|
$js_params = [
|
|
'langs' => array_map(fn($lang) => $lang->value, PostLanguage::cases()),
|
|
'token' => self::getCSRF('editpost'.$post->id),
|
|
'edit' => true,
|
|
'id' => $post->id,
|
|
'texts' => $js_texts
|
|
];
|
|
$form_url = $post->getUrl().'edit/';
|
|
|
|
$this->skin->renderPage('admin_post_form.twig', [
|
|
'is_edit' => true,
|
|
'post' => $post,
|
|
'title' => $text->title,
|
|
'text' => $text->md,
|
|
'date' => $post->getDateForInputField(),
|
|
'visible' => $post->visible,
|
|
'toc' => $text->toc,
|
|
'short_name' => $short_name,
|
|
'source_url' => $post->sourceUrl,
|
|
'keywords' => $text->keywords,
|
|
'saved' => $saved,
|
|
'langs' => PostLanguage::casesAsStrings(),
|
|
'lang' => $text->lang->value,
|
|
'js_params' => $js_params,
|
|
'form_url' => $form_url,
|
|
'bc' => $bc
|
|
]);
|
|
}
|
|
|
|
public function POST_post_edit() {
|
|
self::ensureXhr();
|
|
|
|
list($old_short_name, $short_name, $langs, $date, $source_url) = $this->input('short_name, new_short_name, langs, date, source_url');
|
|
|
|
$post = posts::getByName($old_short_name);
|
|
if (!$post)
|
|
self::notFound();
|
|
|
|
self::checkCSRF('editpost'.$post->id);
|
|
|
|
self::_postEditValidateCommonData($date);
|
|
|
|
if (empty($short_name))
|
|
self::ajaxError(['code' => 'no_short_name']);
|
|
|
|
foreach (explode(',', $langs) as $lang) {
|
|
$lang = PostLanguage::from($lang);
|
|
list($text, $title, $visible, $toc, $keywords) = $this->input("text:{$lang->value}, title:{$lang->value}, b:visible, b:toc:{$lang->value}, keywords:{$lang->value}");
|
|
|
|
$error_code = null;
|
|
if (!$title)
|
|
$error_code = 'no_title';
|
|
else if (!$text)
|
|
$error_code = 'no_text';
|
|
if ($error_code)
|
|
self::ajaxError(['code' => $error_code]);
|
|
|
|
$pt = $post->getText($lang);
|
|
if (!$pt) {
|
|
$pt = $post->addText(
|
|
lang: $lang,
|
|
title: $title,
|
|
md: $text,
|
|
keywords: $keywords,
|
|
toc: $toc
|
|
);
|
|
if (!$pt)
|
|
self::ajaxError(['code' => 'db_err']);
|
|
} else {
|
|
previous_texts::add(PreviousText::TYPE_POST_TEXT, $pt->id, $pt->md, $post->getUpdateTimestamp() ?: $post->getTimestamp());
|
|
$pt->edit([
|
|
'title' => $title,
|
|
'md' => $text,
|
|
'toc' => (int)$toc,
|
|
'keywords' => $keywords
|
|
]);
|
|
}
|
|
}
|
|
|
|
$post_data = [
|
|
'date' => $date,
|
|
'visible' => $visible,
|
|
'source_url' => $source_url
|
|
];
|
|
if ($post->shortName != $short_name)
|
|
$post_data['short_name'] = $short_name;
|
|
$post->edit($post_data);
|
|
|
|
admin::log(new \AdminActions\PostEdit($post->id));
|
|
self::ajaxOk(['url' => $post->getUrl().'edit/?saved=1&lang='.$lang->value]);
|
|
}
|
|
|
|
public function GET_books() {
|
|
$this->skin->setTitle('$admin_books');
|
|
$this->skin->renderPage('admin_books.twig');
|
|
}
|
|
|
|
protected static function _postEditValidateCommonData($date) {
|
|
$dt = DateTime::createFromFormat("Y-m-d", $date);
|
|
$date_is_valid = $dt && $dt->format("Y-m-d") === $date;
|
|
if (!$date_is_valid)
|
|
self::ajaxError(['code' => 'no_date']);
|
|
}
|
|
|
|
protected function setWidePageOptions(): void {
|
|
$this->skin->setRenderOptions([
|
|
'full_width' => true,
|
|
'no_footer' => true
|
|
]);
|
|
}
|
|
|
|
} |