diff --git a/inc/cache.php b/inc/cache.php index 89ca865c..849748d4 100644 --- a/inc/cache.php +++ b/inc/cache.php @@ -19,6 +19,14 @@ class Cache { self::$cache = new Memcached(); self::$cache->addServers($config['cache']['memcached']); break; + case 'redis': + self::$cache = new Redis(); + self::$cache->connect($config['cache']['redis'][0], $config['cache']['redis'][1]); + if ($config['cache']['redis'][2]) { + self::$cache->auth($config['cache']['redis'][2]); + } + self::$cache->select($config['cache']['redis'][3]) or die('cache select failure'); + break; case 'php': self::$cache = array(); break; @@ -45,6 +53,11 @@ class Cache { case 'php': $data = isset(self::$cache[$key]) ? self::$cache[$key] : false; break; + case 'redis': + if (!self::$cache) + self::init(); + $data = json_decode(self::$cache->get($key), true); + break; } // debug @@ -68,6 +81,11 @@ class Cache { self::init(); self::$cache->set($key, $value, $expires); break; + case 'redis': + if (!self::$cache) + self::init(); + self::$cache->setex($key, $expires, json_encode($value)); + break; case 'apc': apc_store($key, $value, $expires); break; @@ -86,6 +104,7 @@ class Cache { switch ($config['cache']['enabled']) { case 'memcached': + case 'redis': if (!self::$cache) self::init(); self::$cache->delete($key); @@ -114,6 +133,10 @@ class Cache { case 'php': self::$cache[$key] = array(); break; + case 'redis': + if (!self::$cache) + self::init(); + return self::$cache->flushDB(); } return false; diff --git a/inc/config.php b/inc/config.php index d47ab59f..913a5686 100644 --- a/inc/config.php +++ b/inc/config.php @@ -91,6 +91,7 @@ $config['cache']['enabled'] = false; // $config['cache']['enabled'] = 'memcached'; + // $config['cache']['enabled'] = 'redis'; // $config['cache']['enabled'] = 'apc'; // $config['cache']['enabled'] = 'xcache'; @@ -104,6 +105,11 @@ $config['cache']['memcached'] = array( array('localhost', 11211) ); + + // Redis server to use. Location, port, password, database id. + // Note that Tinyboard may clear the database at times, so you may want to pick a + // database id just for Tinyboard to use. + $config['cache']['redis'] = array('localhost', 6379, '', 1); /* * ==================== diff --git a/inc/functions.php b/inc/functions.php index 8490fac6..5cf539ce 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -974,16 +974,33 @@ function index($page, $mod=false) { $th['sticky'], $th['locked'], $th['sage'], $th['embed'], $mod ? '?/' : $config['root'], $mod ); - if (!$config['cache']['enabled'] || !$replies = cache::get("thread_index_{$board['uri']}_{$th['id']}")) { + if ($config['cache']['enabled'] && $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}")) { + $replies = $cached['replies']; + $omitted = $cached['omitted']; + } else { $posts = prepare(sprintf("SELECT * FROM `posts_%s` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri'])); $posts->bindValue(':id', $th['id']); $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT); $posts->execute() or error(db_error($posts)); - - $replies = $posts->fetchAll(PDO::FETCH_ASSOC); + + $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC)); + + if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { + $count = prepare(sprintf("SELECT COUNT(`id`) as `num` FROM `posts_%s` WHERE `thread` = :thread UNION ALL SELECT COUNT(`id`) FROM `posts_%s` WHERE `file` IS NOT NULL AND `thread` = :thread", $board['uri'], $board['uri'])); + $count->bindValue(':thread', $th['id'], PDO::PARAM_INT); + $count->execute() or error(db_error($count)); + $count = $count->fetchAll(PDO::FETCH_COLUMN); + + $omitted = array('post_count' => $count[0], 'image_count' => $count[1]); + } else { + $omitted = false; + } if ($config['cache']['enabled']) - cache::set("thread_index_{$board['uri']}_{$th['id']}", $replies); + cache::set("thread_index_{$board['uri']}_{$th['id']}", array( + 'replies' => $replies, + 'omitted' => $omitted, + )); } $num_images = 0; @@ -998,26 +1015,11 @@ function index($page, $mod=false) { ); } - $post_count = count($replies); // $posts->rowCount() - - if ($post_count == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) { - // Yeah, using two cache objects for one thread seems a little inefficient. This code is messy and dumb; please clean it up if you can. - if (!$config['cache']['enabled'] || !$count = cache::get("thread_index_{$board['uri']}_{$th['id']}_omitted")) { - $count = prepare(sprintf("SELECT COUNT(`id`) as `num` FROM `posts_%s` WHERE `thread` = :thread UNION ALL SELECT COUNT(`id`) FROM `posts_%s` WHERE `file` IS NOT NULL AND `thread` = :thread", $board['uri'], $board['uri'])); - $count->bindValue(':thread', $th['id'], PDO::PARAM_INT); - $count->execute() or error(db_error($count)); - $count = $count->fetchAll(PDO::FETCH_COLUMN); - - if ($config['cache']['enabled']) - cache::set("thread_index_{$board['uri']}_{$th['id']}_omitted", $count); - } - - $thread->omitted = $count[0] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); - $thread->omitted_images = $count[1] - $num_images; + if ($omitted) { + $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']); + $thread->omitted_images = $omitted['image_count'] - $num_images; } - $thread->posts = array_reverse($thread->posts); - $body .= $thread->build(true); } @@ -1505,7 +1507,6 @@ function buildThread($id, $return=false, $mod=false) { if ($config['cache']['enabled'] && !$mod) { // Clear cache cache::delete("thread_index_{$board['uri']}_{$id}"); - cache::delete("thread_index_{$board['uri']}_{$id}_omitted"); cache::delete("thread_{$board['uri']}_{$id}"); } diff --git a/inc/mod/pages.php b/inc/mod/pages.php index f6ac3fc4..34945b08 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -209,6 +209,8 @@ function mod_edit_board($boardName) { $query->bindValue(':title', $_POST['title']); $query->bindValue(':subtitle', $_POST['subtitle']); $query->execute() or error(db_error($query)); + + modLog('Edited board information for ' . sprintf($config['board_abbreviation'], $board['uri']), false); } if ($config['cache']['enabled']) { @@ -864,6 +866,8 @@ function mod_move($originBoard, $postID) { } } + modLog("Moved thread #${postID} to " . sprintf($config['board_abbreviation'], $targetBoard) . " (#${newID})", $originBoard); + // build new hread buildThread($newID); buildIndex(); @@ -1019,8 +1023,12 @@ function mod_edit_post($board, $edit_raw_html, $postID) { $query->bindValue(':body', $_POST['body']); $query->execute() or error(db_error($query)); - if (!$edit_raw_html) + if ($edit_raw_html) { + modLog("Edited raw HTML of post #{$postID}"); + } else { + modLog("Edited post #{$postID}"); rebuildPost($postID); + } buildIndex(); diff --git a/js/toggle-locked-threads.js b/js/toggle-locked-threads.js new file mode 100644 index 00000000..50c44eda --- /dev/null +++ b/js/toggle-locked-threads.js @@ -0,0 +1,63 @@ +/* + * toggle-locked-threads.js + * + * Released under the MIT license + * Copyright (c) 2012 Michael Save + * + * Usage: + * $config['additional_javascript'][] = 'js/jquery.min.js'; + * $config['additional_javascript'][] = 'js/toggle-locked-threads.js'; + * + */ + +$(document).ready(function(){ + if($('div.banner').length != 0) + return; // not index + + var hide_locked_threads = localStorage['hidelockedthreads'] ? true : false; + + $('').appendTo($('head')); + + var hideLockedThread = function($thread) { + $thread + .hide() + .addClass('hidden'); + }; + + var restoreLockedThread = function($thread) { + $thread + .show() + .removeClass('hidden'); + }; + + var getThreadFromIcon = function($icon) { + return $icon.parent().parent().parent() + }; + + $('hr:first').before('
-
'); + $('div#toggle-locked-threads a') + .text((hide_locked_threads ? 'Show' : 'Hide') + ' locked threads') + .click(function() { + hide_locked_threads = !hide_locked_threads; + if (hide_locked_threads) { + $('img.icon[title="Locked"]').each(function() { + hideLockedThread(getThreadFromIcon($(this))); + }); + localStorage.hidelockedthreads = true; + } else { + $('img.icon[title="Locked"]').each(function() { + restoreLockedThread(getThreadFromIcon($(this))); + }); + delete localStorage.hidelockedthreads; + } + + $(this).text((hide_locked_threads ? 'Show' : 'Hide') + ' locked threads') + }); + + if (hide_locked_threads) { + $('img.icon[title="Locked"]').each(function() { + hideLockedThread(getThreadFromIcon($(this))); + }); + } +}); + diff --git a/templates/mod/dashboard.html b/templates/mod/dashboard.html index 04da9e70..2f0b03ad 100644 --- a/templates/mod/dashboard.html +++ b/templates/mod/dashboard.html @@ -121,6 +121,10 @@ {% trans 'Debug' %} {% endif %}