Various updates
+ Added tags to stories + Added a view counter to stories + Added a very basic search by tag function ~ Modified imageboard parser so that blank lines are preserved ~ Raised the minimum cache time from 2 seconds to 20 seconds
This commit is contained in:
parent
55c56969ea
commit
e6b1b7a8a1
|
@ -7,8 +7,15 @@ body{
|
||||||
padding:0 10px
|
padding:0 10px
|
||||||
}
|
}
|
||||||
h1,h2,h3{line-height:1.2}
|
h1,h2,h3{line-height:1.2}
|
||||||
p{margin-bottom:0px}
|
p,.tag-list{margin-bottom:0px}
|
||||||
.spoiler,.spoiler2{background:#444}
|
.spoiler,.spoiler2{background:#444}
|
||||||
.spoiler:hover,.spoiler2:hover{color:#FFF}
|
.spoiler:hover,.spoiler2:hover{color:#FFF}
|
||||||
.greentext{color:#282}
|
.greentext{color:#282}
|
||||||
.pinktext{color:#928}
|
.pinktext{color:#928}
|
||||||
|
.tag-list{list-style-type:none}
|
||||||
|
.tag{
|
||||||
|
line-height:1.5em;
|
||||||
|
height:1.5em;
|
||||||
|
padding: 0 1em 0 1em;
|
||||||
|
margin: 0 1px 0 1px;
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ domain * {
|
||||||
route /_claim claim
|
route /_claim claim
|
||||||
route /_download download
|
route /_download download
|
||||||
route /_preview preview
|
route /_preview preview
|
||||||
|
route /_search search
|
||||||
# Leading ^ is needed for dynamic routes, kore says the route is dynamic if it does not start with '/'
|
# Leading ^ is needed for dynamic routes, kore says the route is dynamic if it does not start with '/'
|
||||||
route ^/[^_].* read_story
|
route ^/[^_].* read_story
|
||||||
|
|
||||||
|
@ -58,18 +59,24 @@ domain * {
|
||||||
validate text v_any
|
validate text v_any
|
||||||
validate pasteas v_subdomain
|
validate pasteas v_subdomain
|
||||||
validate markup v_markup
|
validate markup v_markup
|
||||||
|
validate tags v_any
|
||||||
}
|
}
|
||||||
params post /_paste {
|
params post /_paste {
|
||||||
validate title v_any
|
validate title v_any
|
||||||
validate text v_any
|
validate text v_any
|
||||||
validate pasteas v_subdomain
|
validate pasteas v_subdomain
|
||||||
validate markup v_markup
|
validate markup v_markup
|
||||||
|
validate tags v_any
|
||||||
}
|
}
|
||||||
params post /_preview {
|
params post /_preview {
|
||||||
validate title v_any
|
validate title v_any
|
||||||
validate text v_any
|
validate text v_any
|
||||||
validate pasteas v_subdomain
|
validate pasteas v_subdomain
|
||||||
validate markup v_markup
|
validate markup v_markup
|
||||||
|
validate tags v_any
|
||||||
|
}
|
||||||
|
params get /_search {
|
||||||
|
validate tag v_any
|
||||||
}
|
}
|
||||||
params get ^/[^_].* {
|
params get ^/[^_].* {
|
||||||
validate comments v_bool
|
validate comments v_bool
|
||||||
|
|
166
src/lua/init.lua
166
src/lua/init.lua
|
@ -27,6 +27,7 @@ local pagenames = {
|
||||||
"login",
|
"login",
|
||||||
"author_paste",
|
"author_paste",
|
||||||
"author_edit",
|
"author_edit",
|
||||||
|
"search",
|
||||||
}
|
}
|
||||||
local pages = {}
|
local pages = {}
|
||||||
for k,v in pairs(pagenames) do
|
for k,v in pairs(pagenames) do
|
||||||
|
@ -49,11 +50,14 @@ setmetatable(queries,{
|
||||||
|
|
||||||
---sql queries
|
---sql queries
|
||||||
local stmnt_index, stmnt_author_index, stmnt_read, stmnt_paste, stmnt_raw
|
local stmnt_index, stmnt_author_index, stmnt_read, stmnt_paste, stmnt_raw
|
||||||
|
local stmnt_update_views
|
||||||
|
local stmnt_ins_tag, stmnt_drop_tags, stmnt_get_tags
|
||||||
local stmnt_author_create, stmnt_author_acct, stmnt_author_bio
|
local stmnt_author_create, stmnt_author_acct, stmnt_author_bio
|
||||||
local stmnt_cache, stmnt_insert_cache, stmnt_dirty_cache
|
local stmnt_cache, stmnt_insert_cache, stmnt_dirty_cache
|
||||||
local stmnt_get_session, stmnt_insert_session
|
local stmnt_get_session, stmnt_insert_session
|
||||||
local stmnt_edit, stmnt_update, stmnt_update_raw, stmnt_author_of
|
local stmnt_edit, stmnt_update, stmnt_update_raw, stmnt_author_of
|
||||||
local stmnt_comments, stmnt_comment_insert
|
local stmnt_comments, stmnt_comment_insert
|
||||||
|
local stmnt_search
|
||||||
--see https://perishablepress.com/stop-using-unsafe-characters-in-urls/
|
--see https://perishablepress.com/stop-using-unsafe-characters-in-urls/
|
||||||
--no underscore because we use that for our operative pages
|
--no underscore because we use that for our operative pages
|
||||||
local url_characters =
|
local url_characters =
|
||||||
|
@ -92,6 +96,7 @@ local function sqlbind(stmnt,call,position,data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
print("Hello from init.lua")
|
print("Hello from init.lua")
|
||||||
function configure()
|
function configure()
|
||||||
db = sqlassert(sql.open("data/posts.db"))
|
db = sqlassert(sql.open("data/posts.db"))
|
||||||
|
@ -147,7 +152,11 @@ function configure()
|
||||||
--Select the data we need to read a story (and maybe display an edit
|
--Select the data we need to read a story (and maybe display an edit
|
||||||
--button
|
--button
|
||||||
stmnt_read = assert(db:prepare(queries.select_post))
|
stmnt_read = assert(db:prepare(queries.select_post))
|
||||||
|
--Update the view counter when someone reads a story
|
||||||
|
stmnt_update_views = assert(db:prepare(queries.update_views))
|
||||||
|
--Retreive comments on a story
|
||||||
stmnt_comments = assert(db:prepare(queries.select_comments))
|
stmnt_comments = assert(db:prepare(queries.select_comments))
|
||||||
|
--Add a new comment to a story
|
||||||
stmnt_comment_insert = assert(db:prepare(queries.insert_comment))
|
stmnt_comment_insert = assert(db:prepare(queries.insert_comment))
|
||||||
--TODO: actually let authors edit their bio
|
--TODO: actually let authors edit their bio
|
||||||
stmnt_author_bio = assert(db:prepare([[
|
stmnt_author_bio = assert(db:prepare([[
|
||||||
|
@ -173,6 +182,10 @@ function configure()
|
||||||
--Keep a copy of the plain text of a post so we can edit it later
|
--Keep a copy of the plain text of a post so we can edit it later
|
||||||
--It might also be useful for migrations, if that ever needs to happen
|
--It might also be useful for migrations, if that ever needs to happen
|
||||||
stmnt_raw = assert(db:prepare(queries.insert_raw))
|
stmnt_raw = assert(db:prepare(queries.insert_raw))
|
||||||
|
--Tags for a story
|
||||||
|
stmnt_ins_tag = assert(db:prepare(queries.insert_tag))
|
||||||
|
stmnt_get_tags = assert(db:prepare(queries.select_tags))
|
||||||
|
stmnt_drop_tags = assert(db:prepare(queries.delete_tags))
|
||||||
--Get the data we need to display the edit screen
|
--Get the data we need to display the edit screen
|
||||||
stmnt_edit = assert(db:prepare(queries.select_edit))
|
stmnt_edit = assert(db:prepare(queries.select_edit))
|
||||||
--Get the data we need when someone wants to download a paste
|
--Get the data we need when someone wants to download a paste
|
||||||
|
@ -183,15 +196,18 @@ function configure()
|
||||||
--Someone could keep their story on the front page by just editing it a lot.
|
--Someone could keep their story on the front page by just editing it a lot.
|
||||||
--If it gets abused I can disable it I guess.
|
--If it gets abused I can disable it I guess.
|
||||||
stmnt_update = assert(db:prepare(queries.update_post))
|
stmnt_update = assert(db:prepare(queries.update_post))
|
||||||
|
--Check sessions for login support
|
||||||
stmnt_insert_session = assert(db:prepare(queries.insert_session))
|
stmnt_insert_session = assert(db:prepare(queries.insert_session))
|
||||||
stmnt_get_session = assert(db:prepare(queries.select_valid_sessions))
|
stmnt_get_session = assert(db:prepare(queries.select_valid_sessions))
|
||||||
|
--Search by tag name
|
||||||
|
stmnt_search = assert(db:prepare(queries.select_post_tags))
|
||||||
--only refresh pages at most once every 10 seconds
|
--only refresh pages at most once every 10 seconds
|
||||||
stmnt_cache = cache:prepare([[
|
stmnt_cache = cache:prepare([[
|
||||||
SELECT data
|
SELECT data
|
||||||
FROM cache
|
FROM cache
|
||||||
WHERE
|
WHERE
|
||||||
path = :path AND
|
path = :path AND
|
||||||
((dirty = 0) OR (strftime('%s','now') - updated) < 2)
|
((dirty = 0) OR (strftime('%s','now') - updated) < 20)
|
||||||
;
|
;
|
||||||
]])
|
]])
|
||||||
stmnt_insert_cache = cache:prepare([[
|
stmnt_insert_cache = cache:prepare([[
|
||||||
|
@ -258,6 +274,27 @@ local function do_sql(stmnt)
|
||||||
return err
|
return err
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function get_tags(id)
|
||||||
|
local ret = {}
|
||||||
|
stmnt_get_tags:bind_names{
|
||||||
|
id = id
|
||||||
|
}
|
||||||
|
local err
|
||||||
|
repeat
|
||||||
|
err = stmnt_get_tags:step()
|
||||||
|
if err == sql.BUSY then
|
||||||
|
coroutine.yield()
|
||||||
|
elseif err == sql.ROW then
|
||||||
|
table.insert(ret,stmnt_get_tags:get_value(0))
|
||||||
|
elseif err == sql.DONE then
|
||||||
|
stmnt_get_tags:reset()
|
||||||
|
return ret
|
||||||
|
else
|
||||||
|
error(string.format("Failed to get tags for story %d : %d", id, err))
|
||||||
|
end
|
||||||
|
until false
|
||||||
|
end
|
||||||
|
|
||||||
local function dirty_cache(url)
|
local function dirty_cache(url)
|
||||||
print("Dirtying cache:",url)
|
print("Dirtying cache:",url)
|
||||||
stmnt_dirty_cache:bind_names{
|
stmnt_dirty_cache:bind_names{
|
||||||
|
@ -351,6 +388,22 @@ local function render(pagename,callback)
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[Parses a semicolon seperated string into it's parts, trims whitespace, lowercases, and capitalizes the first letter. Tags will not be empty. Returns an array of tags]]
|
||||||
|
local function parse_tags(str)
|
||||||
|
local tags = {}
|
||||||
|
for tag in string.gmatch(str,"([^;]+)") do
|
||||||
|
assert(tag, "Found a nil or false tag in:" .. str)
|
||||||
|
local tag_trimmed = string.match(tag,"%s*(.*)%s*")
|
||||||
|
local tag_lower = string.lower(tag_trimmed)
|
||||||
|
local tag_capitalized = string.gsub(tag_lower,"^%w",string.upper)
|
||||||
|
assert(tag_capitalized, "After processing tag:" .. tag .. " it was falsey.")
|
||||||
|
if string.len(tag_capitalized) > 0 then
|
||||||
|
table.insert(tags, tag_capitalized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tags
|
||||||
|
end
|
||||||
|
|
||||||
function home(req)
|
function home(req)
|
||||||
print("Hello from lua!")
|
print("Hello from lua!")
|
||||||
print("Method:", http_method_text(req))
|
print("Method:", http_method_text(req))
|
||||||
|
@ -368,12 +421,14 @@ function home(req)
|
||||||
--err may be sql.ROW or sql.DONE if we don't have any stories yet
|
--err may be sql.ROW or sql.DONE if we don't have any stories yet
|
||||||
while err == sql.ROW do
|
while err == sql.ROW do
|
||||||
local data = stmnt_index:get_values()
|
local data = stmnt_index:get_values()
|
||||||
|
local tags = get_tags(data[1])
|
||||||
table.insert(latest,{
|
table.insert(latest,{
|
||||||
url = encode_id(data[1]),
|
url = encode_id(data[1]),
|
||||||
title = data[2],
|
title = data[2],
|
||||||
isanon = data[3] == 1,
|
isanon = data[3] == 1,
|
||||||
posted = os.date("%B %d %Y",tonumber(data[4])),
|
posted = os.date("%B %d %Y",tonumber(data[4])),
|
||||||
author = data[5],
|
author = data[5],
|
||||||
|
tags = tags,
|
||||||
})
|
})
|
||||||
err = stmnt_index:step()
|
err = stmnt_index:step()
|
||||||
end
|
end
|
||||||
|
@ -410,10 +465,12 @@ function home(req)
|
||||||
while err == sql.ROW do
|
while err == sql.ROW do
|
||||||
local data = stmnt_author:get_values()
|
local data = stmnt_author:get_values()
|
||||||
local id, title, time = unpack(data)
|
local id, title, time = unpack(data)
|
||||||
|
local tags = get_tags(id)
|
||||||
table.insert(stories,{
|
table.insert(stories,{
|
||||||
url = encode_id(id),
|
url = encode_id(id),
|
||||||
title = title,
|
title = title,
|
||||||
posted = os.date("%B %d %Y",tonumber(time))
|
posted = os.date("%B %d %Y",tonumber(time)),
|
||||||
|
tags = tags,
|
||||||
})
|
})
|
||||||
err = stmnt_author:step()
|
err = stmnt_author:step()
|
||||||
end
|
end
|
||||||
|
@ -556,6 +613,8 @@ function paste(req)
|
||||||
local title = assert(http_argument_get_string(req,"title"))
|
local title = assert(http_argument_get_string(req,"title"))
|
||||||
local text = assert(http_argument_get_string(req,"text"))
|
local text = assert(http_argument_get_string(req,"text"))
|
||||||
local markup = assert(http_argument_get_string(req,"markup"))
|
local markup = assert(http_argument_get_string(req,"markup"))
|
||||||
|
local tag_str = assert(http_argument_get_string(req,"tags"))
|
||||||
|
local tags = parse_tags(tag_str)
|
||||||
local pasteas
|
local pasteas
|
||||||
local raw = zlib.compress(text)
|
local raw = zlib.compress(text)
|
||||||
text = string.gsub(text,"%%(%x%x)",decodeentities)
|
text = string.gsub(text,"%%(%x%x)",decodeentities)
|
||||||
|
@ -591,20 +650,28 @@ function paste(req)
|
||||||
sqlbind(stmnt_paste,"bind_blob",5,"")
|
sqlbind(stmnt_paste,"bind_blob",5,"")
|
||||||
--assert(stmnt_paste:bind_blob(5,"") == sql.OK)
|
--assert(stmnt_paste:bind_blob(5,"") == sql.OK)
|
||||||
err = do_sql(stmnt_paste)
|
err = do_sql(stmnt_paste)
|
||||||
|
stmnt_paste:reset()
|
||||||
if err == sql.DONE then
|
if err == sql.DONE then
|
||||||
local rowid = stmnt_paste:last_insert_rowid()
|
local rowid = stmnt_paste:last_insert_rowid()
|
||||||
assert(stmnt_raw:bind(1,rowid) == sql.OK)
|
assert(stmnt_raw:bind(1,rowid) == sql.OK)
|
||||||
assert(stmnt_raw:bind_blob(2,raw) == sql.OK)
|
assert(stmnt_raw:bind_blob(2,raw) == sql.OK)
|
||||||
|
assert(stmnt_raw:bind(3,markup) == sql.OK)
|
||||||
err = do_sql(stmnt_raw)
|
err = do_sql(stmnt_raw)
|
||||||
|
stmnt_raw:reset()
|
||||||
if err ~= sql.DONE then
|
if err ~= sql.DONE then
|
||||||
print("Failed to save raw text, but paste still went though")
|
print("Failed to save raw text, but paste still went though")
|
||||||
end
|
end
|
||||||
|
for _,tag in pairs(tags) do
|
||||||
|
print("tag 1:",stmnt_ins_tag:bind(1,rowid))
|
||||||
|
print("Looking at tag",tag)
|
||||||
|
print("tag 2:",stmnt_ins_tag:bind(2,tag))
|
||||||
|
err = do_sql(stmnt_ins_tag)
|
||||||
|
stmnt_ins_tag:reset()
|
||||||
|
end
|
||||||
local url = encode_id(rowid)
|
local url = encode_id(rowid)
|
||||||
local loc = string.format("https://%s/%s",domain,url)
|
local loc = string.format("https://%s/%s",domain,url)
|
||||||
http_response_header(req,"Location",loc)
|
http_response_header(req,"Location",loc)
|
||||||
http_response(req,303,"")
|
http_response(req,303,"")
|
||||||
stmnt_paste:reset()
|
|
||||||
stmnt_raw:reset()
|
|
||||||
dirty_cache(string.format("%s/%s",domain,url))
|
dirty_cache(string.format("%s/%s",domain,url))
|
||||||
dirty_cache(string.format("%s",domain))
|
dirty_cache(string.format("%s",domain))
|
||||||
return
|
return
|
||||||
|
@ -640,12 +707,21 @@ function paste(req)
|
||||||
end
|
end
|
||||||
assert(stmnt_paste:bind_blob(5,"") == sql.OK)
|
assert(stmnt_paste:bind_blob(5,"") == sql.OK)
|
||||||
err = do_sql(stmnt_paste)
|
err = do_sql(stmnt_paste)
|
||||||
|
stmnt_paste:reset()
|
||||||
if err == sql.DONE then
|
if err == sql.DONE then
|
||||||
local rowid = stmnt_paste:last_insert_rowid()
|
local rowid = stmnt_paste:last_insert_rowid()
|
||||||
assert(stmnt_raw:bind(1,rowid) == sql.OK)
|
assert(stmnt_raw:bind(1,rowid) == sql.OK)
|
||||||
assert(stmnt_raw:bind_blob(2,raw) == sql.OK)
|
assert(stmnt_raw:bind_blob(2,raw) == sql.OK)
|
||||||
assert(stmnt_raw:bind(3,markup) == sql.OK)
|
assert(stmnt_raw:bind(3,markup) == sql.OK)
|
||||||
err = do_sql(stmnt_raw)
|
err = do_sql(stmnt_raw)
|
||||||
|
stmnt_raw:reset()
|
||||||
|
for _,tag in pairs(tags) do
|
||||||
|
print("tag 1:",stmnt_ins_tag:bind(1,rowid))
|
||||||
|
print("Looking at tag",tag)
|
||||||
|
print("tag 2:",stmnt_ins_tag:bind(2,tag))
|
||||||
|
err = do_sql(stmnt_ins_tag)
|
||||||
|
stmnt_ins_tag:reset()
|
||||||
|
end
|
||||||
if err ~= sql.DONE then
|
if err ~= sql.DONE then
|
||||||
print("Failed to save raw text, but paste still went through")
|
print("Failed to save raw text, but paste still went through")
|
||||||
end
|
end
|
||||||
|
@ -658,8 +734,6 @@ function paste(req)
|
||||||
end
|
end
|
||||||
http_response_header(req,"Location",loc)
|
http_response_header(req,"Location",loc)
|
||||||
http_response(req,303,"")
|
http_response(req,303,"")
|
||||||
stmnt_paste:reset()
|
|
||||||
stmnt_raw:reset()
|
|
||||||
dirty_cache(string.format("%s.%s",author,domain))
|
dirty_cache(string.format("%s.%s",author,domain))
|
||||||
dirty_cache(string.format("%s/%s",domain,url))
|
dirty_cache(string.format("%s/%s",domain,url))
|
||||||
dirty_cache(string.format("%s",domain))
|
dirty_cache(string.format("%s",domain))
|
||||||
|
@ -684,9 +758,15 @@ local function read_story(host,path,idp,show_comments,iam)
|
||||||
else
|
else
|
||||||
cachestr = string.format("%s%s",host,path)
|
cachestr = string.format("%s%s",host,path)
|
||||||
end
|
end
|
||||||
|
local id = decode_id(idp)
|
||||||
|
stmnt_update_views:bind_names{
|
||||||
|
id = id
|
||||||
|
}
|
||||||
|
print("update:",do_sql(stmnt_update_views))
|
||||||
|
stmnt_update_views:reset()
|
||||||
|
dirty_cache(cachestr)
|
||||||
print("cachestr was:",cachestr)
|
print("cachestr was:",cachestr)
|
||||||
local readstoryf = function()
|
local readstoryf = function()
|
||||||
local id = decode_id(idp)
|
|
||||||
stmnt_read:bind_names{
|
stmnt_read:bind_names{
|
||||||
id = id
|
id = id
|
||||||
}
|
}
|
||||||
|
@ -697,8 +777,9 @@ local function read_story(host,path,idp,show_comments,iam)
|
||||||
path = path
|
path = path
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
local tags = get_tags(id)
|
||||||
assert(err == sql.ROW,"Could not get row:" .. tostring(id) .. " Error:" .. tostring(err))
|
assert(err == sql.ROW,"Could not get row:" .. tostring(id) .. " Error:" .. tostring(err))
|
||||||
local title, text, authorid, isanon, authorname = unpack(stmnt_read:get_values())
|
local title, text, authorid, isanon, authorname, views = unpack(stmnt_read:get_values())
|
||||||
stmnt_comments:bind_names{
|
stmnt_comments:bind_names{
|
||||||
id = id
|
id = id
|
||||||
}
|
}
|
||||||
|
@ -726,6 +807,8 @@ local function read_story(host,path,idp,show_comments,iam)
|
||||||
comments = comments,
|
comments = comments,
|
||||||
show_comments = show_comments,
|
show_comments = show_comments,
|
||||||
iam = iam,
|
iam = iam,
|
||||||
|
tags = tags,
|
||||||
|
views = views,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
--Don't cache if we're logged in, someone might see dirty cache information on the page.
|
--Don't cache if we're logged in, someone might see dirty cache information on the page.
|
||||||
|
@ -758,6 +841,7 @@ function read(req)
|
||||||
id = id
|
id = id
|
||||||
}
|
}
|
||||||
local err = do_sql(stmnt_read)
|
local err = do_sql(stmnt_read)
|
||||||
|
local tags = get_tags(id)
|
||||||
if err == sql.DONE then
|
if err == sql.DONE then
|
||||||
--We got no story
|
--We got no story
|
||||||
stmnt_read:reset()
|
stmnt_read:reset()
|
||||||
|
@ -768,7 +852,7 @@ function read(req)
|
||||||
--If we can edit this story, we don't want to cache
|
--If we can edit this story, we don't want to cache
|
||||||
--the page, since it'll have an edit button on it.
|
--the page, since it'll have an edit button on it.
|
||||||
assert(err == sql.ROW)
|
assert(err == sql.ROW)
|
||||||
local title, storytext, tauthor, isanon, authorname = unpack(stmnt_read:get_values())
|
local title, storytext, tauthor, isanon, authorname, views = unpack(stmnt_read:get_values())
|
||||||
storytext = zlib.decompress(storytext)
|
storytext = zlib.decompress(storytext)
|
||||||
stmnt_read:reset()
|
stmnt_read:reset()
|
||||||
if tauthor == authorid then
|
if tauthor == authorid then
|
||||||
|
@ -782,7 +866,9 @@ function read(req)
|
||||||
isanon = isanon == 1,
|
isanon = isanon == 1,
|
||||||
author = authorname,
|
author = authorname,
|
||||||
iam = authorname,
|
iam = authorname,
|
||||||
owner = true
|
owner = true,
|
||||||
|
tags = tags,
|
||||||
|
views = views,
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -923,6 +1009,8 @@ function edit(req)
|
||||||
local data = stmnt_edit:get_values()
|
local data = stmnt_edit:get_values()
|
||||||
local txt_compressed, markup, isanon, title = unpack(data)
|
local txt_compressed, markup, isanon, title = unpack(data)
|
||||||
local text = zlib.decompress(txt_compressed)
|
local text = zlib.decompress(txt_compressed)
|
||||||
|
local tags = get_tags(story_id)
|
||||||
|
local tags_txt = table.concat(tags,";")
|
||||||
stmnt_edit:reset()
|
stmnt_edit:reset()
|
||||||
ret = pages.edit{
|
ret = pages.edit{
|
||||||
title = title,
|
title = title,
|
||||||
|
@ -933,6 +1021,7 @@ function edit(req)
|
||||||
domain = domain,
|
domain = domain,
|
||||||
story = story_id,
|
story = story_id,
|
||||||
err = "",
|
err = "",
|
||||||
|
tags = tags_txt
|
||||||
}
|
}
|
||||||
elseif method == "POST" then
|
elseif method == "POST" then
|
||||||
http_request_populate_post(req)
|
http_request_populate_post(req)
|
||||||
|
@ -941,6 +1030,7 @@ function edit(req)
|
||||||
local text = assert(http_argument_get_string(req,"text"))
|
local text = assert(http_argument_get_string(req,"text"))
|
||||||
local pasteas = assert(http_argument_get_string(req,"pasteas"))
|
local pasteas = assert(http_argument_get_string(req,"pasteas"))
|
||||||
local markup = assert(http_argument_get_string(req,"markup"))
|
local markup = assert(http_argument_get_string(req,"markup"))
|
||||||
|
local tags_str = assert(http_argument_get_string(req,"tags"))
|
||||||
stmnt_author_of:bind_names{
|
stmnt_author_of:bind_names{
|
||||||
id = storyid
|
id = storyid
|
||||||
}
|
}
|
||||||
|
@ -956,6 +1046,7 @@ function edit(req)
|
||||||
local parsed = parsers[markup](text)
|
local parsed = parsers[markup](text)
|
||||||
local compr_raw = zlib.compress(text)
|
local compr_raw = zlib.compress(text)
|
||||||
local compr = zlib.compress(parsed)
|
local compr = zlib.compress(parsed)
|
||||||
|
local tags = parse_tags(tags_str)
|
||||||
assert(stmnt_update_raw:bind_blob(1,compr_raw) == sql.OK)
|
assert(stmnt_update_raw:bind_blob(1,compr_raw) == sql.OK)
|
||||||
assert(stmnt_update_raw:bind(2,markup) == sql.OK)
|
assert(stmnt_update_raw:bind(2,markup) == sql.OK)
|
||||||
assert(stmnt_update_raw:bind(3,storyid) == sql.OK)
|
assert(stmnt_update_raw:bind(3,storyid) == sql.OK)
|
||||||
|
@ -967,6 +1058,17 @@ function edit(req)
|
||||||
assert(stmnt_update:bind(4,storyid) == sql.OK)
|
assert(stmnt_update:bind(4,storyid) == sql.OK)
|
||||||
assert(do_sql(stmnt_update) == sql.DONE, "Failed to update text")
|
assert(do_sql(stmnt_update) == sql.DONE, "Failed to update text")
|
||||||
stmnt_update:reset()
|
stmnt_update:reset()
|
||||||
|
assert(stmnt_drop_tags:bind_names{postid = storyid} == sql.OK)
|
||||||
|
do_sql(stmnt_drop_tags)
|
||||||
|
stmnt_drop_tags:reset()
|
||||||
|
|
||||||
|
for _,tag in pairs(tags) do
|
||||||
|
print("Looking at tag",tag)
|
||||||
|
assert(stmnt_ins_tag:bind(1,storyid) == sql.OK)
|
||||||
|
assert(stmnt_ins_tag:bind(2,tag) == sql.OK)
|
||||||
|
err = do_sql(stmnt_ins_tag)
|
||||||
|
stmnt_ins_tag:reset()
|
||||||
|
end
|
||||||
local id_enc = encode_id(storyid)
|
local id_enc = encode_id(storyid)
|
||||||
local loc = string.format("https://%s/%s",domain,id_enc)
|
local loc = string.format("https://%s/%s",domain,id_enc)
|
||||||
dirty_cache(string.format("%s/%s",domain,id_enc)) -- This place to read this post
|
dirty_cache(string.format("%s/%s",domain,id_enc)) -- This place to read this post
|
||||||
|
@ -1030,6 +1132,8 @@ function preview(req)
|
||||||
local title = assert(http_argument_get_string(req,"title"))
|
local title = assert(http_argument_get_string(req,"title"))
|
||||||
local text = assert(http_argument_get_string(req,"text"))
|
local text = assert(http_argument_get_string(req,"text"))
|
||||||
local markup = assert(http_argument_get_string(req,"markup"))
|
local markup = assert(http_argument_get_string(req,"markup"))
|
||||||
|
local tag_str = assert(http_argument_get_string(req,"tags"))
|
||||||
|
local tags = parse_tags(tag_str)
|
||||||
print("title:",title,"text:",text,"markup:",markup)
|
print("title:",title,"text:",text,"markup:",markup)
|
||||||
local parsed = parsers[markup](text)
|
local parsed = parsers[markup](text)
|
||||||
local ret = pages.read{
|
local ret = pages.read{
|
||||||
|
@ -1038,8 +1142,50 @@ function preview(req)
|
||||||
author = "preview",
|
author = "preview",
|
||||||
idp = "preview",
|
idp = "preview",
|
||||||
text = parsed,
|
text = parsed,
|
||||||
|
tags = tags,
|
||||||
}
|
}
|
||||||
http_response(req,200,ret)
|
http_response(req,200,ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function search(req)
|
||||||
|
local host = http_request_get_host(req)
|
||||||
|
local path = http_request_get_path(req)
|
||||||
|
http_request_populate_qs(req)
|
||||||
|
local tag = http_argument_get_string(req,"tag")
|
||||||
|
if tag then
|
||||||
|
stmnt_search:bind_names{
|
||||||
|
tag = tag
|
||||||
|
}
|
||||||
|
local results = {}
|
||||||
|
local err
|
||||||
|
repeat
|
||||||
|
err = stmnt_search:step()
|
||||||
|
if err == sql.BUSY then
|
||||||
|
coroutine.yield()
|
||||||
|
elseif err == sql.ROW then
|
||||||
|
local id, title, anon, time, author = unpack(stmnt_search:get_values())
|
||||||
|
local idp = encode_id(id)
|
||||||
|
local tags = get_tags(id)
|
||||||
|
table.insert(results,{
|
||||||
|
id = idp,
|
||||||
|
title = title,
|
||||||
|
anon = anon,
|
||||||
|
time = os.date("%B %d %Y",tonumber(time)),
|
||||||
|
author = author,
|
||||||
|
tags = tags
|
||||||
|
})
|
||||||
|
elseif err == sql.DONE then
|
||||||
|
else
|
||||||
|
error("Failed to search, sql error:" .. tostring(err))
|
||||||
|
end
|
||||||
|
until err == sql.DONE
|
||||||
|
local ret = pages.search{
|
||||||
|
domain = domain,
|
||||||
|
results = results,
|
||||||
|
tag = tag,
|
||||||
|
}
|
||||||
|
http_response(req,200,ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
print("Done with init.lua")
|
print("Done with init.lua")
|
||||||
|
|
|
@ -70,8 +70,12 @@ local grammar = P{
|
||||||
marked = V"spoiler" + V"bold" + V"italic" + V"underline" + V"heading" + V"strike" + V"spoiler2" + V"code",
|
marked = V"spoiler" + V"bold" + V"italic" + V"underline" + V"heading" + V"strike" + V"spoiler2" + V"code",
|
||||||
plainline = (V"marked" + word)^0,
|
plainline = (V"marked" + word)^0,
|
||||||
line = Cs(V"greentext" + V"pinktext" + V"plainline" + P"") * P"\n" / function(a)
|
line = Cs(V"greentext" + V"pinktext" + V"plainline" + P"") * P"\n" / function(a)
|
||||||
print("matched line:",a)
|
print("matched line:","\"" .. a .. "\"")
|
||||||
return string.format("<p>%s",a)
|
if a == "\r" then
|
||||||
|
return "<br/>"
|
||||||
|
else
|
||||||
|
return string.format("<p>%s</p>",a)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
ending = C(P(1)^0) / function(a) print("failed with ending:", a) return sanitize(a) end,
|
ending = C(P(1)^0) / function(a) print("failed with ending:", a) return sanitize(a) end,
|
||||||
chunk = V"line"^0 * V"plainline" * V"ending"
|
chunk = V"line"^0 * V"plainline" * V"ending"
|
||||||
|
|
|
@ -29,6 +29,12 @@
|
||||||
</a>
|
</a>
|
||||||
</td><td>
|
</td><td>
|
||||||
By <a href="https://<%= author %>.<%= domain %>"><%= author %></a>
|
By <a href="https://<%= author %>.<%= domain %>"><%= author %></a>
|
||||||
|
</td><td>
|
||||||
|
<ul class="row tag-list">
|
||||||
|
<% for i = 1,math.min(#v.tags, 5) do %>
|
||||||
|
<li><a class="tag button button-outline" href="https://<%= domain %>/_search?tag=<%= v.tags[i] %>"><%= v.tags[i] %></a></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
</td><td>
|
</td><td>
|
||||||
<%= v.posted %>
|
<%= v.posted %>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
|
|
|
@ -26,11 +26,14 @@
|
||||||
<option value="imageboard">Imageboard</option>
|
<option value="imageboard">Imageboard</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<input type="text" name="tags" placeholder="Tags (semicolon;seperated)" class="column"></input>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<textarea name="text" cols=80 rows=24 class="column"><%= text %></textarea><br/>
|
<textarea name="text" cols=80 rows=24 class="column"><%= text %></textarea><br/>
|
||||||
</div>
|
</div>
|
||||||
<input type="submit">
|
<input type="submit">
|
||||||
|
<input type="submit" formtarget="_blank" value="Preview" formaction="https://<%= domain %>/_preview">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
|
|
|
@ -32,6 +32,9 @@
|
||||||
<option value="imageboard">Imageboard</option>
|
<option value="imageboard">Imageboard</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<input type="text" name="tags" value="<%= tags %>" placeholder="Tags (semicolon;seperated)" class="column"></input>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<textarea name="text" cols=80 rows=24 class="column"><%= text %></textarea><br/>
|
<textarea name="text" cols=80 rows=24 class="column"><%= text %></textarea><br/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -36,6 +36,12 @@
|
||||||
<% else %>
|
<% else %>
|
||||||
By <a href="https://<%= v.author %>.<%= domain %>"><%= v.author %></a>
|
By <a href="https://<%= v.author %>.<%= domain %>"><%= v.author %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</td><td>
|
||||||
|
<ul class="row tag-list">
|
||||||
|
<% for i = 1,math.min(#v.tags, 5) do %>
|
||||||
|
<li><a class="tag button button-outline" href="https://<%= domain %>/_search?tag=<%= v.tags[i] %>"><%= v.tags[i] %></a></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
</td><td>
|
</td><td>
|
||||||
<%= v.posted %>
|
<%= v.posted %>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<option value="imageboard">Imageboard</option>
|
<option value="imageboard">Imageboard</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<input type="text" name="tags" placeholder="Tags (semicolon;seperated)" class="column"></input>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<textarea name="text" cols=80 rows=24 class="column"></textarea><br/>
|
<textarea name="text" cols=80 rows=24 class="column"></textarea><br/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,6 +30,13 @@
|
||||||
</h3>
|
</h3>
|
||||||
<%- text %>
|
<%- text %>
|
||||||
</article>
|
</article>
|
||||||
|
<hr/>
|
||||||
|
<p><%= views %> Views</p>
|
||||||
|
<ul class="row tag-list">
|
||||||
|
<% for _,tag in pairs(tags) do -%>
|
||||||
|
<li><a class="tag button button-outline" href="https://<%= domain %>/_search?tag=<%= tag %>"><%= tag %></a></li>
|
||||||
|
<% end -%>
|
||||||
|
</ul>
|
||||||
<form action="https://<%= domain %>/_download" method="get">
|
<form action="https://<%= domain %>/_download" method="get">
|
||||||
<input type="hidden" name="story" value="<%= idp %>"/>
|
<input type="hidden" name="story" value="<%= idp %>"/>
|
||||||
<input type="submit" value="Download TXT" class="button"/>
|
<input type="submit" value="Download TXT" class="button"/>
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>🍑</title>
|
||||||
|
<link href="/_css/milligram.css" rel="stylesheet">
|
||||||
|
<link href="/_css/style.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="container">
|
||||||
|
<main class="wrapper">
|
||||||
|
<h1 class="title">
|
||||||
|
<a href="https://<%= domain %>"><%= domain %></a>/
|
||||||
|
</h1>
|
||||||
|
<div class="content">
|
||||||
|
<% if #results == 0 then %>
|
||||||
|
No stories matched your search.
|
||||||
|
<% else %>
|
||||||
|
<table>
|
||||||
|
<% for k,v in pairs(results) do %>
|
||||||
|
<tr><td>
|
||||||
|
<a href="<%= v.id %>">
|
||||||
|
<%- v.title %>
|
||||||
|
</a>
|
||||||
|
</td><td>
|
||||||
|
<% if v.isanon then %>
|
||||||
|
By Anonymous
|
||||||
|
<% else %>
|
||||||
|
By <a href="https://<%= v.author %>.<%= domain %>"><%= v.author %></a>
|
||||||
|
<% end %>
|
||||||
|
</td><td>
|
||||||
|
<ul class="row tag-list">
|
||||||
|
<% for i = 1,math.min(#v.tags, 5) do %>
|
||||||
|
<li><a class="tag button button-outline" href="https://<%= domain %>/_search?tag=<%= v.tags[i] %>"><%= v.tags[i] %></a></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</td><td>
|
||||||
|
<%= v.time %>
|
||||||
|
</td></tr>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
<body>
|
||||||
|
|
|
@ -22,6 +22,7 @@ int login(struct http_request *);
|
||||||
int claim(struct http_request *);
|
int claim(struct http_request *);
|
||||||
int download(struct http_request *);
|
int download(struct http_request *);
|
||||||
int preview(struct http_request *);
|
int preview(struct http_request *);
|
||||||
|
int search(struct http_request *);
|
||||||
int style(struct http_request *);
|
int style(struct http_request *);
|
||||||
int miligram(struct http_request *);
|
int miligram(struct http_request *);
|
||||||
int do_lua(struct http_request *req, const char *name);
|
int do_lua(struct http_request *req, const char *name);
|
||||||
|
@ -121,6 +122,12 @@ preview(struct http_request *req){
|
||||||
return do_lua(req,"preview");
|
return do_lua(req,"preview");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
search(struct http_request *req){
|
||||||
|
printf("We want to do search!\n");
|
||||||
|
return do_lua(req,"search");
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
home(struct http_request *req){
|
home(struct http_request *req){
|
||||||
return do_lua(req,"home");
|
return do_lua(req,"home");
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
CREATE INDEX tag_index ON tags(tag);
|
|
@ -5,5 +5,6 @@ CREATE TABLE IF NOT EXISTS posts (
|
||||||
authorid REFERENCES authors(id) ON DELETE CASCADE,
|
authorid REFERENCES authors(id) ON DELETE CASCADE,
|
||||||
isanon INTEGER,
|
isanon INTEGER,
|
||||||
hashedip BLOB,
|
hashedip BLOB,
|
||||||
post_time INTEGER
|
post_time INTEGER,
|
||||||
|
views INTEGER DEFAULT 0
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
DELETE FROM tags WHERE postid = :postid;
|
|
@ -0,0 +1,5 @@
|
||||||
|
INSERT INTO tags (
|
||||||
|
postid, tag
|
||||||
|
) VALUES (
|
||||||
|
?, ?
|
||||||
|
);
|
|
@ -3,7 +3,8 @@ SELECT
|
||||||
post_text,
|
post_text,
|
||||||
posts.authorid,
|
posts.authorid,
|
||||||
posts.isanon,
|
posts.isanon,
|
||||||
authors.name
|
authors.name,
|
||||||
|
views
|
||||||
FROM
|
FROM
|
||||||
posts,authors
|
posts,authors
|
||||||
WHERE
|
WHERE
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
SELECT
|
||||||
|
posts.id,
|
||||||
|
posts.post_title,
|
||||||
|
posts.isanon,
|
||||||
|
posts.post_time,
|
||||||
|
authors.name
|
||||||
|
FROM
|
||||||
|
posts,authors,tags
|
||||||
|
WHERE
|
||||||
|
posts.authorid = authors.id AND
|
||||||
|
posts.id = tags.postid AND
|
||||||
|
tags.tag = :tag;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
SELECT tag FROM tags WHERE tags.postid = :id;
|
|
@ -0,0 +1 @@
|
||||||
|
UPDATE posts SET views = views + 1 WHERE id = :id;
|
Loading…
Reference in New Issue