add topics
This commit is contained in:
parent
86b568d0f4
commit
f5485702a8
5
app.lua
5
app.lua
@ -8,11 +8,16 @@ app.layout = require "views.base"
|
|||||||
|
|
||||||
local function inject_methods(req)
|
local function inject_methods(req)
|
||||||
req.avatar_url = util.get_user_avatar_url
|
req.avatar_url = util.get_user_avatar_url
|
||||||
|
req.ntob = function(_, v)
|
||||||
|
return util.ntob(v)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
app:before_filter(inject_methods)
|
app:before_filter(inject_methods)
|
||||||
|
|
||||||
app:include("apps.users", {path = "/user"})
|
app:include("apps.users", {path = "/user"})
|
||||||
|
app:include("apps.topics", {path = "/topics"})
|
||||||
|
app:include("apps.threads", {path = "/threads"})
|
||||||
|
|
||||||
app:get("/", function()
|
app:get("/", function()
|
||||||
return "Welcome to Lapis " .. require("lapis.version")
|
return "Welcome to Lapis " .. require("lapis.version")
|
||||||
|
7
apps/threads.lua
Normal file
7
apps/threads.lua
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
local app = require("lapis").Application()
|
||||||
|
|
||||||
|
app:get("thread_create", "/create", function(self)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
return app
|
125
apps/topics.lua
Normal file
125
apps/topics.lua
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
local app = require("lapis").Application()
|
||||||
|
local lapis_util = require("lapis.util")
|
||||||
|
|
||||||
|
local db = require("lapis.db")
|
||||||
|
local constants = require("constants")
|
||||||
|
|
||||||
|
local util = require("util")
|
||||||
|
|
||||||
|
local models = require("models")
|
||||||
|
local Users = models.Users
|
||||||
|
local Avatars = models.Avatars
|
||||||
|
local Topics = models.Topics
|
||||||
|
local Threads = models.Threads
|
||||||
|
|
||||||
|
local ThreadCreateError = {
|
||||||
|
OK = 0,
|
||||||
|
GUEST = 1,
|
||||||
|
LOGGED_OUT = 2,
|
||||||
|
TOPIC_LOCKED = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
app:get("all_topics", "/", function(self)
|
||||||
|
self.topic_list = db.query("select * from topics limit 25;")
|
||||||
|
self.user = util.get_logged_in_user(self) or util.TransientUser
|
||||||
|
return {render = "topics.topics"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
app:get("topic_create", "/create", function(self)
|
||||||
|
local user = util.get_logged_in_user(self) or util.TransientUser
|
||||||
|
if not user:is_admin() then
|
||||||
|
return {status = 403}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {render = "topics.create"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
app:post("topic_create", "/create", function(self)
|
||||||
|
local user = util.get_logged_in_user(self) or util.TransientUser
|
||||||
|
if not user:is_admin() then
|
||||||
|
return {redirect_to = "all_topics"}
|
||||||
|
end
|
||||||
|
|
||||||
|
local topic_name = lapis_util.trim(self.params.name)
|
||||||
|
local topic_description = self.params.description
|
||||||
|
local time = os.time()
|
||||||
|
local slug = lapis_util.slugify(topic_name) .. "-" .. time
|
||||||
|
|
||||||
|
local topic = Topics:create({
|
||||||
|
name = topic_name,
|
||||||
|
description = topic_description,
|
||||||
|
slug = slug,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {redirect_to = self:url_for("all_topics")}
|
||||||
|
end)
|
||||||
|
|
||||||
|
app:get("topic", "/:slug", function(self)
|
||||||
|
local topic = Topics:find({
|
||||||
|
slug = self.params.slug
|
||||||
|
})
|
||||||
|
if not topic then
|
||||||
|
return {status = 404}
|
||||||
|
end
|
||||||
|
self.topic = topic
|
||||||
|
self.threads_list = Threads:select(db.clause({
|
||||||
|
topic_id = topic.id
|
||||||
|
}))
|
||||||
|
local user = util.get_logged_in_user_or_transient(self)
|
||||||
|
print(topic.is_locked, type(topic.is_locked))
|
||||||
|
self.user = user
|
||||||
|
self.ThreadCreateError = ThreadCreateError
|
||||||
|
self.thread_create_error = ThreadCreateError.OK
|
||||||
|
if user:is_logged_in_guest() then
|
||||||
|
self.thread_create_error = ThreadCreateError.GUEST
|
||||||
|
elseif user:is_guest() then
|
||||||
|
self.thread_create_error = ThreadCreateError.LOGGED_OUT
|
||||||
|
elseif util.ntob(topic.is_locked) and not user:is_admin() then
|
||||||
|
self.thread_create_error = ThreadCreateError.TOPIC_LOCKED
|
||||||
|
end
|
||||||
|
|
||||||
|
return {render = "topics.topic"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
app:get("topic_edit", "/:slug/edit", function(self)
|
||||||
|
local user = util.get_logged_in_user_or_transient(self)
|
||||||
|
if not user:is_admin() then
|
||||||
|
return {redirect_to = self:url_for("topic", {slug = self.params.slug})}
|
||||||
|
end
|
||||||
|
local topic = Topics:find({
|
||||||
|
slug = self.params.slug
|
||||||
|
})
|
||||||
|
if not topic then
|
||||||
|
return {redirect_to = self:url_for("all_topics")}
|
||||||
|
end
|
||||||
|
self.topic = topic
|
||||||
|
return {render = "topics.edit"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
app:post("topic_edit", "/:slug/edit", function(self)
|
||||||
|
local user = util.get_logged_in_user_or_transient(self)
|
||||||
|
if not user:is_admin() then
|
||||||
|
return {redirect_to = self:url_for("topic", {slug = self.params.slug})}
|
||||||
|
end
|
||||||
|
local topic = Topics:find({
|
||||||
|
slug = self.params.slug
|
||||||
|
})
|
||||||
|
if not topic then
|
||||||
|
return {redirect_to = self:url_for("all_topics")}
|
||||||
|
end
|
||||||
|
local name = self.params.name or topic.name
|
||||||
|
local description = self.params.description or topic.description
|
||||||
|
local is_locked = topic.is_locked
|
||||||
|
if self.params.is_locked ~= nil then
|
||||||
|
is_locked = util.form_bool_to_sqlite(self.params.is_locked)
|
||||||
|
end
|
||||||
|
|
||||||
|
topic:update({
|
||||||
|
name = name,
|
||||||
|
description = description,
|
||||||
|
is_locked = is_locked,
|
||||||
|
})
|
||||||
|
return {redirect_to = self:url_for("topic", {slug = self.params.slug})}
|
||||||
|
end)
|
||||||
|
|
||||||
|
return app
|
@ -13,15 +13,6 @@ local Users = models.Users
|
|||||||
local Sessions = models.Sessions
|
local Sessions = models.Sessions
|
||||||
local Avatars = models.Avatars
|
local Avatars = models.Avatars
|
||||||
|
|
||||||
local TransientUser = {
|
|
||||||
is_admin = function (self)
|
|
||||||
return false
|
|
||||||
end,
|
|
||||||
is_guest = function (self)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
local function authenticate_user(user, password)
|
local function authenticate_user(user, password)
|
||||||
return bcrypt.verify(password, user.password_hash)
|
return bcrypt.verify(password, user.password_hash)
|
||||||
end
|
end
|
||||||
@ -81,7 +72,7 @@ app:get("user", "/:username", function(self)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- local me = validate_session(self.session.session_key) or TransientUser
|
-- local me = validate_session(self.session.session_key) or TransientUser
|
||||||
local me = util.get_logged_in_user(self) or TransientUser
|
local me = util.get_logged_in_user(self) or util.TransientUser
|
||||||
self.user = user
|
self.user = user
|
||||||
self.me = me
|
self.me = me
|
||||||
|
|
||||||
|
@ -29,4 +29,16 @@ return {
|
|||||||
})
|
})
|
||||||
schema.add_column("users", "avatar_id", "REFERENCES avatars(id) ON DELETE SET NULL")
|
schema.add_column("users", "avatar_id", "REFERENCES avatars(id) ON DELETE SET NULL")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
[4] = function ()
|
||||||
|
schema.add_column("topics", "description", types.text{default=""})
|
||||||
|
|
||||||
|
-- topic locked = no new threads can be created in the topic, but posts can be made in threads
|
||||||
|
-- thread locked = no new posts can be created in the thread, existing posts can not be edited
|
||||||
|
-- admins bypass both restrictions
|
||||||
|
schema.add_column("topics", "is_locked", "BOOLEAN DEFAULT FALSE")
|
||||||
|
schema.add_column("threads", "is_locked", "BOOLEAN DEFAULT FALSE")
|
||||||
|
-- will appear on top of non-stickied threads in topic view
|
||||||
|
schema.add_column("threads", "is_stickied", "BOOLEAN DEFAULT FALSE")
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ function Users_mt:is_admin()
|
|||||||
return self.permission == constants.PermissionLevel.ADMIN
|
return self.permission == constants.PermissionLevel.ADMIN
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Users_mt:is_logged_in_guest()
|
||||||
|
return self:is_guest() and true
|
||||||
|
end
|
||||||
|
|
||||||
function Users_mt:is_default_avatar()
|
function Users_mt:is_default_avatar()
|
||||||
return self.avatar_id == nil
|
return self.avatar_id == nil
|
||||||
end
|
end
|
||||||
|
37
util.lua
37
util.lua
@ -5,6 +5,18 @@ 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
|
||||||
|
|
||||||
|
util.TransientUser = {
|
||||||
|
is_admin = function (self)
|
||||||
|
return false
|
||||||
|
end,
|
||||||
|
is_guest = function (self)
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
is_logged_in_guest = function (self)
|
||||||
|
return false
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
function util.get_user_avatar_url(req, user)
|
function util.get_user_avatar_url(req, user)
|
||||||
if not user.avatar_id then
|
if not user.avatar_id then
|
||||||
return "/avatars/default.webp"
|
return "/avatars/default.webp"
|
||||||
@ -57,4 +69,29 @@ function util.get_logged_in_user(req)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function util.get_logged_in_user_or_transient(req)
|
||||||
|
return util.get_logged_in_user(req) or util.TransientUser
|
||||||
|
end
|
||||||
|
|
||||||
|
function util.ntob(v)
|
||||||
|
return v ~= 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function util.bton(b)
|
||||||
|
return 1 and b or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function util.stob(s)
|
||||||
|
if s == "true" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if s == "false" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function util.form_bool_to_sqlite(s)
|
||||||
|
return util.bton(util.stob(s))
|
||||||
|
end
|
||||||
|
|
||||||
return util
|
return util
|
6
views/topics/create.etlua
Normal file
6
views/topics/create.etlua
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h1>Create topic</h1>
|
||||||
|
<form method="post">
|
||||||
|
<input type="text" name="name" id="name" placeholder="Topic name" required><br>
|
||||||
|
<textarea id="description" name="description" placeholder="Topic description" required></textarea><br>
|
||||||
|
<input type="submit" value="Create topic">
|
||||||
|
</form>
|
12
views/topics/edit.etlua
Normal file
12
views/topics/edit.etlua
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<h1>Editing topic <%= topic.name %></h1>
|
||||||
|
<form method="post">
|
||||||
|
<input type="text" name="name" id="name" value="<%= topic.name %>" placeholder="Topic name" required><br>
|
||||||
|
<textarea id="description" name="description" value="<%= topic.description %>" placeholder="Topic description"></textarea><br>
|
||||||
|
<input type="checkbox" id="is_locked" name="is_locked" value="<%= ntob(topic.is_locked) %>">
|
||||||
|
<label for="is_locked">Locked</label><br>
|
||||||
|
<input type="submit" value="Save changes">
|
||||||
|
</form>
|
||||||
|
<form method="get" action="<%= url_for("topic", {slug = topic.slug}) %>">
|
||||||
|
<input type="submit" value="Cancel">
|
||||||
|
</form>
|
||||||
|
<i>Note: to preserve history, you cannot change the topic URL.</i>
|
25
views/topics/topic.etlua
Normal file
25
views/topics/topic.etlua
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<h1><%= topic.name %></h1>
|
||||||
|
<h2><%= topic.description %></h2>
|
||||||
|
<% if #threads_list == 0 then %>
|
||||||
|
<p>There are no threads in this topic.</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if thread_create_error == ThreadCreateError.OK then %>
|
||||||
|
<a href=<%= url_for("thread_create", nil, {topic_id = topic.id}) %>>New thread</a>
|
||||||
|
<% elseif thread_create_error == ThreadCreateError.GUEST then %>
|
||||||
|
<p>Your account is still pending confirmation by an administrator. You are not able to create a new thread or post at this time.</p>
|
||||||
|
<% elseif thread_create_error == ThreadCreateError.LOGGED_OUT then %>
|
||||||
|
<p>Only logged in users can create threads. <a href="<%= url_for("user_signup") %>">Sign up</a> or <a href="<%= url_for("user_login")%>">log in</a> to create a thread.</p>
|
||||||
|
<% else %>
|
||||||
|
<p>This topic is locked.</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if user:is_admin() then %>
|
||||||
|
<br>
|
||||||
|
<a href="<%= url_for("topic_edit", {slug = topic.slug}) %>">Edit topic</a>
|
||||||
|
<form method="post" action="<%= url_for("topic_edit", {slug = topic.slug}) %>">
|
||||||
|
<input type="hidden" name="is_locked" value="<%= not ntob(topic.is_locked) %>">
|
||||||
|
<p><%= "This topic is " .. (ntob(topic.is_locked) and "" or "un") .. "locked." %></p>
|
||||||
|
<input type="submit" id="lock" value="<%= ntob(topic.is_locked) and "Unlock" or "Lock" %>">
|
||||||
|
</form>
|
||||||
|
<% end %>
|
16
views/topics/topics.etlua
Normal file
16
views/topics/topics.etlua
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<h1>Topics</h1>
|
||||||
|
|
||||||
|
<% if #topic_list == 0 then %>
|
||||||
|
<p>There are no topics.</p>
|
||||||
|
<% else %>
|
||||||
|
<ul>
|
||||||
|
<% for i, v in ipairs(topic_list) do %>
|
||||||
|
<li>
|
||||||
|
<a href=<%= v.slug %>><%= v.name %></a> - <%= v.description %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% if user:is_admin() then %>
|
||||||
|
<a href="<%= url_for("topic_create") %>">Create new topic</a>
|
||||||
|
<% end %>
|
Loading…
Reference in New Issue
Block a user