🐳chore(库):裁剪 Lapis-chan 项目
parent
4cb90fbc60
commit
37dcdacc05
@ -1,17 +1,16 @@
|
|||||||
|
|
||||||
local mock_request = require("lapis.spec.request").mock_request
|
local mock_request = require("lapis.spec.request").mock_request
|
||||||
local app = require("app")
|
local app = require("app")
|
||||||
|
|
||||||
describe("lapischan", function()
|
describe("lapischan", function()
|
||||||
require("lapis.spec").use_test_env()
|
require("lapis.spec").use_test_env()
|
||||||
|
|
||||||
setup(function()
|
setup(function()
|
||||||
require("lapis.db.migrations").run_migrations(require("migrations"))
|
require("lapis.db.migrations").run_migrations(require("migrations"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("loads install page", function()
|
it("loads install page", function()
|
||||||
local status, body = mock_request(app, "/")
|
local status, body = mock_request(app, "/")
|
||||||
assert.same(200, status)
|
assert.same(200, status)
|
||||||
assert.truthy(body:find("Install Lapis-chan", 1, true))
|
assert.truthy(body:find("Install Lapis-chan", 1, true))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
local lapis = require "lapis"
|
|
||||||
local capture = require("lapis.application").capture_errors_json
|
|
||||||
local r2 = require("lapis.application").respond_to
|
|
||||||
local handle = require("utils.error").handle
|
|
||||||
local app = lapis.Application()
|
|
||||||
app.__base = app
|
|
||||||
app.name = "api.announcements."
|
|
||||||
app.path = "/api/announcements"
|
|
||||||
|
|
||||||
app:match("announcements", "", capture({ on_error=handle, r2(require "apps.api.announcements.announcements") }))
|
|
||||||
app:match("announcement", "/:uri_announcement[%d]", capture({ on_error=handle, r2(require "apps.api.announcements.announcement") }))
|
|
||||||
app:match("global", "/global", capture({ on_error=handle, r2(require "apps.api.announcements.global") }))
|
|
||||||
|
|
||||||
return app
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
|
||||||
local role = require "utils.role"
|
|
||||||
local models = require "models"
|
|
||||||
local Announcements = models.announcements
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Get Announcement
|
|
||||||
local announcement = assert_error(Announcements:get(self.params.uri_announcement))
|
|
||||||
Announcements:format_from_db(announcement)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = announcement
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:PUT()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
id = self.params.uri_announcement,
|
|
||||||
board_id = tonumber(self.params.board_id),
|
|
||||||
text = self.params.text,
|
|
||||||
}
|
|
||||||
trim_filter(params)
|
|
||||||
Announcements:format_to_db(params)
|
|
||||||
assert_valid(params, Announcements.valid_record)
|
|
||||||
|
|
||||||
-- Modify Announcement
|
|
||||||
local announcement = assert_error(Announcements:modify(params))
|
|
||||||
Announcements:format_from_db(announcement)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = announcement
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:DELETE()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Delete Announcement
|
|
||||||
local announcement = assert_error(Announcements:delete(self.params.uri_announcement))
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = {
|
|
||||||
id = announcement.id,
|
|
||||||
text = announcement.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
|
||||||
local role = require "utils.role"
|
|
||||||
local models = require "models"
|
|
||||||
local Announcements = models.announcements
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Get all Announcements
|
|
||||||
local announcements = assert_error(Announcements:get_all())
|
|
||||||
for _, announcement in ipairs(announcements) do
|
|
||||||
Announcements:format_from_db(announcement)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = announcements
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:POST()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
board_id = tonumber(self.params.board_id),
|
|
||||||
text = self.params.text,
|
|
||||||
}
|
|
||||||
trim_filter(params)
|
|
||||||
Announcements:format_to_db(params)
|
|
||||||
assert_valid(params, Announcements.valid_record)
|
|
||||||
|
|
||||||
-- Create Announcement
|
|
||||||
local announcement = assert_error(Announcements:new(params))
|
|
||||||
Announcements:format_from_db(announcement)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = announcement
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local models = require "models"
|
|
||||||
local Announcements = models.announcements
|
|
||||||
|
|
||||||
function action.GET()
|
|
||||||
|
|
||||||
-- Get global Announcements
|
|
||||||
local announcements = assert_error(Announcements:get_global())
|
|
||||||
for _, announcement in ipairs(announcements) do
|
|
||||||
Announcements:format_from_db(announcement)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = announcements
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
local lapis = require "lapis"
|
|
||||||
local capture = require("lapis.application").capture_errors_json
|
|
||||||
local r2 = require("lapis.application").respond_to
|
|
||||||
local handle = require("utils.error").handle
|
|
||||||
local app = lapis.Application()
|
|
||||||
app.__base = app
|
|
||||||
app.name = "api.bans."
|
|
||||||
app.path = "/api/bans"
|
|
||||||
|
|
||||||
app:match("bans", "", capture({ on_error=handle, r2(require "apps.api.bans.bans") }))
|
|
||||||
app:match("ban", "/:uri_ban[%d]", capture({ on_error=handle, r2(require "apps.api.bans.ban") }))
|
|
||||||
app:match("bans_ip", "/ip/:uri_ip", capture({ on_error=handle, r2(require "apps.api.bans.bans_ip") }))
|
|
||||||
|
|
||||||
return app
|
|
||||||
@ -1,70 +1,70 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
local action = setmetatable({}, require "apps.api.internal.action_base")
|
||||||
local assert_error = require("lapis.application").assert_error
|
local assert_error = require("lapis.application").assert_error
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
local assert_valid = require("lapis.validate").assert_valid
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
local trim_filter = require("lapis.util").trim_filter
|
||||||
local role = require "utils.role"
|
local role = require "utils.role"
|
||||||
local models = require "models"
|
local models = require "models"
|
||||||
local Bans = models.bans
|
local Bans = models.bans
|
||||||
|
|
||||||
function action:GET()
|
function action:GET()
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
assert_error(role.mod(self.api_user))
|
assert_error(role.mod(self.api_user))
|
||||||
|
|
||||||
-- Get Ban
|
-- Get Ban
|
||||||
local ban = assert_error(Bans:get(self.params.uri_ban))
|
local ban = assert_error(Bans:get(self.params.uri_ban))
|
||||||
Bans:format_from_db(ban)
|
Bans:format_from_db(ban)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = ban
|
json = ban,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function action:PUT()
|
function action:PUT()
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
assert_error(role.mod(self.api_user))
|
assert_error(role.mod(self.api_user))
|
||||||
|
|
||||||
-- Validate parameters
|
-- Validate parameters
|
||||||
local params = {
|
local params = {
|
||||||
id = self.params.uri_ban,
|
id = self.params.uri_ban,
|
||||||
board_id = tonumber(self.params.board_id),
|
board_id = tonumber(self.params.board_id),
|
||||||
ip = self.params.ip,
|
ip = self.params.ip,
|
||||||
reason = self.params.reason,
|
reason = self.params.reason,
|
||||||
time = os.time(),
|
time = os.time(),
|
||||||
duration = tonumber(self.params.duration)
|
duration = tonumber(self.params.duration),
|
||||||
}
|
}
|
||||||
trim_filter(params)
|
trim_filter(params)
|
||||||
Bans:format_to_db(params)
|
Bans:format_to_db(params)
|
||||||
assert_valid(params, Bans.valid_record)
|
assert_valid(params, Bans.valid_record)
|
||||||
|
|
||||||
-- Modify Ban
|
-- Modify Ban
|
||||||
local ban = assert_error(Bans:modify(params))
|
local ban = assert_error(Bans:modify(params))
|
||||||
Bans:format_from_db(ban)
|
Bans:format_from_db(ban)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = ban
|
json = ban,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function action:DELETE()
|
function action:DELETE()
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
assert_error(role.mod(self.api_user))
|
assert_error(role.mod(self.api_user))
|
||||||
|
|
||||||
-- Delete Ban
|
-- Delete Ban
|
||||||
local ban = assert_error(Bans:delete(self.params.uri_ban))
|
local ban = assert_error(Bans:delete(self.params.uri_ban))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = {
|
json = {
|
||||||
id = ban.id,
|
id = ban.id,
|
||||||
ip = ban.ip,
|
ip = ban.ip,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|||||||
@ -1,54 +1,54 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
local action = setmetatable({}, require "apps.api.internal.action_base")
|
||||||
local assert_error = require("lapis.application").assert_error
|
local assert_error = require("lapis.application").assert_error
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
local assert_valid = require("lapis.validate").assert_valid
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
local trim_filter = require("lapis.util").trim_filter
|
||||||
local role = require "utils.role"
|
local role = require "utils.role"
|
||||||
local models = require "models"
|
local models = require "models"
|
||||||
local Bans = models.bans
|
local Bans = models.bans
|
||||||
|
|
||||||
function action:GET()
|
function action:GET()
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
assert_error(role.mod(self.api_user))
|
assert_error(role.mod(self.api_user))
|
||||||
|
|
||||||
-- Get all Bans
|
-- Get all Bans
|
||||||
local bans = assert_error(Bans:get_all())
|
local bans = assert_error(Bans:get_all())
|
||||||
for _, ban in ipairs(bans) do
|
for _, ban in ipairs(bans) do
|
||||||
Bans:format_from_db(ban)
|
Bans:format_from_db(ban)
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = bans
|
json = bans,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function action:POST()
|
function action:POST()
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
assert_error(role.mod(self.api_user))
|
assert_error(role.mod(self.api_user))
|
||||||
|
|
||||||
-- Validate parameters
|
-- Validate parameters
|
||||||
local params = {
|
local params = {
|
||||||
board_id = tonumber(self.params.board_id),
|
board_id = tonumber(self.params.board_id),
|
||||||
ip = self.params.ip,
|
ip = self.params.ip,
|
||||||
reason = self.params.reason,
|
reason = self.params.reason,
|
||||||
time = os.time(),
|
time = os.time(),
|
||||||
duration = tonumber(self.params.duration)
|
duration = tonumber(self.params.duration),
|
||||||
}
|
}
|
||||||
trim_filter(params)
|
trim_filter(params)
|
||||||
Bans:format_to_db(params)
|
Bans:format_to_db(params)
|
||||||
assert_valid(params, Bans.valid_record)
|
assert_valid(params, Bans.valid_record)
|
||||||
|
|
||||||
-- Create Ban
|
-- Create Ban
|
||||||
local ban = assert_error(Bans:new(params))
|
local ban = assert_error(Bans:new(params))
|
||||||
Bans:format_from_db(ban)
|
Bans:format_from_db(ban)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = ban
|
json = ban,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|||||||
@ -1,25 +1,25 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
local action = setmetatable({}, require "apps.api.internal.action_base")
|
||||||
local assert_error = require("lapis.application").assert_error
|
local assert_error = require("lapis.application").assert_error
|
||||||
local role = require "utils.role"
|
local role = require "utils.role"
|
||||||
local models = require "models"
|
local models = require "models"
|
||||||
local Bans = models.bans
|
local Bans = models.bans
|
||||||
|
|
||||||
function action:GET()
|
function action:GET()
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
assert_error(role.mod(self.api_user))
|
assert_error(role.mod(self.api_user))
|
||||||
|
|
||||||
-- Get Bans
|
-- Get Bans
|
||||||
local bans = assert_error(Bans:get_ip(self.params.uri_ip))
|
local bans = assert_error(Bans:get_ip(self.params.uri_ip))
|
||||||
for _, ban in ipairs(bans) do
|
for _, ban in ipairs(bans) do
|
||||||
Bans:format_from_db(ban)
|
Bans:format_from_db(ban)
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = bans
|
json = bans,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
local lapis = require "lapis"
|
|
||||||
local capture = require("lapis.application").capture_errors_json
|
|
||||||
local r2 = require("lapis.application").respond_to
|
|
||||||
local handle = require("utils.error").handle
|
|
||||||
local app = lapis.Application()
|
|
||||||
app.__base = app
|
|
||||||
app.name = "api.boards."
|
|
||||||
app.path = "/api/boards"
|
|
||||||
|
|
||||||
app:match("boards", "", capture({ on_error=handle, r2(require "apps.api.boards.boards") }))
|
|
||||||
app:match("board", "/:uri_board", capture({ on_error=handle, r2(require "apps.api.boards.board") }))
|
|
||||||
app:match("announcements", "/:uri_board/announcements", capture({ on_error=handle, r2(require "apps.api.boards.announcements") }))
|
|
||||||
app:match("bans", "/:uri_board/bans", capture({ on_error=handle, r2(require "apps.api.boards.bans") }))
|
|
||||||
app:match("reports", "/:uri_board/reports", capture({ on_error=handle, r2(require "apps.api.boards.reports") }))
|
|
||||||
app:match("threads", "/:uri_board/threads(/pages/:uri_page[%d])", capture({ on_error=handle, r2(require "apps.api.boards.threads") }))
|
|
||||||
app:match("archived", "/:uri_board/threads/archived", capture({ on_error=handle, r2(require "apps.api.boards.archived") }))
|
|
||||||
app:match("thread", "/:uri_board/threads/:uri_thread[%d]", capture({ on_error=handle, r2(require "apps.api.boards.thread") }))
|
|
||||||
app:match("thread.reports", "/:uri_board/threads/:uri_thread[%d]/reports", capture({ on_error=handle, r2(require "apps.api.boards.thread_reports") }))
|
|
||||||
app:match("posts", "/:uri_board(/threads/:uri_thread[%d])/posts", capture({ on_error=handle, r2(require "apps.api.boards.posts") }))
|
|
||||||
app:match("post", "/:uri_board/threads/:uri_thread[%d]/posts/:uri_post[%d]", capture({ on_error=handle, r2(require "apps.api.boards.post") }))
|
|
||||||
app:match("post.reports", "/:uri_board/threads/:uri_thread[%d]/posts/:uri_post[%d]/reports", capture({ on_error=handle, r2(require "apps.api.boards.post_reports") }))
|
|
||||||
|
|
||||||
return app
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local models = require "models"
|
|
||||||
local Announcements = models.announcements
|
|
||||||
local Boards = models.boards
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Get Board
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_board))
|
|
||||||
|
|
||||||
-- Get Announcements
|
|
||||||
local announcements = Announcements:get_board(board.id)
|
|
||||||
for _, announcement in ipairs(announcements) do
|
|
||||||
Announcements:format_from_db(announcement)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = announcements
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
local Threads = models.threads
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_name))
|
|
||||||
|
|
||||||
-- Get Threads
|
|
||||||
local threads = board:get_archived()
|
|
||||||
for _, thread in ipairs(threads) do
|
|
||||||
--Threads:format_from_db(thread)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = threads
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local role = require "utils.role"
|
|
||||||
local models = require "models"
|
|
||||||
local Bans = models.bans
|
|
||||||
local Boards = models.boards
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.mod(self.api_user))
|
|
||||||
|
|
||||||
-- Get Board
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_board))
|
|
||||||
|
|
||||||
-- Get Bans
|
|
||||||
local bans = Bans:get_board(board.id)
|
|
||||||
for _, ban in ipairs(bans) do
|
|
||||||
Bans:format_from_db(ban)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = bans
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local yield_error = require("lapis.application").yield_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
|
||||||
local role = require "utils.role"
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Get Board
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_board))
|
|
||||||
Boards:format_from_db(board)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = board
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:PUT()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
name = self.params.name,
|
|
||||||
title = self.params.title,
|
|
||||||
subtext = self.params.subtext,
|
|
||||||
rules = self.params.rules,
|
|
||||||
ban_message = self.params.ban_message,
|
|
||||||
anon_name = self.params.anon_name,
|
|
||||||
theme = self.params.theme,
|
|
||||||
total_posts = tonumber(self.params.total_posts),
|
|
||||||
pages = tonumber(self.params.pages),
|
|
||||||
threads_per_page = tonumber(self.params.threads_per_page),
|
|
||||||
anon_only = self.params.anon_only,
|
|
||||||
text_only = self.params.text_only,
|
|
||||||
draw = self.params.draw,
|
|
||||||
thread_file = self.params.thread_file,
|
|
||||||
thread_comment = self.params.thread_comment,
|
|
||||||
thread_file_limit = self.params.thread_file_limit,
|
|
||||||
post_file = self.params.post_file,
|
|
||||||
post_comment = self.params.post_comment,
|
|
||||||
post_limit = tonumber(self.params.post_limit),
|
|
||||||
archive = self.params.archive,
|
|
||||||
archive_time = tonumber(self.params.archive_time),
|
|
||||||
group = self.params.group,
|
|
||||||
filetype_image = self.params.filetype_image,
|
|
||||||
filetype_audio = self.params.filetype_audio
|
|
||||||
}
|
|
||||||
trim_filter(params)
|
|
||||||
Boards:format_to_db(params)
|
|
||||||
assert_valid(params, Boards.valid_record)
|
|
||||||
|
|
||||||
-- Check if board being modified is reusing name and title
|
|
||||||
local reuse_name = false
|
|
||||||
local reuse_title = false
|
|
||||||
|
|
||||||
if params.name or params.title then
|
|
||||||
local board = Boards:get(self.params.uri_board)
|
|
||||||
reuse_name = board.name == params.name
|
|
||||||
reuse_title = board.title == params.title
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Verify unique or current name and title
|
|
||||||
if not (reuse_name and reuse_title) then
|
|
||||||
local boards = Boards:get_all()
|
|
||||||
for _, board in ipairs(boards) do
|
|
||||||
if not reuse_name and board.name == params.name then
|
|
||||||
yield_error("FIXME")
|
|
||||||
end
|
|
||||||
|
|
||||||
if not reuse_title and board.title == params.title then
|
|
||||||
yield_error("FIXME")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Modify board
|
|
||||||
local board = assert_error(Boards:modify(params, self.params.uri_board))
|
|
||||||
Boards:format_from_db(board)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = board
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:DELETE()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Delete board
|
|
||||||
local board = assert_error(Boards:delete(self.params.uri_board))
|
|
||||||
|
|
||||||
-- TODO: delete adjacent data (announcements, bans, threads, posts, files)
|
|
||||||
-- probably best done by adding actual FK constraints with "ON DELETE CASCADE"
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = {
|
|
||||||
id = board.id,
|
|
||||||
name = board.name,
|
|
||||||
title = board.title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local yield_error = require("lapis.application").yield_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
|
||||||
local role = require "utils.role"
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
|
|
||||||
function action.GET()
|
|
||||||
|
|
||||||
-- Get Boards
|
|
||||||
local boards = assert_error(Boards:get_all())
|
|
||||||
for _, board in ipairs(boards) do
|
|
||||||
Boards:format_from_db(board)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = boards
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:POST()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
name = self.params.name,
|
|
||||||
title = self.params.title,
|
|
||||||
subtext = self.params.subtext,
|
|
||||||
rules = self.params.rules,
|
|
||||||
ban_message = self.params.ban_message,
|
|
||||||
anon_name = self.params.anon_name,
|
|
||||||
theme = self.params.theme,
|
|
||||||
total_posts = tonumber(self.params.total_posts),
|
|
||||||
pages = tonumber(self.params.pages),
|
|
||||||
threads_per_page = tonumber(self.params.threads_per_page),
|
|
||||||
anon_only = self.params.anon_only,
|
|
||||||
text_only = self.params.text_only,
|
|
||||||
draw = self.params.draw,
|
|
||||||
thread_file = self.params.thread_file,
|
|
||||||
thread_comment = self.params.thread_comment,
|
|
||||||
thread_file_limit = self.params.thread_file_limit,
|
|
||||||
post_file = self.params.post_file,
|
|
||||||
post_comment = self.params.post_comment,
|
|
||||||
post_limit = tonumber(self.params.post_limit),
|
|
||||||
archive = self.params.archive,
|
|
||||||
archive_time = tonumber(self.params.archive_time),
|
|
||||||
group = self.params.group,
|
|
||||||
filetype_image = self.params.filetype_image,
|
|
||||||
filetype_audio = self.params.filetype_audio
|
|
||||||
}
|
|
||||||
trim_filter(params)
|
|
||||||
Boards:format_to_db(params)
|
|
||||||
assert_valid(params, Boards.valid_record)
|
|
||||||
|
|
||||||
-- Verify unique name and title
|
|
||||||
local boards = Boards:get_all()
|
|
||||||
for _, board in ipairs(boards) do
|
|
||||||
if board.name == params.name or board.title == params.title then
|
|
||||||
yield_error("FIXME")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create board
|
|
||||||
local board = assert_error(Boards:new(params))
|
|
||||||
Boards:format_from_db(board)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = board
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
local Threads = models.threads
|
|
||||||
local Posts = models.posts
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_name))
|
|
||||||
|
|
||||||
-- Get Post
|
|
||||||
local post = assert_error(Posts:get(board.id, self.params.uri_id))
|
|
||||||
--Posts:format_from_db(post)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = post
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:PUT()
|
|
||||||
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
comment = self.params.comment,
|
|
||||||
subject = self.params.subject,
|
|
||||||
file_spoiler = self.params.file_spoiler
|
|
||||||
}
|
|
||||||
Posts:format_to_db(params)
|
|
||||||
trim_filter(params)
|
|
||||||
assert_valid(params, Posts.valid_record)
|
|
||||||
|
|
||||||
-- Modify post
|
|
||||||
local post = assert_error(Posts:modify(params))
|
|
||||||
Posts:format_from_db(post)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = post
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:DELETE()
|
|
||||||
|
|
||||||
--[[ FIXME: needs proper auth!
|
|
||||||
-- MODS = FAGS
|
|
||||||
if type(session) == "table" and
|
|
||||||
(session.admin or session.mod or session.janitor) then
|
|
||||||
rm_post(board.name)
|
|
||||||
success = true
|
|
||||||
-- Override password
|
|
||||||
elseif type(session) == "string" and
|
|
||||||
session == "override" then
|
|
||||||
rm_post(board.name)
|
|
||||||
success = true
|
|
||||||
-- Password has to match!
|
|
||||||
elseif post and session.password and
|
|
||||||
post.password == session.password then
|
|
||||||
rm_post(board.name)
|
|
||||||
success = true
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
|
|
||||||
-- Delete post
|
|
||||||
local post = assert_error(Posts:get_post_by_id(self.params.uri_id))
|
|
||||||
local thread = post:get_thread()
|
|
||||||
local op = thread:get_op()
|
|
||||||
|
|
||||||
if post.id == op.id then
|
|
||||||
assert_error(Threads:delete(thread.id))
|
|
||||||
|
|
||||||
local posts = thread:get_posts()
|
|
||||||
for _, p in ipairs(posts) do
|
|
||||||
assert_error(Posts:delete(p.id))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
assert_error(Posts:delete(post.id))
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = {
|
|
||||||
id = post.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Get Board
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_name))
|
|
||||||
|
|
||||||
-- Get Reports
|
|
||||||
local reports = board:get_reports()
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = reports
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
local Threads = models.threads
|
|
||||||
local Posts = models.posts
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
local posts
|
|
||||||
|
|
||||||
if self.params.uri_thread then
|
|
||||||
local thread = assert_error(Threads:get(self.params.uri_thread))
|
|
||||||
posts = thread:get_posts()
|
|
||||||
else
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_board))
|
|
||||||
posts = board:get_posts()
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = posts
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:POST()
|
|
||||||
local now = os.time()
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_name))
|
|
||||||
local thread = Threads:get(self.params.uri_id)
|
|
||||||
local op = thread and false or true
|
|
||||||
|
|
||||||
-- Create a new thread if no thread exists
|
|
||||||
if not thread then
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
board_id = board.id,
|
|
||||||
last_active = now,
|
|
||||||
sticky = self.params.sticky,
|
|
||||||
lock = self.params.lock,
|
|
||||||
size_override = self.params.size_override,
|
|
||||||
save = self.params.save
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Only admins and mods can flag threads
|
|
||||||
-- FIXME: API has no session, need proper auth!
|
|
||||||
if not self.session.admin or self.session.mod then
|
|
||||||
params.sticky = nil
|
|
||||||
params.lock = nil
|
|
||||||
params.size_override = nil
|
|
||||||
params.save = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--Threads:format_to_db(params)
|
|
||||||
trim_filter(params)
|
|
||||||
assert_valid(params, Threads.valid_record)
|
|
||||||
|
|
||||||
-- Create thread
|
|
||||||
thread = assert_error(Threads:new(params))
|
|
||||||
--Threads:format_from_db(thread)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- FIXME: there needs to be a better way to do this to avoid race conditions...
|
|
||||||
board.total_posts = board.total_posts + 1
|
|
||||||
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
post_id = board.total_posts,
|
|
||||||
thread_id = thread.id,
|
|
||||||
board_id = board.id,
|
|
||||||
timestamp = now,
|
|
||||||
ip = self.params.ip,
|
|
||||||
comment = self.params.comment,
|
|
||||||
name = self.params.name,
|
|
||||||
trip = self.params.trip,
|
|
||||||
subject = self.params.subject,
|
|
||||||
password = self.params.password,
|
|
||||||
file_name = self.params.file_name,
|
|
||||||
file_path = self.params.file_path,
|
|
||||||
file_type = self.params.file_type,
|
|
||||||
file_md5 = self.params.file_md5,
|
|
||||||
file_size = self.params.file_size,
|
|
||||||
file_width = self.params.file_width,
|
|
||||||
file_height = self.params.file_height,
|
|
||||||
file_duration = self.params.file_duration,
|
|
||||||
file_spoiler = self.params.file_spoiler,
|
|
||||||
file_content = self.params.file_content -- FIXME: we probably want to base64 decode this in format_to_db
|
|
||||||
}
|
|
||||||
Posts:format_to_db(params)
|
|
||||||
trim_filter(params)
|
|
||||||
assert_valid(params, Posts.valid_record)
|
|
||||||
|
|
||||||
-- Create post
|
|
||||||
local post = assert_error(Posts:new(params, board, op))
|
|
||||||
Posts:format_from_db(post)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = post
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local role = require "utils.role"
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
local Posts = models.posts
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.mod(self.api_user))
|
|
||||||
|
|
||||||
-- Get Board
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_board))
|
|
||||||
|
|
||||||
-- Get Reported posts
|
|
||||||
local posts = Posts:get_board_reports(board.id)
|
|
||||||
for _, post in ipairs(posts) do
|
|
||||||
Posts:format_from_db(post)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = posts
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local models = require "models"
|
|
||||||
local Threads = models.threads
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Get Thread
|
|
||||||
local thread = assert_error(Threads:get(self.params.uri_id))
|
|
||||||
--Threads:format_from_db(thread)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = thread
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:PUT()
|
|
||||||
|
|
||||||
local params = { id=self.params.uri_id }
|
|
||||||
|
|
||||||
-- Extract flag from URI
|
|
||||||
if self.params.uri_value:lower() == "true" or
|
|
||||||
self.params.uri_value:lower() == "t" or
|
|
||||||
tonumber(self.params.uri_value) == 1 then
|
|
||||||
self.params.uri_value = true
|
|
||||||
elseif self.params.uri_value:lower() == "false" or
|
|
||||||
self.params.uri_value:lower() == "f" or
|
|
||||||
tonumber(self.params.uri_value) == 0 then
|
|
||||||
self.params.uri_value = false
|
|
||||||
else
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_BAD_REQUEST,
|
|
||||||
json = {}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Extract variable from URI
|
|
||||||
if self.params.uri_action == "sticky" then
|
|
||||||
params.sticky = self.params.uri_value
|
|
||||||
elseif self.params.uri_action == "lock" then
|
|
||||||
params.lock = self.params.uri_value
|
|
||||||
elseif self.params.uri_action == "save" then
|
|
||||||
params.save = self.params.uri_value
|
|
||||||
elseif self.params.uri_action == "size_override" then
|
|
||||||
params.size_override = self.params.uri_value
|
|
||||||
else
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_BAD_REQUEST,
|
|
||||||
json = {}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Modify thread
|
|
||||||
local thread = assert_error(Threads:modify(params))
|
|
||||||
--Threads:format_from_db(thread)
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = thread
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:DELETE()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
-- Get Board
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_name))
|
|
||||||
|
|
||||||
-- Get Reports
|
|
||||||
local reports = board:get_reports()
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = reports
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local models = require "models"
|
|
||||||
local Boards = models.boards
|
|
||||||
local Threads = models.threads
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
|
|
||||||
local threads, pages
|
|
||||||
local board = assert_error(Boards:get(self.params.uri_name))
|
|
||||||
|
|
||||||
-- Get Threads
|
|
||||||
if self.params.uri_page then
|
|
||||||
local paginator = board:get_threads_paginated({ per_page=board.threads_per_page })
|
|
||||||
threads = paginator:get_page(self.params.uri_page)
|
|
||||||
pages = paginator:num_pages()
|
|
||||||
else
|
|
||||||
threads = board:get_threads()
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, thread in ipairs(threads) do
|
|
||||||
--Threads:format_from_db(thread)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = {
|
|
||||||
threads = threads,
|
|
||||||
pages = pages
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,13 +1,19 @@
|
|||||||
local lapis = require "lapis"
|
local lapis = require "lapis"
|
||||||
local capture = require("lapis.application").capture_errors_json
|
local capture = require("lapis.application").capture_errors_json
|
||||||
local r2 = require("lapis.application").respond_to
|
local r2 = require("lapis.application").respond_to
|
||||||
local handle = require("utils.error").handle
|
local handle = require("utils.error").handle
|
||||||
local app = lapis.Application()
|
local app = lapis.Application()
|
||||||
app.__base = app
|
app.__base = app
|
||||||
app.name = "api.core."
|
app.name = "api.core."
|
||||||
app.path = "/api"
|
app.path = "/api"
|
||||||
|
|
||||||
app:match("root", "", capture({ on_error=handle, r2(require "apps.api.core.root") }))
|
app:match("root", "", capture({
|
||||||
app:match("login", "/login", capture({ on_error=handle, r2(require "apps.api.core.login") }))
|
on_error = handle,
|
||||||
|
r2(require "apps.api.core.root"),
|
||||||
|
}))
|
||||||
|
app:match("login", "/login", capture({
|
||||||
|
on_error = handle,
|
||||||
|
r2(require "apps.api.core.login"),
|
||||||
|
}))
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|||||||
@ -1,35 +1,35 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
local action = setmetatable({}, require "apps.api.internal.action_base")
|
||||||
local assert_error = require("lapis.application").assert_error
|
local assert_error = require("lapis.application").assert_error
|
||||||
local yield_error = require("lapis.application").yield_error
|
local yield_error = require("lapis.application").yield_error
|
||||||
local models = require "models"
|
local models = require "models"
|
||||||
local Users = models.users
|
local Users = models.users
|
||||||
|
|
||||||
function action:POST()
|
function action:POST()
|
||||||
|
|
||||||
-- Normally we'd process these inputs a bit but in the case of
|
-- Normally we'd process these inputs a bit but in the case of
|
||||||
-- authentication credentials, we want to use the raw user inputs.
|
-- authentication credentials, we want to use the raw user inputs.
|
||||||
local params = {
|
local params = {
|
||||||
username = self.params.username,
|
username = self.params.username,
|
||||||
password = self.params.password
|
password = self.params.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Early exit if credentials not sent
|
-- Early exit if credentials not sent
|
||||||
if not params.username or not params.password then
|
if not params.username or not params.password then
|
||||||
yield_error("FIXME")
|
yield_error("FIXME")
|
||||||
end
|
end
|
||||||
|
|
||||||
local user = assert_error(Users:login(params))
|
local user = assert_error(Users:login(params))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = {
|
json = {
|
||||||
id = user.id,
|
id = user.id,
|
||||||
username = user.username,
|
username = user.username,
|
||||||
role = user.role,
|
role = user.role,
|
||||||
api_key = user.api_key
|
api_key = user.api_key,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
local action = setmetatable({}, require "apps.api.internal.action_base")
|
||||||
|
|
||||||
function action.GET()
|
function action.GET()
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = {}
|
json = {},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local action = {}
|
local action = {}
|
||||||
|
|
||||||
local function errors()
|
local function errors()
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_NOT_ALLOWED,
|
status = ngx.HTTP_NOT_ALLOWED,
|
||||||
json = {}
|
json = {},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
action.__index = action
|
action.__index = action
|
||||||
action.GET = errors
|
action.GET = errors
|
||||||
action.POST = errors
|
action.POST = errors
|
||||||
action.PUT = errors
|
action.PUT = errors
|
||||||
action.DELETE = errors
|
action.DELETE = errors
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|||||||
@ -1,41 +1,41 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
local assert_error = require("lapis.application").assert_error
|
||||||
local yield_error = require("lapis.application").yield_error
|
local yield_error = require("lapis.application").yield_error
|
||||||
local mime = require "mime"
|
local mime = require "mime"
|
||||||
local models = require "models"
|
local models = require "models"
|
||||||
local Users = models.users
|
local Users = models.users
|
||||||
|
|
||||||
return function(self)
|
return function(self)
|
||||||
|
|
||||||
if self.req.headers["Authorization"] then
|
if self.req.headers["Authorization"] then
|
||||||
|
|
||||||
-- Decode auth info
|
-- Decode auth info
|
||||||
local auth = mime.unb64(self.req.headers["Authorization"]:sub(7))
|
local auth = mime.unb64(self.req.headers["Authorization"]:sub(7))
|
||||||
local username, api_key = auth:match("^(.+)%:(.+)$")
|
local username, api_key = auth:match("^(.+)%:(.+)$")
|
||||||
|
|
||||||
-- DENY if Authorization is malformed
|
-- DENY if Authorization is malformed
|
||||||
if not username or not api_key then
|
if not username or not api_key then
|
||||||
yield_error("FIXME: Corrupt auth!")
|
yield_error("FIXME: Corrupt auth!")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- DENY if a user's key isn't properly set
|
-- DENY if a user's key isn't properly set
|
||||||
if api_key == Users.default_key then
|
if api_key == Users.default_key then
|
||||||
yield_error("FIXME: Bad auth!")
|
yield_error("FIXME: Bad auth!")
|
||||||
end
|
end
|
||||||
|
|
||||||
local params = {
|
local params = {
|
||||||
username = username,
|
username = username,
|
||||||
api_key = api_key
|
api_key = api_key,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Get User
|
-- Get User
|
||||||
self.api_user = assert_error(Users:get_api(params))
|
self.api_user = assert_error(Users:get_api(params))
|
||||||
Users:format_from_db(self.api_user)
|
Users:format_from_db(self.api_user)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set basic User
|
-- Set basic User
|
||||||
self.api_user = {
|
self.api_user = {
|
||||||
id = -1,
|
id = -1,
|
||||||
role = -1
|
role = -1,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
local i18n = require "i18n"
|
local i18n = require "i18n"
|
||||||
local lfs = require "lfs"
|
local lfs = require "lfs"
|
||||||
|
|
||||||
return function(self)
|
return function(self)
|
||||||
|
|
||||||
-- Set locale
|
-- Set locale
|
||||||
self.i18n = i18n
|
self.i18n = i18n
|
||||||
local locale = self.req.headers["Content-Language"] or "en"
|
local locale = self.req.headers["Content-Language"] or "en"
|
||||||
i18n.setLocale(locale)
|
i18n.setLocale(locale)
|
||||||
i18n.loadFile("src/locale/en.lua")
|
i18n.loadFile("src/locale/en.lua")
|
||||||
|
|
||||||
-- Get locale file
|
-- Get locale file
|
||||||
local path = "src/locale"
|
local path = "src/locale"
|
||||||
for file in lfs.dir(path) do
|
for file in lfs.dir(path) do
|
||||||
local name, ext = string.match(file, "^(.+)%.(.+)$")
|
local name, ext = string.match(file, "^(.+)%.(.+)$")
|
||||||
if name == locale and ext == "lua" then
|
if name == locale and ext == "lua" then
|
||||||
i18n.loadFile(string.format("%s/%s.lua", path, name))
|
i18n.loadFile(string.format("%s/%s.lua", path, name))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
local lapis = require "lapis"
|
|
||||||
local capture = require("lapis.application").capture_errors_json
|
|
||||||
local r2 = require("lapis.application").respond_to
|
|
||||||
local handle = require("utils.error").handle
|
|
||||||
local app = lapis.Application()
|
|
||||||
app.__base = app
|
|
||||||
app.name = "api.pages."
|
|
||||||
app.path = "/api/pages"
|
|
||||||
|
|
||||||
app:match("pages", "", capture({ on_error=handle, r2(require "apps.api.pages.pages") }))
|
|
||||||
app:match("page", "/:uri_page", capture({ on_error=handle, r2(require "apps.api.pages.page") }))
|
|
||||||
|
|
||||||
return app
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
|
||||||
local role = require "utils.role"
|
|
||||||
local models = require "models"
|
|
||||||
local Pages = models.pages
|
|
||||||
|
|
||||||
function action:GET()
|
|
||||||
local page = assert_error(Pages:get(self.params.uri_page))
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = page
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:PUT()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
slug = self.params.slug,
|
|
||||||
title = self.params.title,
|
|
||||||
content = self.params.content
|
|
||||||
}
|
|
||||||
trim_filter(params)
|
|
||||||
Pages:format_to_db(params)
|
|
||||||
assert_valid(params, Pages.valid_record)
|
|
||||||
|
|
||||||
-- Modify page
|
|
||||||
local page = assert_error(Pages:modify(params, self.params.uri_page))
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = page
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:DELETE()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Delete Page
|
|
||||||
local page = assert_error(Pages:delete(self.params.uri_page))
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = {
|
|
||||||
slug = page.slug,
|
|
||||||
title = page.title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
local ngx = _G.ngx
|
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
|
||||||
local role = require "utils.role"
|
|
||||||
local models = require "models"
|
|
||||||
local Pages = models.pages
|
|
||||||
|
|
||||||
function action.GET()
|
|
||||||
|
|
||||||
-- Get all Pages
|
|
||||||
local pages = assert_error(Pages:get_all())
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = pages
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function action:POST()
|
|
||||||
|
|
||||||
-- Verify the User's permissions
|
|
||||||
assert_error(role.admin(self.api_user))
|
|
||||||
|
|
||||||
-- Validate parameters
|
|
||||||
local params = {
|
|
||||||
slug = self.params.slug,
|
|
||||||
title = self.params.title,
|
|
||||||
content = self.params.content,
|
|
||||||
}
|
|
||||||
trim_filter(params)
|
|
||||||
Pages:format_to_db(params)
|
|
||||||
assert_valid(params, Pages.valid_record)
|
|
||||||
|
|
||||||
-- Create Page
|
|
||||||
local page = assert_error(Pages:new(params))
|
|
||||||
|
|
||||||
return {
|
|
||||||
status = ngx.HTTP_OK,
|
|
||||||
json = page
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return action
|
|
||||||
@ -1,13 +1,19 @@
|
|||||||
local lapis = require "lapis"
|
local lapis = require "lapis"
|
||||||
local capture = require("lapis.application").capture_errors_json
|
local capture = require("lapis.application").capture_errors_json
|
||||||
local r2 = require("lapis.application").respond_to
|
local r2 = require("lapis.application").respond_to
|
||||||
local handle = require("utils.error").handle
|
local handle = require("utils.error").handle
|
||||||
local app = lapis.Application()
|
local app = lapis.Application()
|
||||||
app.__base = app
|
app.__base = app
|
||||||
app.name = "api.users."
|
app.name = "api.users."
|
||||||
app.path = "/api/users"
|
app.path = "/api/users"
|
||||||
|
|
||||||
app:match("users", "", capture({ on_error=handle, r2(require "apps.api.users.users") }))
|
app:match("users", "", capture({
|
||||||
app:match("user", "/:uri_user", capture({ on_error=handle, r2(require "apps.api.users.user") }))
|
on_error = handle,
|
||||||
|
r2(require "apps.api.users.users"),
|
||||||
|
}))
|
||||||
|
app:match("user", "/:uri_user", capture({
|
||||||
|
on_error = handle,
|
||||||
|
r2(require "apps.api.users.user"),
|
||||||
|
}))
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|||||||
@ -1,108 +1,108 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
local action = setmetatable({}, require "apps.api.internal.action_base")
|
||||||
local assert_error = require("lapis.application").assert_error
|
local assert_error = require("lapis.application").assert_error
|
||||||
local yield_error = require("lapis.application").yield_error
|
local yield_error = require("lapis.application").yield_error
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
local assert_valid = require("lapis.validate").assert_valid
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
local trim_filter = require("lapis.util").trim_filter
|
||||||
local role = require "utils.role"
|
local role = require "utils.role"
|
||||||
local models = require "models"
|
local models = require "models"
|
||||||
local Users = models.users
|
local Users = models.users
|
||||||
|
|
||||||
function action:GET()
|
function action:GET()
|
||||||
|
|
||||||
local user = assert_error(Users:get(self.params.uri_user))
|
local user = assert_error(Users:get(self.params.uri_user))
|
||||||
Users:format_from_db(user)
|
Users:format_from_db(user)
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
local is_admin = role.admin(self.api_user)
|
local is_admin = role.admin(self.api_user)
|
||||||
local is_user = self.api_user.id == user.id
|
local is_user = self.api_user.id == user.id
|
||||||
if not is_admin and not is_user then
|
if not is_admin and not is_user then
|
||||||
yield_error("FIXME")
|
yield_error("FIXME")
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = user
|
json = user,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function action:PUT()
|
function action:PUT()
|
||||||
|
|
||||||
local user = assert_error(Users:get(self.params.uri_user))
|
local user = assert_error(Users:get(self.params.uri_user))
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
local is_admin = role.admin(self.api_user)
|
local is_admin = role.admin(self.api_user)
|
||||||
local is_user = self.api_user.id == user.id
|
local is_user = self.api_user.id == user.id
|
||||||
local is_auth = self.api_user.role > user.role
|
local is_auth = self.api_user.role > user.role
|
||||||
if (not is_admin and not is_user) or not is_auth then
|
if (not is_admin and not is_user) or not is_auth then
|
||||||
yield_error("FIXME")
|
yield_error("FIXME")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Validate parameters
|
-- Validate parameters
|
||||||
local params = {
|
local params = {
|
||||||
username = self.params.username,
|
username = self.params.username,
|
||||||
password = self.params.password,
|
password = self.params.password,
|
||||||
confirm = self.params.confirm,
|
confirm = self.params.confirm,
|
||||||
role = tonumber(self.params.role),
|
role = tonumber(self.params.role),
|
||||||
api_key = self.params.api_key
|
api_key = self.params.api_key,
|
||||||
}
|
}
|
||||||
trim_filter(params)
|
trim_filter(params)
|
||||||
Users:format_to_db(params)
|
Users:format_to_db(params)
|
||||||
assert_valid(params, Users.valid_record)
|
assert_valid(params, Users.valid_record)
|
||||||
|
|
||||||
-- If no role was sent, don't update it
|
-- If no role was sent, don't update it
|
||||||
-- This is kind of dumb since we're just setting it from nil to -1 and back
|
-- This is kind of dumb since we're just setting it from nil to -1 and back
|
||||||
-- to nil, but I want to keep the format_to_db in case of future formatting
|
-- to nil, but I want to keep the format_to_db in case of future formatting
|
||||||
-- concerns.
|
-- concerns.
|
||||||
if params.role == Users.role.INVALID then
|
if params.role == Users.role.INVALID then
|
||||||
params.role = nil
|
params.role = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if params.role then
|
if params.role then
|
||||||
|
|
||||||
-- Only admins can change a role
|
-- Only admins can change a role
|
||||||
if not is_admin then
|
if not is_admin then
|
||||||
yield_error("FIXME")
|
yield_error("FIXME")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Cannot elevate to or above own role
|
-- Cannot elevate to or above own role
|
||||||
if self.api_user.role <= params.role then
|
if self.api_user.role <= params.role then
|
||||||
yield_error("FIXME")
|
yield_error("FIXME")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Modify User
|
-- Modify User
|
||||||
user = assert_error(Users:modify(params, self.params.uri_user, self.params.password))
|
user = assert_error(Users:modify(params, self.params.uri_user, self.params.password))
|
||||||
Users:format_from_db(user)
|
Users:format_from_db(user)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = user
|
json = user,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function action:DELETE()
|
function action:DELETE()
|
||||||
|
|
||||||
local user = assert_error(Users:get(self.params.uri_user))
|
local user = assert_error(Users:get(self.params.uri_user))
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
local is_admin = role.admin(self.api_user)
|
local is_admin = role.admin(self.api_user)
|
||||||
local is_user = self.api_user.id == user.id
|
local is_user = self.api_user.id == user.id
|
||||||
local is_auth = self.api_user.role > user.role
|
local is_auth = self.api_user.role > user.role
|
||||||
if not is_admin and not is_user and not is_auth then
|
if not is_admin and not is_user and not is_auth then
|
||||||
yield_error("FIXME")
|
yield_error("FIXME")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Delete User
|
-- Delete User
|
||||||
user = assert_error(Users:delete(self.params.uri_user))
|
user = assert_error(Users:delete(self.params.uri_user))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = {
|
json = {
|
||||||
id = user.id,
|
id = user.id,
|
||||||
username = user.username
|
username = user.username,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|||||||
@ -1,64 +1,64 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local action = setmetatable({}, require "apps.api.internal.action_base")
|
local action = setmetatable({}, require "apps.api.internal.action_base")
|
||||||
local assert_error = require("lapis.application").assert_error
|
local assert_error = require("lapis.application").assert_error
|
||||||
local yield_error = require("lapis.application").yield_error
|
local yield_error = require("lapis.application").yield_error
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
local assert_valid = require("lapis.validate").assert_valid
|
||||||
local trim_filter = require("lapis.util").trim_filter
|
local trim_filter = require("lapis.util").trim_filter
|
||||||
local role = require "utils.role"
|
local role = require "utils.role"
|
||||||
local models = require "models"
|
local models = require "models"
|
||||||
local Users = models.users
|
local Users = models.users
|
||||||
|
|
||||||
function action:GET()
|
function action:GET()
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
assert_error(role.admin(self.api_user))
|
assert_error(role.admin(self.api_user))
|
||||||
|
|
||||||
-- Get all Users
|
-- Get all Users
|
||||||
local users = assert_error(Users:get_all())
|
local users = assert_error(Users:get_all())
|
||||||
for _, user in ipairs(users) do
|
for _, user in ipairs(users) do
|
||||||
Users:format_from_db(user)
|
Users:format_from_db(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = users
|
json = users,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function action:POST()
|
function action:POST()
|
||||||
|
|
||||||
-- Verify the User's permissions
|
-- Verify the User's permissions
|
||||||
assert_error(role.admin(self.api_user))
|
assert_error(role.admin(self.api_user))
|
||||||
|
|
||||||
-- Validate parameters
|
-- Validate parameters
|
||||||
local params = {
|
local params = {
|
||||||
username = self.params.username,
|
username = self.params.username,
|
||||||
password = self.params.password,
|
password = self.params.password,
|
||||||
confirm = self.params.confirm,
|
confirm = self.params.confirm,
|
||||||
role = tonumber(self.params.role)
|
role = tonumber(self.params.role),
|
||||||
}
|
}
|
||||||
trim_filter(params)
|
trim_filter(params)
|
||||||
Users:format_to_db(params)
|
Users:format_to_db(params)
|
||||||
assert_valid(params, Users.valid_record)
|
assert_valid(params, Users.valid_record)
|
||||||
|
|
||||||
-- DENY if no role was sent
|
-- DENY if no role was sent
|
||||||
if params.role == Users.role.INVALID then
|
if params.role == Users.role.INVALID then
|
||||||
yield_error("FIXME")
|
yield_error("FIXME")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Cannot elevate to or above own role
|
-- Cannot elevate to or above own role
|
||||||
if self.api_user.role <= params.role then
|
if self.api_user.role <= params.role then
|
||||||
yield_error("FIXME")
|
yield_error("FIXME")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create user
|
-- Create user
|
||||||
local user = assert_error(Users:new(params, self.params.password))
|
local user = assert_error(Users:new(params, self.params.password))
|
||||||
Users:format_from_db(user)
|
Users:format_from_db(user)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = ngx.HTTP_OK,
|
status = ngx.HTTP_OK,
|
||||||
json = user
|
json = user,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
local lapis = require "lapis"
|
|
||||||
local app = lapis.Application()
|
|
||||||
app.__base = app
|
|
||||||
app.include = function(self, a)
|
|
||||||
self.__class.include(self, a, nil, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
app:before_filter(require "apps.web.internal.config_site")
|
|
||||||
app:before_filter(require "apps.web.internal.check_auth")
|
|
||||||
app:before_filter(require "apps.web.internal.check_ban")
|
|
||||||
|
|
||||||
app:include("apps.web.admin")
|
|
||||||
app:include("apps.web.pages")
|
|
||||||
app:include("apps.web.boards")
|
|
||||||
|
|
||||||
return app
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
local lapis = require "lapis"
|
|
||||||
local r2 = require("lapis.application").respond_to
|
|
||||||
local app = lapis.Application()
|
|
||||||
app.__base = app
|
|
||||||
app.name = "web.admin."
|
|
||||||
app.path = "/admin"
|
|
||||||
app.handle_404 = require "apps.web.internal.code_404"
|
|
||||||
|
|
||||||
app:match("index", "", r2(require "apps.web.admin.index"))
|
|
||||||
app:match("users", "/:action/user(/:user)", r2(require "apps.web.admin.user"))
|
|
||||||
app:match("boards", "/:action/board(/:uri_name)", r2(require "apps.web.admin.board"))
|
|
||||||
app:match("announcements", "/:action/announcement(/:ann)", r2(require "apps.web.admin.announcement"))
|
|
||||||
app:match("pages", "/:action/page(/:page)", r2(require "apps.web.admin.page"))
|
|
||||||
app:match("reports", "/:action/report(/:report)", r2(require "apps.web.admin.report"))
|
|
||||||
|
|
||||||
return app
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
-- Get announcements
|
|
||||||
self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcements")))
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("admin_panel")
|
|
||||||
|
|
||||||
-- Verify Authorization
|
|
||||||
if self.session.name then
|
|
||||||
if not self.session.admin then
|
|
||||||
assert_error(false, "err_not_admin")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display creation form
|
|
||||||
if self.params.action == "create" then
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("create_ann")
|
|
||||||
)
|
|
||||||
self.announcement = self.params
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display modification form
|
|
||||||
if self.params.action == "modify" then
|
|
||||||
self.announcement = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id=self.params.uri_id })))
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("modify_ann")
|
|
||||||
)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete announcement
|
|
||||||
if self.params.action == "delete" then
|
|
||||||
self.announcement = assert_error(capture.delete(self:url_for("api.announcements.announcement", { uri_id=self.params.uri_id })))
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("deleted_ann", { self.announcement.text })
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "create" then
|
|
||||||
return { render = "admin.announcement" }
|
|
||||||
elseif self.params.action == "modify" then
|
|
||||||
return { render = "admin.announcement" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.admin" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
GET = function(self)
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "create" then
|
|
||||||
return { render = "admin.announcement" }
|
|
||||||
elseif self.params.action == "modify" then
|
|
||||||
return { render = "admin.announcement" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
POST = function(self)
|
|
||||||
-- Validate CSRF token
|
|
||||||
csrf.assert_token(self)
|
|
||||||
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "text", max_length=255, exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Create announcement
|
|
||||||
if self.params.create_announcement then
|
|
||||||
self.announcement = assert_error(capture.post(self:url_for("api.announcements.announcements"), self.params))
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("created_ann", { self.announcement.text })
|
|
||||||
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Modify announcement
|
|
||||||
if self.params.modify_announcement then
|
|
||||||
self.announcement = assert_error(capture.put(self:url_for("api.announcements.announcement", { uri_id=self.params.uri_id }), self.params))
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("modified_ann", { self.announcement.text })
|
|
||||||
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { redirect_to = self:url_for("web.admin.index") }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,136 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local lfs = require "lfs"
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("admin_panel")
|
|
||||||
|
|
||||||
-- Verify Authorization
|
|
||||||
if not self.session.name then return end
|
|
||||||
if not self.session.admin then
|
|
||||||
assert_error(false, "err_not_admin")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get list of themes
|
|
||||||
self.themes = {}
|
|
||||||
for file in lfs.dir("./static/css") do
|
|
||||||
local name, ext = string.match(file, "^(.+)%.(.+)$")
|
|
||||||
if name ~= "reset" and
|
|
||||||
name ~= "posts" and
|
|
||||||
name ~= "style" and
|
|
||||||
name ~= "tegaki" and
|
|
||||||
ext == "css" then
|
|
||||||
table.insert(self.themes, name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display creation form
|
|
||||||
if self.params.action == "create" then
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("create_board")
|
|
||||||
)
|
|
||||||
self.board = self.params
|
|
||||||
|
|
||||||
if not self.board.theme then
|
|
||||||
self.board.theme = "yotsuba_b"
|
|
||||||
end
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display modification form
|
|
||||||
if self.params.action == "modify" then
|
|
||||||
self.board = assert_error(capture.get(self:url_for("api.boards.board", { uri_name=self.params.uri_name })))
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("modify_board")
|
|
||||||
)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete board
|
|
||||||
if self.params.action == "delete" then
|
|
||||||
local board = assert_error(capture.delete(self:url_for("api.boards.board", { uri_name=self.params.uri_name })))
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("deleted_board", { board.name, board.title })
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "create" then
|
|
||||||
return { render = "admin.board" }
|
|
||||||
elseif self.params.action == "modify" then
|
|
||||||
return { render = "admin.board" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.admin" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
GET = function(self)
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "create" then
|
|
||||||
return { render = "admin.board" }
|
|
||||||
elseif self.params.action == "modify" then
|
|
||||||
return { render = "admin.board" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
POST = function(self)
|
|
||||||
-- Validate CSRF token
|
|
||||||
csrf.assert_token(self)
|
|
||||||
|
|
||||||
-- Create new board
|
|
||||||
if self.params.create_board then
|
|
||||||
local board = assert_error(capture.post(self:url_for("api.boards.boards"), self.params))
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("created_board", { board.name, board.title })
|
|
||||||
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Modify board
|
|
||||||
if self.params.modify_board then
|
|
||||||
local board = assert_error(capture.put(self:url_for("api.boards.board", { uri_name=self.params.uri_name }), self.params))
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("modified_board", { board.name, board.title })
|
|
||||||
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { redirect_to = self:url_for("web.admin.index") }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
local Boards = require "models.boards"
|
|
||||||
local Pages = require "models.pages"
|
|
||||||
local Posts = require "models.posts"
|
|
||||||
local Reports = require "models.reports"
|
|
||||||
local Users = require "models.users"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
-- Get data
|
|
||||||
self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcements")))
|
|
||||||
self.pages = Pages:get_pages()
|
|
||||||
self.reports = Reports:get_reports()
|
|
||||||
self.users = Users:get_users()
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("admin_panel")
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Verify Authorization
|
|
||||||
if self.session.name then
|
|
||||||
if not self.session.admin then
|
|
||||||
assert_error(false, "err_not_admin")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { render = "admin.admin" }
|
|
||||||
end,
|
|
||||||
|
|
||||||
GET = function(self)
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { render = "admin.admin" }
|
|
||||||
end,
|
|
||||||
|
|
||||||
POST = function(self)
|
|
||||||
-- Validate CSRF token
|
|
||||||
csrf.assert_token(self)
|
|
||||||
|
|
||||||
-- Verify user credentials
|
|
||||||
if self.params.login then
|
|
||||||
-- Verify user
|
|
||||||
local user = assert_error(Users:verify_user(self.params))
|
|
||||||
|
|
||||||
-- Set username
|
|
||||||
self.session.name = user.username
|
|
||||||
|
|
||||||
return { redirect_to = self.admin_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Must be logged in as an admin!
|
|
||||||
if self.session.admin then
|
|
||||||
-- Redirect to modify user page
|
|
||||||
if self.params.modify_user then
|
|
||||||
return { redirect_to = self:url_for("web.admin.users", { action="modify", user=self.params.user }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to delete user page
|
|
||||||
if self.params.delete_user then
|
|
||||||
return { redirect_to = self:url_for("web.admin.users", { action="delete", user=self.params.user }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to modify board page
|
|
||||||
if self.params.modify_board then
|
|
||||||
return { redirect_to = self:url_for("web.admin.boards", { action="modify", uri_name=self.params.board }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to delete board page
|
|
||||||
if self.params.delete_board then
|
|
||||||
return { redirect_to = self:url_for("web.admin.boards", { action="delete", uri_name=self.params.board }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to modify announcement page
|
|
||||||
if self.params.modify_announcement then
|
|
||||||
return { redirect_to = self:url_for("web.admin.announcements", { action="modify", ann=self.params.ann }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to delete announcement page
|
|
||||||
if self.params.delete_announcement then
|
|
||||||
return { redirect_to = self:url_for("web.admin.announcements", { action="delete", ann=self.params.ann }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to modify page page
|
|
||||||
if self.params.modify_page then
|
|
||||||
return { redirect_to = self:url_for("web.admin.pages", { action="modify", page=self.params.page }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to delete page page
|
|
||||||
if self.params.delete_page then
|
|
||||||
return { redirect_to = self:url_for("web.admin.pages", { action="delete", page=self.params.page }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to reported post
|
|
||||||
if self.params.view_report then
|
|
||||||
local report = Reports:get_report_by_id(self.params.report)
|
|
||||||
local board = Boards:get_board(report.board_id)
|
|
||||||
local post = Posts:get_post(board.id, report.post_id)
|
|
||||||
local op = Posts:get_thread_op(report.thread_id)
|
|
||||||
|
|
||||||
return { redirect_to = self:url_for("web.boards.thread", { board=board.name, thread=op.post_id, anchor="p", id=post.post_id }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Redirect to delete report page
|
|
||||||
if self.params.delete_report then
|
|
||||||
return { redirect_to = self:url_for("web.admin.reports", { action="delete", report=self.params.report }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Regenerate thumbnails
|
|
||||||
if self.params.regen_thumbs then
|
|
||||||
Boards:regen_thumbs()
|
|
||||||
end
|
|
||||||
|
|
||||||
return { render = "admin.admin" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { render = "admin.login" }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,164 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
local Boards = require "models.boards"
|
|
||||||
local Pages = require "models.pages"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
-- Get all board data
|
|
||||||
self.boards = Boards:get_boards()
|
|
||||||
|
|
||||||
-- Get all page data
|
|
||||||
self.pages = Pages:get_pages()
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("admin_panel")
|
|
||||||
|
|
||||||
-- Verify Authorization
|
|
||||||
if self.session.name then
|
|
||||||
if not self.session.admin then
|
|
||||||
assert_error(false, "err_not_admin")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display creation form
|
|
||||||
if self.params.action == "create" then
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("create_page")
|
|
||||||
)
|
|
||||||
self.page = self.params
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display modification form
|
|
||||||
if self.params.action == "modify" then
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("modify_page")
|
|
||||||
)
|
|
||||||
self.page = Pages:get_page(self.params.page)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete page
|
|
||||||
if self.params.action == "delete" then
|
|
||||||
local page = Pages:get_page(self.params.page)
|
|
||||||
assert_error(Pages:delete_page(page))
|
|
||||||
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("deleted_page", { page.slug, page.title })
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "create" then
|
|
||||||
return { render = "admin.page" }
|
|
||||||
elseif self.params.action == "modify" then
|
|
||||||
return { render = "admin.page" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.admin" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
GET = function(self)
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "create" then
|
|
||||||
return { render = "admin.page" }
|
|
||||||
elseif self.params.action == "modify" then
|
|
||||||
return { render = "admin.page" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
POST = function(self)
|
|
||||||
-- Validate CSRF token
|
|
||||||
csrf.assert_token(self)
|
|
||||||
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "slug", max_length=255, exists=true },
|
|
||||||
{ "title", max_length=255, exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Create new page
|
|
||||||
if self.params.create_page then
|
|
||||||
local sl = string.lower
|
|
||||||
-- Verify unique names
|
|
||||||
for _, page in ipairs(self.pages) do
|
|
||||||
if sl(page.slug) == sl(self.params.slug) then
|
|
||||||
assert_error(false, "err_slug_used")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create page
|
|
||||||
local page = assert_error(Pages:create_page(self.params))
|
|
||||||
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("created_page", { page.slug, page.title })
|
|
||||||
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Modify page
|
|
||||||
if self.params.modify_page then
|
|
||||||
local discard = {
|
|
||||||
"page",
|
|
||||||
"modify_page",
|
|
||||||
"ip",
|
|
||||||
"action",
|
|
||||||
"csrf_token",
|
|
||||||
"old"
|
|
||||||
}
|
|
||||||
|
|
||||||
local page = Pages:get_page(self.params.old)
|
|
||||||
|
|
||||||
-- Fill in board with new data
|
|
||||||
for k, param in pairs(self.params) do
|
|
||||||
page[k] = param
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get rid of form trash
|
|
||||||
for _, param in ipairs(discard) do
|
|
||||||
page[param] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_error(Pages:modify_page(page))
|
|
||||||
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("modified_page", { page.slug, page.title })
|
|
||||||
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { redirect_to = self:url_for("web.admin.index") }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
local Boards = require "models.boards"
|
|
||||||
local Reports = require "models.reports"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
-- Get data
|
|
||||||
self.boards = Boards:get_boards()
|
|
||||||
self.reports = Reports:get_reports()
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("admin_panel")
|
|
||||||
|
|
||||||
-- Verify Authorization
|
|
||||||
if self.session.name then
|
|
||||||
if not self.session.admin then
|
|
||||||
assert_error(false, "err_not_admin")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete report
|
|
||||||
if self.params.action == "delete" then
|
|
||||||
local report = Reports:get_report_by_id(self.params.report)
|
|
||||||
assert_error(Reports:delete_report(report))
|
|
||||||
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("deleted_report", { report.id })
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.admin" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
GET = function(self)
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
POST = function(self)
|
|
||||||
return { redirect_to = self:url_for("web.admin.index") }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,190 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
local Boards = require "models.boards"
|
|
||||||
local Users = require "models.users"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
-- Get all board data
|
|
||||||
self.boards = Boards:get_boards()
|
|
||||||
|
|
||||||
-- Get all user data
|
|
||||||
self.users = Users:get_users()
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("admin_panel")
|
|
||||||
|
|
||||||
-- Verify Authorization
|
|
||||||
if self.session.name then
|
|
||||||
if not self.session.admin then
|
|
||||||
assert_error(false, "err_not_admin")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display creation form
|
|
||||||
if self.params.action == "create" then
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("create_user")
|
|
||||||
)
|
|
||||||
self.user = self.params
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display modification form
|
|
||||||
if self.params.action == "modify" then
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("modify_user")
|
|
||||||
)
|
|
||||||
self.user = Users:get_user_by_id(self.params.user)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete user
|
|
||||||
if self.params.action == "delete" then
|
|
||||||
local user = Users:get_user_by_id(self.params.user)
|
|
||||||
assert_error(Users:delete_user(user))
|
|
||||||
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("deleted_user", { user.username })
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "create" then
|
|
||||||
return { render = "admin.user" }
|
|
||||||
elseif self.params.action == "modify" then
|
|
||||||
return { render = "admin.user" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.admin" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
GET = function(self)
|
|
||||||
if not self.session.name then
|
|
||||||
return { render = "admin.login" }
|
|
||||||
elseif self.params.action == "create" then
|
|
||||||
return { render = "admin.user" }
|
|
||||||
elseif self.params.action == "modify" then
|
|
||||||
return { render = "admin.user" }
|
|
||||||
elseif self.params.action == "delete" then
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
POST = function(self)
|
|
||||||
-- Validate CSRF token
|
|
||||||
csrf.assert_token(self)
|
|
||||||
|
|
||||||
-- Create new user
|
|
||||||
if self.params.create_user then
|
|
||||||
local sl = string.lower
|
|
||||||
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "username", exists=true, max_length=255 },
|
|
||||||
{ "new_password", exists=true, equals=self.params.retype_password },
|
|
||||||
{ "retype_password", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
self.params.password = self.params.new_password
|
|
||||||
|
|
||||||
-- Verify unique name
|
|
||||||
for _, user in ipairs(self.users) do
|
|
||||||
if sl(user.username) == sl(self.params.username) then
|
|
||||||
assert_error(false, "err_user_used")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create user
|
|
||||||
local user = assert_error(Users:create_user(self.params))
|
|
||||||
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("created_user", { user.username })
|
|
||||||
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Modify user
|
|
||||||
if self.params.modify_user then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "username", exists=true, max_length=255 },
|
|
||||||
{ "new_password", equals=self.params.retype_password },
|
|
||||||
{ "retype_password", }
|
|
||||||
})
|
|
||||||
|
|
||||||
local discard = {
|
|
||||||
"user",
|
|
||||||
"modify_user",
|
|
||||||
"ip",
|
|
||||||
"action",
|
|
||||||
"csrf_token",
|
|
||||||
"old_password",
|
|
||||||
"new_password",
|
|
||||||
"retype_password",
|
|
||||||
}
|
|
||||||
|
|
||||||
local user = Users:get_user(self.params.username)
|
|
||||||
|
|
||||||
-- Validate user
|
|
||||||
if #self.params.old_password > 0 then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "new_password", exists=true },
|
|
||||||
{ "retype_password", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- TODO: verify user's old password in non-admin setting
|
|
||||||
|
|
||||||
self.params.password = self.params.new_password
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Fill in board with new data
|
|
||||||
for k, param in pairs(self.params) do
|
|
||||||
user[k] = param
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get rid of form trash
|
|
||||||
for _, param in ipairs(discard) do
|
|
||||||
user[param] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_error(Users:modify_user(user))
|
|
||||||
|
|
||||||
self.page_title = string.format(
|
|
||||||
"%s - %s",
|
|
||||||
self.i18n("admin_panel"),
|
|
||||||
self.i18n("success")
|
|
||||||
)
|
|
||||||
self.action = self.i18n("modified_user", { user.username })
|
|
||||||
|
|
||||||
return { render = "admin.success" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { redirect_to = self:url_for("web.admin.index") }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
local lapis = require "lapis"
|
|
||||||
local r2 = require("lapis.application").respond_to
|
|
||||||
local app = lapis.Application()
|
|
||||||
app.__base = app
|
|
||||||
app.name = "web.boards."
|
|
||||||
app.path = "/board"
|
|
||||||
app.handle_404 = require "apps.web.internal.code_404"
|
|
||||||
|
|
||||||
app:match("board", "/:uri_name(/page/:page)", r2(require "apps.web.boards.board"))
|
|
||||||
app:match("catalog", "/:uri_name/catalog", r2(require "apps.web.boards.catalog"))
|
|
||||||
app:match("archive", "/:uri_name/archive", require "apps.web.boards.archive")
|
|
||||||
app:match("thread", "/:uri_name/thread/:thread(#:anchor:id)", r2(require "apps.web.boards.thread"))
|
|
||||||
|
|
||||||
return app
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
local format = require "utils.text_formatter"
|
|
||||||
local Posts = require "models.posts"
|
|
||||||
local Threads = require "models.threads"
|
|
||||||
|
|
||||||
return function(self)
|
|
||||||
|
|
||||||
-- Get board
|
|
||||||
for _, board in ipairs(self.boards) do
|
|
||||||
if board.name == self.params.uri_name then
|
|
||||||
self.board = board
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Board not found
|
|
||||||
if not self.board then
|
|
||||||
return self:write({ redirect_to = self:url_for("web.pages.index") })
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get announcements
|
|
||||||
-- TODO: Consolidate these into a single call
|
|
||||||
self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" })))
|
|
||||||
local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name })))
|
|
||||||
for _, announcement in ipairs(board_announcements) do
|
|
||||||
table.insert(self.announcements, announcement)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = string.format(
|
|
||||||
"/%s/ - %s",
|
|
||||||
self.board.name,
|
|
||||||
self.board.title
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Nav links link to sub page if available
|
|
||||||
self.sub_page = "archive"
|
|
||||||
|
|
||||||
-- Get threads
|
|
||||||
self.threads = assert_error(capture.get(self:url_for("api.boards.archived", { uri_name=self.params.uri_name })))
|
|
||||||
|
|
||||||
-- Get time
|
|
||||||
self.days = math.floor(self.board.archive_time / 24 / 60 / 60)
|
|
||||||
|
|
||||||
-- Get stats
|
|
||||||
for _, thread in ipairs(self.threads) do
|
|
||||||
thread.op = Posts:get_thread_op(thread.id)
|
|
||||||
thread.replies = Posts:count_posts(thread.id) - 1
|
|
||||||
thread.url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=thread.op.post_id })
|
|
||||||
|
|
||||||
-- Process name
|
|
||||||
thread.op.name = thread.op.name or self.board.anon_name
|
|
||||||
|
|
||||||
-- Process tripcode
|
|
||||||
thread.op.trip = thread.op.trip or ""
|
|
||||||
|
|
||||||
-- Process comment
|
|
||||||
if thread.op.comment then
|
|
||||||
local comment = thread.op.comment
|
|
||||||
comment = format.sanitize(comment)
|
|
||||||
comment = format.spoiler(comment)
|
|
||||||
|
|
||||||
if #comment > 110 then
|
|
||||||
comment = comment:sub(1,100)
|
|
||||||
comment = comment .. "..."
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.op.comment = comment
|
|
||||||
else
|
|
||||||
thread.op.comment = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return { render = "archive" }
|
|
||||||
end
|
|
||||||
@ -1,242 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
local format = require "utils.text_formatter"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
local process = require "utils.request_processor"
|
|
||||||
local Posts = require "models.posts"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
|
|
||||||
-- Get board
|
|
||||||
for _, board in ipairs(self.boards) do
|
|
||||||
if board.name == self.params.uri_name then
|
|
||||||
self.board = board
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Board not found
|
|
||||||
if not self.board or self.params.page and not tonumber(self.params.page) then
|
|
||||||
return self:write({ redirect_to = self:url_for("web.pages.index") })
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get announcements
|
|
||||||
-- TODO: Consolidate these into a single call
|
|
||||||
self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" })))
|
|
||||||
local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name })))
|
|
||||||
for _, announcement in ipairs(board_announcements) do
|
|
||||||
table.insert(self.announcements, announcement)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = string.format(
|
|
||||||
"/%s/ - %s",
|
|
||||||
self.board.name,
|
|
||||||
self.board.title
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Flag comments as required or not
|
|
||||||
self.comment_flag = self.board.thread_comment
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Current page
|
|
||||||
self.params.page = self.params.page or 1
|
|
||||||
|
|
||||||
-- Get threads
|
|
||||||
local response = assert_error(capture.get(self:url_for("api.boards.threads", { uri_name=self.params.uri_name, uri_page=self.params.page })))
|
|
||||||
self.threads = response.threads
|
|
||||||
self.pages = response.pages
|
|
||||||
|
|
||||||
-- Get posts
|
|
||||||
for _, thread in ipairs(self.threads) do
|
|
||||||
-- Get posts visible on the board index
|
|
||||||
thread.posts = Posts:get_index_posts(thread.id)
|
|
||||||
|
|
||||||
-- Get hidden posts
|
|
||||||
thread.hidden = Posts:count_hidden_posts(thread.id)
|
|
||||||
|
|
||||||
-- Get op
|
|
||||||
local op = thread.posts[#thread.posts]
|
|
||||||
if not op then
|
|
||||||
assert_error(false, { "err_orphaned", { thread.id } })
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id })
|
|
||||||
|
|
||||||
-- Format comments
|
|
||||||
for _, post in ipairs(thread.posts) do
|
|
||||||
-- OP gets a thread tag
|
|
||||||
if post.post_id == op.post_id then
|
|
||||||
post.thread = post.post_id
|
|
||||||
end
|
|
||||||
|
|
||||||
post.name = post.name or self.board.anon_name
|
|
||||||
post.reply = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="q", id=post.post_id })
|
|
||||||
post.link = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="p", id=post.post_id })
|
|
||||||
post.remix = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="r", id=post.post_id })
|
|
||||||
post.timestamp = os.date("%Y-%m-%d (%a) %H:%M:%S", post.timestamp)
|
|
||||||
post.file_size = math.floor(post.file_size / 1024)
|
|
||||||
post.file_dimensions = ""
|
|
||||||
|
|
||||||
if post.file_width > 0 and post.file_height > 0 then
|
|
||||||
post.file_dimensions = string.format(", %dx%d", post.file_width, post.file_height)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not post.file_duration or post.file_duration == "0" then
|
|
||||||
post.file_duration = ""
|
|
||||||
else
|
|
||||||
post.file_duration = string.format(", %s", post.file_duration)
|
|
||||||
end
|
|
||||||
|
|
||||||
if post.file_path then
|
|
||||||
local name, ext = post.file_path:match("^(.+)(%..+)$")
|
|
||||||
ext = string.lower(ext)
|
|
||||||
|
|
||||||
-- Get thumbnail URL
|
|
||||||
if post.file_type == "audio" then
|
|
||||||
if post == thread.posts[#thread.posts] then
|
|
||||||
post.thumb = self:format_url(self.static_url, "op_audio.png")
|
|
||||||
else
|
|
||||||
post.thumb = self:format_url(self.static_url, "post_audio.png")
|
|
||||||
end
|
|
||||||
elseif post.file_type == "image" then
|
|
||||||
if post.file_spoiler then
|
|
||||||
if post == thread.posts[#thread.posts] then
|
|
||||||
post.thumb = self:format_url(self.static_url, "op_spoiler.png")
|
|
||||||
else
|
|
||||||
post.thumb = self:format_url(self.static_url, "post_spoiler.png")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if ext == ".webm" or ext == ".svg" then
|
|
||||||
post.thumb = self:format_url(self.files_url, self.board.name, 's' .. name .. '.png')
|
|
||||||
else
|
|
||||||
post.thumb = self:format_url(self.files_url, self.board.name, 's' .. post.file_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
post.file_path = self:format_url(self.files_url, self.board.name, post.file_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Process comment
|
|
||||||
if post.comment then
|
|
||||||
local comment = post.comment
|
|
||||||
comment = format.sanitize(comment)
|
|
||||||
comment = format.quote(comment, self, self.board, post)
|
|
||||||
comment = format.green_text(comment)
|
|
||||||
comment = format.blue_text(comment)
|
|
||||||
comment = format.spoiler(comment)
|
|
||||||
comment = format.new_lines(comment)
|
|
||||||
post.comment = comment
|
|
||||||
else
|
|
||||||
post.comment = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
return { render = "board"}
|
|
||||||
end,
|
|
||||||
GET = function()
|
|
||||||
return { render = "board" }
|
|
||||||
end,
|
|
||||||
POST = function(self)
|
|
||||||
-- Validate CSRF token
|
|
||||||
csrf.assert_token(self)
|
|
||||||
|
|
||||||
local board_url = self:url_for("web.boards.board", { uri_name=self.board.name })
|
|
||||||
|
|
||||||
-- Submit new thread
|
|
||||||
if self.params.submit then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "name", max_length=255 },
|
|
||||||
{ "subject", max_length=255 },
|
|
||||||
{ "options", max_length=255 },
|
|
||||||
{ "comment", max_length=self.text_size }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate post
|
|
||||||
local post = assert_error(process.create_thread(self.params, self.session, self.board))
|
|
||||||
return { redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=post.post_id, anchor="p", id=post.post_id }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete thread
|
|
||||||
if self.params.delete and self.params.thread_id then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "post_id", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate deletion
|
|
||||||
assert_error(process.delete_thread(self.params, self.session, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete post
|
|
||||||
if self.params.delete and not self.params.thread_id then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "post_id", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate deletion
|
|
||||||
assert_error(process.delete_post(self.params, self.session, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Report post
|
|
||||||
if self.params.report then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "board", exists=true },
|
|
||||||
{ "post_id", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate report
|
|
||||||
assert_error(process.report_post(self.params, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Admin commands
|
|
||||||
if self.session.admin or self.session.mod then
|
|
||||||
-- Sticky thread
|
|
||||||
if self.params.sticky then
|
|
||||||
assert_error(process.sticky_thread(self.params, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lock thread
|
|
||||||
if self.params.lock then
|
|
||||||
assert_error(process.lock_thread(self.params, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save thread
|
|
||||||
if self.params.save then
|
|
||||||
assert_error(process.save_thread(self.params, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Override thread
|
|
||||||
if self.params.override then
|
|
||||||
assert_error(process.override_thread(self.params, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Ban user
|
|
||||||
if self.params.ban then
|
|
||||||
assert_error(process.ban_user(self.params, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local process = require "utils.request_processor"
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local format = require "utils.text_formatter"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
local Posts = require "models.posts"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
|
|
||||||
-- Get board
|
|
||||||
for _, board in ipairs(self.boards) do
|
|
||||||
if board.name == self.params.uri_name then
|
|
||||||
self.board = board
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Board not found
|
|
||||||
if not self.board then
|
|
||||||
return self:write({ redirect_to = self:url_for("web.pages.index") })
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get announcements
|
|
||||||
-- TODO: Consolidate these into a single call
|
|
||||||
self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" })))
|
|
||||||
local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name })))
|
|
||||||
for _, announcement in ipairs(board_announcements) do
|
|
||||||
table.insert(self.announcements, announcement)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = string.format(
|
|
||||||
"/%s/ - %s",
|
|
||||||
self.board.name,
|
|
||||||
self.board.title
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Nav links link to sub page if available
|
|
||||||
self.sub_page = "catalog"
|
|
||||||
|
|
||||||
-- Flag comments as required or not
|
|
||||||
self.comment_flag = self.board.thread_comment
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Get threads
|
|
||||||
local response = assert_error(capture.get(self:url_for("api.boards.threads", { uri_name=self.params.uri_name })))
|
|
||||||
self.threads = response.threads
|
|
||||||
|
|
||||||
-- Get stats
|
|
||||||
for _, thread in ipairs(self.threads) do
|
|
||||||
thread.op = Posts:get_thread_op(thread.id)
|
|
||||||
thread.replies = Posts:count_posts(thread.id) - 1
|
|
||||||
thread.files = Posts:count_files(thread.id)
|
|
||||||
thread.url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=thread.op.post_id })
|
|
||||||
|
|
||||||
if thread.op.file_path then
|
|
||||||
local name, ext = thread.op.file_path:match("^(.+)(%..+)$")
|
|
||||||
ext = string.lower(ext)
|
|
||||||
|
|
||||||
-- Get thumbnail URL
|
|
||||||
if thread.op.file_type == "audio" then
|
|
||||||
thread.op.thumb = self:format_url(self.static_url, "post_audio.png")
|
|
||||||
elseif thread.op.file_type == "image" then
|
|
||||||
if thread.op.file_spoiler then
|
|
||||||
thread.op.thumb = self:format_url(self.static_url, "post_spoiler.png")
|
|
||||||
else
|
|
||||||
if ext == ".webm" or ext == ".svg" then
|
|
||||||
thread.op.thumb = self:format_url(self.files_url, self.board.name, 's' .. name .. '.png')
|
|
||||||
else
|
|
||||||
thread.op.thumb = self:format_url(self.files_url, self.board.name, 's' .. thread.op.file_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.op.file_path = self:format_url(self.files_url, self.board.name, thread.op.file_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Process comment
|
|
||||||
if thread.op.comment then
|
|
||||||
local comment = thread.op.comment
|
|
||||||
comment = format.sanitize(comment)
|
|
||||||
comment = format.spoiler(comment)
|
|
||||||
comment = format.new_lines(comment)
|
|
||||||
|
|
||||||
if #comment > 260 then
|
|
||||||
comment = comment:sub(1, 250) .. "..."
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.op.comment = comment
|
|
||||||
else
|
|
||||||
thread.op.comment = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
return { render = "catalog"}
|
|
||||||
end,
|
|
||||||
GET = function()
|
|
||||||
return { render = "catalog" }
|
|
||||||
end,
|
|
||||||
POST = function(self)
|
|
||||||
-- Validate CSRF token
|
|
||||||
csrf.assert_token(self)
|
|
||||||
|
|
||||||
-- Submit new thread
|
|
||||||
if self.params.submit and not self.thread then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "name", max_length=255 },
|
|
||||||
{ "subject", max_length=255 },
|
|
||||||
{ "options", max_length=255 },
|
|
||||||
{ "comment", max_length=self.text_size }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate post
|
|
||||||
local post = assert_error(process.create_thread(self.params, self.session, self.board))
|
|
||||||
return { redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=post.post_id, anchor="p", id=post.post_id }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { redirect_to = self:url_for("web.boards.catalog", { uri_name=self.board.name }) }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,242 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local assert_valid = require("lapis.validate").assert_valid
|
|
||||||
local csrf = require "lapis.csrf"
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
local format = require "utils.text_formatter"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
local process = require "utils.request_processor"
|
|
||||||
local Posts = require "models.posts"
|
|
||||||
|
|
||||||
return {
|
|
||||||
before = function(self)
|
|
||||||
|
|
||||||
-- Get board
|
|
||||||
for _, board in ipairs(self.boards) do
|
|
||||||
if board.name == self.params.uri_name then
|
|
||||||
self.board = board
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Board not found
|
|
||||||
if not self.board then
|
|
||||||
return self:write({ redirect_to = self:url_for("web.pages.index") })
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get current thread data
|
|
||||||
local post = Posts:get(self.board.id, self.params.thread)
|
|
||||||
if not post then
|
|
||||||
return self:write({ redirect_to = self:url_for("web.boards.board", { uri_name=self.board.name }) })
|
|
||||||
end
|
|
||||||
|
|
||||||
self.thread = post:get_thread()
|
|
||||||
if not self.thread then
|
|
||||||
return self:write({ redirect_to = self:url_for("web.boards.board", { uri_name=self.board.name }) })
|
|
||||||
end
|
|
||||||
|
|
||||||
local op = self.thread:get_op()
|
|
||||||
if post.post_id ~= op.post_id then
|
|
||||||
return self:write({ redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="p", id=post.post_id }) })
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get announcements
|
|
||||||
-- TODO: Consolidate these into a single call
|
|
||||||
self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" })))
|
|
||||||
local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name })))
|
|
||||||
for _, announcement in ipairs(board_announcements) do
|
|
||||||
table.insert(self.announcements, announcement)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = string.format(
|
|
||||||
"/%s/ - %s",
|
|
||||||
self.board.name,
|
|
||||||
self.board.title
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Flag comments as required or not
|
|
||||||
self.comment_flag = self.board.thread_comment
|
|
||||||
|
|
||||||
-- Generate CSRF token
|
|
||||||
self.csrf_token = csrf.generate_token(self)
|
|
||||||
|
|
||||||
-- Determine if we allow a user to upload a file
|
|
||||||
self.num_files = Posts:count_files(self.thread.id)
|
|
||||||
|
|
||||||
-- Get posts
|
|
||||||
self.posts = self.thread:get_posts()
|
|
||||||
|
|
||||||
-- Format comments
|
|
||||||
for i, post in ipairs(self.posts) do
|
|
||||||
-- OP gets a thread tag
|
|
||||||
if i == 1 then
|
|
||||||
post.thread = post.post_id
|
|
||||||
end
|
|
||||||
|
|
||||||
post.name = post.name or self.board.anon_name
|
|
||||||
post.reply = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=self.posts[1].post_id, anchor="q", id=post.post_id })
|
|
||||||
post.link = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=self.posts[1].post_id, anchor="p", id=post.post_id })
|
|
||||||
post.timestamp = os.date("%Y-%m-%d (%a) %H:%M:%S", post.timestamp)
|
|
||||||
post.file_size = math.floor(post.file_size / 1024)
|
|
||||||
post.file_dimensions = ""
|
|
||||||
|
|
||||||
if post.file_width > 0 and post.file_height > 0 then
|
|
||||||
post.file_dimensions = string.format(", %dx%d", post.file_width, post.file_height)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not post.file_duration or post.file_duration == "0" then
|
|
||||||
post.file_duration = ""
|
|
||||||
else
|
|
||||||
post.file_duration = string.format(", %s", post.file_duration)
|
|
||||||
end
|
|
||||||
|
|
||||||
if post.file_path then
|
|
||||||
local name, ext = post.file_path:match("^(.+)(%..+)$")
|
|
||||||
ext = string.lower(ext)
|
|
||||||
|
|
||||||
-- Get thumbnail URL
|
|
||||||
if post.file_type == "audio" then
|
|
||||||
if post == self.posts[1] then
|
|
||||||
post.thumb = self:format_url(self.static_url, "op_audio.png")
|
|
||||||
else
|
|
||||||
post.thumb = self:format_url(self.static_url, "post_audio.png")
|
|
||||||
end
|
|
||||||
elseif post.file_type == "image" then
|
|
||||||
if post.file_spoiler then
|
|
||||||
if post == self.posts[1] then
|
|
||||||
post.thumb = self:format_url(self.static_url, "op_spoiler.png")
|
|
||||||
else
|
|
||||||
post.thumb = self:format_url(self.static_url, "post_spoiler.png")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if ext == ".webm" or ext == ".svg" then
|
|
||||||
post.thumb = self:format_url(self.files_url, self.board.name, 's' .. name .. '.png')
|
|
||||||
else
|
|
||||||
post.thumb = self:format_url(self.files_url, self.board.name, 's' .. post.file_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
post.file_path = self:format_url(self.files_url, self.board.name, post.file_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Process comment
|
|
||||||
if post.comment then
|
|
||||||
local comment = post.comment
|
|
||||||
comment = format.sanitize(comment)
|
|
||||||
comment = format.quote(comment, self, self.board, post)
|
|
||||||
comment = format.green_text(comment)
|
|
||||||
comment = format.blue_text(comment)
|
|
||||||
comment = format.spoiler(comment)
|
|
||||||
comment = format.new_lines(comment)
|
|
||||||
post.comment = comment
|
|
||||||
else
|
|
||||||
post.comment = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_error = function(self)
|
|
||||||
self.errors = generate.errors(self.i18n, self.errors)
|
|
||||||
return { render = "thread"}
|
|
||||||
end,
|
|
||||||
GET = function()
|
|
||||||
return { render = "thread" }
|
|
||||||
end,
|
|
||||||
POST = function(self)
|
|
||||||
-- Validate CSRF token
|
|
||||||
csrf.assert_token(self)
|
|
||||||
|
|
||||||
local board_url = self:url_for("web.boards.board", { uri_name=self.board.name })
|
|
||||||
local thread_url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=self.posts[1].post_id })
|
|
||||||
|
|
||||||
-- Submit new post
|
|
||||||
if self.params.submit and self.thread then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "thread", exists=true },
|
|
||||||
{ "name", max_length=255 },
|
|
||||||
{ "subject", max_length=255 },
|
|
||||||
{ "options", max_length=255 },
|
|
||||||
{ "comment", max_length=self.text_size }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate post
|
|
||||||
local post = assert_error(process.create_post(self.params, self.session, self.board, self.thread))
|
|
||||||
return { redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=self.posts[1].post_id, anchor="p", id=post.post_id }) }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete thread
|
|
||||||
if self.params.delete and self.params.thread_id then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "post_id", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate deletion
|
|
||||||
assert_error(process.delete_thread(self.params, self.session, self.board))
|
|
||||||
return { redirect_to = board_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete post
|
|
||||||
if self.params.delete and not self.params.thread_id then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "post_id", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate deletion
|
|
||||||
assert_error(process.delete_post(self.params, self.session, self.board))
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Report post
|
|
||||||
if self.params.report then
|
|
||||||
-- Validate user input
|
|
||||||
assert_valid(self.params, {
|
|
||||||
{ "board", exists=true },
|
|
||||||
{ "post_id", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Validate report
|
|
||||||
assert_error(process.report_post(self.params, self.board))
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Admin commands
|
|
||||||
if self.session.admin or self.session.mod then
|
|
||||||
-- Sticky thread
|
|
||||||
if self.params.sticky then
|
|
||||||
assert_error(process.sticky_thread(self.params, self.board))
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lock thread
|
|
||||||
if self.params.lock then
|
|
||||||
assert_error(process.lock_thread(self.params, self.board))
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save thread
|
|
||||||
if self.params.save then
|
|
||||||
assert_error(process.save_thread(self.params, self.board))
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Override thread
|
|
||||||
if self.params.override then
|
|
||||||
assert_error(process.override_thread(self.params, self.board))
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Ban user
|
|
||||||
if self.params.ban then
|
|
||||||
assert_error(process.ban_user(self.params, self.board))
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
|
|
||||||
return { redirect_to = thread_url }
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
local Users = require "models.users"
|
|
||||||
|
|
||||||
return function(self)
|
|
||||||
-- Prepare session names
|
|
||||||
self.session.names = self.session.names or {}
|
|
||||||
|
|
||||||
-- Verify Authorization
|
|
||||||
if self.session.name then
|
|
||||||
local user = Users:get_user(self.session.name)
|
|
||||||
|
|
||||||
if user then
|
|
||||||
user.password = nil
|
|
||||||
self.session.admin = user.admin
|
|
||||||
self.session.mod = user.mod
|
|
||||||
self.session.janitor = user.janitor
|
|
||||||
else
|
|
||||||
self.session.admin = nil
|
|
||||||
self.session.mod = nil
|
|
||||||
self.session.janitor = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.session.admin = nil
|
|
||||||
self.session.mod = nil
|
|
||||||
self.session.janitor = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get IP from ngx
|
|
||||||
self.params.ip = self.req.headers["X-Real-IP"] or self.req.remote_addr
|
|
||||||
end
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
|
|
||||||
return function(self)
|
|
||||||
-- MODS = FAGS
|
|
||||||
if self.session.admin or
|
|
||||||
self.session.mod or
|
|
||||||
self.session.janitor or
|
|
||||||
self.route_name == "admin" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get list of bans by ip
|
|
||||||
local bans = assert_error(capture.get(self:url_for("api.bans.bans_ip", { uri_ip=self.params.ip })))
|
|
||||||
|
|
||||||
-- Get current board
|
|
||||||
local board = {}
|
|
||||||
if self.params.uri_name then
|
|
||||||
board = assert_error(capture.get(self:url_for("api.boards.board", { uri_name=self.params.uri_name })))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If you are banned, gtfo
|
|
||||||
for _, ban in ipairs(bans) do
|
|
||||||
if ban.board_id == 0 or
|
|
||||||
ban.board_id == board.id then
|
|
||||||
|
|
||||||
-- Ban data
|
|
||||||
self.ip = ban.ip
|
|
||||||
self.reason = ban.reason or self.i18n("err_ban_reason")
|
|
||||||
self.expire = os.date("%Y-%m-%d (%a) %H:%M:%S", ban.time + ban.duration)
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("ban_title")
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
return self:write({ render = "banned" })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
local Boards = require "models.boards"
|
|
||||||
|
|
||||||
return function(self)
|
|
||||||
-- Get all board data
|
|
||||||
self.boards = Boards:get_boards()
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("404")
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
return { render = "code_404", status = "404" }
|
|
||||||
end
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
local config = require("lapis.config").get()
|
|
||||||
local assert_error = require("lapis.application").assert_error
|
|
||||||
local i18n = require "i18n"
|
|
||||||
local lfs = require "lfs"
|
|
||||||
local capture = require "utils.capture"
|
|
||||||
|
|
||||||
return function(self)
|
|
||||||
-- Set basic information
|
|
||||||
self.software = "Lapis-chan"
|
|
||||||
self.version = "1.2.5"
|
|
||||||
self.site_name = config.site_name
|
|
||||||
self.text_size = _G.text_size
|
|
||||||
|
|
||||||
-- Get localization files
|
|
||||||
self.locales = {}
|
|
||||||
for file in lfs.dir("src/locale") do
|
|
||||||
local name, ext = string.match(file, "^(.+)%.(.+)$")
|
|
||||||
if ext == "lua" then
|
|
||||||
table.insert(self.locales, name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set localization
|
|
||||||
if self.params.locale then
|
|
||||||
self.session.locale = self.params.locale
|
|
||||||
end
|
|
||||||
|
|
||||||
i18n.setLocale(self.session.locale or "en")
|
|
||||||
i18n.loadFile("src/locale/en.lua")
|
|
||||||
if i18n.getLocale() ~= "en" then
|
|
||||||
i18n.loadFile("locale/" .. i18n.getLocale() .. ".lua")
|
|
||||||
end
|
|
||||||
self.i18n = i18n
|
|
||||||
|
|
||||||
-- Get all boards
|
|
||||||
self.boards = assert_error(capture.get(self:url_for("api.boards.boards")))
|
|
||||||
|
|
||||||
-- Static
|
|
||||||
self.static_url = "/static/%s"
|
|
||||||
self.files_url = "/files/%s/%s"
|
|
||||||
|
|
||||||
function self.format_url(_, pattern, ...)
|
|
||||||
return self:build_url(string.format(pattern, ...))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,333 +0,0 @@
|
|||||||
local Users = require "models.users"
|
|
||||||
local Boards = require "models.boards"
|
|
||||||
local Pages = require "models.pages"
|
|
||||||
local lfs = require "lfs"
|
|
||||||
local validate = require("lapis.validate").validate
|
|
||||||
local faq = [[
|
|
||||||
<div class="table_of_contents">
|
|
||||||
<ol>
|
|
||||||
<li><a href="#what-is-lapis-chan">What is Lapis-chan?</a></li>
|
|
||||||
<li><a href="#what-is-lapchan">What is Lapchan?</a></li>
|
|
||||||
<li><a href="#what-should-i-know">What should I know before posting?</a></li>
|
|
||||||
<li><a href="#chan-101">What are the basics?</a></li>
|
|
||||||
<li><a href="#anonymous">How do I post anonymously?</a></li>
|
|
||||||
<li><a href="#image">Do I have to post an image?</a></li>
|
|
||||||
<li><a href="#quote">How do I quote a post?</a></li>
|
|
||||||
<li><a href="#tripcode">What is a tripcode?</a></li>
|
|
||||||
<li><a href="#spoiler">Can I mark an image as a spoiler?</a></li>
|
|
||||||
<li><a href="#post-options">What are post options?</a></li>
|
|
||||||
<li><a href="#post-menu">How can I interact with posts?</a></li>
|
|
||||||
<li><a href="#board-types">What types of boards are supported?</a></li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
<div class="answers">
|
|
||||||
<div id="what-is-lapis-chan">
|
|
||||||
<h2>What is Lapis-chan?</h2>
|
|
||||||
<p>
|
|
||||||
Lapis-chan is an open source imageboard web application written in Lua
|
|
||||||
using the Lapis web framework.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="what-is-lapchan">
|
|
||||||
<h2>What is Lapchan?</h2>
|
|
||||||
<p>
|
|
||||||
Lapchan is a website that runs the latest version of Lapis-chan. It is
|
|
||||||
both used as a small community and a testing platform for new and
|
|
||||||
experimental features.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="what-should-i-know">
|
|
||||||
<h2>What Should I Know Before Posting?</h2>
|
|
||||||
<p>
|
|
||||||
Before posting, you should read the rules for whichever board you want to
|
|
||||||
post in. If you break the rules, your post may be deleted and you may
|
|
||||||
also earn yourself a temporary or permanent ban from the board or entire
|
|
||||||
website. Please read the rules!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="chan-101">
|
|
||||||
<h2>What are the Basics?</h2>
|
|
||||||
<p>
|
|
||||||
In general, "blue" boards are considered safe for work (SFW) and "red"
|
|
||||||
boards are considered not safe for work (NSFW). The definition of
|
|
||||||
work-safety is often loose, but in general it means there shouldn't be
|
|
||||||
any direct pornographic material on a blue board. This may differ from
|
|
||||||
site to site, but Lapis-chan by default offers blue and red themes that
|
|
||||||
are nearly identical to 4chan's themes. New themes are expected in later
|
|
||||||
releases.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
It is also worth noting that chan culture can be both friendly and
|
|
||||||
abrasive. More often than not, users will be posting anonymously and are
|
|
||||||
able to speak freely because if this. Be prepared for the best and worst
|
|
||||||
of society when interacting with others in an anonymous forum.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="anonymous">
|
|
||||||
<h2>How Do I Post Anonymously?</h2>
|
|
||||||
<p>
|
|
||||||
By default, all users are anonymous to each other. By leaving the "Name"
|
|
||||||
field empty in a post, your name will simply be fille din with the
|
|
||||||
board's default name. Identifiable information such as your IP address is
|
|
||||||
recorded to the Lapis-chan database when you post for legal reasons, but
|
|
||||||
all posts are permanently purged after some time unless otherwise noted,
|
|
||||||
including your IP address and any other information attached to your
|
|
||||||
post.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="image">
|
|
||||||
<h2>Do I Have to Post an Image?</h2>
|
|
||||||
<p>
|
|
||||||
Some boards require you to post an image or a comment, others do not. By
|
|
||||||
default, Lapis-chan will place "(Required)" in or near a field that
|
|
||||||
requires data. Currently, Lapis-chan's only optionally required data
|
|
||||||
include a comment and an image.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="quote">
|
|
||||||
<h2>How Do I Quote a Post?</h2>
|
|
||||||
<p>
|
|
||||||
To quote (and link to) another post, simply type ">>" followed by
|
|
||||||
the post number (e.g. >>2808). To quote a post that is on a
|
|
||||||
different board, You must type ">>>" follow by a slash, the
|
|
||||||
name of the board, another slash, and then the post number
|
|
||||||
(e.g. >>>/a/2808).
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="tripcode">
|
|
||||||
<h2>What is a Tripcode?</h2>
|
|
||||||
<p>
|
|
||||||
A tripcode is a uniquely identifiable hash attached to the end of your
|
|
||||||
name, or in lieu of a name. It is a completely optional feature that
|
|
||||||
allows users to de-anonymize if they so choose. Some boards benefit from
|
|
||||||
de-anonymization such as content-creation boards where being named can
|
|
||||||
help get your content seen and recognized.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Tripcodes come in two sizes: insecure and secure. Insecure tripcodes use
|
|
||||||
a weak hashing method that allows users to game the algorithm to generate
|
|
||||||
a hash that reads out something similar to what they want. A secure
|
|
||||||
tripcode uses a very secure hashing algorithm and a server-specific
|
|
||||||
secret token that is not gameable, but also significantly more difficult
|
|
||||||
to impersonate.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
To use an insecure tripcode, place a hash ("#") sign at the end of your
|
|
||||||
name (or leave a name out entirely) and type your password after the
|
|
||||||
hash. To use a secure tripcode, simply use two hashes instead of one
|
|
||||||
(e.g. lapchan#insecure, lapchan##secure, #nameless, ##nameless).
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="spoiler">
|
|
||||||
<h2>Can I Mark an Image as a Spoiler?</h2>
|
|
||||||
<p>
|
|
||||||
Yes. When you upload an image, there should be a check box beside the
|
|
||||||
file input field. Checking that box will replace the thumbnail of your
|
|
||||||
image with a spoiler image.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
You can also tag text within your post as a spoiler by writing your text
|
|
||||||
with [spoiler]a spoiler tag[/spoiler].
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="post-options">
|
|
||||||
<h2>What Are Post Options?</h2>
|
|
||||||
<p>
|
|
||||||
Post options are optional features you can use to modify how your post
|
|
||||||
affects the thread or board you are posting in. To apply an options,
|
|
||||||
simply type the option code into the options field in your post.
|
|
||||||
Currently, the options available include:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>sage - Do not bump the thread with your post.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="post-menu">
|
|
||||||
<h2>How Can I Interact With Posts?</h2>
|
|
||||||
<p>
|
|
||||||
To interact with a post, click on the menu icon ("▶") on the left of the
|
|
||||||
post. The menu currently has the following interactions:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Report Post - Report a post to moderators that you believe is breaking
|
|
||||||
the rules of the board.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Delete Post - Delete your own post. Lapis-chan saves a unique password
|
|
||||||
to your user session when you make your first post and will allow you
|
|
||||||
to delete any post you make as long as you are using the same session.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Remix Image - Draw boards have a remix feature that allows you to copy
|
|
||||||
the image from a post into the drawing canvas and draw on top of it.
|
|
||||||
You can then post your new image in the thread to show off your
|
|
||||||
updated image.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="board-types">
|
|
||||||
<h2>What Types of Boards are Supported?</h2>
|
|
||||||
<p>
|
|
||||||
Lapis-chan has several different types of boards with more planned in the
|
|
||||||
future. Currently, Lapis-chan supports the following boards:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Image boards - Upload images and chat with other people about various
|
|
||||||
topics or sub-topics. Common image boards include discussing your
|
|
||||||
favourite TV shows, video games, or characters within.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Text boards - Strictly text. Common text boards include discussing
|
|
||||||
latest events, breaking news, politics, or writing stories.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Draw boards - Upload, draw, and remix images. Common draw boards
|
|
||||||
include art critiquing and art remixing.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
]]
|
|
||||||
|
|
||||||
local success = [[
|
|
||||||
<h2>
|
|
||||||
Congratulations! Lapis-chan is now installed! Please rename or delete the
|
|
||||||
`install.lua` file to see your new board. Visit "/admin" to get started!
|
|
||||||
<h2>
|
|
||||||
<h2>Thank you for installing Lapis-chan! <3</h2>
|
|
||||||
]]
|
|
||||||
|
|
||||||
return {
|
|
||||||
GET = function(self)
|
|
||||||
self.page_title = "Install Lapis-chan"
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
-- Do we already have data?
|
|
||||||
local users = Users:get_users()
|
|
||||||
local boards = Boards:get_boards()
|
|
||||||
local pages = Pages:get_pages()
|
|
||||||
|
|
||||||
-- We did it!
|
|
||||||
if #users > 0 and #boards > 0 and #pages > 0 then
|
|
||||||
return success
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get list of themes
|
|
||||||
self.themes = {}
|
|
||||||
for file in lfs.dir("."..self.styles_url) do
|
|
||||||
local name, ext = string.match(file, "^(.+)(%..+)$")
|
|
||||||
if name ~= "reset" and
|
|
||||||
name ~= "posts" and
|
|
||||||
name ~= "style" and
|
|
||||||
name ~= "tegaki" and
|
|
||||||
ext == ".css" then
|
|
||||||
table.insert(self.themes, name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return { render = "install" }
|
|
||||||
end,
|
|
||||||
POST = function(self)
|
|
||||||
self.page_title = "Install Lapis-chan"
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
-- Do we already have data?
|
|
||||||
local users = Users:get_users()
|
|
||||||
local boards = Boards:get_boards()
|
|
||||||
local pages = Pages:get_pages()
|
|
||||||
|
|
||||||
-- We did it!
|
|
||||||
if #users > 0 and #boards > 0 and #pages > 0 then
|
|
||||||
return success
|
|
||||||
end
|
|
||||||
|
|
||||||
local errs = validate(self.params, {
|
|
||||||
{ "user_username", exists=true, max_length=255 },
|
|
||||||
{ "user_password", min_length=4, max_length=255 },
|
|
||||||
{ "name", exists=true, max_length=10 },
|
|
||||||
{ "title", min_length=2, max_length=255 },
|
|
||||||
{ "subtext", max_length=255 },
|
|
||||||
{ "rules" },
|
|
||||||
{ "ban_message", max_length=255 },
|
|
||||||
{ "anon_name", max_length=255 },
|
|
||||||
{ "theme", exists=true },
|
|
||||||
{ "pages", exists=true },
|
|
||||||
{ "threads_per_page", exists=true },
|
|
||||||
{ "thread_file_limit", exists=true },
|
|
||||||
{ "post_limit", exists=true },
|
|
||||||
{ "thread_file", exists=true },
|
|
||||||
{ "thread_comment", exists=true },
|
|
||||||
{ "post_file", exists=true },
|
|
||||||
{ "post_comment", exists=true },
|
|
||||||
{ "text_only", exists=true },
|
|
||||||
{ "filetype_image", exists=true },
|
|
||||||
{ "filetype_audio", exists=true },
|
|
||||||
{ "draw", exists=true },
|
|
||||||
{ "archive", exists=true },
|
|
||||||
{ "archive_time", exists=true },
|
|
||||||
{ "group", exists=true }
|
|
||||||
})
|
|
||||||
|
|
||||||
local out
|
|
||||||
if errs then
|
|
||||||
out = "<div class='install'>\n"
|
|
||||||
for _, err in ipairs(errs) do
|
|
||||||
out = out .. "<h2>" .. err .. "</h2>\n"
|
|
||||||
end
|
|
||||||
out = out .. [[
|
|
||||||
<form action="" method="get">
|
|
||||||
<button>Return</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
]]
|
|
||||||
end
|
|
||||||
|
|
||||||
if out then
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add new user
|
|
||||||
Users:create_user {
|
|
||||||
username = self.params.user_username,
|
|
||||||
password = self.params.user_password,
|
|
||||||
admin = true,
|
|
||||||
mod = false,
|
|
||||||
janitor = false
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Add new board
|
|
||||||
Boards:create_board {
|
|
||||||
name = self.params.name,
|
|
||||||
title = self.params.title,
|
|
||||||
subtext = self.params.subtext,
|
|
||||||
rules = self.params.rules,
|
|
||||||
anon_name = self.params.anon_name,
|
|
||||||
theme = self.params.theme,
|
|
||||||
posts = 0,
|
|
||||||
pages = self.params.pages,
|
|
||||||
threads_per_page = self.params.threads_per_page,
|
|
||||||
text_only = self.params.text_only,
|
|
||||||
filetype_image = self.params.filetype_image,
|
|
||||||
filetype_audio = self.params.filetype_audio,
|
|
||||||
draw = self.params.draw,
|
|
||||||
thread_file = self.params.thread_file,
|
|
||||||
thread_comment = self.params.thread_comment,
|
|
||||||
thread_file_limit = self.params.thread_file_limit,
|
|
||||||
post_file = self.params.post_file,
|
|
||||||
post_comment = self.params.post_comment,
|
|
||||||
post_limit = self.params.post_limit,
|
|
||||||
archive = self.params.archive,
|
|
||||||
archive_time = self.params.archive_time * 24 * 60 * 60,
|
|
||||||
group = self.params.group
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Add FAQ page
|
|
||||||
Pages:create_page {
|
|
||||||
title = "Frequently Asked Questions",
|
|
||||||
slug = "faq",
|
|
||||||
content = faq
|
|
||||||
}
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
local lapis = require "lapis"
|
|
||||||
local app = lapis.Application()
|
|
||||||
app.__base = app
|
|
||||||
app.name = "web.pages."
|
|
||||||
app.handle_404 = require "apps.web.internal.code_404"
|
|
||||||
|
|
||||||
app:match("index", "/", require "apps.web.pages.index")
|
|
||||||
app:match("c404", "/404", require "apps.web.internal.code_404") -- FIXME: remove this route
|
|
||||||
app:match("faq", "/faq", require "apps.web.pages.rules") -- FIXME: need a faq page
|
|
||||||
app:match("rules", "/rules", require "apps.web.pages.rules")
|
|
||||||
app:match("logout", "/logout", require "apps.web.pages.logout")
|
|
||||||
app:match("page", "/:page", require "apps.web.pages.page")
|
|
||||||
|
|
||||||
return app
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
return function(self)
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("index")
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
return { render = "index" }
|
|
||||||
end
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
return function(self)
|
|
||||||
-- Logout
|
|
||||||
self.session.name = nil
|
|
||||||
self.session.admin = nil
|
|
||||||
self.session.mod = nil
|
|
||||||
self.session.janitor = nil
|
|
||||||
|
|
||||||
return { redirect_to = self:url_for("web.pages.index") }
|
|
||||||
end
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
local Boards = require "models.boards"
|
|
||||||
local Pages = require "models.pages"
|
|
||||||
local markdown = require "markdown"
|
|
||||||
|
|
||||||
return function(self)
|
|
||||||
-- Get all board data
|
|
||||||
self.boards = Boards:get_boards()
|
|
||||||
|
|
||||||
-- Get page
|
|
||||||
self.page = Pages:get_page(self.params.page)
|
|
||||||
|
|
||||||
if not self.page then
|
|
||||||
return self:write({ redirect_to = self:url_for("web.pages.c404") })
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.page.title
|
|
||||||
|
|
||||||
-- Markdown
|
|
||||||
if self.page.content then
|
|
||||||
self.page.content = markdown(self.page.content)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
return { render = "page" }
|
|
||||||
end
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
return function(self)
|
|
||||||
|
|
||||||
-- Page title
|
|
||||||
self.page_title = self.i18n("rules")
|
|
||||||
|
|
||||||
-- Display a theme
|
|
||||||
self.board = { theme = "yotsuba_b" }
|
|
||||||
|
|
||||||
for _, board in ipairs(self.boards) do
|
|
||||||
board.url = self:url_for("web.boards.board", { board=board.name })
|
|
||||||
if board.rules then
|
|
||||||
board.rules = _G.markdown(board.rules)
|
|
||||||
else
|
|
||||||
board.rules = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return { render = "rules" }
|
|
||||||
end
|
|
||||||
@ -1,234 +0,0 @@
|
|||||||
return { fr = {
|
|
||||||
|
|
||||||
--==[[ Navigation ]]==--
|
|
||||||
|
|
||||||
archive = "Archive",
|
|
||||||
bottom = "Bas",
|
|
||||||
catalog = "Catalogue",
|
|
||||||
index = "Index",
|
|
||||||
refresh = "Rafraîchir",
|
|
||||||
["return"] = "Retourner",
|
|
||||||
return_board = "Retourner au babillard",
|
|
||||||
return_index = "Retourner à l'index",
|
|
||||||
return_thread = "Retourner au fil de discussion",
|
|
||||||
top = "Haut",
|
|
||||||
|
|
||||||
--==[[ Error Messages ]]==--
|
|
||||||
|
|
||||||
-- Controller error messages
|
|
||||||
err_ban_reason = "Aucune Reçue.",
|
|
||||||
err_board_used = "Le nom du Babillard est déjà utilisé.",
|
|
||||||
err_not_admin = "Vous n'êtes pas un administrateur.",
|
|
||||||
err_orphaned = "Le Fil de Discussion nº%s est orphelin.",
|
|
||||||
err_slug_used = "Le slug de la page est déjà utilisée.",
|
|
||||||
err_user_used = "Le nom d'utilisateur est dèja utilisé.",
|
|
||||||
|
|
||||||
-- Model error messages
|
|
||||||
err_contribute = "Vous devez poster un commentaire ou un fichier.",
|
|
||||||
err_locked_thread = "Le fil de discussion No.%s est verrouillé.",
|
|
||||||
err_no_files = "Les fichiers ne sont pas acceptés sur ce babillard.",
|
|
||||||
|
|
||||||
err_comment_post = "Un commentaire est requis pour poster sur ce babillard.",
|
|
||||||
err_comment_thread = "Un commentaire est requis pour poster un fil de discussion sur ce babillard.",
|
|
||||||
|
|
||||||
err_create_ann = "Impossible de créer un annoncement: %s.",
|
|
||||||
err_create_ban = "Impossible de bannir l'IP: %s.",
|
|
||||||
err_create_board = "Impossible de créer le babillard: /%s/ - %s.",
|
|
||||||
err_create_page = "Impossible de créer la page: /%s/ - %s.",
|
|
||||||
err_create_post = "Impossible de soumettre le poste.",
|
|
||||||
err_create_report = "Impossible de reporter le post No.%s.",
|
|
||||||
err_create_thread = "Impossible de créer un nouveau fil de discussion.",
|
|
||||||
|
|
||||||
err_delete_board = "Impossible de supprimer le babillard: /%s/ - %s.",
|
|
||||||
err_delete_post = "Impossible de supprimer le poste No.%s.",
|
|
||||||
err_create_user = "Impossible de créer l'utilisateur: %s.",
|
|
||||||
|
|
||||||
err_file_exists = "Le fichier existe déjà sur ce babillard.",
|
|
||||||
err_file_limit = "Le fil de discussion No.%s a atteint sa limite de fichier.",
|
|
||||||
err_file_post = "Un fichier est requis pour poster sur ce babillard.",
|
|
||||||
err_file_thread = "Un fichier est requis pour poster un fil de discussion sur ce babillard.",
|
|
||||||
|
|
||||||
err_invalid_board = "Babillard invalide: /%s/.",
|
|
||||||
err_invalid_ext = "Type de fichier invalide: %s.",
|
|
||||||
err_invalid_image = "Données d'image invalides.",
|
|
||||||
err_invalid_post = "Le poste No.%s n'est pas valide.",
|
|
||||||
err_invalid_user = "Nom d'utilisateur ou mot de passe invalide.",
|
|
||||||
|
|
||||||
--==[[ 404 ]]==--
|
|
||||||
|
|
||||||
["404"] = "404 - Page Introuvable",
|
|
||||||
|
|
||||||
--==[[ Administration ]]==--
|
|
||||||
|
|
||||||
-- General
|
|
||||||
admin_panel = "Panneau Administratif",
|
|
||||||
administrator = "Administrateur",
|
|
||||||
announcement = "Annoncement",
|
|
||||||
archive_days = "Nombre de jours à garder les Fils de Discussion dans l'Archive",
|
|
||||||
archive_pruned = "Archiver les Fils de Discussion Réduites",
|
|
||||||
board = "Babillard",
|
|
||||||
board_group = "Group de babillard",
|
|
||||||
board_title = "Nom de Babillard",
|
|
||||||
bump_limit = "Limite pour remonter un fil de discussion",
|
|
||||||
content_md = "Contenu (Markdown)",
|
|
||||||
default_name = "Nom par Défaut",
|
|
||||||
draw_board = "Babillard à Dessin",
|
|
||||||
file = "Fichier",
|
|
||||||
file_limit = "Limite de fichiers dans un Fil de Discussion",
|
|
||||||
global = "Global",
|
|
||||||
index_boards = "Babillards Présent",
|
|
||||||
janitor = "Concierge",
|
|
||||||
login = "Connexion",
|
|
||||||
logout = "Déconnexion",
|
|
||||||
moderator = "Modérateur",
|
|
||||||
num_pages = "Pages Actives",
|
|
||||||
num_threads = "Fils de Discussion par Page",
|
|
||||||
password = "Mot de Passe",
|
|
||||||
password_old = "Ancient Mot de Passe",
|
|
||||||
password_retype = "Confirmer le Mot de Passe",
|
|
||||||
post_comment_required = "Exiger un commentaire pour pouvoir poster",
|
|
||||||
post_file_required = "Exiger un Fichier pour pouvoir poster",
|
|
||||||
regen_thumb = "Régénérer les Miniatures",
|
|
||||||
reply = "Répondre",
|
|
||||||
rules = "Les Règles",
|
|
||||||
name = "Nom",
|
|
||||||
subtext = "Sous-texte",
|
|
||||||
success = "Succès",
|
|
||||||
text_board = "Babillard à Texte",
|
|
||||||
theme = "Thème",
|
|
||||||
thread_comment_required = "Exiger un Commentaire ",
|
|
||||||
thread_file_required = "Exiger un Fil de Discussion",
|
|
||||||
slug = "Slug",
|
|
||||||
username = "Nom d'utilisateur",
|
|
||||||
yes = "Oui",
|
|
||||||
no = "Non",
|
|
||||||
|
|
||||||
-- Announcements
|
|
||||||
create_ann = "Créer un Annoncement",
|
|
||||||
modify_ann = "Modifier un Annoncement",
|
|
||||||
delete_ann = "Supprimer un Annoncement",
|
|
||||||
created_ann = "Création de l'annoncement: %s réussie.",
|
|
||||||
modified_ann = "Modification de l'annoncement: %s réussie.",
|
|
||||||
deleted_ann = "Suppression de l'annoncement: %s réussie.",
|
|
||||||
|
|
||||||
-- Boards
|
|
||||||
create_board = "Créer un Babillard",
|
|
||||||
modify_board = "Modifier un Babillard",
|
|
||||||
delete_board = "Supprimer un Babillard",
|
|
||||||
created_board = "Création du Babillard: /%s/ - %s réussie.",
|
|
||||||
modified_board = "Modification du Babillard : /%s/ - %s réussie.",
|
|
||||||
deleted_board = "Suppression du Babillard : /%s/ - %s réussie.",
|
|
||||||
|
|
||||||
-- Pages
|
|
||||||
create_page = "Créer une Page",
|
|
||||||
modify_page = "Modifier une Page",
|
|
||||||
delete_page = "Supprimer une Page",
|
|
||||||
created_page = "Création de la page : /%s/ - %s réussie.",
|
|
||||||
modified_page = "Modification de la page : /%s/ - %s réussie.",
|
|
||||||
deleted_page = "Suppression de la page : /%s/ - %s réussie.",
|
|
||||||
|
|
||||||
-- Reports
|
|
||||||
view_report = "Afficher un Rapport",
|
|
||||||
delete_report = "Supprimer un Rapport",
|
|
||||||
deleted_report = "Suppression du Rapport: %s réussie.",
|
|
||||||
|
|
||||||
-- Users
|
|
||||||
create_user = "Créer un Utilisateur",
|
|
||||||
modify_user = "Modifier un Utilisateur",
|
|
||||||
delete_user = "Supprimer un Utilisateur",
|
|
||||||
created_user = "Création de l'utilisateur: %s réussie.",
|
|
||||||
modified_user = "Modification de l'utilisateur: %s réussie.",
|
|
||||||
deleted_user = "Suppression de l'utilisateur: %s réussie.",
|
|
||||||
|
|
||||||
--==[[ Archive ]]==--
|
|
||||||
|
|
||||||
arc_display = "Affichage de %{n_thread} %{p_thread} expirés depuis %{n_day} %{p_day} ",
|
|
||||||
arc_number = "nº",
|
|
||||||
arc_name = "Nom",
|
|
||||||
arc_excerpt = "Extrait",
|
|
||||||
arc_replies = "Réponses",
|
|
||||||
arc_view = "Afficher",
|
|
||||||
|
|
||||||
--==[[ Ban ]]==--
|
|
||||||
|
|
||||||
ban_title = "Banni!",
|
|
||||||
ban_reason = "Vous avez été banni pour la raison suivante:",
|
|
||||||
ban_expire = "Votre ban expirera le %{expire}.",
|
|
||||||
ban_ip = "Selon avec notre serveur, votre IP est: %{ip}.",
|
|
||||||
|
|
||||||
--==[[ Catalog ]]==--
|
|
||||||
|
|
||||||
cat_stats = "R: %{replies} / F: %{files}",
|
|
||||||
|
|
||||||
--==[[ Copyright ]]==--
|
|
||||||
|
|
||||||
copy_software = "Réalisé avec %{software} %{version}",
|
|
||||||
copy_download = "Télécharger à partir de %{github}",
|
|
||||||
|
|
||||||
--==[[ Forms ]]==--
|
|
||||||
|
|
||||||
form_ban = "Bannir l'Utilisateur",
|
|
||||||
form_ban_display = "Afficher le Ban",
|
|
||||||
form_ban_board = "Ban Local",
|
|
||||||
form_ban_reason = "Raison du ban",
|
|
||||||
form_ban_time = "Durée (en jours) à bannir l'utilisateur",
|
|
||||||
form_clear = "Effacer",
|
|
||||||
form_delete = "Supprimer le Poste",
|
|
||||||
form_draw = "Dessiner",
|
|
||||||
form_lock = "Verrouiller le Fil de Discussion",
|
|
||||||
form_override = "Fichiers Illimités",
|
|
||||||
form_readme = "Veuillez lire [%{rules}] et la [%{faq}] avant de poster.",
|
|
||||||
form_remix = "Remixer l'Image",
|
|
||||||
form_report = "Reporter le Poste",
|
|
||||||
form_required = "Champ Requis",
|
|
||||||
form_save = "Épargner le Fil de Discussion",
|
|
||||||
form_sticky = "Épingler le Fil de Discussion",
|
|
||||||
form_submit = "Soumettre le Poste",
|
|
||||||
form_submit_name = "Nom",
|
|
||||||
form_submit_name_help = "Donnez-vous un nom , un tripcode ou les deux (facultatif)",
|
|
||||||
form_submit_subject = "Sujet",
|
|
||||||
form_submit_subject_help = "Définir le sujet de la discussion (facultatif)",
|
|
||||||
form_submit_options = "Options",
|
|
||||||
form_submit_options_help = "Sage: poster sans faire monter le fil de discussion (À venir) (facultatif)",
|
|
||||||
form_submit_comment = "Commentaire",
|
|
||||||
form_submit_comment_help = "Contribuer à la discussion (ou non)",
|
|
||||||
form_submit_file = "Fichier",
|
|
||||||
form_submit_file_help = "Télécharger un fichier",
|
|
||||||
form_submit_draw = "Dessiner",
|
|
||||||
form_submit_draw_help = "Dessiner ou remixer une image",
|
|
||||||
form_submit_spoiler = "Spoiler",
|
|
||||||
form_submit_spoiler_help = "Replacer la miniature avec une image sans spoiler",
|
|
||||||
form_submit_mod = "Modérateur",
|
|
||||||
form_submit_mod_help = "Mettre un drapeau sur ce fil de discussion",
|
|
||||||
form_width = "Largeur",
|
|
||||||
form_height = "Hauteur",
|
|
||||||
|
|
||||||
--==[[ Posts ]]==--
|
|
||||||
|
|
||||||
post_link = "Lier à ce poste",
|
|
||||||
post_lock = "Le fil de discussion est verrouillé",
|
|
||||||
post_hidden = "%{n_post} %{p_post} et %{n_file} %{p_file} omis. %{click} pour afficher.",
|
|
||||||
post_override = "Ce fil de discussion accepte un nombre illimité de fichiers",
|
|
||||||
post_reply = "Répondre à ce poste",
|
|
||||||
post_sticky = "Le fil de discussion est épinglé",
|
|
||||||
post_save = "Le fil de discussion ne sera pas enlevé automatiquement",
|
|
||||||
|
|
||||||
--==[[ Plurals ]]==--
|
|
||||||
|
|
||||||
days = {
|
|
||||||
one = "jour",
|
|
||||||
other = "jours"
|
|
||||||
},
|
|
||||||
files = {
|
|
||||||
one = "fichier",
|
|
||||||
other = "fichiers"
|
|
||||||
},
|
|
||||||
posts = {
|
|
||||||
one = "poste",
|
|
||||||
other = "postes"
|
|
||||||
},
|
|
||||||
threads = {
|
|
||||||
one = "fil de discussion",
|
|
||||||
other = "fils de discussion"
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
@ -1,234 +0,0 @@
|
|||||||
return { phpceo = {
|
|
||||||
|
|
||||||
--==[[ Navigation ]]==--
|
|
||||||
|
|
||||||
archive = "Gallery Of Our Greatness",
|
|
||||||
bottom = "Best For Last",
|
|
||||||
catalog = "Products And Services",
|
|
||||||
index = "Home Page",
|
|
||||||
refresh = "Experience A Rejuvinating Meeting",
|
|
||||||
["return"] = "Reconsider",
|
|
||||||
return_board = "Reconsider that product",
|
|
||||||
return_index = "Reconsider that home page",
|
|
||||||
return_thread = "Reconsider that email chain",
|
|
||||||
top = "I'm The Best",
|
|
||||||
|
|
||||||
--==[[ Error Messages ]]==--
|
|
||||||
|
|
||||||
-- Controller error messages
|
|
||||||
err_ban_reason = "Hit 'enter' too early.",
|
|
||||||
err_board_used = "That product name is in use, and we don't want to spend money on lawyers.",
|
|
||||||
err_not_admin = "Call HR.",
|
|
||||||
err_orphaned = "I accidentally lost the email chain about %s, do you know where it went?",
|
|
||||||
err_slug_used = "SEO CONFLICT, GOOGLE GROWTHHACK ERROR.",
|
|
||||||
err_user_used = "You can't be Brandon, *I'm* Brandon!",
|
|
||||||
|
|
||||||
-- Model error messages
|
|
||||||
err_contribute = "Your argument needs more pictures or more words. I can't decide which. I'm fickle like that.",
|
|
||||||
err_locked_thread = "Email chain about %s is locked, I blame Outlook.",
|
|
||||||
err_no_files = "The cloud is busy, it does not want your attachments.",
|
|
||||||
|
|
||||||
err_comment_post = "Your pitch is great, except I don't know what you're talking about. Use your words.",
|
|
||||||
err_comment_thread = "Did you seriously just try to send an email without a body? At least put in your signature. Have some pride, Intern.",
|
|
||||||
|
|
||||||
err_create_ann = "I couldn't stop yelling long enough to say: %s.",
|
|
||||||
err_create_ban = "These lottery numbers didn't work: %s.",
|
|
||||||
err_create_board = "I don't know how to make the product: /%s/ - %s.",
|
|
||||||
err_create_page = "Pages? No, we're paperless now, we don't need: /%s/ - %s.",
|
|
||||||
err_create_post = "I didn't read your email, let's set up a meeting in a few minutes where you read it to me.",
|
|
||||||
err_create_report = "I don't see anything wrong with an email about %s and I have no inclination to in my life.",
|
|
||||||
err_create_thread = "The reply button is broke! This is effecting production.",
|
|
||||||
|
|
||||||
err_delete_board = "I tried to delete those emails about: /%s/ - but I just couldn't %s.",
|
|
||||||
err_delete_post = "I clicked the flag, did that delete %s? No? Well what else am I supposed to do?",
|
|
||||||
err_create_user = "I don't know who %s is and frankly I don't give a big enough damn to learn.",
|
|
||||||
|
|
||||||
err_file_exists = "Johnson! I already have that spreadsheet! I think. Probably.",
|
|
||||||
err_file_limit = "THE CLOUD IS FULL, IF YOU SUBMIT ANYTHING ELSE TO %s THE INTERNET WILL GO DOWN.",
|
|
||||||
err_file_post = "If you want my attention you better use something other than words because I sure as hell ain't reading anything that comes across this desk today.",
|
|
||||||
err_file_thread = "This email chain said 'post your favorite confidential documents' but you forgot to attach any. Try that one again.",
|
|
||||||
|
|
||||||
err_invalid_board = "CORRUPT EMAIL: /%s/.",
|
|
||||||
err_invalid_ext = "Do I look like I know what a %s is?",
|
|
||||||
err_invalid_image = "It looks like you sent a picture but what I got was a digital clown.",
|
|
||||||
err_invalid_post = "Whatever %s was about, I've decided to politely disregard it as 'wrong opinion'.",
|
|
||||||
err_invalid_user = "I don't know who you are or what you're doing. I'm calling the police.",
|
|
||||||
|
|
||||||
--==[[ 404 ]]==--
|
|
||||||
|
|
||||||
["404"] = "404 - That's 3 better than a 401k!",
|
|
||||||
|
|
||||||
--==[[ Administration ]]==--
|
|
||||||
|
|
||||||
-- General
|
|
||||||
admin_panel = "CEO DASHBOARD",
|
|
||||||
administrator = "CEO",
|
|
||||||
announcement = "Important things that come out of my mouth",
|
|
||||||
archive_days = "Days to Archive Threads",
|
|
||||||
archive_pruned = "Gas these.",
|
|
||||||
board = "Chain",
|
|
||||||
board_group = "Chain Group",
|
|
||||||
board_title = "Email Subject",
|
|
||||||
bump_limit = "Burp Excusal Tolerance",
|
|
||||||
content_md = "Markers",
|
|
||||||
default_name = "What is this?",
|
|
||||||
draw_board = "MSPAINT but for the Internet",
|
|
||||||
file = "Datum gap",
|
|
||||||
file_limit = "Maximum Cloud Precipitation Ratio",
|
|
||||||
global = "Everyone Has To Deal With",
|
|
||||||
index_boards = "Product Selection",
|
|
||||||
janitor = "Unpaid Intern",
|
|
||||||
login = "Clock In",
|
|
||||||
logout = "Clock Out",
|
|
||||||
moderator = "Enforcer",
|
|
||||||
num_pages = "Reasons this company is great",
|
|
||||||
num_threads = "How much I can stand of this",
|
|
||||||
password = "Digital Hash Salt",
|
|
||||||
password_old = "That old thing.",
|
|
||||||
password_retype = "Do it again, it'll be funny.",
|
|
||||||
post_comment_required = "Need Context",
|
|
||||||
post_file_required = "Insert Meme",
|
|
||||||
regen_thumb = "Rectify Pixels",
|
|
||||||
reply = "Interject",
|
|
||||||
rules = "Things That Don't Apply To Me",
|
|
||||||
name = "Bob",
|
|
||||||
subtext = "sub-who?",
|
|
||||||
success = "Me, The Physical Embodiment of Greatness",
|
|
||||||
text_board = "Stuff I Won't Read",
|
|
||||||
theme = "Birthday Party",
|
|
||||||
thread_comment_required = "CONVERSE BEFORE CLOUD",
|
|
||||||
thread_file_required = "INSERT FILE FOR CLOUD",
|
|
||||||
slug = "CLOUD RESOURCE IDENTIFIER",
|
|
||||||
username = "User Identifcation String",
|
|
||||||
yes = "I Am Glad To Blindly Accept This",
|
|
||||||
no = "Not Exactly",
|
|
||||||
|
|
||||||
-- Announcements
|
|
||||||
create_ann = "Open Mouth, Insert Foot",
|
|
||||||
modify_ann = "Damage Control",
|
|
||||||
delete_ann = "I never said that, you can't prove it.",
|
|
||||||
created_ann = "I believe %s is the lifeblood of this company.",
|
|
||||||
modified_ann = "What I meant to say was actually %s.",
|
|
||||||
deleted_ann = "What announcement? %s you say? Doesn't ring a bell.",
|
|
||||||
|
|
||||||
-- Boards
|
|
||||||
create_board = "More Email!",
|
|
||||||
modify_board = "Different Email",
|
|
||||||
delete_board = "Less Email!",
|
|
||||||
created_board = "How do you feel about /%s/ - %s? How about agilefall?",
|
|
||||||
modified_board = "I hope we fixed the issue about /%s/ - %s because I was getting tired of covering for him.",
|
|
||||||
deleted_board = "I forgot I don't even like /%s/ - %s.",
|
|
||||||
|
|
||||||
-- Pages
|
|
||||||
create_page = "Create new effigy to my greatness.",
|
|
||||||
modify_page = "Make this less bad.",
|
|
||||||
delete_page = "I ain't readin' this.",
|
|
||||||
created_page = "I heard about /%s/ - %s online, let me tell you how we can pivot this.",
|
|
||||||
modified_page = "I saw some stuff about /%s/ - %s so I fixed it.",
|
|
||||||
deleted_page = "Good news, I got rid of those books about: /%s/ - %s.",
|
|
||||||
|
|
||||||
-- Reports
|
|
||||||
view_report = "I said *what*?",
|
|
||||||
delete_report = "Forget about that.",
|
|
||||||
deleted_report = "I changed my mind about %s and you can too.",
|
|
||||||
|
|
||||||
-- Users
|
|
||||||
create_user = "Hire",
|
|
||||||
modify_user = "Rectify Person",
|
|
||||||
delete_user = "Get Rid Of",
|
|
||||||
created_user = "So %s is this bright young talent we've been hearing about.",
|
|
||||||
modified_user = "Whatever was wrong with %s we squared away.",
|
|
||||||
deleted_user = "I got rid of that %s character for you.",
|
|
||||||
|
|
||||||
--==[[ Archive ]]==--
|
|
||||||
|
|
||||||
arc_display = "Displaying %{n_thread} expired %{p_thread} from the past %{n_day} %{p_day}",
|
|
||||||
arc_number = "In Britan they say 'pound'.",
|
|
||||||
arc_name = "Who?",
|
|
||||||
arc_excerpt = "TL;DR",
|
|
||||||
arc_replies = "Responses",
|
|
||||||
arc_view = "Investigate",
|
|
||||||
|
|
||||||
--==[[ Ban ]]==--
|
|
||||||
|
|
||||||
ban_title = "Not allowed back!",
|
|
||||||
ban_reason = "I decided I don't like you anymore, because: ",
|
|
||||||
ban_expire = "I might forget about this on %{expire}.",
|
|
||||||
ban_ip = "And your stupid raffle ticket was: %{ip}.",
|
|
||||||
|
|
||||||
--==[[ Catalog ]]==--
|
|
||||||
|
|
||||||
cat_stats = "R: %{replies} / F: %{files}",
|
|
||||||
|
|
||||||
--==[[ Copyright ]]==--
|
|
||||||
|
|
||||||
copy_software = "You paid HOW MUCH for %{software}? And it's only version %{version}?! We should have used phpBB, it's way more mature.",
|
|
||||||
copy_download = "Negotiate a license from %{github}",
|
|
||||||
|
|
||||||
--==[[ Forms ]]==--
|
|
||||||
|
|
||||||
form_ban = "Escort Off Property",
|
|
||||||
form_ban_display = "Not allowed in public",
|
|
||||||
form_ban_board = "Not allowed in my hosue",
|
|
||||||
form_ban_reason = "Why don't I like this person",
|
|
||||||
form_ban_time = "How long (in digital ages) to pretend I don't know this guy.",
|
|
||||||
form_clear = "Forget About It",
|
|
||||||
form_delete = "Hide Evidence",
|
|
||||||
form_draw = "Sketch Out",
|
|
||||||
form_lock = "END THIS",
|
|
||||||
form_override = "CLOUD IS LOOSE AND HUNGRY FOR DATA",
|
|
||||||
form_readme = "Frankly, I don't read the [%{rules}] and [%{faq}] so I won't ask you to, either. Hell, I don't even know what they say. And I wrote 'em!",
|
|
||||||
form_remix = "CLAIM AS YOUR OWN",
|
|
||||||
form_report = "I DON'T LIKE IT",
|
|
||||||
form_required = "MANDATORY IF YOU WANT TO KEEP WORKING HERE",
|
|
||||||
form_save = "Fwd to Offshore Account",
|
|
||||||
form_sticky = "IMMORTIALIZE SHAME",
|
|
||||||
form_submit = "HEMMORAGE BRILLIANCE",
|
|
||||||
form_submit_name = "EMPLOYEE ID NUMBER (PRE-ACQUISITION)",
|
|
||||||
form_submit_name_help = "Who are you again? A trip-what?",
|
|
||||||
form_submit_subject = "What Am I On About? Oh, Right.",
|
|
||||||
form_submit_subject_help = "What I've brought you all here today to discuss",
|
|
||||||
form_submit_options = "Stock Options",
|
|
||||||
form_submit_options_help = "sage: Don't let anyone know you're desperate for attention.",
|
|
||||||
form_submit_comment = "What you have to say.",
|
|
||||||
form_submit_comment_help = "Tell us about your great idea.",
|
|
||||||
form_submit_file = "DATUMS",
|
|
||||||
form_submit_file_help = "FOREFIT YOUR DATA TO THE CLOUD",
|
|
||||||
form_submit_draw = "MSPAINT",
|
|
||||||
form_submit_draw_help = "MSPAINT OR GIMPIFY",
|
|
||||||
form_submit_spoiler = "Paywalled Content",
|
|
||||||
form_submit_spoiler_help = "Increase viewer engagement 10%%",
|
|
||||||
form_submit_mod = "Enforcer of Marketability",
|
|
||||||
form_submit_mod_help = "Mark as landmine. Minesweeper humor. It means I think there's a bomb here.",
|
|
||||||
form_width = "Digital Horizon",
|
|
||||||
form_height = "Digital Embiggenment",
|
|
||||||
|
|
||||||
--==[[ Posts ]]==--
|
|
||||||
|
|
||||||
post_link = "FORWARD THIS EMAIL",
|
|
||||||
post_lock = "EMAIL IS IN ARCHIVE MODE, STOP REPLYING TO THINGS I FORGOT ABOUT.",
|
|
||||||
post_hidden = "%{n_post} %{p_post} and %{n_file} %{p_file} omitted. %{click} to view.",
|
|
||||||
post_override = "CLOUD STORAGE ENABLED",
|
|
||||||
post_reply = "Reply to this email",
|
|
||||||
post_sticky = "reply-all'd, cc'd the company, 10/10",
|
|
||||||
post_save = "I refuse to cut down on the fat, on account of all the words I said being fluff meant to distract you.",
|
|
||||||
|
|
||||||
--==[[ Plurals ]]==--
|
|
||||||
|
|
||||||
days = {
|
|
||||||
one = "digital age",
|
|
||||||
other = "digital aegis"
|
|
||||||
},
|
|
||||||
files = {
|
|
||||||
one = "datum",
|
|
||||||
other = "datums"
|
|
||||||
},
|
|
||||||
posts = {
|
|
||||||
one = "email",
|
|
||||||
other = "emailadoodles"
|
|
||||||
},
|
|
||||||
threads = {
|
|
||||||
one = "email chain",
|
|
||||||
other = "clusterfuck"
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
@ -1,240 +0,0 @@
|
|||||||
return { pl = {
|
|
||||||
|
|
||||||
--==[[ Navigation ]]==--
|
|
||||||
|
|
||||||
archive = "Archiwum",
|
|
||||||
bottom = "Przewiń na dół",
|
|
||||||
catalog = "Katalog",
|
|
||||||
index = "Spis treści",
|
|
||||||
refresh = "Odśwież",
|
|
||||||
["return"] = "Powróć",
|
|
||||||
return_board = "Powróć do boardu",
|
|
||||||
return_index = "Powróć do spisu treści",
|
|
||||||
return_thread = "Powróć do wątku",
|
|
||||||
top = "Przewiń na górę",
|
|
||||||
|
|
||||||
--==[[ Error Messages ]]==--
|
|
||||||
|
|
||||||
-- Controller error messages
|
|
||||||
err_ban_reason = "Brak powodu.",
|
|
||||||
err_board_used = "Nazwa boardu jest już w użyciu.",
|
|
||||||
err_not_admin = "Nie jesteś administratorem.",
|
|
||||||
err_orphaned = "Wątek %s został osierocony.",
|
|
||||||
err_slug_used = "Adres slug jest już w użyciu.",
|
|
||||||
err_user_used = "Nazwa użytkownika jest już w użyciu.",
|
|
||||||
|
|
||||||
-- Model error messages
|
|
||||||
err_contribute = "Musisz załączyć treść posta lub załącznik.",
|
|
||||||
err_locked_thread = "Wątek %s jest zablokowany.",
|
|
||||||
err_no_files = "Dodawanie plików jest wyłączone na tym boardzie.",
|
|
||||||
|
|
||||||
err_comment_post = "Na tym boardzie, by odpowiedzieć na wątek, musisz zamieścić komentarz.",
|
|
||||||
err_comment_thread = "Na tym boardzie, by stworzyć wątek, musisz zamieścić komentarz.",
|
|
||||||
|
|
||||||
err_create_ann = "Nie można utworzyć ogłoszenia: %s.",
|
|
||||||
err_create_ban = "Nie można zbananować IP: %s.",
|
|
||||||
err_create_board = "Nie można utworzyć boarda: /%s/ - %s.",
|
|
||||||
err_create_page = "Nie można utworzyć strony: /%s/ - %s.",
|
|
||||||
err_create_post = "Nie można wysłać posta.",
|
|
||||||
err_create_report = "Nie można zgłosić posta %s.",
|
|
||||||
err_create_thread = "Nie można utworzyć wątku.",
|
|
||||||
|
|
||||||
err_delete_board = "Nie można usunąć boarda: /%s/ - %s.",
|
|
||||||
err_delete_post = "Nie można usunąć posta %s.",
|
|
||||||
err_create_user = "Nie można utworzyć użytkownika: %s.",
|
|
||||||
|
|
||||||
err_file_exists = "Ten plik już istnieje na tym boardzie.",
|
|
||||||
err_file_limit = "Wątek %s osiągnął już swój limit plików.",
|
|
||||||
err_file_post = "Na tym boardzie, by odpowiedzieć na wątek, musisz załączyć plik.",
|
|
||||||
err_file_thread = "Na tym boardzie, by stworzyć wątek, musisz załączyć plik.",
|
|
||||||
|
|
||||||
err_invalid_board = "Nieznany board: /%s/.",
|
|
||||||
err_invalid_ext = "Nieznany typ pliku: %s.",
|
|
||||||
err_invalid_image = "Załączony plik nie jest prawidłowym obrazkiem.",
|
|
||||||
err_invalid_post = "Post %s nie jest poprawnym postem.",
|
|
||||||
err_invalid_user = "Niepoprawna nazwa użytkownika lub hasło.",
|
|
||||||
|
|
||||||
--==[[ 404 ]]==--
|
|
||||||
|
|
||||||
["404"] = "404 - Strona nie znaleziona",
|
|
||||||
|
|
||||||
--==[[ Administration ]]==--
|
|
||||||
|
|
||||||
-- General
|
|
||||||
admin_panel = "Panel cwela",
|
|
||||||
administrator = "Cwel",
|
|
||||||
announcement = "Ogłoszenie",
|
|
||||||
archive_days = "Dni do archiwizowania wątków",
|
|
||||||
archive_pruned = "Archiwizuj wątki",
|
|
||||||
board = "Board",
|
|
||||||
board_group = "Grupa boarda",
|
|
||||||
board_title = "Nazwa boarda",
|
|
||||||
bump_limit = "Limit przyjebek",
|
|
||||||
content_md = "Opis (Markdown)",
|
|
||||||
default_name = "Domyślna nazwa",
|
|
||||||
draw_board = "Board do rysowania",
|
|
||||||
file = "Plik",
|
|
||||||
file_limit = "Limit plików w wątku",
|
|
||||||
global = "Globalnie",
|
|
||||||
index_boards = "Aktualne boardy",
|
|
||||||
janitor = "Woźny",
|
|
||||||
login = "Zaloguj",
|
|
||||||
logout = "Wyloguj",
|
|
||||||
moderator = "Moderator",
|
|
||||||
num_pages = "Aktywne strony",
|
|
||||||
num_threads = "Fredy na stronę",
|
|
||||||
password = "Hasło",
|
|
||||||
password_old = "Powtórz hasło",
|
|
||||||
password_retype = "Stare hasło",
|
|
||||||
post_comment_required = "Wymagany komentarz do posta",
|
|
||||||
post_file_required = "Wymagany plik do posta",
|
|
||||||
regen_thumb = "Odśwież miniaturki",
|
|
||||||
reply = "Odpowiedz",
|
|
||||||
rules = "Zasady",
|
|
||||||
name = "Nazwa",
|
|
||||||
subtext = "Podtekst",
|
|
||||||
success = "Sukces",
|
|
||||||
text_board = "Board tekstowy",
|
|
||||||
theme = "Motyw",
|
|
||||||
thread_comment_required = "Wymagany komentarz do freda",
|
|
||||||
thread_file_required = "Wymagany plik do freda",
|
|
||||||
slug = "Slug",
|
|
||||||
username = "Nazwa użytkownika",
|
|
||||||
yes = "Tak",
|
|
||||||
no = "Nie",
|
|
||||||
|
|
||||||
-- Announcements
|
|
||||||
create_ann = "Utwórz ogłoszenie",
|
|
||||||
modify_ann = "Zmień ogłoszenie",
|
|
||||||
delete_ann = "Wyjeb ogłoszenie",
|
|
||||||
created_ann = "Utworzyłeś ogłoszenie %s.",
|
|
||||||
modified_ann = "Zmieniłeś ogłoszenie %s.",
|
|
||||||
deleted_ann = "Wyjebałeś ogłoszenie %s.",
|
|
||||||
|
|
||||||
-- Boards
|
|
||||||
create_board = "Utwórz boarda",
|
|
||||||
modify_board = "Zmień boarda",
|
|
||||||
delete_board = "Wyjeb boarda",
|
|
||||||
created_board = "Utworzyłeś boarda /%s/ - %s.",
|
|
||||||
modified_board = "Zmieniłeś boarda /%s/ - %s.",
|
|
||||||
deleted_board = "Wyjebałeś boarda /%s/ - %s.",
|
|
||||||
|
|
||||||
-- Pages
|
|
||||||
create_page = "Utwórz stronę",
|
|
||||||
modify_page = "Zmień stronę",
|
|
||||||
delete_page = "Wyjeb stronę",
|
|
||||||
created_page = "Utworzyłeś stronę /%s/ - %s.",
|
|
||||||
modified_page = "Zmieniłeś stronę /%s/ - %s.",
|
|
||||||
deleted_page = "Wyjebałeś stronę /%s/ - %s.",
|
|
||||||
|
|
||||||
-- Reports
|
|
||||||
view_report = "Przejrzyj zgłoszenie",
|
|
||||||
delete_report = "Usuń zgłoszenie",
|
|
||||||
deleted_report = "Wyjebałeś zgłoszenie %s.",
|
|
||||||
|
|
||||||
-- Users
|
|
||||||
create_user = "Utwórz użytkownika",
|
|
||||||
modify_user = "Zmień użytkownika",
|
|
||||||
delete_user = "Wyjeb użytkownika",
|
|
||||||
created_user = "Utworzyłeś użytkownika %s.",
|
|
||||||
modified_user = "Zmieniłeś użytkownika %s.",
|
|
||||||
deleted_user = "Wyjebałeś użytkownika %s.",
|
|
||||||
|
|
||||||
--==[[ Archive ]]==--
|
|
||||||
|
|
||||||
arc_display = "Wyświetlanie %{n_thread} %{p_thread} (zarchiwizowane) z %{n_day} %{p_day}",
|
|
||||||
arc_number = "nr ",
|
|
||||||
arc_name = "Nazwa",
|
|
||||||
arc_excerpt = "Kawał freda",
|
|
||||||
arc_replies = "Odpowiedzi",
|
|
||||||
arc_view = "Wyślij",
|
|
||||||
|
|
||||||
--==[[ Ban ]]==--
|
|
||||||
|
|
||||||
ban_title = "Zbanowany!",
|
|
||||||
ban_reason = "Zostałeś zbananowany z powodu o takiego:",
|
|
||||||
ban_expire = "Twój banan usunie się %{expire}.",
|
|
||||||
ban_ip = "Według naszych serwerów NASA, twoje IP to %{ip}.",
|
|
||||||
|
|
||||||
--==[[ Catalog ]]==--
|
|
||||||
|
|
||||||
cat_stats = "Odp.: %{replies} / plików: %{files}",
|
|
||||||
|
|
||||||
--==[[ Copyright ]]==--
|
|
||||||
|
|
||||||
copy_software = "Fredy napędzane przez %{software} %{version}",
|
|
||||||
copy_download = "Pobierz z %{github}",
|
|
||||||
|
|
||||||
--==[[ Forms ]]==--
|
|
||||||
|
|
||||||
form_ban = "Zbanuj użytkownika",
|
|
||||||
form_ban_display = "Wyświetl bana",
|
|
||||||
form_ban_board = "Ban lokalny",
|
|
||||||
form_ban_reason = "Powód bana",
|
|
||||||
form_ban_time = "Długość bana (w minutach)",
|
|
||||||
form_clear = "Wyczyść",
|
|
||||||
form_delete = "Usuń posta",
|
|
||||||
form_draw = "Rysuj",
|
|
||||||
form_lock = "Zablokuj wątek",
|
|
||||||
form_override = "Nielimitowane pliki",
|
|
||||||
form_readme = "Przeczytaj [%{rules}] oraz [%{faq}] zanim zapostujesz.",
|
|
||||||
form_remix = "Przerób obrazek",
|
|
||||||
form_report = "Zgłoś posta",
|
|
||||||
form_required = "pole wymagane",
|
|
||||||
form_save = "Zapisz wątek",
|
|
||||||
form_sticky = "Przyklej freda",
|
|
||||||
form_submit = "Wyślij posta",
|
|
||||||
form_submit_name = "Pseudonim",
|
|
||||||
form_submit_name_help = "Nadaj sobie nazwę lub tripkod (opcjonalne)",
|
|
||||||
form_submit_subject = "Temat",
|
|
||||||
form_submit_subject_help = "Temat dyskusji (opcjonalne)",
|
|
||||||
form_submit_options = "Opcje",
|
|
||||||
form_submit_options_help = "sage: zapostuj bez podbijania freda (opcjonalne)",
|
|
||||||
form_submit_comment = "Komentarz",
|
|
||||||
form_submit_comment_help = "Dodaj coś do dyskusji",
|
|
||||||
form_submit_file = "Plik",
|
|
||||||
form_submit_file_help = "Załącz plik",
|
|
||||||
form_submit_draw = "Rysuj",
|
|
||||||
form_submit_draw_help = "Narysuj lub przerób obrazek",
|
|
||||||
form_submit_spoiler = "Spoiler",
|
|
||||||
form_submit_spoiler_help = "Podmień miniaturkę na spoiler",
|
|
||||||
form_submit_mod = "Moderator",
|
|
||||||
form_submit_mod_help = "Oznacz tego freda",
|
|
||||||
form_width = "Szerokość",
|
|
||||||
form_height = "Wysokość",
|
|
||||||
|
|
||||||
--==[[ Posts ]]==--
|
|
||||||
|
|
||||||
post_link = "Link do tego posta",
|
|
||||||
post_lock = "Wątek jest zablokowany",
|
|
||||||
post_hidden = "%{n_post} %{p_post} i %{n_file} %{p_file} zostały pominięte. %{click} aby wyświetlić.",
|
|
||||||
post_override = "Wątek akceptuje nielimitowaną ilość plików",
|
|
||||||
post_reply = "Odpowiedz na ten post",
|
|
||||||
post_sticky = "Fred jest przyklejony",
|
|
||||||
post_save = "Wątek nie będzie archiwizowany",
|
|
||||||
|
|
||||||
--==[[ Plurals ]]==--
|
|
||||||
|
|
||||||
days = {
|
|
||||||
one = "dzień",
|
|
||||||
other = "dni"
|
|
||||||
},
|
|
||||||
files = {
|
|
||||||
one = "plik",
|
|
||||||
few = "pliki",
|
|
||||||
many = "plików",
|
|
||||||
other = "pliki"
|
|
||||||
},
|
|
||||||
posts = {
|
|
||||||
one = "post",
|
|
||||||
few = "posty",
|
|
||||||
many = "postów",
|
|
||||||
other = "posty"
|
|
||||||
},
|
|
||||||
threads = {
|
|
||||||
one = "wątek",
|
|
||||||
few = "wątki",
|
|
||||||
many = "wątków",
|
|
||||||
other = "wątki"
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
local Model = require("lapis.db.model").Model
|
|
||||||
local Announcements = Model:extend("announcements", {
|
|
||||||
relations = {
|
|
||||||
{ "board", belongs_to="Boards" }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Announcements.valid_record = {
|
|
||||||
{ "board_id", is_integer=true },
|
|
||||||
{ "text", exists=true }
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Create an announcement
|
|
||||||
-- @tparam table params Announcement parameters
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Announcements:new(params)
|
|
||||||
local announcement = self:create(params)
|
|
||||||
return announcement and announcement or nil, { "err_create_ann", { params.text } }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Modify an announcement
|
|
||||||
-- @tparam table params Announcement parameters
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Announcements:modify(params)
|
|
||||||
local announcement = self:get(params.id)
|
|
||||||
if not announcement then
|
|
||||||
return nil, { "err_create_ann", { params.text } } -- FIXME: wrong error
|
|
||||||
end
|
|
||||||
|
|
||||||
local success, err = announcement:update(params)
|
|
||||||
return success and announcement or nil, "FIXME: " .. tostring(err)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Delete an announcement
|
|
||||||
-- @tparam number id Announcement ID
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Announcements:delete(id)
|
|
||||||
local announcement = self:get(id)
|
|
||||||
if not announcement then
|
|
||||||
return nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
local success = announcement:delete()
|
|
||||||
return success and announcement or nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get all announcements
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Announcements:get_all()
|
|
||||||
local announcements = self:select("order by board_id asc")
|
|
||||||
return announcements
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get announcements
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Announcements:get_global()
|
|
||||||
local announcements = self:select("where board_id=0")
|
|
||||||
return announcements
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get board announcements
|
|
||||||
-- @tparam number board_id Board ID
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Announcements:get_board(board_id)
|
|
||||||
local announcements = self:select("where board_id=?", board_id)
|
|
||||||
return announcements
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get announcement
|
|
||||||
-- @tparam number id Announcement ID
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Announcements:get(id)
|
|
||||||
local announcement = self:find(id)
|
|
||||||
return announcement and announcement or nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
function Announcements.format_to_db(_, params)
|
|
||||||
if not params.board_id then
|
|
||||||
params.board_id = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Announcements.format_from_db(_, params)
|
|
||||||
if params.board_id == 0 then
|
|
||||||
params.board_id = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return Announcements
|
|
||||||
@ -1,151 +1,23 @@
|
|||||||
local Model = require("lapis.db.model").Model
|
local lapis = require "lapis"
|
||||||
local Bans = Model:extend("bans", {
|
local capture = require("lapis.application").capture_errors_json
|
||||||
relations = {
|
local r2 = require("lapis.application").respond_to
|
||||||
{ "board", belongs_to="Boards" },
|
local handle = require("utils.error").handle
|
||||||
}
|
local app = lapis.Application()
|
||||||
})
|
app.__base = app
|
||||||
|
app.name = "api.bans."
|
||||||
Bans.valid_record = {
|
app.path = "/api/bans"
|
||||||
{ "board_id", is_integer=true },
|
|
||||||
{ "ip", max_length=255, exists=true },
|
app:match("bans", "", capture({
|
||||||
{ "time", exists=true }
|
on_error = handle,
|
||||||
}
|
r2(require "apps.api.bans.bans"),
|
||||||
|
}))
|
||||||
--- Create a ban
|
app:match("ban", "/:uri_ban[%d]", capture({
|
||||||
-- @tparam table params Ban parameters
|
on_error = handle,
|
||||||
-- @treturn boolean success
|
r2(require "apps.api.bans.ban"),
|
||||||
-- @treturn string err
|
}))
|
||||||
function Bans:new(params)
|
app:match("bans_ip", "/ip/:uri_ip", capture({
|
||||||
local ban = self:create(params)
|
on_error = handle,
|
||||||
return ban and ban or nil, { "err_create_ban", { params.ip } }
|
r2(require "apps.api.bans.bans_ip"),
|
||||||
end
|
}))
|
||||||
|
|
||||||
--- Modify a ban
|
return app
|
||||||
-- @tparam table params Board parameters
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Bans:modify(params)
|
|
||||||
local ban = self:get(params.id)
|
|
||||||
if not ban then
|
|
||||||
return nil, { "err_create_board", { params.name, params.title } } -- FIXME: wrong error message
|
|
||||||
end
|
|
||||||
|
|
||||||
local success, err = ban:update(params)
|
|
||||||
return success and ban or nil, "FIXME: " .. tostring(err)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Delete a ban
|
|
||||||
-- @tparam number id Ban's ID
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string err
|
|
||||||
function Bans:delete(id)
|
|
||||||
local ban = self:get(id)
|
|
||||||
if not ban then
|
|
||||||
return nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
local success = ban:delete()
|
|
||||||
return success and ban or nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get all bans
|
|
||||||
-- @treturn table users List of bans
|
|
||||||
function Bans:get_all()
|
|
||||||
local bans = self:select("order by board_id asc, time + duration desc, ip asc")
|
|
||||||
|
|
||||||
for i=#bans, 1, -1 do
|
|
||||||
local ban = bans[i]
|
|
||||||
if not self:validate(ban) then
|
|
||||||
table.remove(bans, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return bans
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get board bans
|
|
||||||
-- @treturn table users List of bans
|
|
||||||
function Bans:get_board(board_id)
|
|
||||||
local bans = self:select("where board_id=? order by board_id asc, time + duration desc, ip asc", board_id)
|
|
||||||
|
|
||||||
for i=#bans, 1, -1 do
|
|
||||||
local ban = bans[i]
|
|
||||||
if not self:validate(ban) then
|
|
||||||
table.remove(bans, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return bans
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get ban data
|
|
||||||
-- @tparam number id Ban's ID
|
|
||||||
-- @treturn table ban
|
|
||||||
function Bans:get(id)
|
|
||||||
local ban = self:find(id)
|
|
||||||
if not ban then
|
|
||||||
return nil, "FIXME: ALART!"
|
|
||||||
end
|
|
||||||
|
|
||||||
local valid, err = self:validate(ban)
|
|
||||||
if not valid then
|
|
||||||
return nil, err
|
|
||||||
end
|
|
||||||
|
|
||||||
return ban
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get bans for specific ip
|
|
||||||
-- @tparam string ip IP address
|
|
||||||
-- @treturn table ban
|
|
||||||
function Bans:get_ip(ip)
|
|
||||||
local bans = self:select("where ip=?", ip)
|
|
||||||
|
|
||||||
for i=#bans, 1, -1 do
|
|
||||||
local ban = bans[i]
|
|
||||||
if not self:validate(ban) then
|
|
||||||
table.remove(bans, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return bans
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Validate ban
|
|
||||||
-- @tparam table ban Ban data
|
|
||||||
-- @treturn boolean valid
|
|
||||||
function Bans:validate(ban)
|
|
||||||
local time = os.time()
|
|
||||||
local finish = ban.time + ban.duration
|
|
||||||
|
|
||||||
if time >= finish then
|
|
||||||
self:delete(ban)
|
|
||||||
return nil, "FIXME: ban has exired"
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Format ban paramaters for DB insertion
|
|
||||||
-- @tparam table params Ban parameters
|
|
||||||
function Bans.format_to_db(_, params)
|
|
||||||
-- Convert duration from days to seconds
|
|
||||||
params.duration = (tonumber(params.duration) or 0) * 86400
|
|
||||||
|
|
||||||
if not params.board_id then
|
|
||||||
params.board_id = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Format ban parameters for User consumption
|
|
||||||
-- @tparam table params Ban parameters
|
|
||||||
function Bans.format_from_db(_, params)
|
|
||||||
-- Convert duration from seconds to days
|
|
||||||
params.duration = tonumber(params.duration) / 86400
|
|
||||||
|
|
||||||
if params.board_id == 0 then
|
|
||||||
params.board_id = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return Bans
|
|
||||||
|
|||||||
@ -1,226 +0,0 @@
|
|||||||
local db = require "lapis.db"
|
|
||||||
local giflib = require "giflib"
|
|
||||||
local lfs = require "lfs"
|
|
||||||
local magick = require "magick"
|
|
||||||
local Model = require("lapis.db.model").Model
|
|
||||||
local Boards = Model:extend("boards", {
|
|
||||||
relations = {
|
|
||||||
{ "announcements", has_many="Announcements" },
|
|
||||||
{ "bans", has_many="Bans" },
|
|
||||||
{ "posts", has_many="Posts" },
|
|
||||||
{ "reports", has_many="Reports" },
|
|
||||||
{ "threads", has_many="Threads", where={ archive=false }, order="sticky desc, last_active desc" },
|
|
||||||
{ "archived", has_many="Threads", where={ archive=true }, order="last_active desc" },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Boards.valid_record = {
|
|
||||||
{ "name", max_length=255, exists=true },
|
|
||||||
{ "title", max_length=255, exists=true },
|
|
||||||
{ "subtext", max_length=255 },
|
|
||||||
{ "ban_message", max_length=255 },
|
|
||||||
{ "anon_name", max_length=255 },
|
|
||||||
{ "theme", max_length=255 },
|
|
||||||
{ "pages", exists=true },
|
|
||||||
{ "threads_per_page", exists=true },
|
|
||||||
{ "thread_file_limit", exists=true },
|
|
||||||
{ "post_limit", exists=true },
|
|
||||||
{ "archive_time", exists=true },
|
|
||||||
{ "group", exists=true }
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Create a board
|
|
||||||
-- @tparam table params Board parameters
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Boards:new(params)
|
|
||||||
local board = self:create(params)
|
|
||||||
if not board then
|
|
||||||
return false, { "err_create_board", { params.name, params.title } }
|
|
||||||
end
|
|
||||||
|
|
||||||
lfs.mkdir(string.format("./static/%s/", board.name))
|
|
||||||
return board
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Modify a board
|
|
||||||
-- @tparam table params Board parameters
|
|
||||||
-- @tparam old_name Board's current short name
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Boards:modify(params, old_name)
|
|
||||||
local board = self:get(old_name)
|
|
||||||
if not board then
|
|
||||||
return false, { "err_create_board", { params.name, params.title } } -- FIXME: wrong error message
|
|
||||||
end
|
|
||||||
|
|
||||||
local success, err = board:update(params)
|
|
||||||
if not success then
|
|
||||||
return false, "FIXME: " .. tostring(err)
|
|
||||||
end
|
|
||||||
|
|
||||||
if board.name ~= old_name then
|
|
||||||
local old = string.format("./static/%s/", old_name)
|
|
||||||
local new = string.format("./static/%s/", board.name)
|
|
||||||
os.rename(old, new)
|
|
||||||
end
|
|
||||||
|
|
||||||
return board
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Delete a board
|
|
||||||
-- @tparam string name Board's short name
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Boards:delete(name)
|
|
||||||
local board = self:get(name)
|
|
||||||
if not board then
|
|
||||||
return false, { "err_create_board", { name, name } } -- FIXME: wrong error message
|
|
||||||
end
|
|
||||||
|
|
||||||
local announcements = board:get_announcements()
|
|
||||||
local bans = board:get_bans()
|
|
||||||
local posts = board:get_posts()
|
|
||||||
local reports = board:get_reports()
|
|
||||||
local threads = board:get_threads()
|
|
||||||
local dir = string.format("./static/%s/", board.name)
|
|
||||||
|
|
||||||
-- Clear data
|
|
||||||
for _, announcement in ipairs(announcements) do announcement:delete() end
|
|
||||||
for _, ban in ipairs(bans) do ban:delete() end
|
|
||||||
for _, post in ipairs(posts) do post:delete() end
|
|
||||||
for _, report in ipairs(reports) do report:delete() end
|
|
||||||
for _, thread in ipairs(threads) do thread:delete() end
|
|
||||||
|
|
||||||
-- Clear filesystem
|
|
||||||
if lfs.attributes(dir, "mode") == "directory" then
|
|
||||||
-- Delete files
|
|
||||||
for file in lfs.dir(dir) do
|
|
||||||
os.remove(dir .. file)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete directory
|
|
||||||
lfs.rmdir(dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clear board
|
|
||||||
local success = board:delete()
|
|
||||||
return success and board or false, { "err_delete_board", { board.name, board.title } }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get all boards
|
|
||||||
-- @treturn table boards
|
|
||||||
function Boards:get_all()
|
|
||||||
local boards = self:select("order by boards.group asc, name asc")
|
|
||||||
return boards and boards or false, "FIXME: ALART!"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get board data
|
|
||||||
-- @tparam string name Board's short name
|
|
||||||
-- @treturn table board
|
|
||||||
function Boards:get(name)
|
|
||||||
local board = self:find { name=name }
|
|
||||||
return board and board or false, "FIXME: ALART!"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Format board paramaters for DB insertion
|
|
||||||
-- @tparam table params Board parameters
|
|
||||||
function Boards.format_to_db(_, params)
|
|
||||||
-- Convert archive_time from days to seconds
|
|
||||||
params.archive_time = (tonumber(params.archive_time) or 0) * 86400
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Format board parameters for User consumption
|
|
||||||
-- @tparam table params Board parameters
|
|
||||||
function Boards.format_from_db(_, params)
|
|
||||||
-- Convert archive_time from seconds to days
|
|
||||||
params.archive_time = tonumber(params.archive_time) / 86400
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Regenerate thumbnails for all posts
|
|
||||||
-- @treturn none
|
|
||||||
function Boards.regen_thumbs(_)
|
|
||||||
local sql = [[
|
|
||||||
select
|
|
||||||
boards.name as board,
|
|
||||||
posts.thread_id,
|
|
||||||
posts.file_path,
|
|
||||||
posts.file_width,
|
|
||||||
posts.file_height,
|
|
||||||
posts.file_type
|
|
||||||
from posts
|
|
||||||
left join boards on
|
|
||||||
board_id = boards.id
|
|
||||||
where
|
|
||||||
file_path is not null and
|
|
||||||
file_width is not null and
|
|
||||||
file_height is not null and
|
|
||||||
file_type = 'image' and
|
|
||||||
file_spoiler = false
|
|
||||||
order by
|
|
||||||
board_id asc,
|
|
||||||
thread_id asc,
|
|
||||||
post_id asc
|
|
||||||
]]
|
|
||||||
local dir
|
|
||||||
local board
|
|
||||||
local thread = 0
|
|
||||||
local results = db.query(sql)
|
|
||||||
|
|
||||||
for _, result in ipairs(results) do
|
|
||||||
-- Change board, reset thread counter and image directory
|
|
||||||
if result.board ~= board then
|
|
||||||
board = result.board
|
|
||||||
thread = 0
|
|
||||||
dir = string.format("./static/%s/", board)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Filesystem paths
|
|
||||||
local name, ext = result.file_path:match("^(.+)(%..+)$")
|
|
||||||
ext = string.lower(ext)
|
|
||||||
|
|
||||||
local full_path = dir .. result.file_path
|
|
||||||
local thumb_path = dir .. "s" .. result.file_path
|
|
||||||
|
|
||||||
-- Generate a thumbnail
|
|
||||||
if ext == ".webm" then
|
|
||||||
thumb_path = dir .. "s" .. name .. ".png"
|
|
||||||
|
|
||||||
-- Create screenshot of first frame
|
|
||||||
os.execute(string.format("ffmpeg -i %s -ss 00:00:01 -vframes 1 %s -y", full_path, thumb_path))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save thumbnail
|
|
||||||
local w, h
|
|
||||||
if result.thread_id > thread then
|
|
||||||
thread = result.thread_id
|
|
||||||
w = result.file_width < 250 and result.file_width or 250
|
|
||||||
h = result.file_height < 250 and result.file_height or 250
|
|
||||||
else
|
|
||||||
w = result.file_width < 125 and result.file_width or 125
|
|
||||||
h = result.file_height < 125 and result.file_height or 125
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Grab first frame from video
|
|
||||||
if ext == ".webm" then
|
|
||||||
magick.thumb(thumb_path, string.format("%sx%s", w, h), thumb_path)
|
|
||||||
elseif ext == ".svg" then
|
|
||||||
thumb_path = dir .. "s" .. name .. ".png"
|
|
||||||
os.execute(string.format("convert -background none -resize %dx%d %s %s", w, h, full_path, thumb_path))
|
|
||||||
elseif ext == ".gif" then
|
|
||||||
-- Grab first frame of a gif instead of the last
|
|
||||||
local gif, err = giflib.load_gif(full_path)
|
|
||||||
|
|
||||||
if err then
|
|
||||||
magick.thumb(full_path, string.format("%sx%s", w, h), thumb_path)
|
|
||||||
else
|
|
||||||
gif:write_first_frame(thumb_path)
|
|
||||||
magick.thumb(thumb_path, string.format("%sx%s", w, h), thumb_path)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
magick.thumb(full_path, string.format("%sx%s", w, h), thumb_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return Boards
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
local Model = require("lapis.db.model").Model
|
|
||||||
local Pages = Model:extend("pages")
|
|
||||||
|
|
||||||
Pages.valid_record = {
|
|
||||||
{ "slug", exists=true, type="String" },
|
|
||||||
{ "title", exists=true, type="String" },
|
|
||||||
{ "content", exists=true, type="String" }
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Create a new page
|
|
||||||
-- @tparam table page Page data
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Pages:new(params)
|
|
||||||
local unique, err = self:is_unique(params.slug, params.title)
|
|
||||||
if not unique then
|
|
||||||
return nil, err
|
|
||||||
end
|
|
||||||
|
|
||||||
local page = self:create(params)
|
|
||||||
return page and page or nil, { "err_create_page", { page.slug, page.title } }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Modify a page
|
|
||||||
-- @tparam table page Page data
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Pages:modify(params, slug)
|
|
||||||
local page = self:get(slug)
|
|
||||||
if not page then
|
|
||||||
return nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check to see if the page we are modifying is the one that fails validation.
|
|
||||||
-- If it is, that's fine since we're updating the unique values with themselves.
|
|
||||||
-- If #pages > 1 then this will always fail since either the new slug or new
|
|
||||||
-- title is going to belong to some other page.
|
|
||||||
do
|
|
||||||
local unique, err, pages = self:is_unique(params.slug, params.title)
|
|
||||||
if not unique then
|
|
||||||
for _, p in ipairs(pages) do
|
|
||||||
if page.id ~= p.id then
|
|
||||||
return nil, err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local success, err = page:update(params)
|
|
||||||
return success and page or nil, "FIXME: " .. tostring(err)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Delete page
|
|
||||||
-- @tparam table page Page data
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Pages:delete(slug)
|
|
||||||
local page = self:get(slug)
|
|
||||||
if not page then
|
|
||||||
return nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
local success = page:delete()
|
|
||||||
return success and page or nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get all pages
|
|
||||||
-- @treturn table pages List of pages
|
|
||||||
function Pages:get_all()
|
|
||||||
return self:select("order by slug asc")
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get page
|
|
||||||
-- @tparam string slug Page slug
|
|
||||||
-- @treturn table page
|
|
||||||
function Pages:get(slug)
|
|
||||||
local page = self:find { slug=slug:lower() }
|
|
||||||
return page and page or nil, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
function Pages:is_unique(slug, title)
|
|
||||||
local pages = self:select("where slug=? or lower(title)=?", slug, title:lower())
|
|
||||||
return #pages == 0 and true or nil, "FIXME", pages
|
|
||||||
end
|
|
||||||
|
|
||||||
function Pages.format_to_db(_, params)
|
|
||||||
params.slug = params.slug:lower()
|
|
||||||
end
|
|
||||||
|
|
||||||
return Pages
|
|
||||||
@ -1,387 +0,0 @@
|
|||||||
local encoding = require "lapis.util.encoding"
|
|
||||||
local Model = require("lapis.db.model").Model
|
|
||||||
local giflib = require "giflib"
|
|
||||||
local magick = require "magick"
|
|
||||||
local md5 = require "md5"
|
|
||||||
local filetypes = require "utils.file_whitelist"
|
|
||||||
local generate = require "utils.generate"
|
|
||||||
local Posts = Model:extend("posts", {
|
|
||||||
relations = {
|
|
||||||
{ "board", belongs_to="Boards" },
|
|
||||||
{ "thread", belongs_to="Threads" },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
local sf = string.format
|
|
||||||
|
|
||||||
local function get_duration(path)
|
|
||||||
local cmd = sf("ffprobe -i %s -show_entries format=duration -v quiet -of csv=\"p=0\" -sexagesimal", path)
|
|
||||||
local f = io.popen(cmd, "r")
|
|
||||||
local s = f:read("*a")
|
|
||||||
f:close()
|
|
||||||
|
|
||||||
local hr, mn, sc = string.match(s, "(%d+):(%d+):(%d+).%d+")
|
|
||||||
local d = mn..":"..sc
|
|
||||||
|
|
||||||
if hr ~= "0" then
|
|
||||||
d = hr..":"..d
|
|
||||||
end
|
|
||||||
|
|
||||||
return d
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Prepare post for insertion
|
|
||||||
-- @tparam table params Input from the user
|
|
||||||
-- @tparam table session User session
|
|
||||||
-- @tparam table board Board data
|
|
||||||
-- @tparam table thread Thread data
|
|
||||||
-- @tparam number files Number of files in thread
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Posts:prepare_post(params, session, board, thread, files)
|
|
||||||
-- FIXME: this whole function should be web-side, not api-side
|
|
||||||
local time = os.time()
|
|
||||||
|
|
||||||
-- Prepare session stuff
|
|
||||||
session.password = session.password or generate.password(time)
|
|
||||||
|
|
||||||
-- Save names on individual boards
|
|
||||||
-- TODO: Put this code elsewhere
|
|
||||||
if params.name then
|
|
||||||
session.names[board.name] = params.name
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Files take presidence over drawings, but if there is no file, fill in
|
|
||||||
-- the file fields with draw data. This should avoid ugly code branching
|
|
||||||
-- later on in the file.
|
|
||||||
-- TODO: send file data through api!
|
|
||||||
params.file = params.file or {
|
|
||||||
filename = "",
|
|
||||||
content = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if #params.file.content == 0 and params.draw then
|
|
||||||
local pattern = ".-,(.+)"
|
|
||||||
params.draw = params.draw:match(pattern)
|
|
||||||
params.file.filename = "tegaki.png"
|
|
||||||
params.file.content = encoding.decode_base64(params.draw)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check board flags
|
|
||||||
if thread then
|
|
||||||
if thread.lock and not session.admin and not session.mod then
|
|
||||||
return false, { "err_locked_thread", { thread.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
if board.post_comment and not params.comment then
|
|
||||||
return false, { "err_comment_post" }
|
|
||||||
end
|
|
||||||
|
|
||||||
if board.post_file and #params.file.content == 0 then
|
|
||||||
return false, { "err_file_post" }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if board.thread_comment and not params.comment then
|
|
||||||
return false, { "err_comment_thread" }
|
|
||||||
end
|
|
||||||
|
|
||||||
if board.thread_file and #params.file.content == 0 then
|
|
||||||
return false, { "err_file_thread" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse name
|
|
||||||
if params.name then
|
|
||||||
params.name, params.trip = generate.tripcode(params.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set file
|
|
||||||
if #params.file.content > 0 then
|
|
||||||
|
|
||||||
-- Reject files in text-only boards
|
|
||||||
if board.text_only then
|
|
||||||
return false, { "err_no_files" }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Thread limit is already met.
|
|
||||||
if thread then
|
|
||||||
if files >= board.thread_file_limit and not thread.size_override then
|
|
||||||
return false, { "err_file_limit", { thread.post_id } }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local name = sf("%s%s", time, generate.random())
|
|
||||||
local ext = params.file.filename:match("^.+(%..+)$")
|
|
||||||
ext = string.lower(ext)
|
|
||||||
|
|
||||||
-- Figure out how to deal with the file
|
|
||||||
if filetypes.image[ext] and board.filetype_image then
|
|
||||||
params.file_type = "image"
|
|
||||||
|
|
||||||
if ext ~= ".webm" then
|
|
||||||
-- Check if valid image
|
|
||||||
local image = magick.load_image_from_blob(params.file.content)
|
|
||||||
|
|
||||||
if not image then
|
|
||||||
return false, { "err_invalid_image" }
|
|
||||||
end
|
|
||||||
|
|
||||||
params.file_width = image:get_width()
|
|
||||||
params.file_height = image:get_height()
|
|
||||||
end
|
|
||||||
elseif filetypes.audio[ext] and board.filetype_audio then
|
|
||||||
params.file_type = "audio"
|
|
||||||
else
|
|
||||||
return false, { "err_invalid_ext", { ext } }
|
|
||||||
end
|
|
||||||
|
|
||||||
params.file_name = params.file.filename
|
|
||||||
params.file_path = name .. ext
|
|
||||||
params.file_md5 = md5.sumhexa(params.file.content)
|
|
||||||
params.file_size = #params.file.content
|
|
||||||
|
|
||||||
if params.file_spoiler then
|
|
||||||
params.file_spoiler = true
|
|
||||||
else
|
|
||||||
params.file_spoiler = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if file already exists
|
|
||||||
local file = self:find_file(board.id, params.file_md5)
|
|
||||||
if file then
|
|
||||||
return false, { "err_file_exists" }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
params.file_spoiler = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check contributions
|
|
||||||
if not params.comment and not params.file_name then
|
|
||||||
return false, { "err_contribute" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Create a new post
|
|
||||||
-- @tparam table params Post parameters
|
|
||||||
-- @tparam table board Board data
|
|
||||||
-- @tparam boolean op OP flag
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Posts:new(params, board, op)
|
|
||||||
|
|
||||||
-- Create post
|
|
||||||
local post = self:create(params)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_create_post" }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save file
|
|
||||||
if post.file_path then
|
|
||||||
local dir = sf("./static/%s/", board.name)
|
|
||||||
local name, ext = post.file_path:match("^(.+)%.(.+)$")
|
|
||||||
ext = string.lower(ext)
|
|
||||||
|
|
||||||
-- Filesystem paths
|
|
||||||
local full_path = dir .. post.file_path
|
|
||||||
local thumb_path = dir .. "s" .. post.file_path
|
|
||||||
|
|
||||||
-- Save file
|
|
||||||
local file = io.open(full_path, "w")
|
|
||||||
file:write(params.file_content)
|
|
||||||
file:close()
|
|
||||||
|
|
||||||
-- Audio file
|
|
||||||
if post.file_type == "audio" then
|
|
||||||
post.file_duration = get_duration(full_path)
|
|
||||||
post:update("file_duration")
|
|
||||||
return post
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Image file
|
|
||||||
if post.file_type == "image" and not post.file_spoiler then
|
|
||||||
|
|
||||||
-- Save thumbnail
|
|
||||||
local w, h
|
|
||||||
if op then
|
|
||||||
w = post.file_width < 250 and post.file_width or 250
|
|
||||||
h = post.file_height < 250 and post.file_height or 250
|
|
||||||
else
|
|
||||||
w = post.file_width < 125 and post.file_width or 125
|
|
||||||
h = post.file_height < 125 and post.file_height or 125
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Generate a thumbnail
|
|
||||||
if ext == "webm" then
|
|
||||||
thumb_path = dir .. "s" .. name .. ".png"
|
|
||||||
|
|
||||||
-- Create screenshot of first frame
|
|
||||||
os.execute(sf("ffmpeg -i %s -ss 00:00:01 -vframes 1 %s -y", full_path, thumb_path))
|
|
||||||
|
|
||||||
-- Update post info
|
|
||||||
local image = magick.load_image(thumb_path)
|
|
||||||
post.file_width = image:get_width()
|
|
||||||
post.file_height = image:get_height()
|
|
||||||
post.file_duration = get_duration(full_path)
|
|
||||||
post:update("file_width", "file_height", "file_duration")
|
|
||||||
|
|
||||||
-- Resize thumbnail
|
|
||||||
magick.thumb(thumb_path, sf("%sx%s", w, h), thumb_path)
|
|
||||||
|
|
||||||
elseif ext == "svg" then
|
|
||||||
thumb_path = dir .. "s" .. name .. ".png"
|
|
||||||
os.execute(sf("convert -background none -resize %dx%d %s %s", w, h, full_path, thumb_path))
|
|
||||||
|
|
||||||
elseif ext == "gif" then
|
|
||||||
local gif, err = giflib.load_gif(full_path)
|
|
||||||
|
|
||||||
if err then
|
|
||||||
-- Not animated I presume? TODO: check what err represents
|
|
||||||
magick.thumb(full_path, sf("%sx%s", w, h), thumb_path)
|
|
||||||
else
|
|
||||||
-- Grab first frame of a gif instead of the last
|
|
||||||
gif:write_first_frame(thumb_path)
|
|
||||||
|
|
||||||
-- Update post info
|
|
||||||
local width, height = gif:dimensions()
|
|
||||||
post.file_width = width
|
|
||||||
post.file_height = height
|
|
||||||
post:update("file_width", "file_height")
|
|
||||||
|
|
||||||
-- Resize thumbnail
|
|
||||||
magick.thumb(thumb_path, sf("%sx%s", w, h), thumb_path)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
magick.thumb(full_path, sf("%sx%s", w, h), thumb_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Update board
|
|
||||||
board:update("total_posts")
|
|
||||||
|
|
||||||
return post
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Delete post data
|
|
||||||
-- @tparam number id Post ID
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Posts:delete(id)
|
|
||||||
|
|
||||||
-- Get post
|
|
||||||
local post, err = self:get_post_by_id(id)
|
|
||||||
if not post then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete post
|
|
||||||
local success, err = post:delete()
|
|
||||||
if not success then
|
|
||||||
return false, err--{ "err_delete_post", { post.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete files
|
|
||||||
if post.file_path then
|
|
||||||
local board = post:get_board()
|
|
||||||
local dir = sf("./static/%s/", board.name)
|
|
||||||
local name, ext = post.file_path:match("^(.+)%.(.+)$")
|
|
||||||
ext = string.lower(ext)
|
|
||||||
os.remove(dir .. post.file_path)
|
|
||||||
|
|
||||||
-- Change thumbnail path to png
|
|
||||||
if ext == "webm" or ext == "svg" then
|
|
||||||
post.file_path = name .. ".png"
|
|
||||||
end
|
|
||||||
|
|
||||||
os.remove(dir .. "s" .. post.file_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
return post
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get op and last 5 posts of a thread to display on board index
|
|
||||||
-- @tparam number thread_id Thread ID
|
|
||||||
-- @treturn table posts
|
|
||||||
function Posts:get_index_posts(thread_id)
|
|
||||||
local sql = "where thread_id=? order by post_id desc limit 5"
|
|
||||||
local posts = self:select(sql, thread_id)
|
|
||||||
|
|
||||||
if self:count_posts(thread_id) > 5 then
|
|
||||||
local thread = posts[1]:get_thread()
|
|
||||||
local op = thread:get_op()
|
|
||||||
table.insert(posts, op)
|
|
||||||
end
|
|
||||||
|
|
||||||
return posts
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get post data
|
|
||||||
-- @tparam number board_id Board ID
|
|
||||||
-- @tparam number post_id Local Post ID
|
|
||||||
-- @treturn table post
|
|
||||||
function Posts:get(board_id, post_id)
|
|
||||||
local post = self:find {
|
|
||||||
board_id = board_id,
|
|
||||||
post_id = post_id
|
|
||||||
}
|
|
||||||
return post and post or false, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get post data
|
|
||||||
-- @tparam number id Post ID
|
|
||||||
-- @treturn table post
|
|
||||||
function Posts:get_post_by_id(id)
|
|
||||||
local post = self:find(id)
|
|
||||||
return post and post or false, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Find file in active posts
|
|
||||||
-- @tparam number board Board ID
|
|
||||||
-- @tparam string file_md5 Unique hash of file
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Posts:find_file(board_id, file_md5)
|
|
||||||
local sql = "where board_id=? and file_md5=? limit 1"
|
|
||||||
return unpack(self:select(sql, board_id, file_md5))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Count hidden posts in a thread
|
|
||||||
-- @tparam number thread_id Thread ID
|
|
||||||
-- @treturn table hidden
|
|
||||||
function Posts:count_hidden_posts(thread_id)
|
|
||||||
local posts = self:get_index_posts(thread_id)
|
|
||||||
local num_posts = self:count_posts(thread_id)
|
|
||||||
local num_files = self:count_files(thread_id)
|
|
||||||
|
|
||||||
for _, post in ipairs(posts) do
|
|
||||||
-- Reduce number of posts hidden
|
|
||||||
num_posts = num_posts - 1
|
|
||||||
|
|
||||||
-- Reduce number of files hidden
|
|
||||||
if post.file_name then
|
|
||||||
num_files = num_files - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return { posts=num_posts, files=num_files }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Count posts in a thread
|
|
||||||
-- @tparam number thread_id Thread ID
|
|
||||||
-- @treturn number posts
|
|
||||||
function Posts:count_posts(thread_id)
|
|
||||||
local sql = "thread_id=?"
|
|
||||||
return self:count(sql, thread_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Count posts with images in a thread
|
|
||||||
-- @tparam number thread_id Thread ID
|
|
||||||
-- @treturn number files
|
|
||||||
function Posts:count_files(thread_id)
|
|
||||||
local sql = "thread_id=? and file_name is not null"
|
|
||||||
return self:count(sql, thread_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Posts
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
local trim = require("lapis.util").trim_filter
|
|
||||||
local Model = require("lapis.db.model").Model
|
|
||||||
local Reports = Model:extend("reports")
|
|
||||||
|
|
||||||
--- Create a new report
|
|
||||||
-- @tparam table report Report data
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Reports:create_report(report)
|
|
||||||
-- Trim white space
|
|
||||||
trim(report, {
|
|
||||||
"board_id", "thread_id", "post_id",
|
|
||||||
"timestamp", "num_reports"
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
local r = self:create {
|
|
||||||
board_id = report.board_id,
|
|
||||||
thread_id = report.thread_id,
|
|
||||||
post_id = report.post_id,
|
|
||||||
timestamp = report.timestamp,
|
|
||||||
num_reports = report.num_reports
|
|
||||||
}
|
|
||||||
|
|
||||||
if r then
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
return false, { "err_create_report" }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Modify a report
|
|
||||||
-- @tparam table report Report data
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Reports:modify_report(report)
|
|
||||||
local columns = {}
|
|
||||||
for col in pairs(report) do
|
|
||||||
table.insert(columns, col)
|
|
||||||
end
|
|
||||||
|
|
||||||
return report:update(unpack(columns))
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Delete report
|
|
||||||
-- @tparam table report Report data
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Reports:delete_report(report)
|
|
||||||
return report:delete()
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get all reports
|
|
||||||
-- @treturn table reports List of reports
|
|
||||||
function Reports:get_reports()
|
|
||||||
return self:select("order by timestamp asc")
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get report
|
|
||||||
-- @tparam string board_id Board ID
|
|
||||||
-- @tparam string post_id Post ID
|
|
||||||
-- @treturn table report
|
|
||||||
function Reports:get_report(board_id, post_id)
|
|
||||||
return unpack(self:select(
|
|
||||||
"where board_id=? and post_id=? limit 1",
|
|
||||||
board_id, post_id
|
|
||||||
))
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get report
|
|
||||||
-- @tparam string id Report ID
|
|
||||||
-- @treturn table report
|
|
||||||
function Reports:get_report_by_id(id)
|
|
||||||
return unpack(self:select("where id=? limit 1", id))
|
|
||||||
end
|
|
||||||
|
|
||||||
return Reports
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
local Model = require("lapis.db.model").Model
|
|
||||||
local Threads = Model:extend("threads", {
|
|
||||||
relations = {
|
|
||||||
{ "board", belongs_to="Boards" },
|
|
||||||
{ "posts", has_many="Posts" },
|
|
||||||
{ "op", has_one="Posts", order="post_id asc" },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Threads.valid_record = {
|
|
||||||
{ "board_id", exists=true }
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Create thread
|
|
||||||
-- @tparam table params Thread parameters
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Threads:new(params)
|
|
||||||
local thread = self:create(params)
|
|
||||||
return thread and thread or false, { "err_create_thread" }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Modify a thread
|
|
||||||
-- @tparam table params Thread parameters
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Threads:modify(params)
|
|
||||||
local thread = self:get(params.id)
|
|
||||||
if not thread then
|
|
||||||
return false, { "err_create_board" } -- FIXME: wrong error message
|
|
||||||
end
|
|
||||||
|
|
||||||
local success, err = thread:update(params)
|
|
||||||
return success and thread or false, "FIXME: " .. tostring(err)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Delete entire thread
|
|
||||||
-- @tparam number id Thread ID
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Threads:delete(id)
|
|
||||||
-- FIXME: API needs to create a user object for better auth checking
|
|
||||||
local thread, err = self:get(id)
|
|
||||||
if not thread then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
local op = thread:get_op()
|
|
||||||
local success = thread:delete()
|
|
||||||
return success and thread or false, { "err_delete_thread", { op.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get thread data
|
|
||||||
-- @tparam number id Thread ID
|
|
||||||
-- @treturn table thread
|
|
||||||
function Threads:get(id)
|
|
||||||
local thread = self:find(id)
|
|
||||||
return thread and thread or false, "FIXME"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get archived threads
|
|
||||||
-- @tparam number board_id Board ID
|
|
||||||
-- @treturn table threads
|
|
||||||
function Threads:get_archived(board_id)
|
|
||||||
local sql = "where board_id=? and archive=true order by last_active desc"
|
|
||||||
return self:select(sql, board_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Bump threads to archive
|
|
||||||
-- @tparam number board_id Board ID
|
|
||||||
-- @tparam number max_threads Maximum number of threads on this board
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Threads:archive_threads(board_id, max_threads)
|
|
||||||
local threads = self:get_threads(board_id)
|
|
||||||
|
|
||||||
if #threads > max_threads then
|
|
||||||
for i=max_threads+1, #threads do
|
|
||||||
local _, err = self:archive_thread(threads[i])
|
|
||||||
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Archive a thread
|
|
||||||
-- @tparam table thread Thread data
|
|
||||||
-- @treturn boolean success
|
|
||||||
-- @treturn string error
|
|
||||||
function Threads:archive_thread(thread)
|
|
||||||
thread.sticky = false
|
|
||||||
thread.lock = true
|
|
||||||
thread.archive = true
|
|
||||||
thread.last_active = os.time()
|
|
||||||
return thread:update("sticky", "lock", "archive", "last_active")
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Find threads with no posts
|
|
||||||
-- @treturn table threads
|
|
||||||
function Threads:find_orphans()
|
|
||||||
return self:select("where id not in (select distinct thread_id from posts)")
|
|
||||||
end
|
|
||||||
|
|
||||||
return Threads
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
$spacing: 10px;
|
|
||||||
|
|
||||||
.thread_container, .post_container {
|
|
||||||
margin: $spacing 0 0 0;
|
|
||||||
padding: $spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thread_container {
|
|
||||||
overflow: visible;
|
|
||||||
|
|
||||||
.op, .post_container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.op {
|
|
||||||
.post_admin {
|
|
||||||
margin-left: $spacing * 35;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_container {
|
|
||||||
display: table;
|
|
||||||
min-width: 300px;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
.post_admin {
|
|
||||||
margin-left: $spacing * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_file {
|
|
||||||
margin: 0 0 3px 0;
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_image {
|
|
||||||
float: left;
|
|
||||||
margin: 0 $spacing * 2 0 $spacing;
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 1000px;
|
|
||||||
max-height: 1000px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_subject, .post_name {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_comment {
|
|
||||||
padding: $spacing $spacing * 1.5;
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.broken_link {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spoiler {
|
|
||||||
background-color: #000000;
|
|
||||||
color: #000000;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_banned {
|
|
||||||
color: #ff0000;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: $spacing * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_flag, .post_menu {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_menu {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 9;
|
|
||||||
|
|
||||||
div {
|
|
||||||
padding: 3px;
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
input[type="submit"], button {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
HTML5 Reset :: style.css
|
|
||||||
----------------------------------------------------------
|
|
||||||
We have learned much from/been inspired by/taken code where offered from:
|
|
||||||
|
|
||||||
Eric Meyer :: http://meyerweb.com
|
|
||||||
HTML5 Doctor :: http://html5doctor.com
|
|
||||||
and the HTML5 Boilerplate :: http://html5boilerplate.com
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* Let's default this puppy out
|
|
||||||
-------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
html, body, body div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, figure, footer, header, menu, nav, section, time, mark, audio, video, details, summary {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font-weight: normal;
|
|
||||||
vertical-align: baseline;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
article, aside, figure, footer, header, nav, section, details, summary {display: block;}
|
|
||||||
|
|
||||||
/* Handle box-sizing while better addressing child elements:
|
|
||||||
http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */
|
|
||||||
html {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
*,
|
|
||||||
*:before,
|
|
||||||
*:after {
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* consider resetting the default cursor: https://gist.github.com/murtaugh/5247154 */
|
|
||||||
|
|
||||||
/* Responsive images and other embedded objects */
|
|
||||||
/* if you don't have full control over `img` tags (if you have to overcome attributes), consider adding height: auto */
|
|
||||||
img,
|
|
||||||
object,
|
|
||||||
embed {max-width: 100%;}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Note: keeping IMG here will cause problems if you're using foreground images as sprites.
|
|
||||||
In fact, it *will* cause problems with Google Maps' controls at small size.
|
|
||||||
If this is the case for you, try uncommenting the following:
|
|
||||||
|
|
||||||
#map img {
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* force a vertical scrollbar to prevent a jumpy page */
|
|
||||||
html {overflow-y: scroll;}
|
|
||||||
|
|
||||||
/* we use a lot of ULs that aren't bulleted.
|
|
||||||
you'll have to restore the bullets within content,
|
|
||||||
which is fine because they're probably customized anyway */
|
|
||||||
ul {list-style: none;}
|
|
||||||
|
|
||||||
blockquote, q {quotes: none;}
|
|
||||||
|
|
||||||
blockquote:before,
|
|
||||||
blockquote:after,
|
|
||||||
q:before,
|
|
||||||
q:after {content: ''; content: none;}
|
|
||||||
|
|
||||||
a {margin: 0; padding: 0; font-size: 100%; vertical-align: baseline; background: transparent;}
|
|
||||||
|
|
||||||
del {text-decoration: line-through;}
|
|
||||||
|
|
||||||
abbr[title], dfn[title] {border-bottom: 1px dotted #000; cursor: help;}
|
|
||||||
|
|
||||||
/* tables still need cellspacing="0" in the markup */
|
|
||||||
table {border-collapse: collapse; border-spacing: 0;}
|
|
||||||
th {font-weight: bold; vertical-align: bottom;}
|
|
||||||
td {font-weight: normal; vertical-align: top;}
|
|
||||||
|
|
||||||
hr {display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0;}
|
|
||||||
|
|
||||||
input, select {vertical-align: middle;}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
white-space: pre; /* CSS2 */
|
|
||||||
white-space: pre-wrap; /* CSS 2.1 */
|
|
||||||
white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */
|
|
||||||
word-wrap: break-word; /* IE */
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="radio"] {vertical-align: text-bottom;}
|
|
||||||
.ie7 input[type="checkbox"] {vertical-align: baseline;}
|
|
||||||
.ie6 input {vertical-align: text-bottom;}
|
|
||||||
|
|
||||||
select, input, textarea {font: 99% sans-serif;}
|
|
||||||
|
|
||||||
table {font-size: inherit; font: 100%;}
|
|
||||||
|
|
||||||
small {font-size: 85%;}
|
|
||||||
|
|
||||||
strong {font-weight: bold;}
|
|
||||||
|
|
||||||
td, td img {vertical-align: top;}
|
|
||||||
|
|
||||||
/* Make sure sup and sub don't mess with your line-heights http://gist.github.com/413930 */
|
|
||||||
sub, sup {font-size: 75%; line-height: 0; position: relative;}
|
|
||||||
sup {top: -0.5em;}
|
|
||||||
sub {bottom: -0.25em;}
|
|
||||||
|
|
||||||
/* standardize any monospaced elements */
|
|
||||||
pre, code, kbd, samp {font-family: monospace, sans-serif;}
|
|
||||||
|
|
||||||
/* hand cursor on clickable elements */
|
|
||||||
.clickable,
|
|
||||||
input[type=button],
|
|
||||||
input[type=submit],
|
|
||||||
input[type=file],
|
|
||||||
button {cursor: pointer;}
|
|
||||||
|
|
||||||
/* Webkit browsers add a 2px margin outside the chrome of form elements */
|
|
||||||
button, input, select, textarea {margin: 0;}
|
|
||||||
|
|
||||||
/* make buttons play nice in IE */
|
|
||||||
button,
|
|
||||||
input[type=button] {width: auto; overflow: visible;}
|
|
||||||
|
|
||||||
/* scale images in IE7 more attractively */
|
|
||||||
.ie7 img {-ms-interpolation-mode: bicubic;}
|
|
||||||
|
|
||||||
/* prevent BG image flicker upon hover
|
|
||||||
(commented out as usage is rare, and the filter syntax messes with some pre-processors)
|
|
||||||
.ie6 html {filter: expression(document.execCommand("BackgroundImageCache", false, true));}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* let's clear some floats */
|
|
||||||
.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
|
|
||||||
.clearfix:after { clear: both; }
|
|
||||||
.clearfix { zoom: 1; }
|
|
||||||
@ -1,322 +0,0 @@
|
|||||||
@import "reset";
|
|
||||||
@import "posts";
|
|
||||||
|
|
||||||
@mixin display-flex {
|
|
||||||
display: -webkit-box-flex;
|
|
||||||
display: -webkit-flexbox;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin flex($args) {
|
|
||||||
-webkit-flex: $args;
|
|
||||||
-ms-flex: $args;
|
|
||||||
flex: $args;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin flex-direction($dir) {
|
|
||||||
$alt: vertical !default;
|
|
||||||
@if $dir == column {
|
|
||||||
$alt: vertical;
|
|
||||||
} @else {
|
|
||||||
$alt: horizontal;
|
|
||||||
}
|
|
||||||
-webkit-flex-direction: column;
|
|
||||||
-webkit-box-orient: $alt;
|
|
||||||
-ms-flex-direction: column;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
$spacing: 10px;
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: $spacing;
|
|
||||||
font-size: 10pt;
|
|
||||||
font-family: Helvetica, "Noto Sans", "Roboto Sans", Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"],
|
|
||||||
input[type="password"],
|
|
||||||
textarea,
|
|
||||||
select,
|
|
||||||
.admin_main {
|
|
||||||
padding: 4px;
|
|
||||||
width: 280px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="file"] {
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 240%;
|
|
||||||
font-weight: bolder;
|
|
||||||
margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
margin: $spacing 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
clear: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
display: inline-block;
|
|
||||||
margin: $spacing / 2 auto;
|
|
||||||
padding: $spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 1px;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 80%;
|
|
||||||
|
|
||||||
thead {
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
td {
|
|
||||||
font-weight: bolder;
|
|
||||||
padding: $spacing / 2 $spacing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding: 3px $spacing / 2;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rules {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: disc;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-left: $spacing * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
list-style: decimal;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-left: $spacing * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#error {
|
|
||||||
background-color: #ff0000;
|
|
||||||
color: #ffffff;
|
|
||||||
margin-bottom: $spacing;
|
|
||||||
padding: $spacing;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 220%;
|
|
||||||
font-weight: bolder;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#post_form, .admin_form {
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
form {
|
|
||||||
div {
|
|
||||||
@include display-flex;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
@include flex(0);
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: right;
|
|
||||||
min-width: 120px;
|
|
||||||
margin: 1px;
|
|
||||||
padding: 4px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fields {
|
|
||||||
margin: 1px;
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
min-height: 100px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin: $spacing 0 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin_form {
|
|
||||||
form {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: #ff0000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.boards, .return, .copyright {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.archive_stats {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: $spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.announcement {
|
|
||||||
text-align: center;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.banned {
|
|
||||||
border: 7px dashed #000000;
|
|
||||||
display: table;
|
|
||||||
font-size: 180%;
|
|
||||||
min-width: 200px;
|
|
||||||
max-width: 600px;
|
|
||||||
margin: $spacing * 2 auto;
|
|
||||||
padding: $spacing;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: $spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.medium {
|
|
||||||
width: 120px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.short {
|
|
||||||
width: 40px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.catalog_container {
|
|
||||||
display: inline-block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: top;
|
|
||||||
width: 135px;
|
|
||||||
|
|
||||||
.catalog_stats {
|
|
||||||
font-size: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.25);
|
|
||||||
max-width: 125px;
|
|
||||||
max-height: 125px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logout {
|
|
||||||
top: $spacing;
|
|
||||||
right: $spacing;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copyright {
|
|
||||||
margin-top: $spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.front_page {
|
|
||||||
h2 {
|
|
||||||
font-size: 180%;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 140%;
|
|
||||||
margin-top: $spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board {
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 300px;
|
|
||||||
margin: $spacing * 2;
|
|
||||||
vertical-align: top;
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-size: 160%;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtext {
|
|
||||||
display: block;
|
|
||||||
font-size: 120%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table_of_contents {
|
|
||||||
display: table;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.answers,{
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 160%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.install {
|
|
||||||
max-width: 300px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 160%;
|
|
||||||
margin-top: $spacing * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: $spacing * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-size: 120%;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: $spacing * 2;
|
|
||||||
padding: $spacing / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
$background: #ffffee;
|
|
||||||
$bg_light: #ffffff;
|
|
||||||
$border: #d9bfb7;
|
|
||||||
$subject: #cc1105;
|
|
||||||
$header: #800000;
|
|
||||||
$link: #0000ee;
|
|
||||||
$hover: #ff0000;
|
|
||||||
$quote: #000080;
|
|
||||||
$post: #f0e0d6;
|
|
||||||
$name: #117743;
|
|
||||||
$black: #000000;
|
|
||||||
$green: #789922;
|
|
||||||
$blue: #292299;
|
|
||||||
$active: #f0c0b0;
|
|
||||||
$active_border: #d99f91;
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: $background;
|
|
||||||
}
|
|
||||||
|
|
||||||
body, h1, h2 {
|
|
||||||
color: $header;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:visited, .broken_link {
|
|
||||||
color: $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover, .quote_link:hover, .broken_link:hover {
|
|
||||||
color: $hover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote_link {
|
|
||||||
color: $quote;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote_green {
|
|
||||||
color: $green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote_blue {
|
|
||||||
color: $blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.announcement {
|
|
||||||
color: $header;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post_form, .admin_form {
|
|
||||||
form {
|
|
||||||
.title {
|
|
||||||
background-color: $active;
|
|
||||||
border: 1px solid $active_border;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
thead {
|
|
||||||
td {
|
|
||||||
background-color: $post;
|
|
||||||
border: 1px solid $border;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-of-type(odd) {
|
|
||||||
background-color: $background;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-of-type(even) {
|
|
||||||
background-color: $bg_light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_container {
|
|
||||||
background-color: $post;
|
|
||||||
border-right: 1px solid $border;
|
|
||||||
border-bottom: 1px solid $border;
|
|
||||||
|
|
||||||
&:target {
|
|
||||||
background-color: $active;
|
|
||||||
border-right: 1px solid $active_border;
|
|
||||||
border-bottom: 1px solid $active_border;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_subject {
|
|
||||||
color: $subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_name, .post_trip {
|
|
||||||
color: $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_menu .menu {
|
|
||||||
background-color: $post;
|
|
||||||
border:1px solid $border;
|
|
||||||
|
|
||||||
div {
|
|
||||||
border-right: 1px solid $border;
|
|
||||||
border-bottom: 1px solid $border;
|
|
||||||
|
|
||||||
form {
|
|
||||||
button {
|
|
||||||
color: $header;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $hover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
$background: #eef2ff;
|
|
||||||
$bg_light: #f7f9ff;
|
|
||||||
$border: #b7c5d9;
|
|
||||||
$subject: #0f0c5d;
|
|
||||||
$header: #af0a0f;
|
|
||||||
$link: #34345c;
|
|
||||||
$hover: #dd0000;
|
|
||||||
$post: #d6daf0;
|
|
||||||
$name: #117743;
|
|
||||||
$black: #000000;
|
|
||||||
$green: #789922;
|
|
||||||
$blue: #292299;
|
|
||||||
$active: #d6bad0;
|
|
||||||
$active_border: #ba9dbf;
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: $background;
|
|
||||||
color: $black;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2 {
|
|
||||||
color: $header;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:visited, .broken_link {
|
|
||||||
color: $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover, .quote_link, .quote_link:visited, .broken_link:hover {
|
|
||||||
color: $hover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote_green {
|
|
||||||
color: $green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote_blue {
|
|
||||||
color: $blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.announcement {
|
|
||||||
color: $header;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post_form, .admin_form {
|
|
||||||
form {
|
|
||||||
.title {
|
|
||||||
background-color: $active;
|
|
||||||
border: 1px solid $active_border;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
thead {
|
|
||||||
td {
|
|
||||||
background-color: $post;
|
|
||||||
border: 1px solid $border;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-of-type(odd) {
|
|
||||||
background-color: $background;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-of-type(even) {
|
|
||||||
background-color: $bg_light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_container {
|
|
||||||
background-color: $post;
|
|
||||||
border-right: 1px solid $border;
|
|
||||||
border-bottom: 1px solid $border;
|
|
||||||
|
|
||||||
&:target {
|
|
||||||
background-color: $active;
|
|
||||||
border-right: 1px solid $active_border;
|
|
||||||
border-bottom: 1px solid $active_border;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_subject {
|
|
||||||
color: $subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_name, .post_trip {
|
|
||||||
color: $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_menu .menu {
|
|
||||||
background-color: $post;
|
|
||||||
border:1px solid $border;
|
|
||||||
|
|
||||||
div {
|
|
||||||
border-right: 1px solid $border;
|
|
||||||
border-bottom: 1px solid $border;
|
|
||||||
|
|
||||||
form {
|
|
||||||
button {
|
|
||||||
color: $link;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $hover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,35 +1,37 @@
|
|||||||
local ngx = _G.ngx
|
local ngx = _G.ngx
|
||||||
local json = require "cjson"
|
local json = require"utils.json"
|
||||||
|
|
||||||
local function capture(method, uri, body)
|
local function capture(method, uri, body)
|
||||||
local response = ngx.location.capture(uri, {
|
local response = ngx.location.capture(uri, {
|
||||||
method = method,
|
method = method,
|
||||||
body = json.encode(body)
|
body = json.encode(body),
|
||||||
})
|
})
|
||||||
|
|
||||||
if response.truncated then return end
|
if response.truncated then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if response.status ~= ngx.HTTP_OK then
|
if response.status ~= ngx.HTTP_OK then
|
||||||
return nil, json.decode(response.body)
|
return nil, json.decode(response.body)
|
||||||
end
|
end
|
||||||
|
|
||||||
return json.decode(response.body)
|
return json.decode(response.body)
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get = function(...)
|
get = function(...)
|
||||||
return capture(ngx.HTTP_GET, ...)
|
return capture(ngx.HTTP_GET, ...)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
post = function(...)
|
post = function(...)
|
||||||
return capture(ngx.HTTP_POST, ...)
|
return capture(ngx.HTTP_POST, ...)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
put = function(...)
|
put = function(...)
|
||||||
return capture(ngx.HTTP_PUT, ...)
|
return capture(ngx.HTTP_PUT, ...)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
delete = function(...)
|
delete = function(...)
|
||||||
return capture(ngx.HTTP_DELETE, ...)
|
return capture(ngx.HTTP_DELETE, ...)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,22 @@
|
|||||||
-- A whitelist of filetypes
|
-- A whitelist of filetypes
|
||||||
return {
|
return {
|
||||||
-- Image formats
|
-- Image formats
|
||||||
image = {
|
image = {
|
||||||
[".bmp"] = true,
|
[".bmp"] = true,
|
||||||
[".png"] = true,
|
[".png"] = true,
|
||||||
[".gif"] = true,
|
[".gif"] = true,
|
||||||
[".jpg"] = true,
|
[".jpg"] = true,
|
||||||
[".jpeg"] = true,
|
[".jpeg"] = true,
|
||||||
[".webp"] = true,
|
[".webp"] = true,
|
||||||
[".webm"] = true,
|
[".webm"] = true,
|
||||||
[".svg"] = true
|
[".svg"] = true,
|
||||||
},
|
},
|
||||||
|
|
||||||
-- Audio formats
|
-- Audio formats
|
||||||
audio = {
|
audio = {
|
||||||
[".wav"] = true,
|
[".wav"] = true,
|
||||||
[".flac"] = true,
|
[".flac"] = true,
|
||||||
[".mp3"] = true,
|
[".mp3"] = true,
|
||||||
[".ogg"] = true
|
[".ogg"] = true,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
local cjson = require("cjson")
|
||||||
|
local xpcall = xpcall
|
||||||
|
|
||||||
|
-- 设置 处理稀疏数组
|
||||||
|
-- https://www.kyne.com.au/~mark/software/lua-cjson-manual.html#encode_sparse_array
|
||||||
|
cjson.encode_sparse_array(true, 1, 1)
|
||||||
|
|
||||||
|
-- https://github.com/cloudwu/lua-cjson/pull/8
|
||||||
|
cjson.encode_empty_table_as_array("on")
|
||||||
|
|
||||||
|
-- https://github.com/cloudwu/lua-cjson/pull/10
|
||||||
|
cjson.encode_number_precision(16)
|
||||||
|
|
||||||
|
local json = {}
|
||||||
|
|
||||||
|
function json.encode(var)
|
||||||
|
local function _logExce(err)
|
||||||
|
print("json encode", err)
|
||||||
|
end
|
||||||
|
|
||||||
|
local status, result = xpcall(cjson.encode, _logExce, var)
|
||||||
|
if status then
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.decode(text)
|
||||||
|
if not text then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _logExce(err)
|
||||||
|
print("json decode", text, err)
|
||||||
|
end
|
||||||
|
|
||||||
|
local status, result = xpcall(cjson.decode, _logExce, text)
|
||||||
|
if status then
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return json
|
||||||
@ -1,298 +0,0 @@
|
|||||||
local Bans = require "models.bans"
|
|
||||||
local Threads = require "models.threads"
|
|
||||||
local Posts = require "models.posts"
|
|
||||||
local Reports = require "models.reports"
|
|
||||||
local process = {}
|
|
||||||
|
|
||||||
function process.create_thread(params, session, board)
|
|
||||||
|
|
||||||
-- Prepare data for entry
|
|
||||||
local _, err = Posts:prepare_post(params, session, board)
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Archive old threads
|
|
||||||
local max_threads = board.threads_per_page * board.pages
|
|
||||||
Threads:archive_threads(board.id, max_threads)
|
|
||||||
|
|
||||||
-- Delete old archived threads
|
|
||||||
local time = os.time()
|
|
||||||
local threads = Threads:get_archived_threads(board.id)
|
|
||||||
|
|
||||||
for _, t in ipairs(threads) do
|
|
||||||
if time - t.last_active > board.archive_time and not t.save then
|
|
||||||
local posts = Posts:get_thread_posts(t.id)
|
|
||||||
Threads:delete_thread("override", t, posts[1])
|
|
||||||
|
|
||||||
-- Delete all associated posts
|
|
||||||
for _, post in ipairs(posts) do
|
|
||||||
Posts:delete_post("override", board, post)
|
|
||||||
|
|
||||||
-- Delete associated report
|
|
||||||
local report = Reports:get_report(post.id)
|
|
||||||
if report then
|
|
||||||
Reports:delete_report(report)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Insert post data into database
|
|
||||||
local post, err = Posts:create_post(
|
|
||||||
params,
|
|
||||||
session,
|
|
||||||
board,
|
|
||||||
thread,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
return post
|
|
||||||
end
|
|
||||||
|
|
||||||
function process.create_post(params, session, board, thread)
|
|
||||||
local posts = Posts:count_posts(thread.id)
|
|
||||||
local files = Posts:count_files(thread.id)
|
|
||||||
|
|
||||||
-- Prepare data for entry
|
|
||||||
local _, err = Posts:prepare_post(
|
|
||||||
params, session, board, thread, files
|
|
||||||
)
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Insert post data into database
|
|
||||||
local post, err = Posts:create_post(
|
|
||||||
params,
|
|
||||||
session,
|
|
||||||
board,
|
|
||||||
thread,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
posts = posts + 1
|
|
||||||
|
|
||||||
-- Check for [auto]sage
|
|
||||||
if params.options ~= "sage" and
|
|
||||||
posts <= board.post_limit then
|
|
||||||
-- Update thread
|
|
||||||
thread.last_active = os.time()
|
|
||||||
thread:update("last_active")
|
|
||||||
end
|
|
||||||
|
|
||||||
return post
|
|
||||||
end
|
|
||||||
|
|
||||||
function process.delete_thread(params, session, board)
|
|
||||||
-- Validate post
|
|
||||||
local post = Posts:get_post(board.id, params.thread_id)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_invalid_post", { params.thread_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Validate thread
|
|
||||||
local thread = Threads:get_thread(post.thread_id)
|
|
||||||
if not thread then
|
|
||||||
return false, { "err_invalid_thread" }
|
|
||||||
end
|
|
||||||
|
|
||||||
local posts = Posts:get_posts_by_thread(thread.id)
|
|
||||||
|
|
||||||
-- Delete thread
|
|
||||||
local _, err = Threads:delete_thread(session, thread, posts[1])
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete all associated posts
|
|
||||||
for _, post in ipairs(posts) do
|
|
||||||
Posts:delete_post("override", board, post)
|
|
||||||
|
|
||||||
-- Delete associated report
|
|
||||||
local report = Reports:get_report(board.id, post.id)
|
|
||||||
if report then
|
|
||||||
Reports:delete_report(report)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function process.delete_post(params, session, board)
|
|
||||||
-- Validate post
|
|
||||||
local post = Posts:get_post(board.id, params.post_id)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_invalid_post", { params.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Validate thread
|
|
||||||
local thread = Threads:get_thread(post.thread_id)
|
|
||||||
if not thread then
|
|
||||||
return false, { "err_invalid_thread" }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Delete post
|
|
||||||
local _, err = Posts:delete_post(session, board, post)
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Update thread
|
|
||||||
local posts = Posts:get_posts_by_thread(thread.id)
|
|
||||||
thread.last_active = posts[#posts].timestamp
|
|
||||||
thread:update("last_active")
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function process.report_post(params, board)
|
|
||||||
-- Validate post
|
|
||||||
local post = Posts:get_post(board.id, params.post_id)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_invalid_post", { params.thread } }
|
|
||||||
end
|
|
||||||
|
|
||||||
local report = Reports:get_report(board.id, post.post_id)
|
|
||||||
|
|
||||||
-- If report exists, update it
|
|
||||||
if report then
|
|
||||||
report.num_reports = report.num_reports + 1 -- FIXME: race condition
|
|
||||||
local _, err = Reports:modify_report(report)
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
-- If report is new, create it
|
|
||||||
else
|
|
||||||
local _, err = Reports:create_report {
|
|
||||||
board_id = board.id,
|
|
||||||
thread_id = post.thread_id,
|
|
||||||
post_id = post.post_id,
|
|
||||||
timestamp = os.time(),
|
|
||||||
num_reports = 1
|
|
||||||
}
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return post
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sticky thread
|
|
||||||
function process.sticky_thread(params, board)
|
|
||||||
-- Validate post
|
|
||||||
local post = Posts:get_post(board.id, params.post_id)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_invalid_post", { params.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Validate thread
|
|
||||||
local thread = Threads:get_thread(post.thread_id)
|
|
||||||
if not thread then
|
|
||||||
return false, { "err_invalid_thread" }
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.sticky = not thread.sticky
|
|
||||||
thread:update("sticky")
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lock thread
|
|
||||||
function process.lock_thread(params, board)
|
|
||||||
-- Validate post
|
|
||||||
local post = Posts:get_post(board.id, params.post_id)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_invalid_post", { params.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Validate thread
|
|
||||||
local thread = Threads:get_thread(post.thread_id)
|
|
||||||
if not thread then
|
|
||||||
return false, { "err_invalid_thread" }
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.lock = not thread.lock
|
|
||||||
thread:update("lock")
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save thread
|
|
||||||
function process.save_thread(params, board)
|
|
||||||
-- Validate post
|
|
||||||
local post = Posts:get_post(board.id, params.post_id)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_invalid_post", { params.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Validate thread
|
|
||||||
local thread = Threads:get_thread(post.thread_id)
|
|
||||||
if not thread then
|
|
||||||
return false, { "err_invalid_thread" }
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.save = not thread.save
|
|
||||||
thread:update("save")
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Override thread
|
|
||||||
function process.override_thread(params, board)
|
|
||||||
-- Validate post
|
|
||||||
local post = Posts:get_post(board.id, params.post_id)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_invalid_post", { params.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Validate thread
|
|
||||||
local thread = Threads:get_thread(post.thread_id)
|
|
||||||
if not thread then
|
|
||||||
return false, { "err_invalid_thread" }
|
|
||||||
end
|
|
||||||
|
|
||||||
thread.size_override = not thread.size_override
|
|
||||||
thread:update("size_override")
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Ban user
|
|
||||||
function process.ban_user(params, board)
|
|
||||||
-- Validate post
|
|
||||||
local post = Posts:get_post(board.id, params.post_id)
|
|
||||||
if not post then
|
|
||||||
return false, { "err_invalid_post", { params.post_id } }
|
|
||||||
end
|
|
||||||
|
|
||||||
params.ip = post.ip
|
|
||||||
|
|
||||||
-- Convert board name to id if checkbox is set
|
|
||||||
if params.board_id then
|
|
||||||
params.board_id = board.id
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Ban user
|
|
||||||
local _, err = Bans:create_ban(params)
|
|
||||||
if err then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Flag post
|
|
||||||
if params.banned then
|
|
||||||
post.banned = true
|
|
||||||
post:update("banned")
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return process
|
|
||||||
@ -1,31 +1,31 @@
|
|||||||
local models = require "models"
|
local models = require "models"
|
||||||
local get_error = require "utils.error".get_error
|
local get_error = require"utils.error".get_error
|
||||||
local Users = models.users
|
local Users = models.users
|
||||||
local role = {}
|
local role = {}
|
||||||
|
|
||||||
-- User must be the Owner
|
-- User must be the Owner
|
||||||
function role.owner(user)
|
function role.owner(user)
|
||||||
return user.role == Users.role.OWNER and true or nil, get_error.unauthorized_access()
|
return user.role == Users.role.OWNER and true or nil, get_error.unauthorized_access()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- User must be an Admin or higher
|
-- User must be an Admin or higher
|
||||||
function role.admin(user)
|
function role.admin(user)
|
||||||
return user.role >= Users.role.ADMIN and true or nil, get_error.unauthorized_access()
|
return user.role >= Users.role.ADMIN and true or nil, get_error.unauthorized_access()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- User must be a Mod or higher
|
-- User must be a Mod or higher
|
||||||
function role.mod(user)
|
function role.mod(user)
|
||||||
return user.role >= Users.role.MOD and true or nil, get_error.unauthorized_access()
|
return user.role >= Users.role.MOD and true or nil, get_error.unauthorized_access()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- User must be a Janitor or higher
|
-- User must be a Janitor or higher
|
||||||
function role.janitor(user)
|
function role.janitor(user)
|
||||||
return user.role >= Users.role.JANITOR and true or nil, get_error.unauthorized_access()
|
return user.role >= Users.role.JANITOR and true or nil, get_error.unauthorized_access()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- User must be signed in
|
-- User must be signed in
|
||||||
function role.user(user)
|
function role.user(user)
|
||||||
return user.role >= Users.role.USER and true or nil, get_error.unauthorized_access()
|
return user.role >= Users.role.USER and true or nil, get_error.unauthorized_access()
|
||||||
end
|
end
|
||||||
|
|
||||||
return role
|
return role
|
||||||
|
|||||||
@ -1,247 +0,0 @@
|
|||||||
<h1><%= page_title %></h1>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<!-- Report Junk -->
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="view_report" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("view_report") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="report" class="change_submit">
|
|
||||||
<option></option>
|
|
||||||
<% for _, report in ipairs(reports) do %>
|
|
||||||
<option value="<%= report.id %>"><%= report.id %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="delete_report" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("delete_report") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="report" class="change_delete">
|
|
||||||
<option></option>
|
|
||||||
<% for _, report in ipairs(reports) do %>
|
|
||||||
<option value="<%= report.id %>"><%= report.id %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- User Junk -->
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="<%= url_for('web.admin.users', { action='create' }) %>" method="get">
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("create_user") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<button class="admin_main"><%= i18n("create_user") %></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="modify_user" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("modify_user") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="user" class="change_submit">
|
|
||||||
<option></option>
|
|
||||||
<% for _, user in ipairs(users) do %>
|
|
||||||
<option value="<%= user.id %>"><%= user.username %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="delete_user" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("delete_user") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="user" class="change_delete">
|
|
||||||
<option></option>
|
|
||||||
<% for _, user in ipairs(users) do %>
|
|
||||||
<option value="<%= user.id %>"><%= user.username %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Board Junk -->
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="<%= url_for('web.admin.boards', { action='create' }) %>" method="get">
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("create_board") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<button class="admin_main"><%= i18n("create_board") %></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="modify_board" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("modify_board") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="board" class="change_submit">
|
|
||||||
<option></option>
|
|
||||||
<% for _, board in ipairs(boards) do %>
|
|
||||||
<option value="<%= board.name %>"><%= string.format("/%s/ - %s", board.name, board.title) %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="delete_board" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("delete_board") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="board" class="change_delete">
|
|
||||||
<option></option>
|
|
||||||
<% for _, board in ipairs(boards) do %>
|
|
||||||
<option value="<%= board.name %>"><%= string.format("/%s/ - %s", board.name, board.title) %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Announcement Junk -->
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="<%= url_for('web.admin.announcements', { action='create' }) %>" method="get">
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("create_ann") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<button class="admin_main"><%= i18n("create_ann") %></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="modify_announcement" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("modify_ann") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="ann" class="change_submit">
|
|
||||||
<option></option>
|
|
||||||
<% for _, ann in ipairs(announcements) do %>
|
|
||||||
<option value="<%= ann.id %>"><%= ann.text %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="delete_announcement" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("delete_ann") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="ann" class="change_delete">
|
|
||||||
<option></option>
|
|
||||||
<% for _, ann in ipairs(announcements) do %>
|
|
||||||
<option value="<%= ann.id %>"><%= ann.text %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Page Junk -->
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="<%= url_for('web.admin.pages', { action='create' }) %>" method="get">
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("create_page") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<button class="admin_main"><%= i18n("create_page") %></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="modify_page" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("modify_page") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="page" class="change_submit">
|
|
||||||
<option></option>
|
|
||||||
<% for _, page in ipairs(pages) do %>
|
|
||||||
<option value="<%= page.slug %>"><%= page.slug %> - <%= page.title %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input name="delete_page" type="hidden" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("delete_page") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="page" class="change_delete">
|
|
||||||
<option></option>
|
|
||||||
<% for _, page in ipairs(pages) do %>
|
|
||||||
<option value="<%= page.slug %>"><%= page.slug %> - <%= page.title %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Regen Thumbs -->
|
|
||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("regen_thumb") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<button class="admin_main" name="regen_thumbs"><%= i18n("regen_thumb") %></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("board") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="board_id">
|
|
||||||
<option value="0" <%= announcement.board_id == 0 and 'selected' or '' %>><%= i18n("global") %></option>
|
|
||||||
<% for _, board in ipairs(boards) do %>
|
|
||||||
<option value="<%= board.id %>" <%= announcement.board_id == board.id and 'selected' or '' %>>/<%= board.name %>/ - <%= board.title %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("announcement") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="text" value="<%= announcement.text or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% if params.action == "create" then %>
|
|
||||||
<button name="create_announcement"><%= i18n("create_ann") %></button>
|
|
||||||
<% elseif params.action == "modify" then %>
|
|
||||||
<button name="modify_announcement"><%= i18n("modify_ann") %></button>
|
|
||||||
<% end %>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@ -1,189 +0,0 @@
|
|||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("board_title") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="title" placeholder="Random" value="<%= board.title or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("name") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="name" placeholder="b" value="<%= board.name or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("subtext") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="subtext" placeholder="Post at your own peril." value="<%= board.subtext or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("rules") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<textarea name="rules"><%= board.rules or '' %></textarea>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("default_name") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="anon_name" placeholder="Anonymous" value="<%= board.anon_name or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("theme") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="theme">
|
|
||||||
<% for _, theme in ipairs(themes) do %>
|
|
||||||
<option value="<%= theme %>" <%= board.theme == theme and 'selected' or '' %>><%= theme %></option>
|
|
||||||
<% end %>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("num_pages") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="pages" placeholder="10" value="<%= board.pages or '10' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("num_threads") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="threads_per_page" placeholder="10" value="<%= board.threads_per_page or '10' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("text_board") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="text_only">
|
|
||||||
<option value="t" <%= board.text_only == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.text_only == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("filetype_image") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="filetype_image">
|
|
||||||
<option value="t" <%= board.filetype_image == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.filetype_image == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("filetype_audio") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="filetype_audio">
|
|
||||||
<option value="t" <%= board.filetype_audio == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.filetype_audio == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("draw_board") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="draw">
|
|
||||||
<option value="t" <%= board.draw == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.draw == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("thread_file_required") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="thread_file">
|
|
||||||
<option value="t" <%= board.thread_file == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.thread_file == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("thread_comment_required") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="thread_comment">
|
|
||||||
<option value="t" <%= board.thread_comment == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.thread_comment == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("file_limit") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="thread_file_limit" placeholder="100" value="<%= board.thread_file_limit or '100' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("post_file_required") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="post_file">
|
|
||||||
<option value="t" <%= board.post_file == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.post_file == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("post_comment_required") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="post_comment">
|
|
||||||
<option value="t" <%= board.post_comment == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.post_comment == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("bump_limit") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="post_limit" placeholder="250" value="<%= board.post_limit or '250' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("archive_pruned") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="archive">
|
|
||||||
<option value="t" <%= board.archive == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= board.archive == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("archive_days") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="archive_time" placeholder="30" value="<%= board.archive_time or '30' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("board_group") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="group" placeholder="1" value="<%= board.group or '1' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% if params.action == "create" then %>
|
|
||||||
<button name="create_board"><%= i18n("create_board") %></button>
|
|
||||||
<% elseif params.action == "modify" then %>
|
|
||||||
<button name="modify_board"><%= i18n("modify_board") %></button>
|
|
||||||
<% end %>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
<div class="admin_form">
|
|
||||||
<form action="<%= url_for('web.admin.index') %>" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("username") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="username" value="<%= params.username or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("password") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="password" name="password" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button name="login"><%= i18n("login") %></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
<input type="hidden" name="old" value="<%= page.old or page.slug or '' %>" />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("name") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="title" placeholder="Frequently Asked Questions" value="<%= page.title or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("slug") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="slug" placeholder="faq" value="<%= page.slug or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("content_md") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<textarea name="content"><%= page.content or '' %></textarea>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% if params.action == "create" then %>
|
|
||||||
<button name="create_page"><%= i18n("create_page") %></button>
|
|
||||||
<% elseif params.action == "modify" then %>
|
|
||||||
<button name="modify_page"><%= i18n("modify_page") %></button>
|
|
||||||
<% end %>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
<h1>Success</h1>
|
|
||||||
<p><%= action %></p>
|
|
||||||
[<a href="<%= url_for('web.admin.index') %>"><%= i18n("return") %></a>]
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
<div class="admin_form">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>" />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("username") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="text" name="username" placeholder="moot" value="<%= user.username or '' %>" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% if params.action == "modify" then %>
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("password_old") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="password" name="old_password" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("password") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="password" name="new_password" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("password_retype") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<input type="password" name="retype_password" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("administrator") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="admin">
|
|
||||||
<option value="t" <%= user.admin == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= user.admin == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("moderator") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="mod">
|
|
||||||
<option value="t" <%= user.mod == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= user.mod == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="title"><%= i18n("janitor") %></label>
|
|
||||||
<span class="fields">
|
|
||||||
<select name="janitor">
|
|
||||||
<option value="t" <%= user.janitor == true and 'selected' or '' %>><%= i18n("yes") %></option>
|
|
||||||
<option value="f" <%= user.janitor == false and 'selected' or '' %>><%= i18n("no") %></option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% if params.action == "create" then %>
|
|
||||||
<button name="create_user"><%= i18n("create_user") %></button>
|
|
||||||
<% elseif params.action == "modify" then %>
|
|
||||||
<button name="modify_user"><%= i18n("modify_user") %></button>
|
|
||||||
<% end %>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
<% render('views.fragments.board_title') %>
|
|
||||||
<% render('views.fragments.announcements') %>
|
|
||||||
<hr />
|
|
||||||
[<a href="<%= url_for('web.boards.board', { uri_name=board.name }) %>"><%= i18n('return') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.catalog', { uri_name=board.name }) %>"><%= i18n('catalog') %></a>]
|
|
||||||
[<a href="#bottom"><%= i18n('bottom') %></a>]
|
|
||||||
<hr />
|
|
||||||
<p class="archive_stats">
|
|
||||||
<strong>
|
|
||||||
<%= i18n('arc_display', {
|
|
||||||
n_thread = #threads,
|
|
||||||
p_thread = i18n('threads', { count=#threads }),
|
|
||||||
n_day = days,
|
|
||||||
p_day = i18n('days', { count=days })
|
|
||||||
}) %>
|
|
||||||
</strong>
|
|
||||||
</p>
|
|
||||||
<table id="archive_table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td><%= i18n('arc_number') %></td>
|
|
||||||
<td><%= i18n('arc_name') %></td>
|
|
||||||
<td><%= i18n('arc_excerpt') %></td>
|
|
||||||
<td><%= i18n('arc_replies') %></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<% for _, thread in ipairs(threads) do %>
|
|
||||||
<tr>
|
|
||||||
<td><%= thread.op.post_id %></td>
|
|
||||||
<td><strong><%= thread.op.name %><strong> <%= thread.op.trip %></td>
|
|
||||||
<td>
|
|
||||||
<% if thread.op.subject then %>
|
|
||||||
<strong><%= thread.op.subject %>:</strong>
|
|
||||||
<% end %>
|
|
||||||
<%- thread.op.comment %>
|
|
||||||
</td>
|
|
||||||
<td><%= thread.replies %></td>
|
|
||||||
<td>[<a href="<%= thread.url %>"><%= i18n('arc_view') %></a>]</td>
|
|
||||||
</tr>
|
|
||||||
<% end %>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<hr />
|
|
||||||
[<a href="<%= url_for('web.boards.board', { uri_name=board.name }) %>"><%= i18n('return') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.catalog', { uri_name=board.name }) %>"><%= i18n('catalog') %></a>]
|
|
||||||
[<a href="#top"><%= i18n('top') %></a>]
|
|
||||||
<hr />
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
<h1><%= page_title %></h1>
|
|
||||||
<div class="banned">
|
|
||||||
<p><%= i18n('ban_reason') %></p>
|
|
||||||
<p><strong><%= reason %></strong></p>
|
|
||||||
<p><%- i18n('ban_expire', { expire='<strong>'..expire..'</strong>' }) %></p>
|
|
||||||
<p><%- i18n('ban_ip', { ip='<strong>'..ip..'</strong>' }) %></p>
|
|
||||||
<div><img src="/static/banned.jpg" alt="Catfish." /></div>
|
|
||||||
</div>
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
<% render('views.fragments.board_title') %>
|
|
||||||
<% render('views.fragments.form_submit') %>
|
|
||||||
<% render('views.fragments.announcements') %>
|
|
||||||
<hr />
|
|
||||||
[<a href="<%= url_for('web.boards.catalog', { uri_name=board.name }) %>"><%= i18n('catalog') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.archive', { uri_name=board.name }) %>"><%= i18n('archive') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.board', { uri_name=board.name }) %>"><%= i18n('refresh') %></a>]
|
|
||||||
<hr />
|
|
||||||
<% for _, thread in ipairs(threads) do %>
|
|
||||||
<div class="thread_container">
|
|
||||||
<%
|
|
||||||
local posts = thread.posts or {}
|
|
||||||
for i=#posts, 1, -1 do
|
|
||||||
local post = posts[i]
|
|
||||||
local op = posts[#posts]
|
|
||||||
if post.post_id == op.post_id then
|
|
||||||
render('views.fragments.op_content', { thread=thread, post=post, posts=posts, is_board=true })
|
|
||||||
else
|
|
||||||
render('views.fragments.post_content', { thread=thread, post=post, posts=posts, is_board=true, op=op })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
%>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<% end %>
|
|
||||||
<div class="pages">
|
|
||||||
<% for i=1, pages do %>
|
|
||||||
<% if i == params.page then %>
|
|
||||||
[<a href="<%= url_for('web.boards.board', { uri_name=board.name, page=i }) %>"><strong><%= i %></strong></a>]
|
|
||||||
<% else %>
|
|
||||||
[<a href="<%= url_for('web.boards.board', { uri_name=board.name, page=i }) %>"><%= i %></a>]
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
[<a href="<%= url_for('web.boards.catalog', { uri_name=board.name }) %>"><%= i18n('catalog') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.archive', { uri_name=board.name }) %>"><%= i18n('archive') %></a>]
|
|
||||||
</div>
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
<% render('views.fragments.board_title') %>
|
|
||||||
<% render('views.fragments.form_submit') %>
|
|
||||||
<% render('views.fragments.announcements') %>
|
|
||||||
<hr />
|
|
||||||
[<a href="<%= url_for('web.boards.board', { uri_name=board.name }) %>"><%= i18n('return') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.archive', { uri_name=board.name }) %>"><%= i18n('archive') %></a>]
|
|
||||||
[<a href="#bottom"><%= i18n('bottom') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.catalog', { uri_name=board.name }) %>"><%= i18n('refresh') %></a>]
|
|
||||||
<hr />
|
|
||||||
<% for t, thread in ipairs(threads) do %>
|
|
||||||
<div class="catalog_container">
|
|
||||||
<a href="<%= thread.url %>">
|
|
||||||
<% if thread.op.file_name then %>
|
|
||||||
<img src="<%= thread.op.thumb %>" alt="" /><br />
|
|
||||||
<% end %>
|
|
||||||
<span class="catalog_stats">
|
|
||||||
<%- i18n('cat_stats', {
|
|
||||||
replies = '<strong>'..thread.replies..'</strong>',
|
|
||||||
files = '<strong>'..thread.files..'</strong>'
|
|
||||||
}) %>
|
|
||||||
</span><br />
|
|
||||||
<% if thread.op.subject then %>
|
|
||||||
<strong><%= thread.op.subject %>:</strong>
|
|
||||||
<% end %>
|
|
||||||
<%- thread.op.comment %>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<hr />
|
|
||||||
[<a href="<%= url_for('web.boards.board', { uri_name=board.name }) %>"><%= i18n('return') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.archive', { uri_name=board.name }) %>"><%= i18n('archive') %></a>]
|
|
||||||
[<a href="#top"><%= i18n('top') %></a>]
|
|
||||||
[<a href="<%= url_for('web.boards.catalog', { uri_name=board.name }) %>"><%= i18n('refresh') %></a>]
|
|
||||||
<hr />
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
<h1><%= page_title %></h1>
|
|
||||||
<p><a href="<%= url_for('web.pages.index') %>"><%= i18n("return_index") %></a></p>
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
<% if #announcements > 0 then %>
|
|
||||||
<hr />
|
|
||||||
<% for _, announcement in ipairs(announcements) do %>
|
|
||||||
<p class="announcement"><%= announcement.text %></p>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue