Skip to content

Instantly share code, notes, and snippets.

@FeepingCreature
Created October 19, 2012 17:34
Show Gist options
  • Save FeepingCreature/3919519 to your computer and use it in GitHub Desktop.
Save FeepingCreature/3919519 to your computer and use it in GitHub Desktop.
module mspatest;
import std.string, sqlite3, std.boehm, std.zlib, std.http, std.util, std.file;
static import c.stdlib;
import std.fun, std.cgi, std.process;
import mspa;
alias FOURSTRING = (string, string, string, string); // small shortcut
alias FIVESTRING = (string, string, string, string, string);
string filterTag(string tag) {
char[auto~] filtered-tagname;
for auto ch <- tag {
if ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9"
|| "_- .,".find(""~ch) != -1
filtered-tagname ~= ch;
}
return filtered-tagname[];
}
string getThreadName(string url) {
url = url.between("?", "");
if (auto first = url.between("", "/")) url = first;
return url.between("-", "");
}
int counter;
string reformatPost(string content, bool spoilers, string idText, bool linkToAnchors, noNyud = false) {
/* strip out existing anchor links */
do {
auto startpos = content.find("#post");
} while (startpos) {
content = content[0 .. startpos] ~ "\"" ~ content[startpos .. $].between("\"", "");
}
if (linkToAnchors) {
content = content.replace("href=\"showthread.php?p=", "href=\"#post");
} else {
content = content.replace("href=\"showthread.php?p=", "href=\"$idText");
}
string startMarker = "<!-- BEGIN TEMPLATE: bbcode_quote";
string endMarker = "<!-- END TEMPLATE: bbcode_quote";
string handleQuotes(string str) {
do {
auto quotePos = str.find(startMarker);
auto endPos = str.find(endMarker);
} while (quotePos && endPos && quotePos < endPos) {
auto startPos = quotePos + startMarker.length;
while (str[startPos .. endPos].find startMarker) != -1 {
str = str[0 .. startPos] ~ handleQuotes str[startPos .. $]; // recurse
endPos = str.find(endMarker);
if (endPos == -1) {
raise new Error "end marker no longer found after recursion step! unbalanced? $content";
}
}
auto end = int:endPos;
end += str[end .. $].find(">") + 1;
auto quote = str[quotePos .. end];
auto name = quote.between("<strong>", "</strong>");
auto ref = quote.between("href=\"", "\"");
auto msg = quote.between("class=\"message\">", "\t\t</div");
auto newtext = "<table><tr><td valign=\"top\">&gt;</td><td><div style=\"border: 1px solid; \"><div><span style=\"border-bottom: 1px solid; \">Originally posted by <a href=\"$ref\">$name</a></span></div>";
newtext = "$newtext$msg</td></tr></table>";
str = str[0..quotePos] ~ newtext ~ str[end .. $];
}
return str;
}
string nyudImages(string src, string baseUrl) {
int offset;
do {
int srcpos = src[offset .. $].find("src=\"");
} while (srcpos != -1) {
srcpos += offset;
srcpos += 5;
int endpos = src[srcpos .. $].find("\"");
if (endpos == -1) raise new Error "Unterminated img src string wtf!! ";
endpos += srcpos;
auto tag = src[srcpos .. endpos];
tag = baseUrl.followLink tag;
tag = tag.startsWith "http://";
tag = "http://" ~ slice(&tag, "/") ~ ".nyud.net/" ~ tag; // lol side effect abuse
src = src[0 .. srcpos] ~ tag ~ src[endpos .. $];
offset = srcpos + tag.length;
}
return src;
}
content = handleQuotes content;
if (!noNyud) content = nyudImages (content, "http://www.mspaforums.com/");
do {
auto spoilerpos = content.find spoilertext;
} while spoilerpos != -1 {
string replacement;
if (spoilers) {
replacement = `<div style="border: 1px solid gray; padding: 1px; margin: 2px; "><span style="border: 1px dotted; ">Spoiler</span><div>`;
} else {
int id = counter ++;
replacement = `<div style="border: 1px solid gray; padding: 1px; margin: 2px; "><span style="border: 1px dotted; ">
<span class="box" id="??C_plus" onclick="javascript: css_show('??C'); css_show('??C_minus'); css_hide('??C_plus'); ">Show Spoiler</span>
<span class="box" id="??C_minus" onclick="javascript: css_hide('??C'); css_show('??C_plus'); css_hide('??C_minus'); " style="display: none; ">Hide spoiler</span>
</span><div id="??C" style="display: none; ">`.replace("??C", "spoilerdiv$id");
}
content = content[0..spoilerpos] ~ replacement ~ content[spoilerpos + spoilertext.length .. $];
}
content = content.replace("src=\"images/smilies", "src=\"http://www.mspaforums.com/images/smilies");
content = content.replace("<b>", "<div class=\"inline bold\">").replace("</b>", "</div>");
return content;
}
// prevent google from following links that potentially go to very large pages
alias sharedCSS = `
<meta name="robots" content="index, nofollow" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<link rel="shortcut icon" href="favicon.ico" />
<style type="text/css">
table.boxed {
border: 1px solid black;
border-collapse: collapse;
}
th {
border-bottom: 2px solid black;
}
th.first, th.mid, td.first, td.mid {
border-right: 1px solid gray;
}
tr.even {
background-color: #def;
}
div.plustag:hover *.hidden {
visibility: visible;
}
div.minustag:hover *.hidden {
visibility: visible;
}
div.inline {
display: inline;
}
*.bold {
font-weight: bold;
}
*.box {
border: 1px solid;
background-color: #fff;
padding: 0px 2px 0px 2px;
margin: 1px;
}
*.box_outer {
border: 1px solid;
background-color: #fff;
padding: 0px 2px 0px 2px;
margin: 5px;
}
*.box_inner {
border: 1px solid;
padding: 0px 2px 0px 2px;
margin: 1px;
background-color: #efe;
}
*.hidden {
visibility: hidden;
position: absolute;
background-color: #ddd;
}
div.mewdiv {
position: absolute;
display: inline;
background-color: #eef;
visibility: hidden;
width: 60%; height: 90%;
}
table.mewtable {
width: 100%; height: 100%;
border-collapse: collapse;
}
iframe.mewframe {
width: 100%; height: 100%;
background-color: #ffe;
}
</style>`;
alias sharedJS = `
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
function submitForm(name) {
document.getElementById(name).submit();
}
function loadFrame(name, divname, url) {
$('.mewdiv').css('visibility', 'hidden');
var frame = document.getElementById(name), div = document.getElementById(divname);
frame.src = url;
div.style.visibility = "visible";
scrollToThingy (div);
}
function scrollToThingy (thing) {
var targetY = 0;
while (thing) {
targetY += thing.offsetTop;
thing = thing.offsetParent;
}
window.scroll(0, targetY);
}
function css_show (name) {
document.getElementById(name).style.display = "inline";
}
function css_hide (name) {
document.getElementById(name).style.display = "none";
}
</script>`;
import c.stdio, std.time;
extern(C) { int fprintf(void*, char*, ...); void* stderr; }
void main(string[] args) {
sqlite3.profile = false;
bool wroteHeader;
void writeHTMLHeader(bool doctype = true) {
if (wroteHeader) return;
writeln "Content-type: text/html; charset=utf-8";
writeln "";
if (doctype) {
writeln `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">`;
writeln `<html xmlns="http://www.w3.org/1999/xhtml">`;
} else {
writeln `<html>`;
}
wroteHeader = true;
}
set-handler (Error err) {
writeHTMLHeader();
writeln "<head><title>Error</title></head><body>";
writeln "<h2>An error has occurred! </h2>";
writeln "$err";
writeln "</body></html>";
invoke-exit "return";
}
set-handler (SQLiteBusy) {
fprintf(FILE*:stderr, "Database busy. \n");
sleep 1;
fprintf(FILE*:stderr, "Retrying. \n");
invoke-exit "retry";
}
define-exit "return" return;
initBoehm();
auto db = new Database "threads.sqlite3";
onExit db.close;
db.mkfast;
onSuccess if sqlite3.profile {
void errprint(string s) fprintf(FILE*:stderr, toStringz "$s\n");
errprint "profile: ";
for auto tup <- profiledata {
errprint " $(tup[2]): $(tup[1]): $(tup[0])";
}
}
string getEnvVar(string name) {
return CToString c.stdlib.getenv toStringz name;
}
auto query_string = getEnvVar "QUERY_STRING";
auto http_cookie = getEnvVar "HTTP_COOKIE";
// lol wtf
while (query_string.find("&amp;") != -1) {
query_string = query_string.replace("&amp;", "&");
}
(string, string)[auto~] params;
for auto part <- query_string.split "&" {
if (part.find("=") != -1)
params ~= slice(part, "=");
else
params ~= (part, "");
}
void processCookie() {
if (http_cookie) {
(string, string)[auto~] cookieparts;
for auto part <- http_cookie.split(";") {
auto name = slice(&part, "=");
cookieparts ~= (strip name, strip part);
}
for (string name, string value) <- cookieparts if name == "idmask" {
bool hit;
for int i <- 0..params.length if !hit {
if (params[i][0] == "idmask") {
params[i][1] = "$value";
hit = true;
}
}
if !hit params ~= ("idmask", "$value");
}
}
}
string getParam(string name, deflt = null) {
for auto par <- params
if (par[0] == name) return par[1].urldecode();
return deflt;
}
void removeParam(string name) {
(string, string)[auto~] newparams;
for auto par <- params
if (par[0] != name) newparams ~= par;
params = newparams;
}
bool noNyud = getParam("nyud") == "0";
string urlWithParams((string, string)[] array) {
string[auto~] res;
auto handledEntries = new bool[] array.length;
for auto par <- params {
bool foundReplacement;
for (int i, (string, string) tup) <- zip(ints, array) if !foundReplacement {
if (par[0] == tup[0]) {
if (tup[1]) res ~= "$(tup[0])=$(tup[1])";
handledEntries[i] = true;
foundReplacement = true;
}
}
if !foundReplacement res ~= "$(par[0])=$(par[1])";
}
for (int i, bool handled) <- zip(ints, handledEntries)
if !handled res ~= "$(array[i][0])=$(array[i][1])";
return "?" ~ join!(string[auto~], string)(res, "&amp;");
}
string urlWithParam(string name, value) {
return urlWithParams [(name, value)];
}
string authormask, authormask-sql = "and not length(?)";
if auto author = getParam "author" {
auto alts = author.split("|");
int offs;
authormask = author;
authormask-sql = "select ? AUTHORMASK where 0";
for auto alt <- alts {
authormask-sql ~= " or posts.name = substr(AUTHORMASK, $(offs+1), $(alt.length))";
offs += alt.length + 1; // for "|"
}
authormask-sql = "and exists ($authormask-sql)";
}
string threadmask, threadmask-sql = "and not length(?)";
if auto thread = getParam "thread" {
threadmask = thread;
threadmask-sql = "and thread_ids.thread_id = ?";
}
string tagmask, tagmask-sql = "and not length(?)";
if auto tag = getParam "tag" {
tagmask = tag;
tagmask-sql = "and tags.tag = ?";
}
bool textmask;
string textmask-sql = "where 1";
if "yes" == getParam ("noTextPosts", "no") {
textmask = true;
textmask-sql = "join image_post on thread_ids.msgid = image_post.msgid where 1";
}
string idmask, idmask-sql = "and not length(?)";
if auto id = getParam "idmask" {
idmask-sql = "select ? IDMASK where 0";
while (id.length) {
if (auto rest = id.startsWith "from_") {
int from = atoi slice(&rest, "_");
id = rest;
auto start = idmask.length;
idmask ~= "$from";
auto len = idmask.length - start;
idmask-sql ~= " or abs(posts.msgid) >= abs(substr(IDMASK, $(start+1), $(len)))";
} else {
raise new Error "Invalid id: $id (cookie $http_cookie)";
}
}
idmask-sql = "and exists ($idmask-sql)";
}
int count = -1;
if auto c = getParam "count"
count = atoi c;
bool showSpoilers;
if (getParam("spoilers") == "visible") showSpoilers = true;
void writeSiteHeader(bool handleSpoilers, linkToOverview, linkToList, linkToFullView, showRSS = true, floatIt = true, showFilters = true, string additionalText = null) {
if (floatIt) writeln "<div style=\"background-color: #eef; position: fixed; top: 0px; border: 1px solid; \">";
else writeln "<div style=\"background-color: #eef; border: 1px solid; \">";
bool firstLine = true;
int linecount;
void myWriteLine(string msg, bool sameLine = false) {
if (firstLine) { firstLine = false; linecount = 1; }
else if (!sameLine) {
writeln "<br />";
linecount ++;
}
writeln msg;
}
if (linkToOverview) myWriteLine "<a href=\"$(urlWithParam(\"site\", \"main\"))\">Back to overview</a>&nbsp;|&nbsp;";
if (handleSpoilers) {
string spoiler-change;
if showSpoilers {
spoiler-change = "<a href=\"$(urlWithParam(\"spoilers\", null))\">Hide Spoilers</a>&nbsp;";
} else {
spoiler-change = "<a href=\"$(urlWithParam(\"spoilers\", \"visible\"))\">Always Show Spoilers</a>&nbsp;|";
}
myWriteLine ("$spoiler-change", true);
}
if (linkToList) myWriteLine ("<a href=\"$(urlWithParam(\"site\", \"overview\"))\">Go to post list</a> |", true);
if (linkToFullView) myWriteLine ("<a href=\"$(urlWithParams [(\"site\", \"full-html\"), (\"count\", \"50\")])\">
Go to full HTML view</a>&nbsp;|", true);
if (showRSS) myWriteLine ("<a href=\"$(urlWithParams [(\"site\", \"rss\"), (\"count\", string:null)])\">RSS</a>&nbsp;|", true);
myWriteLine ("<a href=\"$(urlWithParam(\"noTextPosts\", \"yes\"))\">No Text Posts</a>", true);
myWriteLine (additionalText, true);
if (showFilters) {
if (authormask) {
myWriteLine "Filter by author: $authormask (<a href=\"$(urlWithParam(\"author\", null))\">remove</a>)";
}
if (threadmask) {
myWriteLine "Filter by thread: $threadmask (<a href=\"$(urlWithParam(\"thread\", null))\">remove</a>)";
}
if (tagmask) {
myWriteLine "Filter by tag: $tagmask (<a href=\"$(urlWithParam(\"tag\", null))\">remove</a>)";
}
if (idmask) {
myWriteLine "Only show posts after $idmask (<a href=\"$(urlWithParam(\"id\", null))\">remove</a>)";
}
if (count != -1) {
myWriteLine "Only show $count posts (<a href=\"$(urlWithParam(\"count\", null))\">remove</a>)";
}
if (textmask) {
myWriteLine "Filter: show only image posts (<a href=\"$(urlWithParam(\"noTextPosts\", null))\">remove</a>)";
}
}
writeln "</div>";
if floatIt
for 0..linecount
writeln "<br style=\"clear: both; \" />";
}
void writeFooter() {
writeln "<div style=\"color: #aaa; font-size: 70%; \">disclaimer: all copyrights remain with the respective owners. this site acts merely as a caching layer for mspaforums.com and tgchan.org;
the creator disclaims all liability for the site itself or the content displayed within. </div>";
}
auto site = getParam ("site");
if (!site || site == "main") {
removeParam("id"); // cleanup
writeHTMLHeader();
writeln "<head><title>Thread Overview</title>";
writeln sharedCSS;
writeln sharedJS;
writeln "</head><body>";
onSuccess {
p \{
a.href "http://validator.w3.org/check?uri=referer"
$img #.src "http://www.w3.org/Icons/valid-xhtml10" #.alt "Valid XHTML 1.0 Transitional" #.height 31 #.width 88;
a.href "http://jigsaw.w3.org/css-validator/check/referer"
$img #.style "border:0;width:88px;height:31px" #.src "http://jigsaw.w3.org/css-validator/images/vcss" #.alt "Valid CSS!";
$img #.src "valid-rss-rogers.png" #.height 31 #.alt "RSS feeds validated";
}
writeFooter();
writeln "</body></html>";
}
writeln "<div>";
writeSiteHeader(handleSpoilers => false, linkToOverview => false, linkToList => true, linkToFullView => true, showRSS => eval authormask || threadmask || tagmask);
writeln "</div>";
writeln "<p>";
(string, string)[] postParams;
string getPostParam(string name, deflt = null) {
for auto par <- postParams
if (par[0] == name) return par[1];
return deflt;
}
if (getEnvVar "REQUEST_METHOD") == "POST" {
auto postText = string: join readfile 0;
// wtf some more
while (postText.find("&amp;") != -1) {
postText = postText.replace("&amp;", "&");
}
// writeln "Post mode <br />";
// writeln "$postText";
for auto part <- postText.split "&" {
(string name, string value) = slice(part, "=");
value = urldecode value;
postParams ~= (name, value);
}
auto action = getPostParam("action");
if (action == "add_tag") {
string tagname = filterTag getPostParam "tagname";
if (!tagname.length)
writeln "Tag invalid! debug $postParams";
else {
int threadId = getPostParam "threadId" #.atoi();
db.exec("insert or replace into tags(thread_id, tag) values(?, ?)", threadId, tagname);
}
}
if (action == "rm_tag") {
string tagname = filterTag getPostParam "tagname";
if (!tagname.length)
writeln "Tag invalid! ";
else {
int threadId = getPostParam "threadId" #.atoi();
db.exec("delete from tags where thread_id=? and tag=?", threadId, tagname);
}
}
if (action == "add_thread") {
string url = getPostParam "url";
string[] tags = getPostParam "tags" #.split "," #.map &strip #.eval[];
void addThread(string url) {
db.exec("insert or replace into page_urls (msgid, page_url) values(null, ?)", url);
}
void update(string[] urls) {
join readback("./mspa_update.sh", urls);
}
void setTags(string url) {
eval int id <- db.exec("select distinct thread_id from page_urls left outer join thread_ids where page_url = ? and page_urls.msgid = thread_ids.msgid", url);
if tags.length && tags[0] {
db.exec("delete from tags where thread_id = ?", id);
for auto tag <- tags
db.exec("insert or replace into tags(thread_id, tag) values(?, ?)", id, tag);
}
}
if (url.find("tgchan.org/wiki/")) {
auto links = string: url.downloadCached() #.betweens("href=\"", "\"") #.select \(string s) -> s.find "tgchan.org/kusaba/quest/" #.eval;
for auto link <- links addThread (link);
update links[];
for auto link <- links setTags (link);
writeln "$(links.length) threads added! ";
} else if (!url.startsWith("http://www.mspaforums.com/showthread.php?") && !url.startsWith "http://tgchan.org/kusaba/") {
writeln "Can only submit MSPA or tgchan threads/wiki pages! ";
} else {
if (auto base = url.between("", "/page")) url = base;
addThread (url);
update [url];
setTags (url);
writeln "Thread added!";
}
}
}
writeln "</p>";
form #.action "?site=main" #.method "post" p \{
text "URL";
input #.type "text" #.name "url" #.size 40;
input #.type "submit" #.value "Add Thread";
br;
text "Tags";
input #.type "text" #.name "tags" #.size 28;
input #.type "hidden" #.name "action" #.value "add_thread";
}
p \{
writeln "Hint: You can add Tags to threads! To do this, use the + symbol next to the threads. Then, by clicking on the tag name, you can select only threads that have the same tag."; br;
writeln "This is useful if you want to follow a quest that spans multiple threads."; br;
}
writeln "<table class=\"boxed\">";
onSuccess writeln "</table>";
tr \{
th #.class "first" "Thread";
th #.class "mid" "Starter";
th #.class "mid" "Posts";
th #.class "mid" "First post date";
th #.class "mid" "Last post date";
th #.class "last" "Tags";
}
auto evenodd_iter = loop ["even", "odd"];
int formid;
if (getParam("hax") == "yes") {
for (int msgid, ubyte[] data) <- db.exec "select msgid, content from content" {
auto pureText = removeSpoilers string: inflate data;
bool hasVisiblePics = eval pureText.find("img src=\"http") != -1;
if (hasVisiblePics) {
db.exec("insert or replace into image_post (msgid) values(?)", msgid);
} else {
db.exec("insert or replace into text_post (msgid) values(?)", msgid);
}
}
return;
}
for (string thread, int thread_id, string threadname) <- db.exec ("
select page_url, A.thread_id, titles.title from (
select thread_ids.thread_id, thread_ids.msgid from thread_ids
left outer join tags on thread_ids.thread_id = tags.thread_id
$textmask-sql $tagmask-sql $threadmask-sql
group by thread_ids.thread_id
) A
join page_urls B on A.msgid=B.msgid
join posts on A.msgid=posts.msgid
left outer join titles on A.thread_id=titles.thread_id
order by A.thread_id desc", tagmask, threadmask)
{
(thread, string bogus) = slice(thread, "/page");
if (!threadname)
threadname = getThreadName thread;
db.openStatementList();
eval (string threadStarter, int threadStarterId) <- db.exec("
select name, A.msgid from thread_ids A
join postnums B on A.msgid = B.msgid
join posts C on A.msgid = C.msgid
where A.thread_id=? and B.postnr=1", thread_id);
if (!authormask || authormask == threadStarter) {
eval int posts <- db.exec("select count(*) from thread_ids join posts on thread_ids.msgid = posts.msgid $textmask-sql and thread_id=? $authormask-sql", thread_id, authormask);
eval int maxPostNum <- db.exec("
select max(postnr) from thread_ids A
join posts on A.msgid = posts.msgid
join postnums B on A.msgid = B.msgid
where thread_id=? $authormask-sql", thread_id, authormask);
eval string firstDate <- db.exec ("select date from posts where msgid=? $authormask-sql", threadStarterId, authormask);
eval (string lastDate, int lastDateId) <- db.exec("
select posts.date, posts.msgid from thread_ids A
join postnums B on A.msgid = B.msgid
join posts on A.msgid = posts.msgid
where A.thread_id=? and B.postnr=? $authormask-sql", thread_id, maxPostNum, authormask);
string[auto~] tags;
for string tag <- db.exec("select tag from tags where thread_id = ?", thread_id)
tags ~= tag.dup;
string genTag(string tag) {
string res = "<div class=\"inline box\"><a href=\"$(urlWithParam(\"tag\", \"$tag\"))\">$tag</a></div>";
/*string minus;
using scoped outputfn = \(string s) minus ~= s; {
b "-";
div #.class "hidden box inline" \{
form #.id "form$formid" #.method "post" #.action "#" \{
input #.type "hidden" #.name "threadId" #.value "$thread_id");
input #.type "hidden" #.name "tagname" #.value "$tag";
input #.type "hidden" #.name "action" #.value "rm_tag";
a #.href "javascript:submitForm('form$formid');" "Remove Tag";
}
}
}
res = "$res<div class=\"minustag inline box\" style=\"vertical-align: super; \">$minus</div>";*/
formid ++;
return res;
}
string tagstr = [for str <- tags extra &genTag: extra(str)].join ", ";
string firstDateText = "$firstDate";
string lastDateText = "$lastDate";
if (thread_id >= 0) {
firstDateText = "<a href=\"http://www.mspaforums.com/showthread.php?$thread_id\">$firstDate</a>";
lastDateText = "<a href=\"http://www.mspaforums.com/showthread.php?$thread_id&amp;p=$lastDateId&amp;viewfull=1#post$lastDateId\">$lastDate</a>";
}
eval string evenodd <- evenodd_iter;
// writeln "TEST <<$evenodd>>";
tr .class evenodd \{
td .class "first" a .href urlWithParam("thread", "$thread_id") threadname;
td .class "mid" a .href urlWithParam("author", threadStarter) threadStarter;
td .class "mid" "$posts";
td .class "mid" firstDateText;
td .class "mid" lastDateText;
td .class "last" \{
text tagstr;
div .class "plustag inline box" \{
text "+";
div #.class "hidden box inline " #.style "position: absolute; " \{
form #.id "form$formid" #.method "post" #.action "#" \{
input #.type "text" #.name "tagname";
input #.type "hidden" #.name "threadId" #.value "$thread_id";
input #.type "hidden" #.name "action" #.value "add_tag";
a #.href "javascript:submitForm('form$formid');" "Add Tag";
}
}
}
}
formid ++;
}
}
db.finStatementList();
}
return;
}
if (site == "overview") {
writeHTMLHeader(doctype => false);
std.cgi.head \{
title "Overview";
text sharedCSS;
text sharedJS;
}
writeln "<body>";
onSuccess {
writeFooter();
writeln "</body></html>";
}
writeSiteHeader(handleSpoilers => true, linkToOverview => true, linkToList => false, linkToFullView => true, floatIt => false);
{
writeln "<table class=\"boxed\">";
tr \{
th.class "first" "Thread";
th.class "mid" "Name";
th.class "mid" "Date";
th.class "last" "Post ID";
}
auto evenodd_iter = loop ["even", "odd"];
int iframe_id;
void writeRowFor(string page_url, int next_msg_id, msg_id, prev_msg_id, string name, string date, bool showSpoilers) {
eval string evenodd <- evenodd_iter;
string threadname = getThreadName page_url;
string spoilerpar;
if (showSpoilers) spoilerpar = "&amp;spoilers";
string next_post_link = "?site=rss&amp;id=$next_msg_id$spoilerpar";
string post_link = "?site=rss&amp;id=$msg_id$spoilerpar";
string prev_post_link = "?site=rss&amp;id=$prev_msg_id$spoilerpar";
tr.class evenodd \{
td.class "first" threadname;
td.class "mid" a.href urlWithParam("author", name) name;
td.class "mid" date;
td.class "last" \{
a.href "javascript: loadFrame('mewframe$iframe_id', 'mewdiv$iframe_id', '$post_link'); " "$msg_id";
div #.class "mewdiv" #.id "mewdiv$iframe_id" \{
table.class "mewtable boxed" \{
tr.style "height: 20px; " \{
td.onclick "javascript:loadFrame('mewframe$(iframe_id-1)', 'mewdiv$(iframe_id-1)', '$prev_post_link');" "&lt; Back";
td.onclick "javascript:loadFrame('mewframe$(iframe_id+1)', 'mewdiv$(iframe_id+1)', '$next_post_link');" "Forth &gt;";
}
tr td.colspan 2 $ iframe #.class "mewframe" #.id "mewframe$iframe_id" #.src "" #.scrolling "auto" #.frameborder 0 "mew!";
}
}
}
}
writeln("");
iframe_id ++;
}
{
auto first = cat([true], loop [false]);
string page_url;
int msg_id = -1, prev_msg_id = -1;
string name, date;
bool aborted;
alias limit = 64;
string sqltext = "
select page_urls.page_url, posts.msgid, name, date from posts
join thread_ids on posts.msgid = thread_ids.msgid
join page_urls on posts.msgid = page_urls.msgid
join (
select thread_ids.thread_id, thread_ids.msgid from thread_ids
left outer join tags on thread_ids.thread_id = tags.thread_id
where 1 $tagmask-sql $threadmask-sql
group by thread_ids.thread_id
) B on thread_ids.thread_id = B.thread_id
join date_codes on posts.msgid = date_codes.msgid
$textmask-sql $authormask-sql
order by date_codes.datecode asc";
for (int i, (string next_page_url, int next_msg_id, string next_name, string next_date)) <- zip(0..limit, db.exec!FOURSTRING!(string, int, string, string)(sqltext, tagmask, threadmask, authormask))
{
eval bool b <- first;
if !b {
writeRowFor (page_url, next_msg_id, msg_id, prev_msg_id, name, date, showSpoilers);
prev_msg_id = msg_id;
}
(page_url, msg_id, name, date) = (next_page_url.dup, next_msg_id, next_name.dup, next_date.dup);
if (i == limit - 1) aborted = true;
}
if (aborted) {
writeln "</table>";
writeln "<br />";
writeln "Rest omitted due to sanity checking. ";
} else {
writeRowFor (page_url, -1, msg_id, prev_msg_id, name, date, showSpoilers);
writeln "</table>";
}
// writeln ">> $sqltext";
}
}
return;
}
alias base_url = "http://demented.no-ip.org/~feep/threadfeed.cgi";
if (site == "rss") {
processCookie();
if auto id = getParam "id" {
bool spoilers = eval getParam ("spoilers");
writeHTMLHeader();
writeln "<head><title>Post view</title>";
writeln sharedCSS;
writeln sharedJS;
writeln "</head><body>";
onSuccess {
writeFooter();
writeln "</body></html>";
}
auto msgid = id.atoi;
eval int thread_id <- db.exec("select thread_id from thread_ids where msgid = ?", msgid);
string backlink = "|&nbsp;<a href=\"http://www.mspaforums.com/showthread.php?$thread_id&p=$msgid&viewfull=1#post$msgid\">MSPA forum link</a>";
writeSiteHeader(handleSpoilers => true, linkToOverview => true, linkToList => false, linkToFullView => false,
showRSS => false, floatIt => false, showFilters => false, additionalText => backlink);
for ubyte[] data <- db.exec("select content from content where msgid = ?", msgid)
{
string idText = urlWithParam("id", "");
string content = reformatPost (string: inflate data, spoilers, idText, linkToAnchors => false, noNyud => noNyud);
writeln "$content";
}
return;
}
string sqlcode = "
select posts.msgid, postnr, A.thread_id, name, date, content from
(
select thread_ids.thread_id, msgid from thread_ids
left outer join tags on thread_ids.thread_id = tags.thread_id
where 1 $tagmask-sql $threadmask-sql
group by thread_ids.thread_id
) A
join thread_ids on A.thread_id = thread_ids.thread_id
join posts on posts.msgid = thread_ids.msgid
join postnums C on C.msgid = thread_ids.msgid
join content D on D.msgid = thread_ids.msgid
join date_codes on date_codes.msgid = thread_ids.msgid
$textmask-sql $authormask-sql $idmask-sql
order by date_codes.datecode asc";
alias Tup = (int, int, int, string, string, string);
auto qit = db.exec!FIVESTRING!Tup(sqlcode, tagmask, threadmask, authormask, idmask);
int lastMsgId;
for (int msgId, int postId, int threadId, string name, string date, string content) <- qit {
lastMsgId = msgId;
}
writeln "Content-type: application/rss+xml";
if (lastMsgId != 0)
writeln "Set-Cookie: idmask=from_$lastMsgId";
writeln "";
writeln "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>";
writeln "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">";
onSuccess writeln "</rss>";
writeln "<channel>";
onSuccess writeln "</channel>";
writeln "<title>Thread Feed</title>";
writeln "<link>$base_url</link>";
writeln "<description>RSS feed for MSPA and tgchan forums</description>";
// writeln "<atom:link href=\"$base_url?$query_string\" rel=\"self\" type=\"application/rss+xml\" />";
qit = db.exec!FIVESTRING!Tup(sqlcode, tagmask, threadmask, authormask, idmask);
for (int msgId, int postId, int threadId, string name, string date, string content) <- qit
{
// parse the date
date.cleanupDate();
writeln "<item>";
onSuccess writeln "</item>";
writeln "<title>$name</title>";
writeln "<pubDate>$date</pubDate>";
writeln "<guid isPermaLink=\"false\">mspa_forum_post_id_on_feeps_rss_$msgId</guid>";
writeln "<link>$base_url$(urlWithParam(\"id\", \"\\$msgId\"))</link>"; // .replace("&amp;", "&"); /* not necessary for rss */
// writeln "<link>$base_url?id=$msgId</link>";
}
return;
}
if (site == "full-html") {
string sqlcode = "
select content, name, date, posts.msgid, thread_ids.thread_id from
(
select thread_ids.thread_id, msgid from thread_ids
left outer join tags on thread_ids.thread_id = tags.thread_id
where 1 $tagmask-sql $threadmask-sql
group by thread_ids.thread_id
) A
join thread_ids on A.thread_id = thread_ids.thread_id
join posts on thread_ids.msgid = posts.msgid
join content B on posts.msgid = B.msgid
join date_codes on posts.msgid = date_codes.msgid
$textmask-sql $authormask-sql $idmask-sql
order by date_codes.datecode asc";
auto qit = db.exec!FIVESTRING!(ubyte[], string, string, int, int)(sqlcode, tagmask, threadmask, authormask, idmask);
if (getParam("funtimemode") == "yes") {
writeln "Content-type: text/html";
writeln "";
for (ubyte[] data, string name, string date, int postId, int threadId) <- qit {
auto text = string: inflate data #.dup;
// writeln "[$name]";
/*char[auto~] newtext;
while (text.length) {
void flush(int to) { newtext ~= text[0 .. to]; }
auto bpos = text.find("<b>");
auto bcpos = text.find("</b>");
if (bpos != -1 && bpos < bcpos) {
text = text[bpos + 3 .. $];
}
else if (bcpos != -1 && bcpos < bpos) {
flush bcpos;
text = text[bcpos + 4 .. $];
} else text = new char[] 0;
}
text = newtext[];*/
writeln string:text;
}
return;
}
writeHTMLHeader();
writeln "<head><title>Full HTML View</title>";
writeln sharedCSS;
writeln sharedJS;
writeln "</head><body>";
// writeln ">> $sqlcode";
onSuccess {
writeFooter();
writeln "</body></html>";
}
writeSiteHeader(handleSpoilers => true, linkToOverview => true, linkToList => true, linkToFullView => false, floatIt => false);
if (!authormask && !tagmask && !threadmask) {
writeln "Uh-oh! You loaded the Full-HTML view but no author, thread or tag mask is set! <br />
This means that I should by all rights dump my entire post database at your feet now. <br />
Since I'm a nice person and don't want to crash your system, I'm not going to do that. <br />
You should go back. There is nothing for you here. ";
return;
}
void loopBody(ubyte[] data, string name, date, int postId, threadId) {
string idText = urlWithParam("id", "");
string content = reformatPost (string: inflate data #.dup, showSpoilers, idText, linkToAnchors => true, noNyud => noNyud);
a.name "post$postId" "";
div.class "box_outer" \{
string backlink = "http://www.mspaforums.com/showthread.php?$threadId&amp;p=$postId&amp;viewfull=1#post$postId";
div.class "box_inner" \{
b "Name: ";
text name;
text "&nbsp;&nbsp;";
b "Date: ";
text date;
text "&nbsp;&nbsp;";
b \{ if (postId >= 0) a.href backlink "MSPA link"; } // otherwise non-mspa
a #.style "text-decoration: none; color: #cdc; " #.href urlWithParam("idmask", "from_$postId") "▼";
}
text content;
}
}
if (count == -1) {
for (ubyte[] data, string name, string date, int postId, int threadId) <- qit {
loopBody(data, name, date, postId, threadId);
}
} else {
int lastPostId = 0;
for (int k, (ubyte[] data, string name, string date, int postId, int threadId)) <- zip(0..count + 1, qit) {
if (k != count) loopBody(data, name, date, postId, threadId);
else lastPostId = postId;
}
if (lastPostId != 0) {
int left = 1;
int[auto~] restIds;
for (ubyte[] data, string name, string date, int postId, int threadId) <- qit { restIds ~= postId; }
left += restIds.length;
string extralink;
a .href urlWithParam("idmask", "from_$lastPostId") "Next";
if (restIds.length) {
int lastpage;
if (count < restIds.length) lastpage = restIds[$-count+1];
else lastpage = restIds[0];
text "($left posts left)";
a.href urlWithParam("idmask", "from_$lastpage") "Last Page";
}
br;
}
}
return;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment