uploads: notes on english and russian

This commit is contained in:
E. S. 2024-03-14 21:53:34 +00:00
parent 53d8861dbb
commit 699affded9
14 changed files with 162 additions and 133 deletions

View File

@ -243,13 +243,14 @@ class AdminHandler extends request_handler {
set_title('$blog_upload');
render('admin/uploads',
error: $error,
uploads: $uploads);
uploads: $uploads,
langs: PostLanguage::cases());
}
function POST_uploads() {
csrf_check('addupl');
list($custom_name, $note) = input('name, note');
list($custom_name, $note_en, $note_ru) = input('name, note_en, note_ru');
if (!isset($_FILES['files']))
redirect('/admin/uploads/?error='.urlencode('no file'));
@ -266,7 +267,8 @@ class AdminHandler extends request_handler {
}
if (count($files) > 1) {
$note = '';
$note_en = '';
$note_ru = '';
$custom_name = '';
}
@ -285,12 +287,13 @@ class AdminHandler extends request_handler {
$upload_id = uploads::add(
$f['tmp_name'],
$name,
$note);
$note_en,
$note_ru);
if (!$upload_id)
redirect('/admin/uploads/?error='.urlencode('failed to create upload'));
admin_log(new \AdminActions\UploadsAdd($upload_id, $name, $note));
admin_log(new \AdminActions\UploadsAdd($upload_id, $name, $note_en, $note_ru));
}
redirect('/admin/uploads/');
@ -308,7 +311,10 @@ class AdminHandler extends request_handler {
}
function POST_upload_edit_note() {
list($id, $note) = input('i:id, note');
list($id, $note, $lang) = input('i:id, note, lang');
$lang = PostLanguage::tryFrom($lang);
if (!$lang)
not_found();
$upload = uploads::get($id);
if (!$upload)
@ -316,16 +322,19 @@ class AdminHandler extends request_handler {
csrf_check('editupl'.$id);
$upload->setNote($note);
$upload->setNote($lang, $note);
admin_log(new \AdminActions\UploadsEditNote($id, $note));
admin_log(new \AdminActions\UploadsEditNote($id, $note, $lang->value));
redirect('/admin/uploads/');
}
function POST_ajax_md_preview() {
ensure_xhr();
list($md, $title, $use_image_previews) = input('md, title, b:use_image_previews');
$html = markup::markdownToHtml($md, $use_image_previews);
list($md, $title, $use_image_previews, $lang) = input('md, title, b:use_image_previews, lang');
$lang = PostLanguage::tryFrom($lang);
if (!$lang)
invalid_request();
$html = markup::markdownToHtml($md, $use_image_previews, $lang);
$ctx = skin('admin');
$html = $ctx->markdownPreview(
unsafe_html: $html,

View File

@ -81,7 +81,8 @@ extend(AdminWriteEditForm.prototype, {
this.previewRequest.abort();
var params = {
md: this.form.elements.text.value,
use_image_previews: this.isPage() ? 1 : 0
use_image_previews: this.isPage() ? 1 : 0,
lang: this.getCurrentLang()
};
if (this.isPost())
params.title = this.form.elements.title.value;

View File

@ -95,7 +95,13 @@
font-size: $fs - 2px;
}
.blog-upload-item-note {
padding: 0 0 4px;
padding: 8px 0 0;
> span {
background-color: $hover-hl;
padding: 1px 5px;
color: $dark-grey;
margin-right: 6px;
}
}
.blog-upload-item-md {
margin-top: 3px;
@ -376,51 +382,43 @@ body.wide .blog-post {
color: $grey;
}
.blog-list-table-wrap {
.blog-list {
&-wrap {
padding: 5px 0;
}
.blog-list-table {
border-collapse: collapse;
&-item {
display: block;
padding: 6px 8px;
margin-left: -8px;
margin-right: -8px;
font-size: $fs;
//line-height: 140%;
&:hover {
text-decoration: none;
background-color: $hover-hl;
@include radius(3px);
}
.blog-list-table td {
vertical-align: top;
padding: 0 0 13px;
}
.blog-list-table tr:last-child td {
padding-bottom: 0;
}
td.blog-item-date-cell {
width: 1px;
white-space: nowrap;
//text-align: right;
padding-right: 10px;
}
.blog-item-date {
&-date {
color: $grey;
//text-transform: lowercase;
float: left;
}
td.blog-item-title-cell {
&-title {
margin-left: 70px;
text-align: left;
}
.blog-item-title {
//font-weight: bold;
}
.blog-item-row {
font-size: $fs;
line-height: 140%;
}
.blog-item-row.ishidden a.blog-item-title {
&.is-hidden &-title {
color: $fg;
}
.blog-item-row-year {
td {
padding-top: 10px;
//text-align: right;
font-size: $fs + 2px;
//letter-spacing: -0.5px;
}
&:first-child td {
padding-top: 0;
}
}
.blog-list-title {
font-size: $fs + 2px;
padding-top: 14px;
margin-bottom: 8px;
&:first-child { padding-top: 0; }
}

View File

@ -282,9 +282,10 @@ table.contacts div.note {
max-width: 100%;
}
.md-image-note {
line-height: 150%;
line-height: 130%;
color: $dark-grey;
padding: 2px 0 4px;
padding: 8px 0 4px;
font-style: italic;
}
.md-video video {

View File

@ -1,6 +1,6 @@
@import '../vars';
$form-field-label-width: 120px;
$form-field-label-width: 160px;
form { display: block; margin: 0; }

View File

@ -7,7 +7,8 @@ class UploadsAdd extends BaseAction {
public function __construct(
public int $uploadId,
public string $name,
public string $note
public string $noteEn,
public string $noteRu
) {}
}

View File

@ -6,7 +6,8 @@ class UploadsEditNote extends BaseAction {
public function __construct(
public int $uploadId,
public string $note
public string $note,
public string $lang
) {}
}

View File

@ -1,12 +1,16 @@
<?php
require_once 'engine/skin.php';
require_once 'lib/posts.php';
class MyParsedown extends ParsedownExtended {
protected array $options;
function __construct(
?array $opts = null,
protected bool $useImagePreviews = false
protected bool $useImagePreviews = false,
protected ?PostLanguage $lang = null,
) {
$parsedown_opts = [
'tables' => [
@ -44,15 +48,13 @@ class MyParsedown extends ParsedownExtended {
unset($result['element']['text']);
$ctx = self::getSkinContext();
$result['element']['rawHtml'] = $ctx->fileupload($upload->name, $upload->getDirectUrl(), $upload->note, $upload->getSize());
$result['element']['rawHtml'] = $ctx->fileupload($upload->name, $upload->getDirectUrl(), $upload->noteRu, $upload->getSize());
return $result;
}
}
protected function inlineImage($excerpt) {
global $config;
if (preg_match('/^{image:([\w]{8}),(.*?)}{\/image}/', $excerpt['text'], $matches)) {
$random_id = $matches[1];
@ -115,7 +117,7 @@ class MyParsedown extends ParsedownExtended {
url: $image_url,
direct_url: $image->getDirectUrl(),
note: $image->note
note: $this->lang !== null && $this->lang == PostLanguage::Russian ? $image->noteRu : $image->noteEn
);
return $result;

View File

@ -1,11 +1,14 @@
<?php
require_once 'lib/ext/MyParsedown.php';
require_once 'lib/posts.php';
class markup {
public static function markdownToHtml(string $md, bool $use_image_previews = true): string {
$pd = new MyParsedown(useImagePreviews: $use_image_previews);
public static function markdownToHtml(string $md,
bool $use_image_previews = true,
?PostLanguage $lang = null): string {
$pd = new MyParsedown(useImagePreviews: $use_image_previews, lang: $lang);
return $pd->text($md);
}

View File

@ -21,7 +21,7 @@ class Post extends model {
protected array $texts = [];
public function addText(PostLanguage $lang, string $title, string $md, bool $toc): ?PostText {
$html = markup::markdownToHtml($md);
$html = markup::markdownToHtml($md, lang: $lang);
$text = markup::htmlToText($html);
$data = [
@ -128,7 +128,7 @@ class PostText extends model {
public function edit(array $fields) {
if ($fields['md'] != $this->md) {
$fields['html'] = markup::markdownToHtml($fields['md']);
$fields['html'] = markup::markdownToHtml($fields['md'], lang: $this->lang);
$fields['text'] = markup::htmlToText($fields['html']);
}
@ -141,13 +141,13 @@ class PostText extends model {
}
public function updateHtml(): void {
$html = markup::markdownToHtml($this->md);
$html = markup::markdownToHtml($this->md, lang: $this->lang);
$this->html = $html;
DB()->query("UPDATE posts_texts SET html=? WHERE id=?", $html, $this->id);
}
public function updateText(): void {
$html = markup::markdownToHtml($this->md);
$html = markup::markdownToHtml($this->md, lang: $this->lang);
$text = markup::htmlToText($html);
$this->text = $text;
DB()->query("UPDATE posts_texts SET text=? WHERE id=?", $text, $this->id);

View File

@ -19,7 +19,7 @@ class uploads {
return in_array($ext, UPLOADS_ALLOWED_EXTENSIONS);
}
static function add(string $tmp_name, string $name, string $note): ?int {
static function add(string $tmp_name, string $name, string $note_en, string $note_ru): ?int {
global $config;
$name = sanitize_filename($name);
@ -44,7 +44,8 @@ class uploads {
'image' => (int)$is_image,
'image_w' => $image_w,
'image_h' => $image_h,
'note' => $note,
'note_ru' => $note_ru,
'note_en' => $note_en,
'downloads' => 0,
])) {
return null;
@ -163,7 +164,8 @@ class Upload extends model {
public int $image; // TODO: remove
public int $imageW;
public int $imageH;
public string $note;
public string $noteRu;
public string $noteEn;
function getDirectory(): string {
global $config;
@ -212,9 +214,9 @@ class Upload extends model {
return $md;
}
function setNote(string $note) {
function setNote(PostLanguage $lang, string $note) {
$db = DB();
$db->query("UPDATE uploads SET note=? WHERE id=?", $note, $this->id);
$db->query("UPDATE uploads SET note_{$lang->value}=? WHERE id=?", $note, $this->id);
}
function isImage(): bool {

View File

@ -2,6 +2,7 @@
namespace skin\admin;
use PostLanguage;
use Stringable;
use function skin\base\layout;
@ -64,7 +65,7 @@ HTML;
// uploads page
// ------------
function uploads($ctx, $uploads, $error) {
function uploads($ctx, $uploads, $error, array $langs) {
return <<<HTML
{$ctx->if_true($error, $ctx->formError, $error)}
@ -91,12 +92,8 @@ return <<<HTML
</div>
</div>
<div class="form-field-wrap clearfix">
<div class="form-field-label">{$ctx->lang('blog_upload_form_note')}:</div>
<div class="form-field">
<input type="text" name="note" size="55">
</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>
@ -108,12 +105,14 @@ return <<<HTML
</div>
<div class="blog-upload-list">
{$ctx->for_each($uploads, fn($u) => $ctx->uploadsItem(
{$ctx->for_each($uploads, fn($u) => $ctx->uploads_item(
id: $u->id,
name: $u->name,
direct_url: $u->getDirectUrl(),
note: $u->note,
addslashes_note: $u->note,
note_ru: $u->noteRu,
note_en: $u->noteEn,
addslashes_note_ru: $u->noteRu,
addslashes_note_en: $u->noteEn,
markdown: $u->getMarkdown(),
size: $u->getSize(),
))}
@ -121,17 +120,35 @@ return <<<HTML
HTML;
}
function uploadsItem($ctx, $id, $direct_url, $note, $addslashes_note, $markdown, $name, $size) {
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}/?token={$ctx->csrf('editupl'.$id)}', prompt('Note:', '{$addslashes_note}'))">Edit note</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>
{$ctx->if_true($note, '<div class="blog-upload-item-note">'.$note.'</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>

View File

@ -45,36 +45,35 @@ HTML;
function articles($ctx, array $posts, PostLanguage $selected_lang): string {
if (empty($posts))
return $ctx->articlesEmpty($selected_lang);
return $ctx->articles_empty($selected_lang);
return <<<HTML
<div class="blog-list">
{$ctx->articlesPostsTable($posts, $selected_lang)}
{$ctx->articles_posts_table($posts, $selected_lang)}
</div>
HTML;
}
function articlesEmpty($ctx, PostLanguage $selected_lang) {
function articles_empty($ctx, PostLanguage $selected_lang) {
return <<<HTML
<div class="empty">
{$ctx->lang('blog_no')}
{$ctx->articlesRightLinks($selected_lang->value)}
{$ctx->articles_right_links($selected_lang->value)}
</div>
HTML;
}
function articlesPostsTable($ctx, array $posts, PostLanguage $selected_lang): string {
function articles_posts_table($ctx, array $posts, PostLanguage $selected_lang): string {
$ctx->year = 3000;
return <<<HTML
<div class="blog-list-table-wrap">
<table class="blog-list-table" width="100%" cellspacing="0" cellpadding="0">
{$ctx->for_each($posts, fn($post, $i) => $ctx->articlesPostRow($i, $post, $selected_lang))}
</table>
<div class="blog-list-wrap">
{$ctx->for_each($posts,
fn($post, $i) => $ctx->articles_post_row($i, $post, $selected_lang))}
</div>
HTML;
}
function articlesPostRow($ctx, int $index, Post $post, PostLanguage $selected_lang): string {
function articles_post_row($ctx, int $index, Post $post, PostLanguage $selected_lang): string {
$year = $post->getYear();
$date = $post->getDate();
$url = $post->getUrl($selected_lang);
@ -83,32 +82,29 @@ $pt = $post->getText($selected_lang);
$title = $pt->title;
return <<<HTML
{$ctx->if_true($ctx->year > $year, $ctx->articlesIndexYearLine, $year, $index === 0, $selected_lang->value)}
<tr class="blog-item-row{$ctx->if_not($post->visible, ' ishidden')}">
<td class="blog-item-date-cell">
<span class="blog-item-date">{$date}</span>
</td>
<td class="blog-item-title-cell">
<a class="blog-item-title" href="{$url}">{$title}</a>
</td>
</tr>
{$ctx->if_true($ctx->year > $year,
fn() => $ctx->articles_index_year_line($year, $index === 0, $selected_lang->value))}
<a href="{$url}" class="blog-list-item clearfix{$ctx->if_not($post->visible, ' is-hidden')}">
<div class="blog-list-item-date">{$date}</div>
<div class="blog-list-item-title">{$title}</div>
</a>
HTML;
}
function articlesIndexYearLine($ctx, $year, $show_right_links, string $selected_lang): string {
function articles_index_year_line($ctx, $year, $show_right_links, string $selected_lang): string {
$ctx->year = $year;
return <<<HTML
<tr class="blog-item-row-year">
<td class="blog-item-date-cell"><span>{$year}</span></td>
<td>
{$ctx->if_true($show_right_links, $ctx->articlesRightLinks($selected_lang))}
</td>
</tr>
<div class="blog-list-title">
{$year}
{$ctx->if_true($show_right_links,
fn() => $ctx->articles_right_links($selected_lang))}
</div>
HTML;
}
function articlesRightLinks($ctx, string $selected_lang) {
function articles_right_links($ctx, string $selected_lang) {
$links = [
['url' => $selected_lang != 'en' ? '/articles/' : null, 'label' => lang('lang_en')],
['url' => $selected_lang != 'ru' ? '/articles/?lang=ru' : null, 'label' => lang('lang_ru')],
@ -119,12 +115,12 @@ if (is_admin()) {
return <<<HTML
<div class="blog-item-right-links">
{$ctx->for_each($links, fn($link, $index) => $ctx->articlesRightLink($link['url'], $link['label'], $index))}
{$ctx->for_each($links, fn($link, $index) => $ctx->articles_right_link($link['url'], $link['label'], $index))}
</div>
HTML;
}
function articlesRightLink($ctx, $url, string $label, int $index) {
function articles_right_link($ctx, $url, string $label, int $index) {
$buf = '';
if ($index > 0)
$buf .= ' <span class="blog-links-separator">|</span> ';
@ -142,15 +138,15 @@ $html = <<<HTML
<!--<div class="blog-post-title-nav">
<a href="/">{$ctx->lang('index')}</a> <span>&rsaquo;</span>
</div>-->
{$ctx->if_admin($ctx->pageAdminLinks, $page_url, $short_name)}
{$ctx->if_admin($ctx->page_admin_links, $page_url, $short_name)}
<div class="blog-post-text">{$unsafe_html}</div>
</div>
HTML;
return [$html, markdownThemeChangeListener()];
return [$html, js_markdownThemeChangeListener()];
}
function pageAdminLinks($ctx, $url, $short_name) {
function page_admin_links($ctx, $url, $short_name) {
return <<<HTML
<div class="page-edit-links">
<a href="{$url}edit/">{$ctx->lang('edit')}</a>
@ -177,8 +173,8 @@ $html = <<<HTML
<div class="blog-post-date">
{$ctx->if_not($visible, $ctx->lang('blog_post_hidden').' |')}
{$date}
{$ctx->if_true($other_langs, $ctx->postOtherLangs($url, $other_langs))}
{$ctx->if_admin($ctx->postAdminLinks, $url, $id, $lang)}
{$ctx->if_true($other_langs, $ctx->post_other_langs($url, $other_langs))}
{$ctx->if_admin($ctx->post_admin_links, $url, $id, $lang)}
</div>
</div>
<div class="blog-post-text">{$unsafe_html}</div>
@ -188,10 +184,10 @@ $html = <<<HTML
</div>
HTML;
return [$html, markdownThemeChangeListener()];
return [$html, js_markdownThemeChangeListener()];
}
function postOtherLangs($ctx, $url, $other_langs) {
function post_other_langs($ctx, $url, $other_langs) {
$buf = '';
foreach ($other_langs as $lang) {
$buf .= ' | <a href="'.$url.'?lang='.$lang.'">'.$ctx->lang('blog_read_in_'.$lang).'</a>';
@ -213,14 +209,14 @@ HTML;
}
function postAdminLinks($ctx, $url, $id, string $lang) {
function post_admin_links($ctx, $url, $id, string $lang) {
return <<<HTML
| <a href="{$url}edit/?lang={$lang}">{$ctx->lang('edit')}</a>
| <a href="{$url}delete/?token={$ctx->csrf('delpost'.$id)}" onclick="return confirm('{$ctx->lang('blog_post_delete_confirmation')}')">{$ctx->lang('delete')}</a>
HTML;
}
function markdownThemeChangeListener() {
function js_markdownThemeChangeListener() {
return <<<JS
ThemeSwitcher.addOnChangeListener(function(isDark) {
var nodes = document.querySelectorAll('.md-image-wrap');

View File

@ -23,11 +23,9 @@ return <<<HTML
<a href="{$direct_url}">
<div style="background: url('{$url}') no-repeat; background-size: contain; width: {$w}px; padding-top: {$padding_top}%;"></div>
</a>
{$ctx->if_true(
$note != '' && !$nolabel,
'<div class="md-image-note">'.$note.'</div>'
)}
</div>
{$ctx->if_true($note != '' && !$nolabel,
fn() => '<div class="md-image-note">'.$note.'</div>')}
</div>
HTML;
}