Add auto-refresh thread support

This commit is contained in:
Trevor Slocum 2021-03-13 00:39:08 -08:00
parent f4037dfb1c
commit 8c6c67e491
10 changed files with 293 additions and 24 deletions

View file

@ -19,6 +19,7 @@ See [TinyIB Installations](https://gitlab.com/tslocum/tinyib/wikis/Home) for dem
- [hCaptcha](https://hcaptcha.com) is supported.
- [ReCAPTCHA](https://www.google.com/recaptcha/about/) is supported. (But [not recommended](https://nearcyan.com/you-probably-dont-need-recaptcha/))
- Reference links. `>>###`
- Fetch new replies automatically. (See `TINYIB_AUTOREFRESH`)
- Delete posts via password.
- Report posts.
- Block keywords.

View file

@ -26,6 +26,7 @@ a:hover {
hr {
color: #34345C;
background-color: #34345C;
background-image: -webkit-linear-gradient(left,rgba(238,242,255,1),rgba(52,52,92,0.75),rgba(238,242,255,1));
background-image: -moz-linear-gradient(left,rgba(238,242,255,1),rgba(52,52,92,0.75),rgba(238,242,255,1));
background-image: -ms-linear-gradient(left,rgba(238,242,255,1),rgba(52,52,92,0.75),rgba(238,242,255,1));

View file

@ -13,6 +13,7 @@ a:hover {
hr {
color: #800000;
background-color: #800000;
background-image: -webkit-linear-gradient(left,rgba(255,255,238,1),rgba(128,0,0,0.75),rgba(255,255,238,1));
background-image: -moz-linear-gradient(left,rgba(255,255,238,1),rgba(128,0,0,0.75),rgba(255,255,238,1));
background-image: -ms-linear-gradient(left,rgba(255,255,238,1),rgba(128,0,0,0.75),rgba(255,255,238,1));

View file

@ -13,7 +13,7 @@ form {
hr {
clear: both;
border: 0;
border: 0 none;
height: 1px;
}
@ -143,3 +143,8 @@ hr {
#switchStylesheet {
margin-left: 5px;
}
#newreplies {
margin: 5px 0 0 2px;
font-weight: bold;
}

View file

@ -497,6 +497,30 @@ if (!isset($_GET['delete']) && !isset($_GET['manage']) && (isset($_POST['name'])
echo __('Updating index...') . '<br>';
rebuildIndexes();
}
// Check if the request is to auto-refresh a thread
} elseif (isset($_GET['posts']) && !isset($_GET['manage'])) {
if (TINYIB_AUTOREFRESH <= 0) {
fancyDie(__('Automatic refreshing is disabled.'));
}
$thread_id = intval($_GET['posts']);
$new_since = intval($_GET['since']);
if ($thread_id <= 0 || $new_since < 0) {
fancyDie('');
}
$json_posts = array();
$posts = postsInThreadByID($thread_id);
if ($new_since > 0) {
foreach ($posts as $i => $post) {
if ($post['id'] <= $new_since) {
continue;
}
$json_posts[$post['id']] = fixLinksInRes(buildPost($post, true));
}
}
echo json_encode($json_posts);
die();
// Check if the request is to report a post
} elseif (isset($_GET['report']) && !isset($_GET['manage'])) {
if (!TINYIB_REPORT) {

View file

@ -58,6 +58,9 @@ if (!defined('TINYIB_REPORT')) {
if (!defined('TINYIB_REQMOD')) {
define('TINYIB_REQMOD', '');
}
if (!defined('TINYIB_AUTOREFRESH')) {
define('TINYIB_AUTOREFRESH', 30);
}
if (!defined('TINYIB_DISALLOWTHREADS')) {
define('TINYIB_DISALLOWTHREADS', '');
}

View file

@ -540,7 +540,7 @@ EOF;
return $return;
}
function buildPage($htmlposts, $parent, $pages = 0, $thispage = 0) {
function buildPage($htmlposts, $parent, $pages = 0, $thispage = 0, $lastpostid = 0) {
$cataloglink = TINYIB_CATALOG ? ('[<a href="catalog.html" style="text-decoration: underline;">' . __('Catalog') . '</a>]') : '';
$managelink = basename($_SERVER['PHP_SELF']) . "?manage";
@ -594,8 +594,13 @@ EOF;
}
$postform = '';
if ($parent >= TINYIB_NEWTHREAD) {
$postform = buildPostForm($parent);
if ($parent >= 0) { // Negative values indicate the post form should be hidden
$postform = buildPostForm($parent) . '<hr>';
}
$js_autorefresh = '';
if ($parent != TINYIB_NEWTHREAD && TINYIB_AUTOREFRESH > 0) {
$js_autorefresh = '<script type="text/javascript">var autoRefreshDelay = ' . TINYIB_AUTOREFRESH . ';var autoRefreshThreadID = ' . $parent . ';var autoRefreshPostID = ' . $lastpostid . ';</script>';
}
$txt_manage = __('Manage');
@ -617,13 +622,16 @@ EOF;
<hr width="90%">
$postingmode
$postform
<hr>
$js_autorefresh
<form id="delform" action="imgboard.php?delete" method="post">
<input type="hidden" name="board"
EOF;
$body .= 'value="' . TINYIB_BOARD . '">' . <<<EOF
<div id="posts">
$htmlposts
<table class="userdelete">
</div>
<hr>
<table class ="userdelete">
<tbody>
<tr>
<td>
@ -667,7 +675,6 @@ function rebuildCatalog() {
foreach ($threads as $post) {
$htmlposts .= buildCatalogPost($post);
}
$htmlposts .= '<hr size="1">';
writePage('catalog.html', buildPage($htmlposts, -1));
}
@ -689,7 +696,10 @@ function rebuildIndexes() {
$htmlreplies[] = buildPost($replies[$j], TINYIB_INDEXPAGE);
}
$htmlposts .= buildPost($thread, TINYIB_INDEXPAGE) . implode('', array_reverse($htmlreplies)) . "\n<hr>";
if ($i > 0) {
$htmlposts .= "\n<hr>";
}
$htmlposts .= buildPost($thread, TINYIB_INDEXPAGE) . implode('', array_reverse($htmlreplies));
if (++$i >= TINYIB_THREADSPERPAGE) {
$file = ($page == 0) ? TINYIB_INDEX : ($page . '.html');
@ -726,12 +736,13 @@ function rebuildThread($id) {
}
$htmlposts = "";
$lastpostid = 0;
foreach ($posts as $post) {
$htmlposts .= buildPost($post, TINYIB_RESPAGE);
$lastpostid = $post['id'];
}
$htmlposts .= "\n<hr>";
writePage('res/' . $id . '.html', fixLinksInRes(buildPage($htmlposts, $id)));
writePage('res/' . $id . '.html', fixLinksInRes(buildPage($htmlposts, $id, 0, 0, $lastpostid)));
if (TINYIB_JSON) {
writePage('res/' . $id . '.json', buildSingleThreadJSON($id));

4
js/jquery.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,8 @@
var newRepliesCount = 0;
var newRepliesNotice = [];
var originalTitle = "";
var blinkTitle = false;
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
@ -14,10 +19,14 @@ function getCookie(cname) {
return "";
}
function quotePost(postID) {
$("#message").val($("#message").val() + '>>' + postID + "\n").focus();
function setStylesheet(style) {
document.cookie = 'tinyib_style=' + style + '; expires=Tue, 19 Jan 2038 03:14:07 UTC; path=/; SameSite=Strict';
return false;
if ($("#mainStylesheet").attr('href').substring(0, 3) == '../') {
$("#mainStylesheet").attr('href', '../css/' + style + '.css');
} else {
$("#mainStylesheet").attr('href', 'css/' + style + '.css');
}
}
function reloadCAPTCHA() {
@ -27,6 +36,12 @@ function reloadCAPTCHA() {
return false;
}
function quotePost(postID) {
$("#message").val($("#message").val() + '>>' + postID + "\n").focus();
return false;
}
function expandFile(e, id) {
if (e == undefined || e.which == undefined || e.which == 1) {
if ($("#thumbfile" + id).attr('expanded') != 'true') {
@ -49,17 +64,75 @@ function expandFile(e, id) {
return true;
}
function setStylesheet(style) {
document.cookie = 'tinyib_style=' + style + '; expires=Tue, 19 Jan 2038 03:14:07 UTC; path=/; SameSite=Strict';
if ($("#mainStylesheet").attr('href').substring(0, 3) == '../') {
$("#mainStylesheet").attr('href', '../css/' + style + '.css');
} else {
$("#mainStylesheet").attr('href', 'css/' + style + '.css');
function updateTitle() {
if (originalTitle == "") {
originalTitle = document.title;
}
if (!blinkTitle) {
document.title = originalTitle;
return;
}
if (document.title == originalTitle) {
document.title = "(" + newRepliesCount + " new)";
} else {
document.title = originalTitle;
}
setTimeout(updateTitle, 1000);
}
window.addEventListener('DOMContentLoaded', (event) => {
function autoRefresh() {
$.ajax("../imgboard.php?posts=" + autoRefreshThreadID + "&since=" + autoRefreshPostID)
.done(function (d) {
try {
data = JSON.parse(d);
} catch (e) {
console.log("Failed to auto-refresh thread: " + e);
return;
}
if (Object.keys(data).length == 0) {
return
}
posts = $("#posts");
if (newRepliesNotice.length > 0) {
if (!newRepliesNotice.is(":visible")) {
newRepliesNotice.appendTo(posts);
newRepliesNotice.show();
}
} else {
newRepliesNotice = $("<div>", {
"id": "newreplies"
});
newRepliesNotice.text('New');
posts.append(newRepliesNotice);
}
Object.keys(data).forEach(function (key) {
posts.append(data[key]);
autoRefreshPostID = key;
newRepliesCount++;
});
if (!document.hasFocus() && !blinkTitle) {
blinkTitle = true;
updateTitle();
}
})
.fail(function () {
console.log("Failed to auto-refresh thread.");
})
.always(function () {
setTimeout(autoRefresh, autoRefreshDelay * 1000);
});
}
window.addEventListener('DOMContentLoaded', function (e) {
var style = getCookie("tinyib_style");
if (style && style != "") {
setStylesheet(style);
@ -106,6 +179,24 @@ window.addEventListener('DOMContentLoaded', (event) => {
}
}
}
if (typeof autoRefreshDelay !== 'undefined' && typeof autoRefreshPostID !== 'undefined' && typeof autoRefreshThreadID !== 'undefined') {
setTimeout(autoRefresh, autoRefreshDelay * 1000);
}
});
$(window).focus(function() {
newRepliesCount = 0;
blinkTitle = false;
});
$(window).blur(function() {
if (newRepliesNotice.length == 0) {
return;
}
newRepliesCount = 0;
newRepliesNotice.hide();
});
/*
@ -118,4 +209,135 @@ window.addEventListener('DOMContentLoaded', (event) => {
* Licensed under the terms of the MIT license
* http://www.opensource.org/licenses/mit-license.php
*/
(function(f){var c={vertical:{x:false,y:true},horizontal:{x:true,y:false},both:{x:true,y:true},x:{x:true,y:false},y:{x:false,y:true}};var b={duration:"fast",direction:"both"};var e=/^(?:html)$/i;var g=function(k,j){j=j||(document.defaultView&&document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(k,null):k.currentStyle);var i=document.defaultView&&document.defaultView.getComputedStyle?true:false;var h={top:(parseFloat(i?j.borderTopWidth:f.css(k,"borderTopWidth"))||0),left:(parseFloat(i?j.borderLeftWidth:f.css(k,"borderLeftWidth"))||0),bottom:(parseFloat(i?j.borderBottomWidth:f.css(k,"borderBottomWidth"))||0),right:(parseFloat(i?j.borderRightWidth:f.css(k,"borderRightWidth"))||0)};return{top:h.top,left:h.left,bottom:h.bottom,right:h.right,vertical:h.top+h.bottom,horizontal:h.left+h.right}};var d=function(h){var j=f(window);var i=e.test(h[0].nodeName);return{border:i?{top:0,left:0,bottom:0,right:0}:g(h[0]),scroll:{top:(i?j:h).scrollTop(),left:(i?j:h).scrollLeft()},scrollbar:{right:i?0:h.innerWidth()-h[0].clientWidth,bottom:i?0:h.innerHeight()-h[0].clientHeight},rect:(function(){var k=h[0].getBoundingClientRect();return{top:i?0:k.top,left:i?0:k.left,bottom:i?h[0].clientHeight:k.bottom,right:i?h[0].clientWidth:k.right}})()}};f.fn.extend({scrollintoview:function(j){j=f.extend({},b,j);j.direction=c[typeof(j.direction)==="string"&&j.direction.toLowerCase()]||c.both;var n="";if(j.direction.x===true){n="horizontal"}if(j.direction.y===true){n=n?"both":"vertical"}var l=this.eq(0);var i=l.closest(":scrollable("+n+")");if(i.length>0){i=i.eq(0);var m={e:d(l),s:d(i)};var h={top:m.e.rect.top-(m.s.rect.top+m.s.border.top),bottom:m.s.rect.bottom-m.s.border.bottom-m.s.scrollbar.bottom-m.e.rect.bottom,left:m.e.rect.left-(m.s.rect.left+m.s.border.left),right:m.s.rect.right-m.s.border.right-m.s.scrollbar.right-m.e.rect.right};var k={};if(j.direction.y===true){if(h.top<0){k.scrollTop=m.s.scroll.top+h.top}else{if(h.top>0&&h.bottom<0){k.scrollTop=m.s.scroll.top+Math.min(h.top,-h.bottom)}}}if(j.direction.x===true){if(h.left<0){k.scrollLeft=m.s.scroll.left+h.left}else{if(h.left>0&&h.right<0){k.scrollLeft=m.s.scroll.left+Math.min(h.left,-h.right)}}}if(!f.isEmptyObject(k)){if(e.test(i[0].nodeName)){i=f("html,body")}i.animate(k,j.duration).eq(0).queue(function(o){f.isFunction(j.complete)&&j.complete.call(i[0]);o()})}else{f.isFunction(j.complete)&&j.complete.call(i[0])}}return this}});var a={auto:true,scroll:true,visible:false,hidden:false};f.extend(f.expr[":"],{scrollable:function(k,i,n,h){var m=c[typeof(n[3])==="string"&&n[3].toLowerCase()]||c.both;var l=(document.defaultView&&document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(k,null):k.currentStyle);var o={x:a[l.overflowX.toLowerCase()]||false,y:a[l.overflowY.toLowerCase()]||false,isRoot:e.test(k.nodeName)};if(!o.x&&!o.y&&!o.isRoot){return false}var j={height:{scroll:k.scrollHeight,client:k.clientHeight},width:{scroll:k.scrollWidth,client:k.clientWidth},scrollableX:function(){return(o.x||o.isRoot)&&this.width.scroll>this.width.client},scrollableY:function(){return(o.y||o.isRoot)&&this.height.scroll>this.height.client}};return m.y&&j.scrollableY()||m.x&&j.scrollableX()}})})(jQuery);
(function (f) {
var c = {
vertical: {x: false, y: true},
horizontal: {x: true, y: false},
both: {x: true, y: true},
x: {x: true, y: false},
y: {x: false, y: true}
};
var b = {duration: "fast", direction: "both"};
var e = /^(?:html)$/i;
var g = function (k, j) {
j = j || (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(k, null) : k.currentStyle);
var i = document.defaultView && document.defaultView.getComputedStyle ? true : false;
var h = {
top: (parseFloat(i ? j.borderTopWidth : f.css(k, "borderTopWidth")) || 0),
left: (parseFloat(i ? j.borderLeftWidth : f.css(k, "borderLeftWidth")) || 0),
bottom: (parseFloat(i ? j.borderBottomWidth : f.css(k, "borderBottomWidth")) || 0),
right: (parseFloat(i ? j.borderRightWidth : f.css(k, "borderRightWidth")) || 0)
};
return {
top: h.top,
left: h.left,
bottom: h.bottom,
right: h.right,
vertical: h.top + h.bottom,
horizontal: h.left + h.right
}
};
var d = function (h) {
var j = f(window);
var i = e.test(h[0].nodeName);
return {
border: i ? {top: 0, left: 0, bottom: 0, right: 0} : g(h[0]),
scroll: {top: (i ? j : h).scrollTop(), left: (i ? j : h).scrollLeft()},
scrollbar: {
right: i ? 0 : h.innerWidth() - h[0].clientWidth,
bottom: i ? 0 : h.innerHeight() - h[0].clientHeight
},
rect: (function () {
var k = h[0].getBoundingClientRect();
return {
top: i ? 0 : k.top,
left: i ? 0 : k.left,
bottom: i ? h[0].clientHeight : k.bottom,
right: i ? h[0].clientWidth : k.right
}
})()
}
};
f.fn.extend({
scrollintoview: function (j) {
j = f.extend({}, b, j);
j.direction = c[typeof (j.direction) === "string" && j.direction.toLowerCase()] || c.both;
var n = "";
if (j.direction.x === true) {
n = "horizontal"
}
if (j.direction.y === true) {
n = n ? "both" : "vertical"
}
var l = this.eq(0);
var i = l.closest(":scrollable(" + n + ")");
if (i.length > 0) {
i = i.eq(0);
var m = {e: d(l), s: d(i)};
var h = {
top: m.e.rect.top - (m.s.rect.top + m.s.border.top),
bottom: m.s.rect.bottom - m.s.border.bottom - m.s.scrollbar.bottom - m.e.rect.bottom,
left: m.e.rect.left - (m.s.rect.left + m.s.border.left),
right: m.s.rect.right - m.s.border.right - m.s.scrollbar.right - m.e.rect.right
};
var k = {};
if (j.direction.y === true) {
if (h.top < 0) {
k.scrollTop = m.s.scroll.top + h.top
} else {
if (h.top > 0 && h.bottom < 0) {
k.scrollTop = m.s.scroll.top + Math.min(h.top, -h.bottom)
}
}
}
if (j.direction.x === true) {
if (h.left < 0) {
k.scrollLeft = m.s.scroll.left + h.left
} else {
if (h.left > 0 && h.right < 0) {
k.scrollLeft = m.s.scroll.left + Math.min(h.left, -h.right)
}
}
}
if (!f.isEmptyObject(k)) {
if (e.test(i[0].nodeName)) {
i = f("html,body")
}
i.animate(k, j.duration).eq(0).queue(function (o) {
f.isFunction(j.complete) && j.complete.call(i[0]);
o()
})
} else {
f.isFunction(j.complete) && j.complete.call(i[0])
}
}
return this
}
});
var a = {auto: true, scroll: true, visible: false, hidden: false};
f.extend(f.expr[":"], {
scrollable: function (k, i, n, h) {
var m = c[typeof (n[3]) === "string" && n[3].toLowerCase()] || c.both;
var l = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(k, null) : k.currentStyle);
var o = {
x: a[l.overflowX.toLowerCase()] || false,
y: a[l.overflowY.toLowerCase()] || false,
isRoot: e.test(k.nodeName)
};
if (!o.x && !o.y && !o.isRoot) {
return false
}
var j = {
height: {scroll: k.scrollHeight, client: k.clientHeight},
width: {scroll: k.scrollWidth, client: k.clientWidth},
scrollableX: function () {
return (o.x || o.isRoot) && this.width.scroll > this.width.client
},
scrollableY: function () {
return (o.y || o.isRoot) && this.height.scroll > this.height.client
}
};
return m.y && j.scrollableY() || m.x && j.scrollableX()
}
})
})(jQuery);

View file

@ -27,6 +27,7 @@ define('TINYIB_CAPTCHA', ''); // Reduce spam by requiring users to pass
define('TINYIB_MANAGECAPTCHA', ''); // Improve security by requiring users to pass a CAPTCHA when logging in to the management panel: simple / hcaptcha / recaptcha ['' to disable]
define('TINYIB_REPORT', false); // Allow users to report posts
define('TINYIB_REQMOD', ''); // Require moderation before displaying posts: files / all ['' to disable]
define('TINYIB_AUTOREFRESH', 30); // Delay (in seconds) between attempts to refresh threads automatically [0 to disable]
define('TINYIB_DISALLOWTHREADS', ''); // When set, users attempting to post a new thread are shown this message instead ['' to disable]
define('TINYIB_DISALLOWREPLIES', ''); // When set, users attempting to post a reply are shown this message instead ['' to disable]