Allow users to edit biographies

This commit is contained in:
Robin Malley 2022-11-21 00:32:49 +00:00
parent e25d2fd06a
commit 411bcb494d
8 changed files with 56 additions and 19 deletions

View File

@ -7,7 +7,25 @@ concerns with pastebin.com taking down certain kinds of content. SMR aims to
be small, fast, and secure. It is built on top of [Kore](https://kore.io), using be small, fast, and secure. It is built on top of [Kore](https://kore.io), using
[luajit](https://luajit.org) to expose a Lua programming environment. It uses [luajit](https://luajit.org) to expose a Lua programming environment. It uses
[sqlite3](https://sqlite.org) as it's database. SMR is implemented in about [sqlite3](https://sqlite.org) as it's database. SMR is implemented in about
3.5k SLOC and is expected to never exceed 5k SLOC. Contributions welcome. 4k SLOC and is expected to never exceed 5k SLOC. Contributions welcome.
```
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Lua 36 190 306 1993
C 4 61 116 709
HTML 18 21 0 561
SQL 36 6 35 266
JavaScript 3 19 21 203
CSS 3 4 8 73
C/C++ Header 4 3 0 46
-------------------------------------------------------------------------------
SUM: 104 304 486 3851
-------------------------------------------------------------------------------
```
## Roadmap ## Roadmap
@ -15,13 +33,21 @@ be small, fast, and secure. It is built on top of [Kore](https://kore.io), using
* Comments (complete) * Comments (complete)
* Tags (complete) * Tags (complete)
* Search (complete) * Search (complete)
* Archive (in progress) * Archive (complete)
* Author biographies * Author biographies (complete)
* Kore 4.2.0 * Kore 4.2.0 (complete)
* addon api
TODO's: TODO's:
Currently, people can post comments to unlisted stories even if they don't have
* Currently, people can post comments to unlisted stories even if they don't have
the correct link. the correct link.
* Find a replacement preprocessor
* The archive is currently generated weekly from a cron job, and served
syncronously. We can generate a zip file on-the-fly instead, and if the client
disconnects, it's fine to drop the whole thing.
* We can simplify a lot of error handling logic by setting sql prepared statements to reset during error unwinding.
* We can simplify a lot of business logic by having requests parse their parameters eagerly.
## Hacking ## Hacking

View File

@ -127,6 +127,7 @@ domain * {
methods get post methods get post
validate post text v_any validate post text v_any
validate post author v_subdomain
} }
route /_login { route /_login {

View File

@ -30,7 +30,7 @@ local function bio_edit_get(req)
errcodemsg = "Not authorized", errcodemsg = "Not authorized",
explanation = "You must be logged in to edit your biography." explanation = "You must be logged in to edit your biography."
} }
http_response(req,400,ret) http_response(req,401,ret)
end end
--Get the logged in author's bio to display --Get the logged in author's bio to display
@ -56,7 +56,7 @@ found, please report this error.
end end
assert(err == sql.ROW) assert(err == sql.ROW)
local data = stmnt_bio:get_values() local data = stmnt_bio:get_values()
local bio = unpack(data) local bio = zlib.decompress(data[1])
stmnt_bio:reset() stmnt_bio:reset()
ret = pages.edit_bio{ ret = pages.edit_bio{
text = bio, text = bio,

View File

@ -27,16 +27,17 @@ local function edit_bio(req)
http_request_populate_post(req) http_request_populate_post(req)
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 parsed = parsers[markup](text) local parsed = parsers.plain(text) -- Make sure the plain parser can deal with it, even though we don't store this result.
local compr_raw = zlib.compress(text) local compr_raw = zlib.compress(text)
local compr = zlib.compress(parsed) local compr = zlib.compress(parsed)
assert(stmnt_update_bio:bind_blob(1,compr_raw) == sql.OK) assert(stmnt_update_bio:bind_blob(1,compr_raw) == sql.OK)
assert(stmnt_update_bio:bind(2, author_id) == sql.OK) assert(stmnt_update_bio:bind(2, author_id) == sql.OK)
assert(util.do_sql(stmnt_update_bio) == sql.DONE, "Failed to update biography") if util.do_sql(stmnt_update_bio) ~= sql.DONE then
stmnt_update_bio:reset() stmnt_update_bio:reset()
error("Faled to update biography")
end
local loc = string.format("https://%s.%s",author,config.domain) local loc = string.format("https://%s.%s",author,config.domain)
-- Dirty the cache for the author's index, the only place where the bio is displayed. -- Dirty the cache for the author's index, the only place where the bio is displayed.
cache.dirty(string.format("%s.%s",author,config.domain)) cache.dirty(string.format("%s.%s",author,config.domain))
@ -45,4 +46,4 @@ local function edit_bio(req)
return return
end end
return edit_post return edit_bio

View File

@ -8,6 +8,7 @@ local config = require("config")
local pages = require("pages") local pages = require("pages")
local libtags = require("tags") local libtags = require("tags")
local session = require("session") local session = require("session")
local parsers = require("parsers")
local stmnt_index, stmnt_author, stmnt_author_bio local stmnt_index, stmnt_author, stmnt_author_bio
@ -57,9 +58,12 @@ local function get_author_home(req, loggedin)
author = subdomain author = subdomain
} }
end end
assert(err == sql.ROW,"failed to get author:" .. subdomain .. " error:" .. tostring(err)) if err ~= sql.ROW then
stmnt_author_bio:reset()
error(string.format("Failed to get author %q error: %q",subdomain, tostring(err)))
end
local data = stmnt_author_bio:get_values() local data = stmnt_author_bio:get_values()
local bio = data[1] local bio = parsers.plain(zlib.decompress(data[1]))
stmnt_author_bio:reset() stmnt_author_bio:reset()
stmnt_author:bind_names{author=subdomain} stmnt_author:bind_names{author=subdomain}
local stories = {} local stories = {}

View File

@ -96,11 +96,14 @@ for funcname, spec in pairs({
assert(_G[funcname] == nil, "Tried to overwrite an endpoint, please define endpoints exactly once") assert(_G[funcname] == nil, "Tried to overwrite an endpoint, please define endpoints exactly once")
for k,v in pairs(spec) do for k,v in pairs(spec) do
assert(http_m_rev[k], "Unknown http method '" .. k .. "' defined for endpoint '" .. funcname .. "'") assert(http_m_rev[k], "Unknown http method '" .. k .. "' defined for endpoint '" .. funcname .. "'")
assert(type(v) == "function", "Endpoint %s %s must be a function, but was a %s",funcname, k, type(v))
end end
_G[funcname] = function(req) _G[funcname] = function(req)
local method = http_method_text(req) local method = http_method_text(req)
if spec[method] == nil then if spec[method] == nil then
log(LOG_NOTICE,string.format("Endpoint %s called with http method %s, but no such route defined.", funcname, method)) log(LOG_WARNING,string.format("Endpoint %s called with http method %s, but no such route defined.", funcname, method))
else
log(LOG_DEBUG,string.format("Endpoint %s called with method %s",funcname,method))
end end
spec[method](req) spec[method](req)
end end

View File

@ -11,8 +11,8 @@
<a href="/_login" class="button column column-0">Log in</a> <a href="/_login" class="button column column-0">Log in</a>
<% else %> <% else %>
<a href="/_logout" class="button column column-0">Log out</a> <a href="/_logout" class="button column column-0">Log out</a>
<% end %>
<a href="/_bio" class="button column column-0">Edit bio</a> <a href="/_bio" class="button column column-0">Edit bio</a>
<% end %>
<span class="column column-0"></span> <span class="column column-0"></span>
<form action="https://<%= domain %>/_search" method="get" class="search column row"> <form action="https://<%= domain %>/_search" method="get" class="search column row">
<input class="column" type="text" name="q" placeholder="+greentext -dotr +hits>20"/> <input class="column" type="text" name="q" placeholder="+greentext -dotr +hits>20"/>
@ -20,8 +20,8 @@
</form> </form>
</div> </div>
</div> </div>
<div class="content"> <div class="container">
<%= bio %> <%- bio %>
</div> </div>
<div class="content"> <div class="content">
<% if #stories == 0 then %> <% if #stories == 0 then %>

View File

@ -9,7 +9,9 @@
<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>
<div class="row">
<input type="submit"> <input type="submit">
</div>
</fieldset> </fieldset>
</form> </form>
<{cat src/pages/parts/footer.etlua}> <{cat src/pages/parts/footer.etlua}>