summaryrefslogtreecommitdiff
path: root/templates
diff options
context:
space:
mode:
Diffstat (limited to 'templates')
-rw-r--r--templates/themes/calendar/calendar.html191
-rw-r--r--templates/themes/calendar/calendarpost.php53
-rw-r--r--templates/themes/calendar/info.php36
-rw-r--r--templates/themes/calendar/theme.php40
-rw-r--r--templates/themes/calendar/thumb.pngbin0 -> 7872 bytes
-rw-r--r--templates/themes/staffapplication/info.php36
-rw-r--r--templates/themes/staffapplication/staffapplication.html80
-rw-r--r--templates/themes/staffapplication/staffapplicationpost.php54
-rw-r--r--templates/themes/staffapplication/theme.php40
-rw-r--r--templates/themes/staffapplication/thumb.pngbin0 -> 7872 bytes
-rw-r--r--templates/themes/ukko4/info.php55
-rw-r--r--templates/themes/ukko4/theme.php162
-rw-r--r--templates/themes/ukko4/thumb.pngbin0 -> 13085 bytes
-rw-r--r--templates/themes/ukko4/ukko.js116
14 files changed, 863 insertions, 0 deletions
diff --git a/templates/themes/calendar/calendar.html b/templates/themes/calendar/calendar.html
new file mode 100644
index 00000000..47b53eae
--- /dev/null
+++ b/templates/themes/calendar/calendar.html
@@ -0,0 +1,191 @@
+<!doctype html>
+<html>
+<head>
+ <link rel="stylesheet" media="screen" href="/stylesheets/style.css">
+ <meta charset="utf-8">
+ {% if config.meta_keywords %}<meta name="keywords" content="{{ config.meta_keywords }}">{% endif %}
+ <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes">
+ <link rel='stylesheet' href='/stylesheets/fullcalendar.min.css' />
+ <script type='text/javascript'>
+ var active_page = "news";
+ </script>
+ <title>{{ settings.title }}</title>
+ {% include 'header.html' %}
+ <script src='/js/moment.min.js'></script>
+ <script src='/js/fullcalendar.min.js'></script>
+</head>
+<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
+ <div class="bar top">
+ {{ boardlist.top }}
+ </div>
+ <header>
+ <h1 class="glitch" data-text="{{ settings.title }}"> {{ settings.title }}</h1>
+ <div class="subtitle">{{ settings.subtitle }}</div>
+ </header>
+ <div class="ban">
+ <div id='calendar'></div>
+ </div>
+
+ <hr/>
+ <footer>
+ <p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
+ <a href='https://int.vichan.net/devel/'>vichan</a> +
+ <a href='https://github.com/lainchan/lainchan'>lainchan</a> {{ config.version }} -
+ <br><a href="http://tinyboard.org/">Tinyboard</a> Copyright &copy; 2010-2014 Tinyboard Development Group
+ <br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2016 vichan-devel
+ <br><a href="https://github.com/lainchan/lainchan">lainchan</a> Copyright &copy; 2014-2017 lainchan Administration</p>
+ </footer>
+ <div class="pages"></div>
+ <script type='text/javascript'>
+ $(document).ready(function() {
+
+ var calendar = $('#calendar').fullCalendar({
+ editable: true,
+ header: {
+ left: 'prev,next today',
+ center: 'title',
+ right: 'month,agendaWeek,agendaDay'
+ },
+
+ events: "calendarpost.php",
+
+ eventRender: function(event, element, view) {
+ if (event.allDay === 'true') {
+ event.allDay = true;
+ } else {
+ event.allDay = false;
+ }
+ element.find('.fc-title').append("<br/>" + event.description);
+ element.css('background-color',event.color);
+ },
+ selectable: true,
+ selectHelper: true,
+ select: function(start, end, allDay) {
+ var title = prompt('Event Title:');
+ var description = prompt('Event Description:');
+ var url = prompt("Event URL");
+ var color = prompt("Event Color");
+ var starttime = prompt("Event Start Time");
+ var endtime = prompt("Event End Time");
+ if (title) {
+ var startmoment = moment(start);
+ var endmoment = moment(end);
+ var start = startmoment.format('YYYY-MM-DD HH:mm:ss');
+ var end = endmoment.format('YYYY-MM-DD HH:mm:ss');
+ if (starttime && endtime){
+ var starttimemoment = moment(starttime,'HH:mm');
+ var endtimemoment = moment(endtime,'HH:mm');
+ startmoment.hour(starttimemoment.hour());
+ startmoment.minute(starttimemoment.minute());
+ endmoment.hour(endtimemoment.hour());
+ endmoment.minute(endtimemoment.minute());
+ endmoment.subtract(1, "days");
+ allday = false;
+ start = startmoment.format('YYYY-MM-DD HH:mm:ss');
+ end = endmoment.format('YYYY-MM-DD HH:mm:ss');
+ }
+ $.ajax({
+ url: 'calendarpost.php',
+ data: 'type=add&title='+ title+'&description='+ description +'&start='+ start +'&end='+ end + '&url='+url+'&color='+color,
+ type: "POST",
+ success: function(json) {
+ alert('Added Successfully');
+ }
+ });
+ calendar.fullCalendar('renderEvent',
+ {
+ title: title,
+ description: description,
+ start: start,
+ end: end,
+ allDay: allDay,
+ url: url,
+ color:color,
+ },
+ true
+ );
+ }
+ calendar.fullCalendar('unselect');
+ },
+
+ editable: true,
+ eventDrop: function(event, delta) {
+ var startmoment = moment(start);
+ var endmoment = moment(end);
+ var start = startmoment.format('YYYY-MM-DD HH:mm:ss');
+ var end = endmoment.format('YYYY-MM-DD HH:mm:ss');
+ var starttime = prompt("Event Start Time");
+ var endtime = prompt("Event End Time");
+ if (starttime && endtime){
+ var starttimemoment = moment(starttime,'HH:mm');
+ var endtimemoment = moment(endtime,'HH:mm');
+ startmoment.hour(starttimemoment.hour());
+ startmoment.minute(starttimemoment.minute());
+ endmoment.hour(endtimemoment.hour());
+ endmoment.minute(endtimemoment.minute());
+ allday = false;
+ endmoment.subtract(1, "days");
+ start = startmoment.format('YYYY-MM-DD HH:mm:ss');
+ end = endmoment.format('YYYY-MM-DD HH:mm:ss');
+ }
+ $.ajax({
+ url: 'calendarpost.php',
+ data: 'type=update&title='+ event.title+'&description='+ event.description + '&start='+ start +'&end='+ end +'&id='+ event.id ,
+ type: "POST",
+ success: function(json) {
+ alert("Updated Successfully");
+ }
+ });
+ },
+ eventClick: function(event) {
+ var decision = confirm("Do you really want to do that?");
+ if (decision) {
+ $.ajax({
+ type: "POST",
+ url: 'calendarpost.php',
+ data: "type=delete&id=" + event.id,
+ success: function(json) {
+ $('#calendar').fullCalendar('removeEvents', event.id);
+ alert("Updated Successfully");}
+ });
+
+ }
+ },
+ eventResize: function(event) {
+ var startmoment = moment(start);
+ var endmoment = moment(end);
+ var start = moment(start).format('YYYY-MM-DD HH:mm:ss');
+ var end = moment(end).format('YYYY-MM-DD HH:mm:ss');
+ var starttime = prompt("Event Start Time");
+ var endtime = prompt("Event End Time");
+ if (starttime && endtime){
+ var starttimemoment = moment(starttime,'HH:mm');
+ var endtimemoment = moment(endtime,'HH:mm');
+ startmoment.hour(starttimemoment.hour());
+ startmoment.minute(starttimemoment.minute());
+ endmoment.hour(endtimemoment.hour());
+ endmoment.minute(endtimemoment.minute());
+ allday = false;
+ endmoment.subtract(1, "days");
+ start = startmoment.format('YYYY-MM-DD HH:mm:ss');
+ end = endmoment.format('YYYY-MM-DD HH:mm:ss');
+ }
+ $.ajax({
+ url: 'calendarpost.php',
+ data: 'type=update&title='+ event.title+ '&description='+ event.description + '&start='+ start +'&end='+ end +'&id='+ event.id ,
+ type: "POST",
+ success: function(json) {
+ alert("Updated Successfully");
+ }
+ });
+ }
+
+ });
+
+ });
+ </script>
+ <script type="text/javascript">{% raw %}
+ ready();
+ {% endraw %}</script>
+</body>
+</html>
diff --git a/templates/themes/calendar/calendarpost.php b/templates/themes/calendar/calendarpost.php
new file mode 100644
index 00000000..a36e080c
--- /dev/null
+++ b/templates/themes/calendar/calendarpost.php
@@ -0,0 +1,53 @@
+<?php
+require '/var/www/html/inc/functions.php';
+
+$method = $_SERVER['REQUEST_METHOD'];
+ if ($method == 'POST') {
+ // Method is POST
+ $type = $_POST['type'];
+ switch ($type) {
+ case "add":
+ $title = $_POST['title'];
+ $description = $_POST['description'];
+ $start = $_POST['start'];
+ $end = $_POST['end'];
+ $url = $_POST['url'];
+ $color = $_POST['color'];
+ $query = prepare("INSERT INTO calendar_events (title, description, start, end,url,color) VALUES (:title,:description, :start, :end,:url, :color )");
+ $query->bindValue(':title', $title);
+ $query->bindValue(':description', $description);
+ $query->bindValue(':start', $start);
+ $query->bindValue(':end', $end);
+ $query->bindValue(':url', $url);
+ $query->bindValue(':color', $color);
+ $query->execute() or error(db_error($query));
+ break;
+ case "delete":
+ $id = $_POST['id'];
+ $query = query("DELETE from calendar_events WHERE id = :id") or error(db_error());
+ break;
+ case "update":
+ $id = $_POST['id'];
+ $title = $_POST['title'];
+ $description = $_POST['description'];
+ $start = $_POST['start'];
+ $end = $_POST['end'];
+ $query = prepare(" UPDATE calendar_events SET title = :title, description = :description, start = :start, end = :end, url = :url, color =:color WHERE id = :id");
+ $query->bindValue(':id', $id);
+ $query->bindValue(':title', $title);
+ $query->bindValue(':description', $description);
+ $query->bindValue(':start', $start);
+ $query->bindValue(':end', $end);
+ $query->bindValue(':url', $url);
+ $query->bindValue(':color', $color);
+ $query->execute() or error(db_error($query));
+ break;
+ default:
+ }
+ } elseif ($method == 'GET') {
+ // Method is GET
+ $query = query("SELECT * FROM calendar_events ORDER BY id") or error(db_error());
+ echo json_encode($query->fetchAll(PDO::FETCH_ASSOC));
+ }
+
+?>
diff --git a/templates/themes/calendar/info.php b/templates/themes/calendar/info.php
new file mode 100644
index 00000000..0687a26d
--- /dev/null
+++ b/templates/themes/calendar/info.php
@@ -0,0 +1,36 @@
+<?php
+ $theme = Array();
+
+ // Theme name
+ $theme['name'] = 'Calendar';
+ // Description (you can use Tinyboard markup here)
+ $theme['description'] = 'Calendar theme.';
+ $theme['version'] = 'v0.0.1';
+
+ // Theme configuration
+ $theme['config'] = Array();
+
+ $theme['config'][] = Array(
+ 'title' => 'Site title',
+ 'name' => 'title',
+ 'type' => 'text'
+ );
+
+ $theme['config'][] = Array(
+ 'title' => 'Slogan',
+ 'name' => 'subtitle',
+ 'type' => 'text',
+ 'comment' => '(optional)'
+ );
+
+ $theme['config'][] = Array(
+ 'title' => 'File',
+ 'name' => 'file',
+ 'type' => 'text',
+ 'default' => $config['file_index'],
+ 'comment' => '(eg. "index.html")'
+ );
+
+ // Unique function name for building everything
+ $theme['build_function'] = 'calendar_build';
+
diff --git a/templates/themes/calendar/theme.php b/templates/themes/calendar/theme.php
new file mode 100644
index 00000000..8e9d32db
--- /dev/null
+++ b/templates/themes/calendar/theme.php
@@ -0,0 +1,40 @@
+<?php
+ require 'info.php';
+
+ function calendar_build($action, $settings, $board) {
+ // Possible values for $action:
+ // - all (rebuild everything, initialization)
+ // - news (news has been updated)
+ // - boards (board list changed)
+
+ Calendar::build($action, $settings);
+ }
+
+ // Wrap functions in a class so they don't interfere with normal Tinyboard operations
+ class Calendar {
+ public static function build($action, $settings) {
+ global $config;
+
+ if ($action == 'all' || $action == 'news')
+ file_write($config['dir']['home'] . $settings['file'], Calendar::htmlpage($settings));
+ file_write($config['dir']['home'] . "calendarpost.php", Calendar::phppage($settings));
+ }
+
+ // Build staff application page
+ public static function htmlpage($settings) {
+ global $config;
+
+ return Element('themes/calendar/calendar.html', Array(
+ 'settings' => $settings,
+ 'config' => $config,
+ 'boardlist' => createBoardlist()
+ ));
+ }
+ public static function phppage($settings) {
+ global $config;
+ $page = file_get_contents('templates/themes/calendar/calendarpost.php');
+ return $page;
+ }
+ };
+
+?>
diff --git a/templates/themes/calendar/thumb.png b/templates/themes/calendar/thumb.png
new file mode 100644
index 00000000..d0c3c771
--- /dev/null
+++ b/templates/themes/calendar/thumb.png
Binary files differ
diff --git a/templates/themes/staffapplication/info.php b/templates/themes/staffapplication/info.php
new file mode 100644
index 00000000..a8093093
--- /dev/null
+++ b/templates/themes/staffapplication/info.php
@@ -0,0 +1,36 @@
+<?php
+ $theme = Array();
+
+ // Theme name
+ $theme['name'] = 'StaffApplication';
+ // Description (you can use Tinyboard markup here)
+ $theme['description'] = 'Staff Application theme.';
+ $theme['version'] = 'v0.0.1';
+
+ // Theme configuration
+ $theme['config'] = Array();
+
+ $theme['config'][] = Array(
+ 'title' => 'Site title',
+ 'name' => 'title',
+ 'type' => 'text'
+ );
+
+ $theme['config'][] = Array(
+ 'title' => 'Slogan',
+ 'name' => 'subtitle',
+ 'type' => 'text',
+ 'comment' => '(optional)'
+ );
+
+ $theme['config'][] = Array(
+ 'title' => 'File',
+ 'name' => 'file',
+ 'type' => 'text',
+ 'default' => $config['file_index'],
+ 'comment' => '(eg. "index.html")'
+ );
+
+ // Unique function name for building everything
+ $theme['build_function'] = 'staffapplication_build';
+
diff --git a/templates/themes/staffapplication/staffapplication.html b/templates/themes/staffapplication/staffapplication.html
new file mode 100644
index 00000000..9ff98802
--- /dev/null
+++ b/templates/themes/staffapplication/staffapplication.html
@@ -0,0 +1,80 @@
+{% filter remove_whitespace %}
+<!doctype html>
+<html>
+<head>
+ <link rel="stylesheet" media="screen" href="/stylesheets/style.css">
+ <meta charset="utf-8">
+ {% if config.meta_keywords %}<meta name="keywords" content="{{ config.meta_keywords }}">{% endif %}
+ <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes">
+ <script type='text/javascript'>
+ var active_page = "news";
+ </script>
+ <title>{{ settings.title }}</title>
+ {% include 'header.html' %}
+</head>
+<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
+ <div class="bar top">
+ {{ boardlist.top }}
+ </div>
+ <header>
+ <h1 class="glitch" data-text="{{ settings.title }}"> {{ settings.title }}</h1>
+ <div class="subtitle">{{ settings.subtitle }}</div>
+ </header>
+ <center>
+ <img src="static/lain_is_cute_datass_small_teal.png" alt="mascot"/>
+ </center>
+ <div class="ban">
+ <p>
+ Please use to the form below to apply to be staff.</br>
+ If you don't want to use this form you can also email admin@lainchan.org or ask in #questions on the Lainchan IRC channel.
+ In the rest of your application section, please specify the following information: </br>
+ Do you have to specify all the requested information below ? </br>
+ No of course not, but the more you specify the more likely your application will be approved, because applications
+ that demostrate that you can put effort in things, tell us that you can be bothered putting effort into things.</br>
+ What position you wanted to apply for ? E.g. Chan staff ? IRC staff ? Sysadmin ? Developer ? All of the above ?
+ None of the above ? Special snowflake role invented just for you ?</br>
+ Your reasons for applying ? </br>
+ Your existing qualifications for the position </br>
+ A summary of your strengths, weaknesses and interesting points / quirks </br>
+ If chan staff , then please specify which boards you are interested in and your experience with their subject matter.
+ </br>
+ Things to not include on your application. </br>
+ 1. NSFW pictures </br>
+ 2. Anything that is against the rules on the Rules page.</br>
+ 3. Short stories about ducks and ribbons and that guy Jimmy and other off topic things.</br>
+ 4. Your entire life story. </br>
+ 5. Your passport number or SSN and / or other identifying information. </br>
+ 6. Your rant about XYZ and how much all ABC need to suffer DEF because of it. </br>
+ </p>
+ <form action="staffapplication.php" method="post">
+ <p><label for="name">Name: </label><input type="text" name="name" size="50"></p>
+ <p><label for="contactmethod">Preferred Contact Method: </label><select name="contactmethod">
+ <option value="email" selected>Email</option>
+ <option value="irc">IRC</option>
+ <option value="chan">Lainchan</option>
+ </select></p>
+ <p><label for="email">Email: </label><input type="email" name="email" size="50"></p>
+ <p>
+ <label for="application">The rest of your application.</label><textarea name="application" rows="30" cols="50"></textarea>
+ </p>
+ <p><label for="anitspam">Antispam: (What is Cyberpunk , answer in all CAPS, single word, no spaces) </label><input type="text" name="antispam" size="50"></p>
+ <input type="submit" value="Send">
+ </form>
+ </div>
+
+ <hr/>
+ <footer>
+ <p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
+ <a href='https://int.vichan.net/devel/'>vichan</a> +
+ <a href='https://github.com/lainchan/lainchan'>lainchan</a> {{ config.version }} -
+ <br><a href="http://tinyboard.org/">Tinyboard</a> Copyright &copy; 2010-2014 Tinyboard Development Group
+ <br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2016 vichan-devel
+ <br><a href="https://github.com/lainchan/lainchan">lainchan</a> Copyright &copy; 2014-2017 lainchan Administration</p>
+ </footer>
+ <div class="pages"></div>
+ <script type="text/javascript">{% raw %}
+ ready();
+ {% endraw %}</script>
+</body>
+</html>
+{% endfilter %}
diff --git a/templates/themes/staffapplication/staffapplicationpost.php b/templates/themes/staffapplication/staffapplicationpost.php
new file mode 100644
index 00000000..0e1b14ca
--- /dev/null
+++ b/templates/themes/staffapplication/staffapplicationpost.php
@@ -0,0 +1,54 @@
+<?php
+$name = $contactmethod = $email = $application = $antispam = $displaymessage = "";
+require '/var/www/html/inc/functions.php';
+if (isset ($_POST["antispam"])){
+ if ($_POST["antispam"] == "DUCK"){
+ $namecheck = ! empty($_POST["name"]);
+ $contactmethodcheck = $_POST["contactmethod"] == "email";
+ $emailcheck = ! empty($_POST["email"]);
+ $emailischecked = $contactmethodcheck ? $emailcheck : true;
+
+ if ($emailischecked && $namecheck){
+ $name = $_POST["name"];
+ $contactmethod = $_POST["contactmethod"];
+ $email = $_POST["email"];
+ $application = $_POST["application"];
+ $antispam = $_POST["antispam"];
+ $to = "admin@lainchan.org";
+ $subject = "Lainchan.org Staff Application for " . $name;
+
+ $message = "Name: " . $name . "\r\n";
+ $message .= "Contact method: " . $contactmethod . "\r\n";
+ $message .= $application;
+ $message = wordwrap($message, 70, "\r\n");
+
+ $source = $emailcheck ? $email : $to;
+ $headers = 'From: ' . $source . "\r\n" .
+ 'Reply-To: ' . $source . "\r\n" .
+ 'X-Mailer: PHP/' . phpversion();
+ $sent = mail($to, $subject, $message, $headers);
+ $sentmessage = $sent ? "was submitted successfully.</p>\r\n" : "was unable to be submitted.</p>\r\n";
+ $displaymessage = "<p style=\"text-align:center;\"> Your staff application " . $sentmessage;
+ }
+
+ }
+ else
+ {
+ $displaymessage = "<p style=\"text-align:center;\">ANTISPAM VALUE INCORRECT</p>\r\n";
+
+ }
+}
+else {
+$displaymessage = "<p style=\"text-align:center;\">ANTISPAM NOT SET</p>\r\n";
+}
+$displaymessage .= '<span style="text-align:center; display: block;" >[ <a href="https://lainchan.org/"> Return Home </a> ]</span>';
+
+echo Element('page.html', array(
+ 'index' => $config['root'],
+ 'title' => _('Staff Application'),
+ 'config' => $config,
+ 'boardlist' => createBoardlist(isset($mod) ? $mod : false),
+ 'body' => $displaymessage,
+ )
+ );
+?>
diff --git a/templates/themes/staffapplication/theme.php b/templates/themes/staffapplication/theme.php
new file mode 100644
index 00000000..a827a7f8
--- /dev/null
+++ b/templates/themes/staffapplication/theme.php
@@ -0,0 +1,40 @@
+<?php
+ require 'info.php';
+
+ function staffapplication_build($action, $settings, $board) {
+ // Possible values for $action:
+ // - all (rebuild everything, initialization)
+ // - news (news has been updated)
+ // - boards (board list changed)
+
+ StaffApplication::build($action, $settings);
+ }
+
+ // Wrap functions in a class so they don't interfere with normal Tinyboard operations
+ class StaffApplication {
+ public static function build($action, $settings) {
+ global $config;
+
+ if ($action == 'all' || $action == 'news')
+ file_write($config['dir']['home'] . $settings['file'], StaffApplication::htmlpage($settings));
+ file_write($config['dir']['home'] . "staffapplication.php", StaffApplication::phppage($settings));
+ }
+
+ // Build staff application page
+ public static function htmlpage($settings) {
+ global $config;
+
+ return Element('themes/staffapplication/staffapplication.html', Array(
+ 'settings' => $settings,
+ 'config' => $config,
+ 'boardlist' => createBoardlist()
+ ));
+ }
+ public static function phppage($settings) {
+ global $config;
+ $page = file_get_contents('templates/themes/staffapplication/staffapplicationpost.php');
+ return $page;
+ }
+ };
+
+?>
diff --git a/templates/themes/staffapplication/thumb.png b/templates/themes/staffapplication/thumb.png
new file mode 100644
index 00000000..d0c3c771
--- /dev/null
+++ b/templates/themes/staffapplication/thumb.png
Binary files differ
diff --git a/templates/themes/ukko4/info.php b/templates/themes/ukko4/info.php
new file mode 100644
index 00000000..dbbf6da8
--- /dev/null
+++ b/templates/themes/ukko4/info.php
@@ -0,0 +1,55 @@
+<?php
+ $theme = array();
+
+ // Theme name
+ $theme['name'] = 'Overboard (Ukko) 4';
+ // Description (you can use Tinyboard markup here)
+ $theme['description'] = 'Board with threads and messages from all boards';
+ $theme['version'] = 'v0.2';
+
+ // Theme configuration
+ $theme['config'] = array();
+
+ $theme['config'][] = array(
+ 'title' => 'Board name',
+ 'name' => 'title',
+ 'type' => 'text',
+ 'default' => 'Ukko'
+ );
+ $theme['config'][] = array(
+ 'title' => 'Board URI',
+ 'name' => 'uri',
+ 'type' => 'text',
+ 'default' => '*',
+ 'comment' => '(ukko for example)'
+ );
+ $theme['config'][] = array(
+ 'title' => 'Subtitle',
+ 'name' => 'subtitle',
+ 'type' => 'text',
+ 'comment' => '(%s = thread limit. for example "%s freshly bumped threads")'
+ );
+ $theme['config'][] = array(
+ 'title' => 'Excluded boards',
+ 'name' => 'exclude',
+ 'type' => 'text',
+ 'comment' => '(space seperated)'
+ );
+ $theme['config'][] = array(
+ 'title' => 'Number of threads',
+ 'name' => 'thread_limit',
+ 'type' => 'text',
+ 'default' => '15',
+ );
+ // Unique function name for building everything
+ $theme['build_function'] = 'ukko4_build';
+ $theme['install_callback'] = 'ukko4_install';
+
+ if(!function_exists('ukko4_install')) {
+ function ukko4_install($settings) {
+ if (!file_exists($settings['uri']))
+ @mkdir($settings['uri'], 0777) or error("Couldn't create " . $settings['uri'] . ". Check permissions.", true);
+ file_write($settings['uri'] . '/ukko.js', Element('themes/ukko/ukko.js', array()));
+ }
+ }
+
diff --git a/templates/themes/ukko4/theme.php b/templates/themes/ukko4/theme.php
new file mode 100644
index 00000000..4511483e
--- /dev/null
+++ b/templates/themes/ukko4/theme.php
@@ -0,0 +1,162 @@
+<?php
+ require 'info.php';
+
+ function ukko4_build($action, $settings) {
+ global $config;
+
+ $ukko = new ukko();
+ $ukko->settings = $settings;
+
+ if (! ($action == 'all' || $action == 'post' || $action == 'post-thread' || $action == 'post-delete')) {
+ return;
+ }
+
+ $action = generation_strategy('sb_ukko', array());
+
+ if ($action == 'delete') {
+ file_unlink($settings['uri'] . '/index.html');
+ if ($config['api']['enabled']) {
+ $jsonFilename = $settings['uri'] . '/0.json';
+ file_unlink($jsonFilename);
+ $jsonFilename = $settings['uri'] . '/catalog.json';
+ file_unlink($jsonFilename);
+ $jsonFilename = $settings['uri'] . '/threads.json';
+ file_unlink($jsonFilename);
+ }
+ }
+ elseif ($action == 'rebuild') {
+ file_write($settings['uri'] . '/index.html', $ukko->build());
+ }
+ }
+
+ class ukko4 {
+ public $settings;
+ public function build($mod = false) {
+ global $config;
+ $boards = listBoards();
+
+ $body = '';
+ $overflow = array();
+ $board = array(
+ 'dir' => $this->settings['uri'] . "/",
+ 'url' => $this->settings['uri'],
+ 'uri' => $this->settings['uri'],
+ 'name' => $this->settings['title'],
+ 'title' => sprintf($this->settings['subtitle'], $this->settings['thread_limit'])
+ );
+
+ $boardsforukko = array();
+ $query = '';
+ foreach($boards as &$_board) {
+ if(in_array($_board['uri'], explode(' ', $this->settings['exclude'])))
+ continue;
+ $query .= sprintf("SELECT *, '%s' AS `board` FROM ``posts_%s`` WHERE `thread` IS NULL UNION ALL ", $_board['uri'], $_board['uri']);
+ array_push($boardsforukko,$_board);
+ }
+ $query = preg_replace('/UNION ALL $/', 'ORDER BY `bump` DESC', $query);
+ $query = query($query) or error(db_error());
+
+ $count = 0;
+ $threads = array();
+ if ($config['api']['enabled']) {
+ $apithreads = array();
+ }
+ while($post = $query->fetch()) {
+
+ if(!isset($threads[$post['board']])) {
+ $threads[$post['board']] = 1;
+ } else {
+ $threads[$post['board']] += 1;
+ }
+
+ if($count < $this->settings['thread_limit']) {
+ openBoard($post['board']);
+ $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod);
+
+ $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `sticky` DESC, `id` DESC LIMIT :limit", $post['board']));
+ $posts->bindValue(':id', $post['id']);
+ $posts->bindValue(':limit', ($post['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT);
+ $posts->execute() or error(db_error($posts));
+
+ $num_images = 0;
+ while ($po = $posts->fetch()) {
+ if ($po['files'])
+ $num_images++;
+ $post2 = new Post($po, $mod ? '?/' : $config['root'], $mod);
+ $thread->add($post2);
+
+ }
+ if ($posts->rowCount() == ($post['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) {
+ $ct = prepare(sprintf("SELECT COUNT(`id`) as `num` FROM ``posts_%s`` WHERE `thread` = :thread UNION ALL SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `files` IS NOT NULL AND `thread` = :thread", $post['board'], $post['board']));
+ $ct->bindValue(':thread', $post['id'], PDO::PARAM_INT);
+ $ct->execute() or error(db_error($count));
+
+ $c = $ct->fetch();
+ $thread->omitted = $c['num'] - ($post['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']);
+
+ $c = $ct->fetch();
+ $thread->omitted_images = $c['num'] - $num_images;
+ }
+
+
+ $thread->posts = array_reverse($thread->posts);
+ $body .= '<h2><a href="' . $config['root'] . $post['board'] . '">/' . $post['board'] . '/</a></h2>';
+ $body .= $thread->build(true);
+ if ($config['api']['enabled']) {
+ array_push($apithreads,$thread);
+ }
+ } else {
+ $page = 'index';
+ if(floor($threads[$post['board']] / $config['threads_per_page']) > 0) {
+ $page = floor($threads[$post['board']] / $config['threads_per_page']) + 1;
+ }
+ $overflow[] = array('id' => $post['id'], 'board' => $post['board'], 'page' => $page . '.html');
+ }
+
+ $count += 1;
+ }
+
+ $body .= '<script> var overflow = ' . json_encode($overflow) . '</script>';
+ $body .= '<script type="text/javascript" src="/'.$this->settings['uri'].'/ukko.js"></script>';
+
+ // json api
+ if ($config['api']['enabled']) {
+ require_once __DIR__. '/../../../inc/api.php';
+ $api = new Api();
+ $jsonFilename = $board['dir'] . '0.json';
+ $json = json_encode($api->translatePage($apithreads));
+ file_write($jsonFilename, $json);
+
+
+ $catalog = array();
+ $catalog[0] = $apithreads;
+
+ $json = json_encode($api->translateCatalog($catalog));
+ $jsonFilename = $board['dir'] . 'catalog.json';
+ file_write($jsonFilename, $json);
+
+ $json = json_encode($api->translateCatalog($catalog, true));
+ $jsonFilename = $board['dir'] . 'threads.json';
+ file_write($jsonFilename, $json);
+ }
+ $antibot = null;
+ if (!$antibot) {
+ $antibot = create_antibot($board['uri']);
+ }
+ $antibot->reset();
+
+ return Element('index.html', array(
+ 'config' => $config,
+ 'board' => $board,
+ 'no_post_form' => $config['overboard_post_form'] ? false : true,
+ 'body' => $body,
+ 'mod' => $mod,
+ 'boardlist' => createBoardlist($mod),
+ 'boards' => $boardsforukko,
+ 'antibot' => $antibot )
+ );
+ }
+
+ };
+
+?>
diff --git a/templates/themes/ukko4/thumb.png b/templates/themes/ukko4/thumb.png
new file mode 100644
index 00000000..eb616ef7
--- /dev/null
+++ b/templates/themes/ukko4/thumb.png
Binary files differ
diff --git a/templates/themes/ukko4/ukko.js b/templates/themes/ukko4/ukko.js
new file mode 100644
index 00000000..b2668e0b
--- /dev/null
+++ b/templates/themes/ukko4/ukko.js
@@ -0,0 +1,116 @@
+(function(){
+
+var cache = new Array(),
+ thread = false,
+ loading = false,
+ ukkotimer = false;
+
+if (localStorage.hiddenboards === undefined) {
+ localStorage.hiddenboards = "{}";
+}
+
+// Load data from HTML5 localStorage
+var hiddenboards = JSON.parse(localStorage.hiddenboards);
+
+var storeboards = function() {
+ localStorage.hiddenboards = JSON.stringify(hiddenboards);
+};
+
+$(document).ready(function() {
+ var addukkohide = function() {
+ var ukkohide = $('<a href="javascript:void(0);" class="unimportant ukkohide"></a>');
+ var board = $(this).next().data("board");
+ var hr = $("<hr />");
+
+ $(this).append(ukkohide);
+ $(this).append(hr);
+
+ if (hiddenboards[board] !== true) {
+ ukkohide.html(_("(hide threads from this board)"));
+ hr.hide();
+ }
+ else {
+ ukkohide.html(_("(show threads from this board)"));
+ $(this).next().hide();
+ }
+ ukkohide.click(function() {
+ hiddenboards[board] = (hiddenboards[board] !== true);
+ if (hiddenboards[board] !== true) {
+ $('[data-board="'+board+'"]:not([data-cached="yes"])').show().prev().
+ find('.ukkohide').html(_("(hide threads from this board)")).
+ parent().find('hr').hide();
+ }
+ else {
+ $('[data-board="'+board+'"]:not([data-cached="yes"])').hide().prev().
+ find('.ukkohide').html(_("(show threads from this board)"))
+ .parent().find('hr').show();
+ }
+ storeboards();
+ return false;
+ });
+
+ };
+ $("h2").each(addukkohide);
+
+ $('.pages').hide();
+ var loadnext = function() {
+ if (overflow.length == 0) {
+ $('.pages').show().html(_("No more threads to display"));
+ }
+ while($(window).scrollTop() + $(window).height() + 1000 > $(document).height() && !loading && overflow.length > 0) {
+ var page = modRoot + overflow[0].board + '/' + overflow[0].page;
+ thread = $('div#thread_' + overflow[0].id + '[data-board="' + overflow[0].board + '"]');
+ if (thread.length > 0 && thread.attr("data-cached") !== 'yes') { // already present
+ overflow.shift();
+ continue;
+ }
+
+ var boardheader = $('<h2><a href="/' + overflow[0].board + '/">/' + overflow[0].board + '/</a> </h2>');
+
+ if($.inArray(page, cache) != -1) {
+ if (thread.length > 0) {
+ $('div[id*="thread_"]').last().after(thread.attr('data-board', overflow[0].board).attr("data-cached", "no").css('display', 'block'));
+ boardheader.insertBefore(thread);
+ addukkohide.call(boardheader);
+ $(document).trigger('new_post', thread);
+ }
+ overflow.shift();
+ } else {
+ loading = true;
+ $('.pages').show().html(_("Loading..."));
+ $.get(page, function(data) {
+ cache.push(page);
+
+ $(data).find('div[id*="thread_"]').each(function() {
+ var checkout = $(this).attr('id').replace('thread_', '');
+ if ($('div#thread_' + checkout + '[data-board="' + overflow[0].board + '"]').length == 0) {
+ $('form[name="postcontrols"]').prepend($(this).css('display', 'none').attr("data-cached", "yes").attr('data-board', overflow[0].board));
+ }
+ });
+
+ thread = $('div#thread_' + overflow[0].id + '[data-board="' + overflow[0].board + '"][data-cached="yes"]');
+
+ if(thread.length > 0) {
+ $('div[id*="thread_"]').last().after(thread.attr('data-board', overflow[0].board).attr("data-cached", "no").css('display', 'block'));
+ boardheader.insertBefore(thread);
+ addukkohide.call(boardheader);
+ $(document).trigger('new_post', thread);
+ }
+ overflow.shift();
+
+ loading = false;
+ $('.pages').hide().html("");
+ });
+ break;
+ }
+ }
+ clearTimeout(ukkotimer);
+ ukkotimer = setTimeout(loadnext, 1000);
+ };
+
+ $(window).on('scroll', loadnext);
+
+ ukkotimer = setTimeout(loadnext, 1000);
+});
+
+})();