diff options
author | Evgeny Zinoviev <me@ch1p.io> | 2022-07-09 19:40:17 +0300 |
---|---|---|
committer | Evgeny Zinoviev <me@ch1p.io> | 2022-07-09 19:40:17 +0300 |
commit | f7bfdf58def6aadc922e1632f407d1418269a0d7 (patch) | |
tree | d7a0b2819e6a26c11d40ee0b27267ea827fbb345 /skin |
initial
Diffstat (limited to 'skin')
-rw-r--r-- | skin/admin.skin.php | 344 | ||||
-rw-r--r-- | skin/base.skin.php | 190 | ||||
-rw-r--r-- | skin/error.skin.php | 40 | ||||
-rw-r--r-- | skin/main.skin.php | 195 | ||||
-rw-r--r-- | skin/markdown.skin.php | 43 | ||||
-rw-r--r-- | skin/rss.skin.php | 29 |
6 files changed, 841 insertions, 0 deletions
diff --git a/skin/admin.skin.php b/skin/admin.skin.php new file mode 100644 index 0000000..f03d7ce --- /dev/null +++ b/skin/admin.skin.php @@ -0,0 +1,344 @@ +<?php + +namespace skin\admin; + +use Stringable; + +// login page +// ---------- + +function login($ctx) { +$html = <<<HTML +<form action="/admin/login/" method="post" class="form-layout-h"> + <input type="hidden" name="token" value="{$ctx->csrf('adminlogin')}" /> + <div class="form-field-wrap clearfix"> + <div class="form-field-label">{$ctx->lang('as_form_password')}:</div> + <div class="form-field"> + <input id="as_password" 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('submit')}</button> + </div> + </div> +</form> +HTML; + +$js = <<<JAVASCRIPT +ge('as_password').focus(); +JAVASCRIPT; + +return [$html, $js]; +} + + +// index page +// ---------- + +function index($ctx) { + return <<<HTML +<div class="admin-page"> +<!-- <a href="/admin/log/">Log</a><br/>--> + <a href="/admin/logout/?token={$ctx->csrf('logout')}">Sign out</a> +</div> +HTML; +} + + +// uploads page +// ------------ + +function uploads($ctx, $uploads, $error) { +return <<<HTML +{$ctx->if_true($error, $ctx->formError, $error)} + +<div class="blog-upload-form"> + <form action="/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> + + <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> + + <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->uploadsItem( + id: $u->id, + name: $u->name, + direct_url: $u->getDirectUrl(), + note: $u->note, + addslashes_note: $u->note, + markdown: $u->getMarkdown(), + size: $u->getSize(), + ))} +</div> +HTML; +} + +function uploadsItem($ctx, $id, $direct_url, $note, $addslashes_note, $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('/uploads/edit_note/{$id}/?token={$ctx->csrf('editupl'.$id)}', prompt('Note:', '{$addslashes_note}'))">Edit note</a> + | <a href="/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> + <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($ctx, + string|Stringable $title, + string|Stringable $text, + string|Stringable $short_name, + string|Stringable $tags = '', + bool $is_edit = false, + $error_code = null, + ?bool $saved = null, + ?bool $visible = null, + string|Stringable|null $post_url = null, + ?int $post_id = null): array { +$form_url = !$is_edit ? '/write/' : $post_url.'edit/'; + +$html = <<<HTML +{$ctx->if_true($error_code, '<div class="form-error">'.$ctx->lang('err_blog_'.$error_code).'</div>')} +{$ctx->if_true($saved, '<div class="form-success">'.$ctx->lang('info_saved').'</div>')} +<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" enctype="multipart/form-data"> + <input type="hidden" name="token" value="{$ctx->if_then_else($is_edit, $ctx->csrf('editpost'.$post_id), $ctx->csrf('addpost'))}" /> + + <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_write_form_tags')}</div> + <div class="form-field"> + <input class="form-field-input" type="text" name="tags" value="{$tags}" /> + </div> + </div> + </td> + <td> + <div class="clearfix"> + <div class="form-field-label">{$ctx->lang('blog_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('blog_write_form_visible')}</label> + </div> + </div> + </td> + </tr> + <tr> + <td> + <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="clearfix"> + <div class="form-field-label"> </div> + <div class="form-field"> + <button type="submit" name="submit_btn"><b>{$ctx->lang('blog_write_form_submit_btn')}</b></button> + </div> + </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 = json_encode($is_edit + ? ['edit' => true, 'id' => $post_id] + : (object)[]); +$js = "AdminWriteForm.init({$js_params});"; + +return [$html, $js]; +} + + +function pageForm($ctx, + string|Stringable $title, + string|Stringable $text, + string|Stringable $short_name, + bool $is_edit = false, + $error_code = null, + ?bool $saved = null, + bool $visible = false): array { +$form_url = '/'.$short_name.'/'.($is_edit ? 'edit' : 'create').'/'; +$html = <<<HTML +{$ctx->if_true($error_code, '<div class="form-error">'.$ctx->lang('err_pages_'.$error_code).'</div>')} +{$ctx->if_true($saved, '<div class="form-success">'.$ctx->lang('info_saved').'</div>')} +<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"> + <input type="hidden" name="token" value="{$ctx->if_then_else($is_edit, $ctx->csrf('editpage'.$short_name), $ctx->csrf('addpage'))}" /> + + <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 = json_encode(['pages' => true, 'edit' => $is_edit]); +$js = <<<JAVASCRIPT +AdminWriteForm.init({$js_params}); +JAVASCRIPT; + +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"> + {$ctx->if_true($title, '<div class="blog-post-title"><h1>'.$title.'</h1></div>')} + <div class="blog-post-text">{$unsafe_html}</div> +</div> +HTML; + +}
\ No newline at end of file diff --git a/skin/base.skin.php b/skin/base.skin.php new file mode 100644 index 0000000..b0ebac3 --- /dev/null +++ b/skin/base.skin.php @@ -0,0 +1,190 @@ +<?php + +namespace skin\base; + +function layout($ctx, $title, $unsafe_body, $static, $meta, $js, $opts, $exec_time, $unsafe_lang) { +return <<<HTML +<!doctype html> +<html lang="en"> + <head> + <meta http-equiv="content-type" content="text/html; charset=utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon"> + <link rel="alternate" type="application/rss+xml" href="/feed.rss"> + <title>{$title}</title> + {$ctx->renderMeta($meta)} + {$ctx->renderStatic($static)} + </head> + <body{$ctx->if_true($opts['full_width'], ' class="full-width"')}> + {$ctx->renderHeader(renderLogo($ctx, $opts['logo_path_map'], $opts['logo_link_map']))} + <div class="page-content base-width"> + <div class="page-content-inner">{$unsafe_body}</div> + </div> + {$ctx->if_true($js != '' || !empty($lang) || $opts['dynlogo_enabled'], + $ctx->renderScript, $js, $unsafe_lang, $opts['dynlogo_enabled'])} + </body> +</html> +<!-- exec time: {$exec_time}s --> +HTML; +} + +function renderScript($ctx, $unsafe_js, $unsafe_lang, $enable_dynlogo) { +return <<<HTML +<script type="text/javascript"> +{$ctx->if_true($unsafe_js, '(function(){'.$unsafe_js.'})();')} +{$ctx->if_true($unsafe_lang, 'extend(__lang, '.$unsafe_lang.');')} +{$ctx->if_true($enable_dynlogo, 'DynamicLogo.init();')} +</script> +HTML; +} + +function renderMeta($ctx, $meta) { + if (empty($meta)) + return ''; + return implode('', array_map(function(array $item): string { + $s = '<meta'; + foreach ($item as $k => $v) + $s .= ' '.htmlescape($k).'="'.htmlescape($v).'"'; + $s .= '>'; + return $s; + }, $meta)); +} + +function renderStatic($ctx, $static) { + global $config; + $html = []; + foreach ($static as $name) { + // list($name, $options) = $item; + $version = $config['is_dev'] ? time() : $config['static'][substr($name, 1)] ?? 'notfound'; + if (str_ends_with($name, '.js')) + $html[] = jsLink($name, $version); + else if (str_ends_with($name, '.css')) + $html[] = cssLink($name, $version/*, $options*/); + } + return implode("\n", $html); +} + +function renderHeader($ctx, $unsafe_logo_html) { + return <<<HTML +<div class="head base-width"> + <div class="head-inner clearfix"> + <div class="head-logo">{$unsafe_logo_html}</div> + <div class="head-items clearfix"> + <a class="head-item" href="/"><span><span>blog</span></span></a> + <a class="head-item" href="/projects/"><span><span>projects</span></span></a> + <a class="head-item" href="https://git.ch1p.io/?s=idle"><span><span>git</span></span></a> + <a class="head-item" href="/misc/"><span><span>misc</span></span></a> + <a class="head-item" href="/contacts/"><span><span>contacts</span></span></a> + {$ctx->if_admin('<a class="head-item" href="/admin/"><span><span>admin</span></span></a>')} + </div> + </div> +</div> +HTML; +} + +// TODO rewrite this fcking crap +function renderLogo($ctx, array $path_map = [], array $link_map = []): string { + $uri = \RequestDispatcher::path(); + + if (!\admin::isAdmin()) { + $prompt_sign = '<span class="head-logo-dolsign">$</span>'; + } else { + $prompt_sign = '<span class="head-logo-dolsign is_root">#</span>'; + } + + if ($uri == '/') { + $html = '<span class="head-logo-path">/home/'.$ctx->lang('ch1p').'</span> '.$prompt_sign; + } else { + $uri_len = strlen($uri); + + $html = '<a href="/" id="head_dyn_link">'; + $close_tags = 0; + + $path_parts = []; + $path_links = []; + + $last_pos = 0; + $cur_path = ''; + while ($last_pos < $uri_len) { + $first = $last_pos === 0; + $end = false; + + $pos = strpos($uri, '/', $last_pos); + if ($pos === false || $pos == $uri_len-1) { + $pos = $uri_len-1; + $end = true; + } + + $part = substr($uri, $last_pos, $pos - $last_pos + 1); + $cur_path .= $part; + + if ($end) { + if (substr($part, -1) == '/') + $part = substr($part, 0, strlen($part)-1); + $cur_path = '/'; + $html .= str_repeat('</span>', $close_tags-1); + $close_tags = 1; + } + + $span_class = 'head-logo-path'; + if ($first) { + $span_class .= ' alwayshover'; + } else if ($end) { + $span_class .= ' neverhover'; + } + + $html .= '<span class="'.$span_class.'" data-url="$[['.count($path_links).']]">${{'.count($path_parts).'}}'; + $path_parts[] = ($first ? '~' : '').$part; + $path_links[] = $cur_path; + + $last_pos = $pos + 1; + $close_tags++; + } + $html .= str_repeat('</span>', $close_tags).' '.$prompt_sign.' <span class="head-logo-cd">cd <span id="head_cd_text">~</span> <span class="head-logo-enter"><span class="head-logo-enter-icon"></span>Enter</span></span></a>'; + + for ($i = count($path_parts)-1, $j = 0; $i >= 0; $i--, $j++) { + if (isset($path_map[$j])) { + $tmp = htmlescape(strtrim($path_map[$j], 40, $trimmed)); + if ($trimmed) + $tmp .= '…'; + $tmp_html = '<span class="head-logo-path-mapped">'.$tmp.'</span>'; + if ($j > 0) + $tmp_html .= '/'; + $html = str_replace_once('${{'.$i.'}}', $tmp_html, $html); + } else { + $html = str_replace_once('${{'.$i.'}}', $path_parts[$i], $html); + } + + if (isset($link_map[$j])) { + $html = str_replace_once('$[['.$i.']]', $link_map[$j], $html); + } else { + $html = str_replace_once('$[['.$i.']]', $path_links[$i], $html); + } + } + } + + return $html; +} + +function jsLink(string $name, $version = null): string { + if ($version !== null) + $name .= '?'.$version; + return '<script src="'.$name.'" type="text/javascript"></script>'; +} + +function cssLink(string $name, $version = null/*, $options = null*/): string { + global $config; + if ($config['is_dev']) { + $bname = basename($name); + if (($pos = strrpos($bname, '.'))) + $bname = substr($bname, 0, $pos); + $href = '/sass.php?name='.urlencode($bname); + } else { + $href = $name.($version !== null ? '?'.$version : ''); + } + $s = '<link rel="stylesheet" type="text/css" href="'.$href.'"'; + // if (!is_null($options)) + // $s .= ' media="'.$options.'"'; + $s .= '>'; + return $s; +}
\ No newline at end of file diff --git a/skin/error.skin.php b/skin/error.skin.php new file mode 100644 index 0000000..b0925d3 --- /dev/null +++ b/skin/error.skin.php @@ -0,0 +1,40 @@ +<?php + +namespace skin\error; + +use Stringable; + +function forbidden($ctx, $message) { + return $ctx->common(403, 'Forbidden', $message); +} + +function not_found($ctx, $message) { + return $ctx->common(404, 'Not Found', $message); +} + +function unauthorized($ctx, $message) { + return $ctx->common(401, 'Unauthorized', $message); +} + +function not_implemented($ctx, $message) { + return $ctx->common(501, 'Not Implemented', $message); +} + +function common($ctx, + int $code, + string|Stringable $title, + string|Stringable|null $message = null) { +return <<<HTML +<html> + <head><title>$code $title</title></head> + <body> + <center><h1>$code $title</h1></center> + {$ctx->if_true($message, + '<hr><p align="center">'.$message.'</p>' + )} + + </body> +</html> +HTML; + +}
\ No newline at end of file diff --git a/skin/main.skin.php b/skin/main.skin.php new file mode 100644 index 0000000..40813b9 --- /dev/null +++ b/skin/main.skin.php @@ -0,0 +1,195 @@ +<?php + +namespace skin\main; + +// index page +// ---------- + +function index($ctx, array $posts, array $tags) { + return empty($posts) ? $ctx->indexEmtpy() : $ctx->indexBlog($posts); +} + +function indexEmtpy($ctx): string { +return <<<HTML +<div class="empty"> + {$ctx->lang('blog_no')} + {$ctx->if_admin('<a href="/blog/write/">'.$ctx->lang('write').'</a>')} +</div> +HTML; +} + +function indexBlog($ctx, array $posts): string { +return <<<HTML +<div class="blog-list"> + <div class="blog-list-title"> + all posts + {$ctx->if_admin( + '<span> + <a href="/write/">new</a> + <a href="/uploads/">uploads</a> + </span>' + )} + </div> + {$ctx->indexPostsTable($posts)} +</div> +HTML; +} + +function indexPostsTable($ctx, array $posts): 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) => $ctx->indexPostRow( + $post->getYear(), + $post->visible, + $post->getDate(), + $post->getUrl(), + $post->title + ))} + </table> +</div> +HTML; +} + +function indexPostRow($ctx, $year, $is_visible, $date, $url, $title): string { +return <<<HTML +{$ctx->if_true($ctx->year > $year, $ctx->indexYearLine, $year)} +<tr class="blog-item-row{$ctx->if_not($is_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> +HTML; +} + +function indexYearLine($ctx, $year): string { +$ctx->year = $year; +return <<<HTML +<tr class="blog-item-row-year"> + <td class="blog-item-date-cell"><span>{$year}</span></td> + <td></td> +</tr> +HTML; +} + + +// contacts page +// ------------- + +function contacts($ctx, $email) { +return <<<HTML +<table class="contacts" cellpadding="0" cellspacing="0"> + <tr> + <td class="wide" colspan="2" style="line-height: 170%; padding-bottom: 18px;"> + Feel free to contact me by any of the following means: + </td> + </tr> + <tr> + <td class="label">Email:</td> + <td class="value"> + <a href="mailto:{$email}">{$email}</a> + <div class="note">Please use <a href="/openpgp-pubkey.txt?1">PGP</a>.</div> + </td> + </tr> + <tr> + <td class="label">Telegram:</td> + <td class="value"> + <a href="https://t.me/eacces">@eacces</a> + <div class="note">Please use Secret Chats.</div> + </td> + </tr> + <tr> + <td class="label">Libera.Chat:</td> + <td class="value"><span>ch1p</span></td> + </tr> +</table> +HTML; + +} + + +// any page +// -------- + +function page($ctx, $page_url, $short_name, $unsafe_html) { +return <<<HTML +<div class="page"> + {$ctx->if_admin($ctx->pageAdminLinks, $page_url, $short_name)} + <div class="blog-post-text">{$unsafe_html}</div> +</div> +HTML; +} + +function pageAdminLinks($ctx, $url, $short_name) { +return <<<HTML +<div class="page-edit-links"> + <a href="{$url}edit/">{$ctx->lang('edit')}</a> + <a href="{$url}delete/?token={$ctx->csrf('delpage'.$short_name)}" onclick="return confirm('{$ctx->lang('pages_page_delete_confirmation')}')">{$ctx->lang('delete')}</a> +</div> +HTML; + +} + + +// post page +// --------- + +function post($ctx, $id, $title, $unsafe_html, $date, $visible, $url, $tags, $email, $urlencoded_reply_subject) { +return <<<HTML +<div class="blog-post"> + <div class="blog-post-title"> + <h1>{$title}</h1> + <div class="blog-post-date"> + {$ctx->if_not($visible, '<b>'.$ctx->lang('blog_post_hidden').'</b> |')} + {$date} + {$ctx->if_admin($ctx->postAdminLinks, $url, $id)} + </div> + <div class="blog-post-tags clearfix"> + {$ctx->for_each($tags, fn($tag) => $ctx->postTag($tag->getUrl(), $tag->tag))} + </div> + </div> + <div class="blog-post-text">{$unsafe_html}</div> +</div> +<div class="blog-post-comments"> + {$ctx->langRaw('blog_comments_text', $email, $urlencoded_reply_subject)} +</div> +HTML; +} + +function postAdminLinks($ctx, $url, $id) { +return <<<HTML +<a href="{$url}edit/">{$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 postTag($ctx, $url, $name) { +return <<<HTML +<a href="{$url}"><span>#</span>{$name}</a> +HTML; + +} + + +// tag page +// -------- + +function tag($ctx, $count, $posts, $tag) { +if (!$count) + return <<<HTML + <div class="empty"> + {$ctx->lang('blog_tag_not_found')} + </div> +HTML; + +return <<<HTML +<div class="blog-list"> + <div class="blog-list-title">#{$tag}</div> + {$ctx->indexPostsTable($posts)} +</div> +HTML; +}
\ No newline at end of file diff --git a/skin/markdown.skin.php b/skin/markdown.skin.php new file mode 100644 index 0000000..02d3a0f --- /dev/null +++ b/skin/markdown.skin.php @@ -0,0 +1,43 @@ +<?php + +namespace skin\markdown; + +function fileupload($ctx, $name, $direct_url, $note, $size) { +return <<<HTML +<div class="md-file-attach"> + <span class="md-file-attach-icon"></span><a href="{$direct_url}">{$name}</a> + {$ctx->if_true($note, '<span class="md-file-attach-note">'.$note.'</span>')} + <span class="md-file-attach-size">{$size}</span> +</div> +HTML; +} + +function image($ctx, + // options + $align, $nolabel, $w, $padding_top, + // image data + $direct_url, $url, $note) { +return <<<HTML +<div class="md-image align-{$align}"> + <div class="md-image-wrap"> + <a href="{$direct_url}"> + <div style="background: #f2f2f2 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> +</div> +HTML; +} + +function video($ctx, $url, $w, $h) { +return <<<HTML +<div class="md-video"> + <div class="md-video-wrap"> + <video src="{$url}" controls{$ctx->if_true($w, ' width="'.$w.'"')}{$ctx->if_true($h, ' height="'.$h.'"')}></video> + </div> +</div> +HTML; +}
\ No newline at end of file diff --git a/skin/rss.skin.php b/skin/rss.skin.php new file mode 100644 index 0000000..0806182 --- /dev/null +++ b/skin/rss.skin.php @@ -0,0 +1,29 @@ +<?php + +namespace skin\rss; + +function atom($ctx, $title, $link, $rss_link, $items) { +return <<<HTML +<?xml version="1.0" encoding="UTF-8"?> +<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"> + <channel> + <title>{$title}</title> + <link>{$link}</link> + <description/> + <atom:link href="{$rss_link}" rel="self" type="application/rss+xml"/> + {$ctx->for_each($items, fn($item) => $ctx->item(...$item))} + </channel> +</rss> +HTML; +} + +function item($ctx, $title, $link, $pub_date, $description) { +return <<<HTML +<item> + <title>{$title}</title> + <link>{$link}</link> + <pubDate>{$pub_date}</pubDate> + <description>{$description}</description> +</item> +HTML; +}
\ No newline at end of file |