towards-a-new-leftypol
3 years ago
8 changed files with 23 additions and 478 deletions
@ -1,23 +0,0 @@ |
|||||
<?php |
|
||||
|
|
||||
// Basic theme properties |
|
||||
$theme = array( |
|
||||
'name' => 'Overboards', |
|
||||
// Description (you can use Tinyboard markup here) |
|
||||
'description' => 'Add one or more overboards, such as a normal overboard and a SFW overboard.', |
|
||||
'version' => 'v0.1', |
|
||||
// Unique function name for building and installing whatever's necessary |
|
||||
'build_function' => 'overboards_build', |
|
||||
); |
|
||||
|
|
||||
// Theme configuration |
|
||||
$theme['config'] = array( |
|
||||
array( |
|
||||
'title' => 'Edit overboards.php manually to add, remove or modify overboards', |
|
||||
'name' => 'instruct1', |
|
||||
'type' => 'checkbox', |
|
||||
'default' => false, |
|
||||
'comment' => 'Located at templates/themes/overboards/overboards.php' |
|
||||
), |
|
||||
|
|
||||
); |
|
@ -1,158 +0,0 @@ |
|||||
$(document).ready(function() { |
|
||||
var cachedPages = [], |
|
||||
loading = false, |
|
||||
timer = null; |
|
||||
|
|
||||
// Load data from HTML5 localStorage
|
|
||||
var hiddenBoards = JSON.parse(localStorage.getItem('hiddenboards')); |
|
||||
|
|
||||
var storeHiddenBoards = function() { |
|
||||
localStorage.setItem('hiddenboards', JSON.stringify(hiddenBoards)); |
|
||||
}; |
|
||||
|
|
||||
// No board are hidden by default
|
|
||||
if (!hiddenBoards) { |
|
||||
hiddenBoards = {}; |
|
||||
storeHiddenBoards(); |
|
||||
} |
|
||||
|
|
||||
// Hide threads from the same board and remember for next time
|
|
||||
var onHideClick = function(e) { |
|
||||
e.preventDefault(); |
|
||||
var board = $(this).parent().next().data('board'), |
|
||||
threads = $('[data-board="'+board+'"]:not([data-cached="yes"])'), |
|
||||
btns = threads.prev().find('.threads-toggle'), |
|
||||
hrs = btns.next(); |
|
||||
|
|
||||
if (hiddenBoards[board]) { |
|
||||
threads.show(); |
|
||||
btns.find('.threads-toggle').html(_('(hide threads from this board)')); |
|
||||
hrs.hide(); |
|
||||
} else { |
|
||||
threads.hide(); |
|
||||
btns.html(_('(show threads from this board)')); |
|
||||
hrs.show(); |
|
||||
} |
|
||||
|
|
||||
hiddenBoards[board] = !hiddenBoards[board]; |
|
||||
storeHiddenBoards(); |
|
||||
}; |
|
||||
|
|
||||
// Add a hiding link and horizontal separator to each thread
|
|
||||
var addHideButton = function() { |
|
||||
var board = $(this).next().data('board'), |
|
||||
// Create the link and separator
|
|
||||
button = $('<a href="#" class="unimportant threads-toggle"></a>') |
|
||||
.click(onHideClick), |
|
||||
myHr = $('<hr />'); |
|
||||
|
|
||||
// Insert them after the board name
|
|
||||
$(this).append(' ').append(button).append(myHr); |
|
||||
|
|
||||
if (hiddenBoards[board]) { |
|
||||
button.html(_('(show threads from this board)')); |
|
||||
$(this).next().hide(); |
|
||||
} else { |
|
||||
button.html(_('(hide threads from this board)')); |
|
||||
myHr.hide(); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
$('h2').each(addHideButton); |
|
||||
|
|
||||
var appendThread = function(elem, data) { |
|
||||
var boardLink = $('<h2><a href="' + modRoot + data.board + '/">/' + |
|
||||
data.board + '/</a></h2>'); |
|
||||
|
|
||||
// Push the thread after the currently last one
|
|
||||
$('div[id*="thread_"]').last() |
|
||||
.after(elem.data('board', data.board) |
|
||||
.data('cached', 'no') |
|
||||
.show()); |
|
||||
// Add the obligatory board link
|
|
||||
boardLink.insertBefore(elem); |
|
||||
// Set up the hiding link
|
|
||||
addHideButton.call(boardLink); |
|
||||
// Trigger an event to let the world know that we have a new thread aboard
|
|
||||
$(document).trigger('new_post', elem); |
|
||||
}; |
|
||||
|
|
||||
var attemptLoadNext = function() { |
|
||||
if (!ukko_overflow.length) { |
|
||||
$('.pages').show().html(_('No more threads to display')); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var viewHeight = $(window).scrollTop() + $(window).height(), |
|
||||
pageHeight = $(document).height(); |
|
||||
// Keep loading deferred threads as long as we're close to the bottom of the
|
|
||||
// page and there are threads remaining
|
|
||||
while(viewHeight + 1000 > pageHeight && !loading && ukko_overflow.length > 0) { |
|
||||
// Take the first unloaded post
|
|
||||
var post = ukko_overflow.shift(), |
|
||||
page = modRoot + post.board + '/' + post.page; |
|
||||
|
|
||||
var thread = $('div#thread_' + post.id + '[data-board="' + post.board + '"]'); |
|
||||
// Check that the thread hasn't been inserted yet
|
|
||||
if (thread.length && thread.data('cached') !== 'yes') { |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
// Check if we've already downloaded the index page on which this thread
|
|
||||
// is located
|
|
||||
if ($.inArray(page, cachedPages) !== -1) { |
|
||||
if (thread.length) { |
|
||||
appendThread(thread, post); |
|
||||
} |
|
||||
// Otherwise just load the page and cache its threads
|
|
||||
} else { |
|
||||
// Make sure that no other thread does the job that we're about to do
|
|
||||
loading = true; |
|
||||
$('.pages').show().html(_('Loading…')); |
|
||||
|
|
||||
// Retrieve the page from the server
|
|
||||
$.get(page, function(data) { |
|
||||
cachedPages.push(page); |
|
||||
|
|
||||
// Cache each retrieved thread
|
|
||||
$(data).find('div[id*="thread_"]').each(function() { |
|
||||
var thread_id = $(this).attr('id').replace('thread_', ''); |
|
||||
|
|
||||
// Check that this thread hasn't already been loaded somehow
|
|
||||
if ($('div#thread_' + thread_id + '[data-board="' + |
|
||||
post.board + '"]').length) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Hide the freshly loaded threads somewhere at the top
|
|
||||
// of the page for now
|
|
||||
$('form[name="postcontrols"]') |
|
||||
.prepend($(this).hide() |
|
||||
.data('cached', 'yes') |
|
||||
.data('data-board', post.board)); |
|
||||
}); |
|
||||
|
|
||||
// Find the current thread in the newly retrieved ones
|
|
||||
thread = $('div#thread_' + post.id + '[data-board="' + |
|
||||
post.board + '"][data-cached="yes"]'); |
|
||||
|
|
||||
if (thread.length) { |
|
||||
appendThread(thread, post); |
|
||||
} |
|
||||
|
|
||||
// Release the lock
|
|
||||
loading = false; |
|
||||
$('.pages').hide().html(''); |
|
||||
}); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
clearTimeout(timer); |
|
||||
// Check again in one second
|
|
||||
timer = setTimeout(attemptLoadNext, 1000); |
|
||||
}; |
|
||||
|
|
||||
attemptLoadNext(); |
|
||||
}); |
|
@ -1,34 +0,0 @@ |
|||||
<?php |
|
||||
|
|
||||
/* |
|
||||
* When adding a new board, rebuild this theme. If necessary, reconfigure the catalog theme. |
|
||||
* Exclude list is a PHP array e.g. 'exclude' => array('b', 'games', 'music') |
|
||||
*/ |
|
||||
$thread_limit = 30; |
|
||||
|
|
||||
// Define list of overboards |
|
||||
$overboards_config = array( |
|
||||
array( |
|
||||
'title' => 'Overboard', |
|
||||
'uri' => 'overboard', |
|
||||
'subtitle' => '30 most recently bumped threads', |
|
||||
'exclude' => array('assembly', 'assembly_archive', 'gulag'), |
|
||||
'thread_limit' => $thread_limit, |
|
||||
), |
|
||||
array( |
|
||||
'title' => 'SFW Overboard', |
|
||||
'uri' => 'sfw', |
|
||||
'subtitle' => '30 most recently bumped threads from work-safe boards', |
|
||||
'exclude' => array('assembly', 'assembly_archive', 'gulag', 'b'), |
|
||||
'thread_limit' => $thread_limit, |
|
||||
), |
|
||||
array( |
|
||||
'title' => 'Alternate Overboard', |
|
||||
'uri' => 'alt', |
|
||||
'subtitle' => '30 most recently bumped threads from smaller interest boards', |
|
||||
'exclude' => array('assembly', 'assembly_archive', 'gulag', 'leftypol', 'b', 'meta'), |
|
||||
'thread_limit' => $thread_limit, |
|
||||
), |
|
||||
); |
|
||||
|
|
||||
?> |
|
@ -1,235 +0,0 @@ |
|||||
<?php |
|
||||
|
|
||||
require 'info.php'; |
|
||||
|
|
||||
/** |
|
||||
* Generate the board's HTML and move it and its JavaScript in place, whence |
|
||||
* it's served |
|
||||
*/ |
|
||||
function overboards_build($action, $settings) { |
|
||||
global $config; |
|
||||
|
|
||||
if ($action !== 'all' && $action !== 'post' && $action !== 'post-thread' && |
|
||||
$action !== 'post-delete') |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if ($config['smart_build']) { |
|
||||
file_unlink($settings['uri'] . '/index.html'); |
|
||||
} else { |
|
||||
require 'overboards.php'; |
|
||||
|
|
||||
$overboards = new overboards($overboards_config); |
|
||||
|
|
||||
foreach ($overboards_config as &$overboard) { |
|
||||
if (!file_exists($overboard['uri'])) { |
|
||||
@mkdir($overboard['uri'], 0777) or error("Couldn't create {$overboard['uri']}. Check permissions.", true); |
|
||||
} |
|
||||
// Copy the generated board HTML to its place |
|
||||
file_write($overboard['uri'] . '/index.html', $overboards->build($overboard)); |
|
||||
file_write($overboard['uri'] . '/overboard.js', |
|
||||
Element('themes/overboards/overboard.js', array())); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Encapsulation of the theme's internals |
|
||||
*/ |
|
||||
class overboards { |
|
||||
private $settings; |
|
||||
|
|
||||
function __construct($settings) { |
|
||||
$this->settings = $this->parseSettings($settings); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Parse and validate configuration parameters passed from the UI |
|
||||
*/ |
|
||||
private function parseSettings(&$settings) { |
|
||||
foreach ($settings as &$overboard) { |
|
||||
if (!is_int($overboard['thread_limit']) || $overboard['thread_limit'] < 1) |
|
||||
{ |
|
||||
error('thread_limit must be an integer above 1.', true); |
|
||||
} |
|
||||
if (!is_array($overboard['exclude'])) |
|
||||
{ |
|
||||
error('Exclude list must be array of strings.', true); |
|
||||
} |
|
||||
foreach ($overboard['exclude'] as &$board) { |
|
||||
if (!is_string($board)){ |
|
||||
error('Exclude list must be array of strings.', true); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
return $settings; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Obtain list of all threads from all non-excluded boards |
|
||||
*/ |
|
||||
private function fetchThreads($overboard) { |
|
||||
$query = ''; |
|
||||
$boards = listBoards(true); |
|
||||
|
|
||||
foreach ($boards as $b) { |
|
||||
if (in_array($b, $overboard['exclude'])) |
|
||||
continue; |
|
||||
// Threads are those posts that have no parent thread |
|
||||
$query .= "SELECT *, '$b' AS `board` FROM ``posts_$b`` " . |
|
||||
"WHERE `thread` IS NULL UNION ALL "; |
|
||||
} |
|
||||
|
|
||||
$query = preg_replace('/UNION ALL $/', 'ORDER BY `bump` DESC', $query); |
|
||||
$result = query($query) or error(db_error()); |
|
||||
|
|
||||
return $result->fetchAll(PDO::FETCH_ASSOC); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Retrieve all replies to a given thread |
|
||||
*/ |
|
||||
private function fetchReplies($board, $thread_id, $preview_count) { |
|
||||
$query = prepare("SELECT * FROM (SELECT * FROM ``posts_$board`` WHERE `thread` = :id ORDER BY `time` DESC LIMIT :limit) as |
|
||||
t ORDER BY t.time ASC"); |
|
||||
$query->bindValue(':id', $thread_id, PDO::PARAM_INT); |
|
||||
$query->bindValue(':limit', $preview_count, PDO::PARAM_INT); |
|
||||
$query->execute() or error(db_error($query)); |
|
||||
|
|
||||
return $query->fetchAll(PDO::FETCH_ASSOC); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Retrieve count of images and posts in a thread |
|
||||
*/ |
|
||||
private function fetchThreadCount($board, $thread_id, $preview_count) { |
|
||||
$query = prepare("SELECT SUM(t.num_files) as file_count, COUNT(t.id) as post_count FROM (SELECT * FROM ``posts_$board`` WHERE `thread` = :id ORDER BY `time` DESC LIMIT :offset , 18446744073709551615) as t;"); |
|
||||
$query->bindValue(':id', $thread_id, PDO::PARAM_INT); |
|
||||
$query->bindValue(':offset', $preview_count, PDO::PARAM_INT); |
|
||||
$query->execute() or error(db_error($query)); |
|
||||
|
|
||||
return $query->fetch(PDO::FETCH_ASSOC); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Build the HTML of a single thread in the catalog |
|
||||
*/ |
|
||||
private function buildOne($post, $mod = false) { |
|
||||
global $config; |
|
||||
|
|
||||
openBoard($post['board']); |
|
||||
$thread = new Thread($post, $mod ? '?/' : $config['root'], $mod); |
|
||||
// Number of replies to a thread that are displayed beneath it |
|
||||
$preview_count = $post['sticky'] ? $config['threads_preview_sticky'] : |
|
||||
$config['threads_preview']; |
|
||||
$replies = $this->fetchReplies($post['board'], $post['id'], $preview_count); |
|
||||
|
|
||||
$disp_replies = $replies; |
|
||||
foreach ($disp_replies as $reply) { |
|
||||
// Append the reply to the thread as it's being built |
|
||||
$thread->add(new Post($reply, $mod ? '?/' : $config['root'], $mod)); |
|
||||
} |
|
||||
|
|
||||
$threadCount = $this->fetchThreadCount($post['board'], $post['id'], $preview_count); |
|
||||
$thread->omitted = $threadCount['post_count']; |
|
||||
$thread->omitted_images = $threadCount['file_count']; |
|
||||
|
|
||||
// Board name and link |
|
||||
$html = '<h2><a href="' . $config['root'] . $post['board'] . '/">/' . |
|
||||
$post['board'] . '/</a></h2>'; |
|
||||
// The thread itself |
|
||||
$html .= $thread->build(true); |
|
||||
|
|
||||
return $html; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Query the required information and generate the HTML |
|
||||
*/ |
|
||||
public function build($overboard, $mod = false) { |
|
||||
if (!isset($this->settings)) { |
|
||||
error('Theme is not configured properly.'); |
|
||||
} |
|
||||
|
|
||||
global $config; |
|
||||
|
|
||||
$html = ''; |
|
||||
$overflow = array(); |
|
||||
|
|
||||
// Fetch threads from all boards and chomp the first 'n' posts, depending |
|
||||
// on the setting |
|
||||
$threads = $this->fetchThreads($overboard); |
|
||||
$total_count = count($threads); |
|
||||
// Top threads displayed on load |
|
||||
$top_threads = array_splice($threads, 0, $overboard['thread_limit']); |
|
||||
// Number of processed threads by board |
|
||||
$counts = array(); |
|
||||
|
|
||||
// Output threads up to the specified limit |
|
||||
foreach ($top_threads as $post) { |
|
||||
if (array_key_exists($post['board'], $counts)) { |
|
||||
++$counts[$post['board']]; |
|
||||
} else { |
|
||||
$counts[$post['board']] = 1; |
|
||||
} |
|
||||
|
|
||||
$html .= $this->buildOne($post, $mod); |
|
||||
} |
|
||||
|
|
||||
foreach ($threads as $post) { |
|
||||
if (array_key_exists($post['board'], $counts)) { |
|
||||
++$counts[$post['board']]; |
|
||||
} else { |
|
||||
$counts[$post['board']] = 1; |
|
||||
} |
|
||||
|
|
||||
$page = 'index'; |
|
||||
$board_page = floor($counts[$post['board']] / $config['threads_per_page']); |
|
||||
if ($board_page > 0) { |
|
||||
$page = $board_page + 1; |
|
||||
} |
|
||||
$overflow[] = array( |
|
||||
'id' => $post['id'], |
|
||||
'board' => $post['board'], |
|
||||
'page' => $page . '.html' |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
$html .= '<script>var ukko_overflow = ' . json_encode($overflow) . '</script>'; |
|
||||
$html .= '<script type="text/javascript" src="/'.$overboard['uri'].'/overboard.js"></script>'; |
|
||||
|
|
||||
return Element('index.html', array( |
|
||||
'config' => $config, |
|
||||
'board' => array( |
|
||||
'dir' => $overboard['uri'] . "/", |
|
||||
'url' => $overboard['uri'], |
|
||||
'title' => $overboard['title'], |
|
||||
'subtitle' => str_replace('%s', $overboard['thread_limit'], |
|
||||
strval(min($overboard['subtitle'], $total_count))), |
|
||||
), |
|
||||
'no_post_form' => true, |
|
||||
'body' => $html, |
|
||||
'mod' => $mod, |
|
||||
'boardlist' => createBoardlist($mod), |
|
||||
)); |
|
||||
} |
|
||||
|
|
||||
}; |
|
||||
|
|
||||
if (!function_exists('array_column')) { |
|
||||
/** |
|
||||
* Pick out values from subarrays by given key |
|
||||
*/ |
|
||||
function array_column($array, $key) { |
|
||||
$result = []; |
|
||||
foreach ($array as $val) { |
|
||||
$result[] = $val[$key]; |
|
||||
} |
|
||||
return $result; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
Before Width: | Height: | Size: 13 KiB |
Loading…
Reference in new issue