add threads n posts
This commit is contained in:
parent
f5485702a8
commit
4039d6d299
112
apps/threads.lua
112
apps/threads.lua
@ -1,7 +1,119 @@
|
|||||||
local app = require("lapis").Application()
|
local app = require("lapis").Application()
|
||||||
|
local lapis_util = require("lapis.util")
|
||||||
|
|
||||||
|
local db = require("lapis.db")
|
||||||
|
local util = require("util")
|
||||||
|
|
||||||
|
local models = require("models")
|
||||||
|
local Topics = models.Topics
|
||||||
|
local Threads = models.Threads
|
||||||
|
local Posts = models.Posts
|
||||||
|
|
||||||
app:get("thread_create", "/create", function(self)
|
app:get("thread_create", "/create", function(self)
|
||||||
|
local user = util.get_logged_in_user(self)
|
||||||
|
if not user then
|
||||||
|
self.session.flash = {error = "You must be logged in to perform this action."}
|
||||||
|
return {redirect_to = self:url_for("user_login")}
|
||||||
|
end
|
||||||
|
local all_topics = db.query("select * from topics limit 25;")
|
||||||
|
if #all_topics == 0 then
|
||||||
|
return "how did you get here?"
|
||||||
|
end
|
||||||
|
self.all_topics = all_topics
|
||||||
|
return {render = "threads.create"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
app:post("thread_create", "/create", function(self)
|
||||||
|
local user = util.get_logged_in_user(self)
|
||||||
|
if not user then
|
||||||
|
self.session.flash = {error = "You must be logged in to perform this action."}
|
||||||
|
return {redirect_to = self:url_for("user_login")}
|
||||||
|
end
|
||||||
|
local topic = Topics:find(self.params.topic_id)
|
||||||
|
if not topic then
|
||||||
|
return {redirect_to = self:url_for("topics")}
|
||||||
|
end
|
||||||
|
|
||||||
|
local title = lapis_util.trim(self.params.title)
|
||||||
|
local time = os.time()
|
||||||
|
local slug = lapis_util.slugify(title) .. "-" .. time
|
||||||
|
|
||||||
|
local post_content = self.params.initial_post
|
||||||
|
|
||||||
|
local thread = Threads:create({
|
||||||
|
topic_id = topic.id,
|
||||||
|
user_id = user.id,
|
||||||
|
title = title,
|
||||||
|
slug = slug,
|
||||||
|
created_at = time,
|
||||||
|
})
|
||||||
|
|
||||||
|
local post = util.create_post(thread.id, user.id, post_content)
|
||||||
|
if not post then
|
||||||
|
return {redirect_to = self:url_for("topics")}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {redirect_to = self:url_for("thread", {slug = slug})}
|
||||||
|
end)
|
||||||
|
|
||||||
|
app:get("thread", "/:slug", function(self)
|
||||||
|
local thread = Threads:find({
|
||||||
|
slug = self.params.slug
|
||||||
|
})
|
||||||
|
if not thread then
|
||||||
|
return {status = 404}
|
||||||
|
end
|
||||||
|
self.thread = thread
|
||||||
|
local posts = db.query([[
|
||||||
|
SELECT
|
||||||
|
posts.id, post_history.content, users.username, avatars.file_path AS avatar_path
|
||||||
|
FROM
|
||||||
|
posts
|
||||||
|
JOIN
|
||||||
|
post_history ON posts.current_revision_id = post_history.id
|
||||||
|
JOIN
|
||||||
|
users ON posts.user_id = users.id
|
||||||
|
LEFT JOIN
|
||||||
|
avatars ON users.avatar_id = avatars.id
|
||||||
|
WHERE
|
||||||
|
posts.thread_id = ? and posts.id > ?
|
||||||
|
ORDER BY
|
||||||
|
posts.created_at ASC
|
||||||
|
LIMIT 20
|
||||||
|
]], thread.id, tonumber(self.params.cursor or 0))
|
||||||
|
self.user = util.get_logged_in_user_or_transient(self)
|
||||||
|
self.posts = posts
|
||||||
|
self.next_cursor = #posts > 0 and posts[#posts].id or nil
|
||||||
|
return {render = "threads.thread"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
app:post("thread", "/:slug", function(self)
|
||||||
|
local thread = Threads:find({
|
||||||
|
slug = self.params.slug
|
||||||
|
})
|
||||||
|
if not thread then
|
||||||
|
return {redirect_to = self:url_for("all_topics")}
|
||||||
|
end
|
||||||
|
local user = util.get_logged_in_user(self)
|
||||||
|
if not user then
|
||||||
|
return {redirect_to = self:url_for("all_topics")}
|
||||||
|
end
|
||||||
|
|
||||||
|
if user:is_guest() then
|
||||||
|
return {redirect_to = self:url_for("thread", {slug = thread.slug})}
|
||||||
|
end
|
||||||
|
|
||||||
|
if util.is_thread_locked(thread) and not user:is_admin() then
|
||||||
|
return {redirect_to = self:url_for("thread", {slug = thread.slug})}
|
||||||
|
end
|
||||||
|
|
||||||
|
local post_content = self.params.post_content
|
||||||
|
local post = util.create_post(thread.id, user.id, post_content)
|
||||||
|
if not post then
|
||||||
|
return {redirect_to = self:url_for("thread", {slug = thread.slug})}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {redirect_to = self:url_for("thread", {slug = thread.slug})}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
@ -19,7 +19,7 @@ local ThreadCreateError = {
|
|||||||
TOPIC_LOCKED = 3,
|
TOPIC_LOCKED = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
app:get("all_topics", "/", function(self)
|
app:get("all_topics", "", function(self)
|
||||||
self.topic_list = db.query("select * from topics limit 25;")
|
self.topic_list = db.query("select * from topics limit 25;")
|
||||||
self.user = util.get_logged_in_user(self) or util.TransientUser
|
self.user = util.get_logged_in_user(self) or util.TransientUser
|
||||||
return {render = "topics.topics"}
|
return {render = "topics.topics"}
|
||||||
|
@ -41,4 +41,11 @@ return {
|
|||||||
-- will appear on top of non-stickied threads in topic view
|
-- will appear on top of non-stickied threads in topic view
|
||||||
schema.add_column("threads", "is_stickied", "BOOLEAN DEFAULT FALSE")
|
schema.add_column("threads", "is_stickied", "BOOLEAN DEFAULT FALSE")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
[5] = function ()
|
||||||
|
db.query("CREATE INDEX idx_posts_thread ON posts(thread_id, created_at, id)")
|
||||||
|
db.query("CREATE INDEX idx_users_avatar ON users(avatar_id)")
|
||||||
|
db.query("CREATE INDEX idx_topics_slug ON topics(slug)")
|
||||||
|
db.query("CREATE INDEX idx_threads_slug ON threads(slug)")
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
|
28
util.lua
28
util.lua
@ -4,6 +4,8 @@ local db = require("lapis.db")
|
|||||||
|
|
||||||
local Avatars = require("models").Avatars
|
local Avatars = require("models").Avatars
|
||||||
local Users = require("models").Users
|
local Users = require("models").Users
|
||||||
|
local Posts = require("models").Posts
|
||||||
|
local PostHistory = require("models").PostHistory
|
||||||
|
|
||||||
util.TransientUser = {
|
util.TransientUser = {
|
||||||
is_admin = function (self)
|
is_admin = function (self)
|
||||||
@ -15,6 +17,7 @@ util.TransientUser = {
|
|||||||
is_logged_in_guest = function (self)
|
is_logged_in_guest = function (self)
|
||||||
return false
|
return false
|
||||||
end,
|
end,
|
||||||
|
username = "Deleted User",
|
||||||
}
|
}
|
||||||
|
|
||||||
function util.get_user_avatar_url(req, user)
|
function util.get_user_avatar_url(req, user)
|
||||||
@ -94,4 +97,29 @@ function util.form_bool_to_sqlite(s)
|
|||||||
return util.bton(util.stob(s))
|
return util.bton(util.stob(s))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function util.is_thread_locked(thread)
|
||||||
|
return util.ntob(thread.is_locked)
|
||||||
|
end
|
||||||
|
|
||||||
|
function util.create_post(thread_id, user_id, content)
|
||||||
|
db.query("BEGIN")
|
||||||
|
local post = Posts:create({
|
||||||
|
thread_id = thread_id,
|
||||||
|
user_id = user_id,
|
||||||
|
current_revision_id = db.NULL,
|
||||||
|
})
|
||||||
|
|
||||||
|
local revision = PostHistory:create({
|
||||||
|
post_id = post.id,
|
||||||
|
user_id = user_id,
|
||||||
|
content = content,
|
||||||
|
is_initial_revision = true,
|
||||||
|
})
|
||||||
|
|
||||||
|
post:update({current_revision_id = revision.id})
|
||||||
|
|
||||||
|
db.query("COMMIT")
|
||||||
|
return post
|
||||||
|
end
|
||||||
|
|
||||||
return util
|
return util
|
13
views/threads/create.etlua
Normal file
13
views/threads/create.etlua
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<h1>New thread</h1>
|
||||||
|
<form method="post">
|
||||||
|
<label for="topic_id">Topic:</label>
|
||||||
|
<select name="topic_id", id="topic_id" autocomplete="off">
|
||||||
|
<% for _, topic in ipairs(all_topics) do %>
|
||||||
|
<option value="<%= topic.id %>" <%- params.topic_id == tostring(topic.id) and "selected" or "" %>><%= topic.name %></value>
|
||||||
|
<% end %>
|
||||||
|
</select><br>
|
||||||
|
<label for="title">Thread title:</label>
|
||||||
|
<input type="text" id="title" name="title" required><br>
|
||||||
|
<textarea id="initial_post" name="initial_post" placeholder="Post body" required></textarea><br>
|
||||||
|
<input type="submit" value="Create thread">
|
||||||
|
</form>
|
20
views/threads/thread.etlua
Normal file
20
views/threads/thread.etlua
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<% for _, post in ipairs(posts) do %>
|
||||||
|
<div>
|
||||||
|
<img src="<%= post.avatar_path or "/avatars/default.webp" %>">
|
||||||
|
<div><%= post.username %></div>
|
||||||
|
<div><p><%- require("lapis.html").escape(post.content):gsub("\n", "<br>") %></p></div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if not user:is_guest() then %>
|
||||||
|
<h1>Respond to "<%= thread.title %>"</h1>
|
||||||
|
<form method="post">
|
||||||
|
<textarea id="post_content" name="post_content" placeholder="Response body" required></textarea><br>
|
||||||
|
<input type="submit" value="Reply">
|
||||||
|
</form>
|
||||||
|
<% end %>
|
||||||
|
<% if next_cursor then %>
|
||||||
|
<a href="<%= url_for('thread', {slug = thread.slug}, {cursor = next_cursor}) %>">
|
||||||
|
Older posts →
|
||||||
|
</a>
|
||||||
|
<% end %>
|
@ -6,7 +6,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<% for i, v in ipairs(topic_list) do %>
|
<% for i, v in ipairs(topic_list) do %>
|
||||||
<li>
|
<li>
|
||||||
<a href=<%= v.slug %>><%= v.name %></a> - <%= v.description %>
|
<a href=<%= url_for("topic", {slug = v.slug}) %>><%= v.name %></a> - <%= v.description %>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
Loading…
Reference in New Issue
Block a user