703 lines
24 KiB
PHP
703 lines
24 KiB
PHP
<?php
|
|
|
|
namespace skin\admin;
|
|
|
|
use PostLanguage;
|
|
use Stringable;
|
|
use function skin\base\layout;
|
|
|
|
// login page
|
|
// ----------
|
|
|
|
function login($ctx) {
|
|
$html = <<<HTML
|
|
<form action="/admin/login/" method="post" class="form-layout-h" name="admin_login">
|
|
<input type="hidden" name="token" value="{$ctx->csrf('adminlogin')}" />
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$ctx->lang('admin_login')}:</div>
|
|
<div class="form-field">
|
|
<input class="form-field-input" type="text" name="login" size="50" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$ctx->lang('admin_password')}:</div>
|
|
<div class="form-field">
|
|
<input class="form-field-input" type="password" name="password" size="50" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label"></div>
|
|
<div class="form-field">
|
|
<button type="submit">{$ctx->lang('sign_in')}</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
HTML;
|
|
|
|
$js = <<<JS
|
|
document.forms.admin_login.login.focus();
|
|
JS;
|
|
|
|
return [$html, $js];
|
|
}
|
|
|
|
|
|
// index page
|
|
// ----------
|
|
|
|
function index($ctx, $admin_login) {
|
|
return <<<HTML
|
|
<div class="admin-page">
|
|
Authorized as <b>{$admin_login}</b> | <a href="/admin/logout/?token={$ctx->csrf('logout')}">Sign out</a><br>
|
|
<!--<a href="/admin/log/">Log</a><br/>-->
|
|
<a href="/admin/uploads/">Uploads</a><br>
|
|
<a href="/admin/errors/">{$ctx->lang('admin_errors')}</a><br>
|
|
<a href="/admin/auth-log/">{$ctx->lang('admin_auth_log')}</a><br>
|
|
<a href="/admin/actions-log/">{$ctx->lang('admin_actions_log')}</a><br>
|
|
</div>
|
|
HTML;
|
|
}
|
|
|
|
|
|
// uploads page
|
|
// ------------
|
|
|
|
function uploads($ctx, $uploads, $error, array $langs) {
|
|
return <<<HTML
|
|
{$ctx->if_true($error, $ctx->formError, $error)}
|
|
|
|
{$ctx->bc([
|
|
['text' => $ctx->lang('admin_title'), 'url' => '/admin/'],
|
|
['text' => $ctx->lang('blog_uploads')],
|
|
])}
|
|
|
|
<div class="blog-upload-form">
|
|
<form action="/admin/uploads/" method="post" enctype="multipart/form-data" class="form-layout-h">
|
|
<input type="hidden" name="token" value="{$ctx->csrf('addupl')}" />
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$ctx->lang('blog_upload_form_file')}:</div>
|
|
<div class="form-field">
|
|
<input type="file" name="files[]" multiple>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$ctx->lang('blog_upload_form_custom_name')}:</div>
|
|
<div class="form-field">
|
|
<input type="text" name="name">
|
|
</div>
|
|
</div>
|
|
|
|
{$ctx->for_each($langs,
|
|
fn($l) => $ctx->uploads_form_note_field($l))}
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label"></div>
|
|
<div class="form-field">
|
|
<input type="submit" value="Upload">
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="blog-upload-list">
|
|
{$ctx->for_each($uploads, fn($u) => $ctx->uploads_item(
|
|
id: $u->id,
|
|
name: $u->name,
|
|
direct_url: $u->getDirectUrl(),
|
|
note_ru: $u->noteRu,
|
|
note_en: $u->noteEn,
|
|
addslashes_note_ru: $u->noteRu,
|
|
addslashes_note_en: $u->noteEn,
|
|
markdown: $u->getMarkdown(),
|
|
size: $u->getSize(),
|
|
))}
|
|
</div>
|
|
HTML;
|
|
}
|
|
|
|
function uploads_form_note_field($ctx, PostLanguage $lang) {
|
|
$label = $ctx->lang('blog_upload_form_note');
|
|
$label .= ' ('.$lang->name.')';
|
|
|
|
return <<<HTML
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$label}:</div>
|
|
<div class="form-field">
|
|
<input type="text" name="note_{$lang->value}" size="55">
|
|
</div>
|
|
</div>
|
|
HTML;
|
|
}
|
|
|
|
function uploads_item($ctx, $id, $direct_url, $note_en, $note_ru, $addslashes_note_en, $addslashes_note_ru, $markdown, $name, $size) {
|
|
return <<<HTML
|
|
<div class="blog-upload-item">
|
|
<div class="blog-upload-item-actions">
|
|
<a href="javascript:void(0)" onclick="var mdel = ge('upload{$id}_md'); mdel.style.display = (mdel.style.display === 'none' ? 'block' : 'none')">{$ctx->lang('blog_upload_show_md')}</a>
|
|
| <a href="javascript:void(0)" onclick="BlogUploadList.submitNoteEdit('/admin/uploads/edit_note/{$id}/?lang=ru&token={$ctx->csrf('editupl'.$id)}', prompt('Note (Ru):', '{$addslashes_note_ru}'))">Edit note Ru</a>
|
|
| <a href="javascript:void(0)" onclick="BlogUploadList.submitNoteEdit('/admin/uploads/edit_note/{$id}/?lang=en&token={$ctx->csrf('editupl'.$id)}', prompt('Note (En):', '{$addslashes_note_en}'))">Edit note En</a>
|
|
| <a href="/admin/uploads/delete/{$id}/?token={$ctx->csrf('delupl'.$id)}" onclick="return confirm('{$ctx->lang('blog_upload_delete_confirmation')}')">{$ctx->lang('blog_upload_delete')}</a>
|
|
</div>
|
|
<div class="blog-upload-item-name"><a href="{$direct_url}">{$name}</a></div>
|
|
<div class="blog-upload-item-info">{$size}</div>
|
|
{$ctx->if_true($note_en,
|
|
fn() => '<div class="blog-upload-item-note"><span>En</span>'.$note_en.'</div>')}
|
|
{$ctx->if_true($note_ru,
|
|
fn() => '<div class="blog-upload-item-note"><span>Ru</span>'.$note_ru.'</div>')}
|
|
<div class="blog-upload-item-md" id="upload{$id}_md" style="display: none">
|
|
<input type="text" value="{$markdown}" onclick="this.select()" readonly size="30">
|
|
</div>
|
|
</div>
|
|
HTML;
|
|
}
|
|
|
|
function postForm(\SkinContext $ctx,
|
|
string|Stringable $title,
|
|
string|Stringable $text,
|
|
string|Stringable $short_name,
|
|
array $langs,
|
|
array $js_texts,
|
|
string|Stringable|null $date = null,
|
|
bool $is_edit = false,
|
|
?bool $saved = null,
|
|
?bool $visible = null,
|
|
?bool $toc = null,
|
|
string|Stringable|null $post_url = null,
|
|
?int $post_id = null,
|
|
?string $lang = null): array {
|
|
|
|
$form_url = !$is_edit ? '/articles/write/' : $post_url.'edit/';
|
|
|
|
// breadcrumbs
|
|
$bc_tree = [
|
|
['url' => '/articles/?lang='.$lang, 'text' => $ctx->lang('articles')]
|
|
];
|
|
if ($is_edit) {
|
|
$bc_tree[] = ['url' => $post_url.'?lang='.$lang, 'text' => $ctx->lang('blog_view_post')];
|
|
} else {
|
|
$bc_tree[] = ['text' => $ctx->lang('blog_new_post')];
|
|
}
|
|
|
|
$html = <<<HTML
|
|
<div class="form-error" id="form-error" style="display:none"></div>
|
|
{$ctx->if_true($saved, fn() => '<div class="form-success">'.$ctx->lang('info_saved').'</div>')}
|
|
{$ctx->bc($bc_tree, 'padding-bottom: 12px')}
|
|
<table cellpadding="0" cellspacing="0" class="blog-write-table">
|
|
<tr>
|
|
<td id="form_first_cell">
|
|
<form class="blog-write-form form-layout-v" name="postForm" action="{$form_url}" method="post">
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$ctx->lang('blog_write_form_title')}</div>
|
|
<div class="form-field">
|
|
<input class="form-field-input" type="text" name="title" value="{$title}" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$ctx->lang('blog_write_form_text')}</div>
|
|
<div class="form-field">
|
|
<textarea class="form-field-input" name="text" wrap="soft">{$text}</textarea><br/>
|
|
<a class="blog-write-form-toggle-link" id="toggle_wrap" href="">{$ctx->lang('blog_write_form_toggle_wrap')}</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<table class="blog-write-options-table">
|
|
<tr>
|
|
<td>
|
|
<div class="clearfix">
|
|
<div class="form-field-label">{$ctx->lang('blog_post_options')}</div>
|
|
<div class="form-field">
|
|
<label for="visible_cb"><input type="checkbox" id="visible_cb" name="visible"{$ctx->if_true($visible, ' checked="checked"')}> {$ctx->lang('blog_write_form_visible')}</label>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="clearfix">
|
|
<div class="form-field-label">{$ctx->lang('blog_text_options')}</div>
|
|
<div class="form-field">
|
|
<label for="toc_cb"><input type="checkbox" id="toc_cb" name="toc"{$ctx->if_true($toc, ' checked="checked"')}> {$ctx->lang('blog_write_form_toc')}</label>
|
|
<select name="lang">
|
|
{$ctx->for_each($langs, fn($l) => '<option value="'.$l->value.'"'.($l->value == $lang ? ' selected="selected"' : '').'>'.$l->value.'</option>')}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="clearfix">
|
|
<div class="form-field-label">{$ctx->lang('blog_write_form_date')}</div>
|
|
<div class="form-field">
|
|
<input type="date" name="date"{$ctx->if_true($date, ' value="'.$date.'"')}>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="2">
|
|
<div class="clearfix">
|
|
<div class="form-field-label">{$ctx->lang('blog_write_form_short_name')}</div>
|
|
<div class="form-field">
|
|
<input class="form-field-input" type="text" name="{$ctx->if_then_else($is_edit, 'new_short_name', 'short_name')}" value="{$short_name}" />
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="form-field-label"> </div>
|
|
<div class="form-field">
|
|
<button type="submit" name="submit_btn"><b>{$ctx->lang($is_edit ? 'save' : 'blog_write_form_submit_btn')}</b></button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</form>
|
|
<div id="form_placeholder"></div>
|
|
</td>
|
|
<td>
|
|
<div class="blog-write-form-preview post_text" id="preview_html"></div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
HTML;
|
|
|
|
$js_params = [
|
|
'langs' => array_map(fn($lang) => $lang->value, $langs),
|
|
'token' => $is_edit ? csrf_get('editpost'.$post_id) : csrf_get('post_add')
|
|
];
|
|
if ($is_edit)
|
|
$js_params += [
|
|
'edit' => true,
|
|
'id' => $post_id,
|
|
'texts' => $js_texts
|
|
];
|
|
$js_params = jsonEncode($js_params);
|
|
|
|
$js = <<<JAVASCRIPT
|
|
cur.form = new AdminWriteEditForm({$js_params});
|
|
JAVASCRIPT;
|
|
|
|
return [$html, $js];
|
|
}
|
|
|
|
|
|
function pageForm($ctx,
|
|
string|Stringable $title,
|
|
string|Stringable $text,
|
|
string|Stringable $short_name,
|
|
array $langs,
|
|
bool $is_edit = false,
|
|
?bool $saved = null,
|
|
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: 12px');
|
|
} else {
|
|
$bc_html = '';
|
|
}
|
|
|
|
$html = <<<HTML
|
|
<div class="form-error" id="form-error" style="display:none"></div>
|
|
{$ctx->if_true($saved, fn() => '<div class="form-success">'.$ctx->lang('info_saved').'</div>')}
|
|
{$bc_html}
|
|
<table cellpadding="0" cellspacing="0" class="blog-write-table">
|
|
<tr>
|
|
<td id="form_first_cell">
|
|
<form class="blog-write-form form-layout-v" name="pageForm" action="{$form_url}" method="post">
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$ctx->lang('pages_write_form_title')}</div>
|
|
<div class="form-field">
|
|
<input class="form-field-input" type="text" name="title" value="{$title}" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label">{$ctx->lang('pages_write_form_text')}</div>
|
|
<div class="form-field">
|
|
<textarea class="form-field-input" name="text" wrap="soft">{$text}</textarea><br/>
|
|
<a class="blog-write-form-toggle-link" id="toggle_wrap" href="">{$ctx->lang('pages_write_form_toggle_wrap')}</a>
|
|
</div>
|
|
</div>
|
|
|
|
{$ctx->if_then_else($is_edit,
|
|
fn() => $ctx->pageFormEditOptions($short_name, $visible),
|
|
fn() => $ctx->pageFormAddOptions($short_name))}
|
|
|
|
</form>
|
|
<div id="form_placeholder"></div>
|
|
</td>
|
|
<td>
|
|
<div class="blog-write-form-preview post_text" id="preview_html"></div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
HTML;
|
|
|
|
$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 = <<<JS
|
|
cur.form = new AdminWriteEditForm({$js_params});
|
|
JS;
|
|
|
|
return [$html, $js];
|
|
}
|
|
|
|
function pageFormEditOptions($ctx, $short_name, $visible) {
|
|
return <<<HTML
|
|
<div class="form-field-wrap clearfix">
|
|
<table class="blog-write-options-table">
|
|
<tr>
|
|
<td>
|
|
<div class="clearfix">
|
|
<div class="form-field-label">{$ctx->lang('pages_write_form_short_name')}</div>
|
|
<div class="form-field">
|
|
<input class="form-field-input" type="text" name="new_short_name" value="{$short_name}" />
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="clearfix">
|
|
<div class="form-field-label">{$ctx->lang('pages_write_form_options')}</div>
|
|
<div class="form-field">
|
|
<label for="visible_cb"><input type="checkbox" id="visible_cb" name="visible"{$ctx->if_true($visible, ' checked="checked"')}> {$ctx->lang('pages_write_form_visible')}</label>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td rowspan="2">
|
|
<button type="submit" name="submit_btn"><b>{$ctx->lang('pages_write_form_submit_btn')}</b></button>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
HTML;
|
|
}
|
|
|
|
function pageFormAddOptions($ctx, $short_name) {
|
|
return <<<HTML
|
|
<div class="form-field-wrap clearfix">
|
|
<div class="form-field-label"></div>
|
|
<div class="form-field">
|
|
<button type="submit" name="submit_btn"><b>{$ctx->lang('pages_write_form_submit_btn')}</b></button>
|
|
</div>
|
|
</div>
|
|
<input name="short_name" value="{$short_name}" type="hidden" />
|
|
HTML;
|
|
}
|
|
|
|
function pageNew($ctx, $short_name) {
|
|
return <<<HTML
|
|
<div class="page">
|
|
<div class="empty">
|
|
<a href="/{$short_name}/create/">{$ctx->lang('pages_create')}</a>
|
|
</div>
|
|
</div>
|
|
HTML;
|
|
|
|
}
|
|
|
|
// misc
|
|
function formError($ctx, $error) {
|
|
return <<<HTML
|
|
<div class="form-error">{$ctx->lang('error')}: {$error}</div>
|
|
HTML;
|
|
}
|
|
|
|
function markdownPreview($ctx, $unsafe_html, $title) {
|
|
return <<<HTML
|
|
<div class="blog-post">
|
|
<div class="blog-post">
|
|
{$ctx->if_true($title, '<div class="blog-post-title"><h1>'.$title.'</h1></div>')}
|
|
<div class="blog-post-text">{$unsafe_html}</div>
|
|
</div>
|
|
HTML;
|
|
}
|
|
|
|
|
|
function books($ctx) {
|
|
return <<<HTML
|
|
{$ctx->bc([
|
|
['text' => $ctx->lang('admin_title'), 'url' => '/admin/'],
|
|
['text' => $ctx->lang('admin_books')],
|
|
])}
|
|
|
|
HTML;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------
|
|
// ---------------------- ERRORS ----------------------
|
|
// ----------------------------------------------------
|
|
|
|
|
|
function errors($ctx,
|
|
array $list,
|
|
int $count,
|
|
int $pn_page,
|
|
int $pn_pages,
|
|
string $url,
|
|
?string $ip_filter = null,
|
|
?int $ip = null,
|
|
?string $query = null,
|
|
?string $url_query = null,
|
|
?string $file_query = null,
|
|
?string $line_query = null) {
|
|
return <<<HTML
|
|
{$ctx->bc([
|
|
['text' => $ctx->lang('admin_title'), 'url' => '/admin/'],
|
|
['text' => $ctx->lang('admin_errors')],
|
|
])}
|
|
|
|
<form action="/admin/errors/" method="get" class="admin_common_query_form">
|
|
{$ctx->if_true($ip, fn() => '<input type="hidden" name="ip" value="'.$ip.'" />')}
|
|
<input type="text" name="query" placeholder="text_like" value="{$query}" />
|
|
<input type="text" name="url_query" placeholder="url_like" value="{$url_query}" />
|
|
<input type="text" name="file_query" placeholder="file" value="{$file_query}" />
|
|
<input type="text" name="line_query" placeholder="line" value="{$line_query}" style="width: 50px" />
|
|
<input class="blue" type="submit" value="query" />
|
|
</form>
|
|
|
|
{$ctx->if_then_else(!empty($list),
|
|
fn() => $ctx->errors_table($list),
|
|
fn() => '<div class="empty_block">Error log is empty.</div>')}
|
|
|
|
{$ctx->pagenav($pn_page, $pn_pages, $url.'page={page}')}
|
|
HTML;
|
|
}
|
|
|
|
function errors_table($ctx,
|
|
array $list) {
|
|
return <<<HTML
|
|
<table border="1" width="100%" cellpadding="0" cellspacing="0" class="admin-error-log">
|
|
<thead>
|
|
<tr>
|
|
<th width="5%">Time</th>
|
|
<th width="20%">Source</th>
|
|
<th>Error</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{$ctx->for_each($list, fn($item) => $ctx->errors_table_item(
|
|
date: $item['date'],
|
|
is_cli: (bool)$item['is_cli'],
|
|
is_custom: (bool)$item['custom'],
|
|
user_agent: $item['ua'],
|
|
ip: (int)$item['ip'],
|
|
ip_s: $item['ip_s'],
|
|
full_url: $item['full_url'],
|
|
url: $item['url'],
|
|
file: $item['file'],
|
|
line: (int)$item['line'],
|
|
admin_id: (int)$item['admin_id'],
|
|
nl2br_text: $item['text'],
|
|
num: (int)$item['num'],
|
|
errtype: $item['errtype'] ?? null,
|
|
time: $item['time'],
|
|
stacktrace: $item['stacktrace'],
|
|
item_id: (int)$item['id']
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
HTML;
|
|
}
|
|
|
|
function errors_table_item($ctx,
|
|
int $item_id,
|
|
string $date,
|
|
bool $is_cli,
|
|
bool $is_custom,
|
|
string $user_agent,
|
|
int $ip,
|
|
string $ip_s,
|
|
string $full_url,
|
|
string $url,
|
|
string $file,
|
|
int $line,
|
|
int $admin_id,
|
|
string $nl2br_text,
|
|
int $num,
|
|
?string $errtype,
|
|
string $time,
|
|
string $stacktrace) {
|
|
return <<<HTML
|
|
<tr>
|
|
<td>
|
|
{$date}
|
|
</td>
|
|
<td>
|
|
{$ctx->if_then_else(!$is_cli,
|
|
fn() => '<span class="admin-error-log-num"><a href="/admin/errors/?ip='.$ip.'">'.$ip_s.'</a></span> <a class="admin-error-log-link" href="'.$full_url.'">'.$url.'</a><br/>'.$user_agent,
|
|
fn() => '<span class="admin-error-log-num">cmd</span>')}
|
|
</td>
|
|
|
|
<td class="admin_error_log_ms">
|
|
{$ctx->if_true($admin_id,
|
|
fn() => '<span class="admin-error-log-num">admin='.$admin_id.'</span>')}
|
|
{$ctx->if_then_else($is_custom,
|
|
fn() => '<span class="admin-error-log-num">'.$num.', '.$time.'</span> <b>'.$file.'</b>:'.$line.'<br/>'
|
|
.'<span class="admin-error-log-num">'.$errtype.'</span> '.$nl2br_text,
|
|
fn() => '<span class="admin-error-log-num">'.$num.', '.$time.'</span> '.$nl2br_text)}
|
|
|
|
{$ctx->if_true($stacktrace,
|
|
fn() => $ctx->errors_table_item_stacktrace($item_id, $stacktrace))}
|
|
</td>
|
|
</tr>
|
|
HTML;
|
|
}
|
|
|
|
function errors_table_item_stacktrace($ctx, $item_id, $nl2br_stacktrace) {
|
|
return <<<HTML
|
|
<div class="admin-error-log-stacktrace-wrap">
|
|
<a href="javascript:void(0)" onclick="toggle(ge('admin_error_log_stacktrace{$item_id}'))">Show/hide stacktrace</a>
|
|
<div id="admin_error_log_stacktrace{$item_id}" style="display: none">{$nl2br_stacktrace}</div>
|
|
</div>
|
|
HTML;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
// ---------------------- AUTH LOG ----------------------
|
|
// ------------------------------------------------------
|
|
|
|
function auth_log($ctx, array $list, int $pn_page, int $pn_pages) {
|
|
return <<<HTML
|
|
{$ctx->bc([
|
|
['text' => $ctx->lang('admin_title'), 'url' => '/admin/'],
|
|
['text' => $ctx->lang('admin_auth_log')],
|
|
])}
|
|
|
|
{$ctx->if_then_else(!empty($list),
|
|
fn() => $ctx->auth_log_table($list),
|
|
fn() => '<div class="empty_block">Auth log is empty.</div>')}
|
|
|
|
{$ctx->pagenav($pn_page, $pn_pages, '/admin/auth-log/?page={page}')}
|
|
HTML;
|
|
}
|
|
|
|
function auth_log_table($ctx, array $list) {
|
|
return <<<HTML
|
|
<table border="1" width="100%" cellpadding="0" cellspacing="0" class="admin-error-log">
|
|
<thead>
|
|
<tr>
|
|
<th width="20%">Admin</th>
|
|
<th width="15%">Time</th>
|
|
<th width="10%">IP</th>
|
|
<th width="55%">User-Agent</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{$ctx->for_each($list,
|
|
fn($item) => $ctx->auth_log_table_item(
|
|
date: $item['date'],
|
|
ip: $item['ip'],
|
|
user_agent: $item['ua'],
|
|
admin_login: $item['login'],
|
|
admin_id: (int)$item['admin_id'],
|
|
activity_ts: $item['activity_ts_s']))}
|
|
</tbody>
|
|
</table>
|
|
HTML;
|
|
}
|
|
|
|
function auth_log_table_item($ctx, $date, $ip, $user_agent, string $admin_login, int $admin_id, string $activity_ts) {
|
|
return <<<HTML
|
|
<tr>
|
|
<td>
|
|
<span title="Last activity: {$activity_ts}">{$admin_login}</span> (id={$admin_id})
|
|
</td>
|
|
<td>{$date}</td>
|
|
<td>{$ip}</td>
|
|
<td>{$user_agent}</td>
|
|
</tr>
|
|
HTML;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------
|
|
// ---------------------- ACTIONS LOG ----------------------
|
|
// ---------------------------------------------------------
|
|
|
|
function actions_log($ctx,
|
|
array $list,
|
|
array $admin_logins,
|
|
string $url,
|
|
array $action_types,
|
|
int $pn_page,
|
|
int $pn_pages) {
|
|
return <<<HTML
|
|
{$ctx->bc([
|
|
['text' => $ctx->lang('admin_title'), 'url' => '/admin/'],
|
|
['text' => $ctx->lang('admin_actions_log')],
|
|
])}
|
|
|
|
{$ctx->if_then_else(!empty($list),
|
|
fn() => $ctx->actions_log_table($list, $admin_logins, $action_types),
|
|
fn() => '<div class="empty_block">Actions log is empty.</div>')}
|
|
|
|
{$ctx->pagenav($pn_page, $pn_pages, $url.'page={page}')}
|
|
HTML;
|
|
}
|
|
|
|
function actions_log_table($ctx, array $list, array $admin_logins, array $action_types) {
|
|
return <<<HTML
|
|
<table border="1" width="100%" cellpadding="0" cellspacing="0" class="admin-error-log">
|
|
<thead>
|
|
<tr>
|
|
<th width="9%">Time</th>
|
|
<th width="14%">Who</th>
|
|
<th width="11%">Action</th>
|
|
<th>Data</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{$ctx->for_each($list,
|
|
fn(\AdminActions\BaseAction $item) => $ctx->actions_log_table_item(
|
|
date: $item->getDate(),
|
|
ip: $item->getIPv4(),
|
|
is_cli: $item->isCommandLineAction(),
|
|
admin_login: $admin_logins[$item->getAdminId()],
|
|
action_name: $item->getActionName(),
|
|
unsafe_data: $item->renderHtml()))}
|
|
</tbody>
|
|
</table>
|
|
HTML;
|
|
}
|
|
|
|
function actions_log_table_item($ctx,
|
|
string $date,
|
|
string $ip,
|
|
bool $is_cli,
|
|
string $admin_login,
|
|
string $action_name,
|
|
string $unsafe_data) {
|
|
return <<<HTML
|
|
<tr>
|
|
<td>{$date}</td>
|
|
<td>
|
|
{$ctx->if_then_else(!$is_cli,
|
|
fn() => $admin_login.', '.$ip,
|
|
fn() => 'console')}
|
|
</td>
|
|
<td>{$action_name}</td>
|
|
<td>{$unsafe_data}</td>
|
|
</tr>
|
|
HTML;
|
|
} |