Browse Source
adds an option to use textarea in theme settings. Merged most Basic, Recent and Frameset theme functions in one. you can add a video picture icon and quote in the homepage. @ctrlcctrlv feel free to add suggestions and fix bladly formed code or let me know and I will try to fix. i installed it on my demo site: https://hikichan.com/main
H1K1CH4N
6 years ago
10 changed files with 645 additions and 0 deletions
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 38 KiB |
@ -0,0 +1,84 @@ |
|||
.box-wrap { |
|||
max-width: 670px; |
|||
min-width: 300px; |
|||
margin: 30px auto; |
|||
padding: 0; |
|||
} |
|||
.box { |
|||
background: white; |
|||
border: 1px solid #98E; |
|||
width: 330px; |
|||
margin: 8px 0; |
|||
padding: 0; |
|||
} |
|||
.box h2 { |
|||
padding: 3px 7px; |
|||
font-size: 12pt; |
|||
} |
|||
.box ul { |
|||
padding: 2px 15px; |
|||
} |
|||
.box ul li { |
|||
list-style: none; |
|||
margin: 0; |
|||
} |
|||
.mainBox { |
|||
background: transparent; |
|||
border: 1px solid #008080; |
|||
width: 100%; |
|||
margin-bottom: 5px; |
|||
} |
|||
.mainBox h2 { |
|||
background: #59A; |
|||
color: white; |
|||
} |
|||
.box.image { |
|||
background: transparent; |
|||
border: none; |
|||
width: 100%; |
|||
min-height: 250px; |
|||
max-height: 700px; |
|||
overflow: hidden auto; |
|||
} |
|||
.box.image h2 { |
|||
background: #9C6; |
|||
color: #060; |
|||
} |
|||
.box img { |
|||
float: left; |
|||
margin: 10px auto; |
|||
width: auto !important; |
|||
height: 100px !important; |
|||
} |
|||
.icon { |
|||
display: block; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
max-width: 100px; |
|||
} |
|||
.imageofnow { |
|||
display: block; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
max-width: 300px; |
|||
} |
|||
.videoofnow { |
|||
display: block; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
max-width: 500px; |
|||
max-height: 300px; |
|||
} |
|||
.quoteofnow { |
|||
text-align: center; |
|||
font-size: 20px; |
|||
font-family: -WEBKIT-PICTOGRAPH; |
|||
} |
|||
.description { |
|||
text-align: center; |
|||
font-weight: bolder; |
|||
} |
|||
.boardlinksurl { |
|||
float: left; |
|||
margin-right: 100px; |
|||
} |
@ -0,0 +1,111 @@ |
|||
{% filter remove_whitespace %} |
|||
<!doctype html> |
|||
<html> |
|||
<head> |
|||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" /> |
|||
<title>{{ settings.title }}</title> |
|||
<link rel="stylesheet" media="screen" href="{{ config.url_stylesheet }}"/> |
|||
<link rel="stylesheet" media="screen" href="{{ config.root }}{{ settings.css }}"/> |
|||
{% if config.url_favicon %}<link rel="shortcut icon" href="{{ config.url_favicon }}" />{% endif %} |
|||
{% if config.default_stylesheet.1 != '' %}<link rel="stylesheet" type="text/css" id="stylesheet" href="{{ config.uri_stylesheets }}{{ config.default_stylesheet.1 }}">{% endif %} |
|||
{% if config.font_awesome %}<link rel="stylesheet" href="{{ config.root }}{{ config.font_awesome_css }}">{% endif %} |
|||
{% include 'header.html' %} |
|||
</head> |
|||
<body> |
|||
{{ boardlist.top }} |
|||
<header> |
|||
<img class="icon" src="{{ settings.icon }}"> |
|||
<h1>{{ settings.title }}</h1> |
|||
<div class="subtitle">{{ settings.subtitle }}</div> |
|||
</header> |
|||
{% if config.url_banner %}<img class="board_image" src="{{ config.url_banner }}" {% if config.banner_width or config.banner_height %}style="{% if config.banner_width %}width:{{ config.banner_width }}px{% endif %};{% if config.banner_width %}height:{{ config.banner_height }}px{% endif %}" {% endif %}alt="" />{% endif %} |
|||
|
|||
<div class="box-wrap"> |
|||
<fieldset> |
|||
<legend>Boards</legend> |
|||
<ul> |
|||
{% for board in boards %} |
|||
<li class="boardlinksurl"> |
|||
<a href="{{ config.board_path|sprintf(board.uri) }}"> |
|||
{{ board.title|e }} |
|||
</a> |
|||
</li> |
|||
{% endfor %} |
|||
</ul> |
|||
</fieldset> |
|||
<br> |
|||
<div class="mainBox"> |
|||
<br> |
|||
<div class="description">{{ settings.description }}</div> |
|||
<br> |
|||
<img class="imageofnow" src="{{ settings.imageofnow }}"> |
|||
<br> |
|||
<div class="quoteofnow">{{ settings.quoteofnow }}</div> |
|||
<br> |
|||
<iframe class ="videoofnow" width="560" height="315" src="{{ settings.videoofnow }}"></iframe> |
|||
<br> |
|||
</div> |
|||
<div class="ban"> |
|||
{% if news|count == 0 %} |
|||
<p style="text-align:center" class="unimportant">(No news to show.)</p> |
|||
{% else %} |
|||
{% for entry in news %} |
|||
<h2 id="{{ entry.id }}"> |
|||
{% if entry.subject %} |
|||
{{ entry.subject }} |
|||
{% else %} |
|||
<em>no subject</em> |
|||
{% endif %} |
|||
<span class="unimportant"> — by {{ entry.name }} at {{ entry.time|date(config.post_date, config.timezone) }}</span> |
|||
</h2> |
|||
<p>{{ entry.body }}</p> |
|||
{% endfor %} |
|||
{% endif %} |
|||
</div> |
|||
<div class="box image"> |
|||
<h2>Recent Images</h2> |
|||
<ul> |
|||
{% for post in recent_images %} |
|||
<li> |
|||
<a href="{{ post.link }}"> |
|||
<img src="{{ post.src }}" style="width:{{ post.thumbwidth }}px;height:{{ post.thumbheight }}px" alt=""> |
|||
</a> |
|||
</li> |
|||
{% endfor %} |
|||
</ul> |
|||
</div> |
|||
<div class="mainBox"> |
|||
<h2>Latest Posts</h2> |
|||
<ul> |
|||
{% for post in recent_posts %} |
|||
<li> |
|||
<strong>{{ post.board_name }}</strong>: |
|||
<a href="{{ post.link }}"> |
|||
{{ post.snippet }} |
|||
</a> |
|||
</li> |
|||
{% endfor %} |
|||
</ul> |
|||
</div> |
|||
<div class="mainBox"> |
|||
<h2>Stats</h2> |
|||
<ul> |
|||
<li>Total posts: {{ stats.total_posts }}</li> |
|||
<li>Unique posters: {{ stats.unique_posters }}</li> |
|||
<li>Active content: {{ stats.active_content|filesize }}</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
|
|||
<hr/> |
|||
<footer> |
|||
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard + |
|||
<a href="https://engine.vichan.net/">vichan</a> + |
|||
<a href="https://github.com/fallenPineapple/NPFchan">NPFchan</a> {{ config.version }} - |
|||
<br>Tinyboard Copyright © 2010-2014 Tinyboard Development Group |
|||
<br><a href="https://engine.vichan.net/">vichan</a> Copyright © 2012-2016 vichan-devel |
|||
<br><a href="https://github.com/fallenPineapple/NPFchan">NPFchan</a> Copyright © 2017 NPFchan</p> |
|||
</footer> |
|||
</body> |
|||
</html> |
|||
{% endfilter %} |
@ -0,0 +1,73 @@ |
|||
body { |
|||
color: #CCCCCC; |
|||
background: #1E1E1E; |
|||
} |
|||
|
|||
header div.subtitle, h1 { |
|||
color: #CCCCCC; |
|||
} |
|||
|
|||
a:link, a:visited, p.intro a.email span.name { |
|||
color: #CCCCCC; |
|||
text-decoration: underline; |
|||
font-family: sans-serif; |
|||
} |
|||
a:link:hover, a:visited:hover { |
|||
color: #FF0000; |
|||
font-family: sans-serif; |
|||
text-decoration: underline overline; |
|||
} |
|||
|
|||
.box-wrap { |
|||
max-width: 670px; |
|||
min-width: 332px; |
|||
margin: 30px auto; |
|||
overflow: auto; |
|||
padding: 0; |
|||
} |
|||
.box { |
|||
background: white; |
|||
border: 1px solid #98E; |
|||
width: 330px; |
|||
margin: 8px 0; |
|||
padding: 0; |
|||
} |
|||
.box ul { |
|||
padding: 2px 15px; |
|||
} |
|||
.box ul li { |
|||
list-style: none; |
|||
margin: 0; |
|||
} |
|||
.box.left { |
|||
background: #333333; |
|||
color: #CCCCCC; |
|||
border: #555555 1px solid; |
|||
float: left; |
|||
} |
|||
.box.right { |
|||
background: #333333; |
|||
color: #CCCCCC; |
|||
border: #555555 1px solid; |
|||
float: right; |
|||
} |
|||
|
|||
.box h2 { |
|||
padding: 3px 7px; |
|||
font-size: 12pt; |
|||
border: #555555 1px solid; |
|||
} |
|||
.box.left h2 { |
|||
background: #333333; |
|||
color: #CCCCCC; |
|||
border: #555555 1px solid; |
|||
} |
|||
.box.right h2 { |
|||
background: #333333; |
|||
color: #CCCCCC; |
|||
border: #555555 1px solid; |
|||
} |
|||
.box img { |
|||
float: none; |
|||
margin: 10px auto; |
|||
} |
@ -0,0 +1,57 @@ |
|||
.box-wrap { |
|||
max-width: 670px; |
|||
min-width: 332px; |
|||
margin: 30px auto; |
|||
overflow: auto; |
|||
padding: 0; |
|||
} |
|||
.box { |
|||
background: white; |
|||
border: 1px solid #98E; |
|||
width: 330px; |
|||
margin: 8px 0; |
|||
padding: 0; |
|||
} |
|||
.box ul { |
|||
padding: 2px 15px; |
|||
} |
|||
.box ul li { |
|||
list-style: none; |
|||
margin: 0; |
|||
} |
|||
.box.left { |
|||
background: #FDF6AF; |
|||
color: #9E914F; |
|||
border: 1px solid #9E914F; |
|||
float: left; |
|||
} |
|||
.box.right { |
|||
background: #F2DCE5; |
|||
color: #525; |
|||
border: 1px solid #CA759E; |
|||
float: right; |
|||
} |
|||
|
|||
.box h2 { |
|||
padding: 3px 7px; |
|||
font-size: 12pt; |
|||
} |
|||
.box img { |
|||
float: none; |
|||
margin: 10px auto; |
|||
} |
|||
.box.left h2 { |
|||
background: #FEE78F; |
|||
color: #9E914F; |
|||
} |
|||
.box.right h2 { |
|||
background: #EB81B4; |
|||
color: #F8F8F8; |
|||
} |
|||
|
|||
body { |
|||
background: #F7F8F9; |
|||
} |
|||
header div.subtitle, h1 { |
|||
color: #888A8C; |
|||
} |
@ -0,0 +1,137 @@ |
|||
<?php |
|||
$theme = Array(); |
|||
|
|||
// Theme name |
|||
$theme['name'] = 'Index'; |
|||
// Description (you can use Tinyboard markup here) |
|||
$theme['description'] = 'Show a homepage'; |
|||
$theme['version'] = 'v1.0'; |
|||
|
|||
// Theme configuration |
|||
$theme['config'] = Array(); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'Icon', |
|||
'name' => 'icon', |
|||
'type' => 'text', |
|||
'default' => '../templates/themes/index/hikichanIcon.png', |
|||
'size' => 50 |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'Title', |
|||
'name' => 'title', |
|||
'type' => 'text', |
|||
'default' => 'Welcome to my Image Board', |
|||
'size' => 50 |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'Subtitle', |
|||
'name' => 'subtitle', |
|||
'type' => 'text', |
|||
'default' => 'What is chaos for the fly is normal for the spider.', |
|||
'size' => 50 |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'Description', |
|||
'name' => 'description', |
|||
'type' => 'textarea', |
|||
'default' => 'Short description for your website.' |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'Image of the now.', |
|||
'name' => 'imageofnow', |
|||
'type' => 'text', |
|||
'default' => '../templates/themes/index/hotweels.jpg', |
|||
'size' => 50 |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'Quote of the now.', |
|||
'name' => 'quoteofnow', |
|||
'type' => 'textarea', |
|||
'default' => '"Great minds discuss ideas; average minds discuss events; small minds discuss people." - QUOTE' |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'Video of the Now', |
|||
'name' => 'videoofnow', |
|||
'type' => 'text', |
|||
'default' => 'https://www.youtube.com/embed/zndkMAHKjNM', |
|||
'size' => 50 |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => '# of recent entries', |
|||
'name' => 'no_recent', |
|||
'type' => 'text', |
|||
'default' => 5, |
|||
'size' => 3, |
|||
'comment' => '(number of recent news entries to display; "0" is infinite)' |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'Excluded boards', |
|||
'name' => 'exclude', |
|||
'type' => 'text', |
|||
'comment' => '(space seperated)' |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => '# of recent images', |
|||
'name' => 'limit_images', |
|||
'type' => 'text', |
|||
'default' => '15', |
|||
'comment' => '(maximum images to display)' |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => '# of recent posts', |
|||
'name' => 'limit_posts', |
|||
'type' => 'text', |
|||
'default' => '30', |
|||
'comment' => '(maximum posts to display)' |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'HTML file', |
|||
'name' => 'html', |
|||
'type' => 'text', |
|||
'default' => 'index.html', |
|||
'comment' => '(eg. "index.html")' |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'CSS file', |
|||
'name' => 'css', |
|||
'type' => 'text', |
|||
'default' => 'index.css', |
|||
'comment' => '(eg. "index.css")' |
|||
); |
|||
|
|||
$theme['config'][] = Array( |
|||
'title' => 'CSS stylesheet name', |
|||
'name' => 'basecss', |
|||
'type' => 'text', |
|||
'default' => 'index.css', |
|||
'comment' => '(eg. "index.css" - see templates/themes/index for details)' |
|||
); |
|||
|
|||
// Unique function name for building everything |
|||
$theme['build_function'] = 'index_build'; |
|||
$theme['install_callback'] = 'index_install'; |
|||
|
|||
if (!function_exists('index_install')) { |
|||
function index_install($settings) { |
|||
if (!is_numeric($settings['limit_images']) || $settings['limit_images'] < 0) |
|||
return Array(false, '<strong>' . utf8tohtml($settings['limit_images']) . '</strong> is not a non-negative integer.'); |
|||
if (!is_numeric($settings['limit_posts']) || $settings['limit_posts'] < 0) |
|||
return Array(false, '<strong>' . utf8tohtml($settings['limit_posts']) . '</strong> is not a non-negative integer.'); |
|||
if (!is_numeric($settings['no_recent']) || $settings['no_recent'] < 0) |
|||
return Array(false, '<strong>' . utf8tohtml($settings['no_recent']) . '</strong> is not a non-negative integer.'); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,175 @@ |
|||
<?php |
|||
require 'info.php'; |
|||
|
|||
function index_build($action, $settings, $board) { |
|||
// Possible values for $action: |
|||
// - all (rebuild everything, initialization) |
|||
// - news (news has been updated) |
|||
// - boards (board list changed) |
|||
// - post (a post has been made) |
|||
// - post-thread (a thread has been made) |
|||
|
|||
$b = new index(); |
|||
$b->build($action, $settings); |
|||
} |
|||
|
|||
// Wrap functions in a class so they don't interfere with normal Tinyboard operations |
|||
class index { |
|||
public function build($action, $settings) { |
|||
global $config, $_theme; |
|||
|
|||
if ($action == 'all') { |
|||
copy('templates/themes/index/' . $settings['basecss'], $config['dir']['home'] . $settings['css']); |
|||
} |
|||
|
|||
|
|||
$this->excluded = explode(' ', $settings['exclude']); |
|||
|
|||
if ($action == 'all' || $action == 'post' || $action == 'post-thread' || $action == 'post-delete') { |
|||
$action = generation_strategy('sb_index', array()); |
|||
if ($action == 'delete') { |
|||
file_unlink($config['dir']['home'] . $settings['html']); |
|||
} |
|||
elseif ($action == 'rebuild') { |
|||
file_write($config['dir']['home'] . $settings['html'], $this->homepage($settings)); |
|||
} |
|||
} |
|||
if ($action == 'all' || $action == 'news' || $action == 'boards'){ |
|||
file_write($config['dir']['home'] . $settings['html'], $this->homepage($settings)); |
|||
} |
|||
} |
|||
|
|||
|
|||
// Build news page |
|||
public function homepage($settings) { |
|||
global $config, $board; |
|||
|
|||
$recent_images = Array(); |
|||
$recent_posts = Array(); |
|||
$stats = Array(); |
|||
|
|||
$boards = listBoards(); |
|||
|
|||
$query = ''; |
|||
foreach ($boards as &$_board) { |
|||
if (in_array($_board['uri'], $this->excluded)) |
|||
continue; |
|||
$query .= sprintf("SELECT *, '%s' AS `board` FROM ``posts_%s`` WHERE `files` IS NOT NULL UNION ALL ", $_board['uri'], $_board['uri']); |
|||
} |
|||
$query = preg_replace('/UNION ALL $/', 'ORDER BY `time` DESC LIMIT ' . (int)$settings['limit_images'], $query); |
|||
|
|||
if ($query == '') { |
|||
error(_("Can't build the Index theme, because there are no boards to be fetched.")); |
|||
} |
|||
|
|||
$query = query($query) or error(db_error()); |
|||
|
|||
while ($post = $query->fetch(PDO::FETCH_ASSOC)) { |
|||
openBoard($post['board']); |
|||
|
|||
if (isset($post['files'])) |
|||
$files = json_decode($post['files']); |
|||
|
|||
if ($files[0]->file == 'deleted' || $files[0]->thumb == 'file') continue; |
|||
|
|||
// board settings won't be available in the template file, so generate links now |
|||
$post['link'] = $config['root'] . $board['dir'] . $config['dir']['res'] |
|||
. link_for($post) . '#' . $post['id']; |
|||
|
|||
if ($files) { |
|||
if ($files[0]->thumb == 'spoiler') { |
|||
$tn_size = @getimagesize($config['spoiler_image']); |
|||
$post['src'] = $config['spoiler_image']; |
|||
$post['thumbwidth'] = $tn_size[0]; |
|||
$post['thumbheight'] = $tn_size[1]; |
|||
} |
|||
else { |
|||
$post['src'] = $config['uri_thumb'] . $files[0]->thumb; |
|||
$post['thumbwidth'] = $files[0]->thumbwidth; |
|||
$post['thumbheight'] = $files[0]->thumbheight; |
|||
} |
|||
} |
|||
|
|||
$recent_images[] = $post; |
|||
} |
|||
|
|||
|
|||
$query = ''; |
|||
foreach ($boards as &$_board) { |
|||
if (in_array($_board['uri'], $this->excluded)) |
|||
continue; |
|||
$query .= sprintf("SELECT *, '%s' AS `board` FROM ``posts_%s`` UNION ALL ", $_board['uri'], $_board['uri']); |
|||
} |
|||
$query = preg_replace('/UNION ALL $/', 'ORDER BY `time` DESC LIMIT ' . (int)$settings['limit_posts'], $query); |
|||
$query = query($query) or error(db_error()); |
|||
|
|||
while ($post = $query->fetch(PDO::FETCH_ASSOC)) { |
|||
openBoard($post['board']); |
|||
|
|||
$post['link'] = $config['root'] . $board['dir'] . $config['dir']['res'] . link_for($post) . '#' . $post['id']; |
|||
if ($post['body'] != "") |
|||
$post['snippet'] = pm_snippet($post['body'], 30); |
|||
else |
|||
$post['snippet'] = "<em>" . _("(no comment)") . "</em>"; |
|||
$post['board_name'] = $board['name']; |
|||
|
|||
$recent_posts[] = $post; |
|||
} |
|||
|
|||
// Total posts |
|||
$query = 'SELECT SUM(`top`) FROM ('; |
|||
foreach ($boards as &$_board) { |
|||
if (in_array($_board['uri'], $this->excluded)) |
|||
continue; |
|||
$query .= sprintf("SELECT MAX(`id`) AS `top` FROM ``posts_%s`` UNION ALL ", $_board['uri']); |
|||
} |
|||
$query = preg_replace('/UNION ALL $/', ') AS `posts_all`', $query); |
|||
$query = query($query) or error(db_error()); |
|||
$stats['total_posts'] = number_format($query->fetchColumn()); |
|||
|
|||
// Unique IPs |
|||
$query = 'SELECT COUNT(DISTINCT(`ip`)) FROM ('; |
|||
foreach ($boards as &$_board) { |
|||
if (in_array($_board['uri'], $this->excluded)) |
|||
continue; |
|||
$query .= sprintf("SELECT `ip` FROM ``posts_%s`` UNION ALL ", $_board['uri']); |
|||
} |
|||
$query = preg_replace('/UNION ALL $/', ') AS `posts_all`', $query); |
|||
$query = query($query) or error(db_error()); |
|||
$stats['unique_posters'] = number_format($query->fetchColumn()); |
|||
|
|||
// Active content |
|||
$query = 'SELECT DISTINCT(`files`) FROM ('; |
|||
foreach ($boards as &$_board) { |
|||
if (in_array($_board['uri'], $this->excluded)) |
|||
continue; |
|||
$query .= sprintf("SELECT `files` FROM ``posts_%s`` UNION ALL ", $_board['uri']); |
|||
} |
|||
$query = preg_replace('/UNION ALL $/', ' WHERE `num_files` > 0) AS `posts_all`', $query); |
|||
$query = query($query) or error(db_error()); |
|||
$files = $query->fetchAll(); |
|||
$stats['active_content'] = 0; |
|||
foreach ($files as &$file) { |
|||
preg_match_all('/"size":([0-9]*)/', $file[0], $matches); |
|||
$stats['active_content'] += array_sum($matches[1]); |
|||
} |
|||
|
|||
//news entries |
|||
$settings['no_recent'] = (int) $settings['no_recent']; |
|||
$query = query("SELECT * FROM ``news`` ORDER BY `time` DESC" . ($settings['no_recent'] ? ' LIMIT ' . $settings['no_recent'] : '')) or error(db_error()); |
|||
$news = $query->fetchAll(PDO::FETCH_ASSOC); |
|||
|
|||
return Element('themes/index/index.html', Array( |
|||
'settings' => $settings, |
|||
'config' => $config, |
|||
'boardlist' => createBoardlist(), |
|||
'recent_images' => $recent_images, |
|||
'recent_posts' => $recent_posts, |
|||
'stats' => $stats, |
|||
'news' => $news, |
|||
'boards' => listBoards() |
|||
)); |
|||
} |
|||
}; |
|||
|
|||
?> |
After Width: | Height: | Size: 15 KiB |
Loading…
Reference in new issue