1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-02 02:36:24 -07:00

Add non-RPC changes to master

This commit is contained in:
Eggbertx 2023-10-19 11:33:21 -07:00
parent 6e40ae8a16
commit b3a57f50a8
20 changed files with 360 additions and 247 deletions

View file

@ -27,8 +27,8 @@ release_files = (
"html/favicon.png",
"html/firstrun.html",
"html/js/",
"sample-configs/",
"sample-plugins/",
"examples/configs/",
"examples/plugins/",
"templates/",
"sql/initdb_master.sql",
"sql/initdb_mysql.sql",
@ -380,12 +380,12 @@ def install(prefix="/usr", document_root="/srv/gochan", symlinks=False, js_only=
print(
"gochan was successfully installed. If you haven't already, you should copy\n",
"sample-configs/gochan.example.json to /etc/gochan/gochan.json (modify as needed)\n",
"examples/configs/gochan.example.json to /etc/gochan/gochan.json (modify as needed)\n",
"You may also need to go to https://yourgochansite/manage/rebuildall to rebuild the javascript config")
if gcos == "linux":
print(
"If your Linux distribution has systemd, you will also need to run the following commands:\n",
"cp sample-configs/gochan-[mysql|postgresql|sqlite3].service /lib/systemd/system/gochan.service\n",
"cp examples/configs/gochan-[mysql|postgresql|sqlite3].service /lib/systemd/system/gochan.service\n",
"systemctl daemon-reload\n",
"systemctl enable gochan.service\n",
"systemctl start gochan.service")

View file

@ -1,9 +1,11 @@
local string = require("string")
local events = require("events")
local gcsql = require("gcsql")
local recognized_tlds = {"com", "net", "org", "edu", "gov", "us", "uk"}
local function is_new_poster(ip)
rows, err = db_query("SELECT COUNT(*) FROM DBPREFIXposts WHERE ip = ?", {ip})
rows, err = gcsql.query_rows("SELECT COUNT(*) FROM DBPREFIXposts WHERE ip = ?", {ip})
if(err ~= nil) then
return true, err
end
@ -11,7 +13,7 @@ local function is_new_poster(ip)
is_new = true
while rows:Next() do
rows_table = {}
err = db_scan_rows(rows, rows_table)
err = gcsql.scan_rows(rows, rows_table)
if(err ~= nil) then
rows:Close()
return true, err
@ -26,7 +28,7 @@ local function is_new_poster(ip)
end
event_register({"message-pre-format"}, function(tr, post)
events.register_event({"message-pre-format"}, function(tr, post)
is_new, err = is_new_poster(post.IP)
if(err ~= nil) then
error_log(err:Error())

View file

@ -1,11 +1,14 @@
-- requires ghostscript to be installed
local config = require("config")
local os = require("os")
local uploads = require("uploads")
local cmd = "gs -q -sDEVICE=jpeg -dLastPage=1 -dNOPAUSE -r720 -g%dx%d -dPDFFitPage -dFIXEDMEDIA -dCompatibilityLevel=1.4 -o %q - < %q" -- width, height outpath, inpath
register_upload_handler(".pdf", function(upload, post, board, filePath, thumbPath, catalogThumbPath, infoEv, accessEv, errEv)
uploads.register_handler(".pdf", function(upload, post, board, filePath, thumbPath, catalogThumbPath, infoEv, accessEv, errEv)
-- width, height = get_pdf_dimensions(filePath)
local boardcfg = board_config(board)
local boardcfg = config.board_config(board)
upload.ThumbnailWidth = boardcfg.ThumbWidthReply
upload.ThumbnailHeight = boardcfg.ThumbHeightReply
if (post.IsTopPost) then
@ -24,4 +27,4 @@ register_upload_handler(".pdf", function(upload, post, board, filePath, thumbPat
return nil
end)
set_thumbnail_ext(".pdf", ".jpg")
uploads.set_thumbnail_ext(".pdf", ".jpg")

View file

@ -1,7 +1,8 @@
-- testing manage page registering from Lua plugins
local strings = require("strings")
local manage = require("manage")
register_manage_page("mgmtplugintest",
manage.register_manage_page("mgmtplugintest",
"Staff Plugin Testing",
3, 1,
function(writer, request, staff, wantsJSON, infoEv, errEv)
@ -12,7 +13,7 @@ register_manage_page("mgmtplugintest",
-- testing template parsing from Lua plugins
register_manage_page("templateplugintest",
manage.register_manage_page("templateplugintest",
"Template Plugin Testing",
3, 0,
function(writer, request, staff, wantsJSON, infoEv, errEv)

View file

@ -0,0 +1,21 @@
local events = require("events")
local gcsql = require("gcsql")
events.register_event({"incoming-upload"}, function(tr, upload)
rows, err = gcsql.query_rows("SELECT COUNT(*) FROM DBPREFIXfiles WHERE original_filename = ?", {upload.OriginalFilename})
if(err ~= nil) then
return err:Error()
end
while rows:Next() do
rows_table = {}
err = gcsql.scan_rows(rows, rows_table)
if(err ~= nil) then
rows:Close()
return err:Error()
end
if(rows_table["COUNT(*)"] > 0) then
rows:Close()
return "a file with that filename has already been uploaded"
end
end
end)

View file

@ -1,6 +1,9 @@
event_register({"db-initialized"}, function(tr)
local events = require('events')
local gcsql = require("gcsql")
events.register_event({"db-initialized"}, function(tr)
print("Testing SELECT query from Lua plugin")
rows, err = db_query("SELECT id, dir, title FROM DBPREFIXboards where id > ?", {1})
rows, err = gcsql.query_rows("SELECT id, dir, title FROM DBPREFIXboards where id > ?", {1})
if(err ~= nil) then
print(err:Error())
return
@ -9,7 +12,7 @@ event_register({"db-initialized"}, function(tr)
print("Boards (id > 1):")
while rows:Next() do
rows_table = {}
err = db_scan_rows(rows, rows_table)
err = gcsql.scan_rows(rows, rows_table)
if(err ~= nil) then
print(err:Error())
rows:Close()
@ -21,14 +24,14 @@ event_register({"db-initialized"}, function(tr)
rows:Close()
print("Testing SELECT COUNT(*) query from Lua plugin")
rows, err = db_query("SELECT COUNT(*) FROM DBPREFIXstaff WHERE id > ?", {1})
rows, err = gcsql.query_rows("SELECT COUNT(*) FROM DBPREFIXstaff WHERE id > ?", {1})
if(err ~= nil) then
print(err:Error())
return
end
while rows:Next() do
rows_table = {}
err = db_scan_rows(rows, rows_table)
err = gcsql.scan_rows(rows, rows_table)
if(err ~= nil) then
print(err:Error())
rows:Close()

View file

@ -1,5 +1,7 @@
local events = require("events")
-- a simple demonstration of using the event system from a Lua plugin to modify an incoming upload
event_register({"incoming-upload"}, function(tr, upload)
events.register_event({"incoming-upload"}, function(tr, upload)
print("Received upload, making the original filename upper case")
before = upload.OriginalFilename
upload.OriginalFilename = string.upper(upload.OriginalFilename)

View file

@ -36,7 +36,7 @@ var (
func initLua() {
if lState == nil {
lState = lua.NewState()
registerLuaFunctions()
preloadLua()
}
}
@ -123,179 +123,260 @@ func luaEventRegisterHandlerAdapter(l *lua.LState, fn *lua.LFunction) events.Eve
}
}
func registerLuaFunctions() {
func preloadLua() {
luaFilePath.Preload(lState)
luaStrings.Preload(lState)
lState.Register("info_log", createLuaLogFunc("info"))
lState.Register("warn_log", createLuaLogFunc("warn"))
lState.Register("error_log", createLuaLogFunc("error"))
lState.Register("register_upload_handler", func(l *lua.LState) int {
ext := l.CheckString(1)
handler := l.CheckFunction(2)
uploads.RegisterUploadHandler(ext, func(upload *gcsql.Upload, post *gcsql.Post, board, filePath, thumbPath, catalogThumbPath string, infoEv, accessEv, errEv *zerolog.Event) error {
l.CallByParam(lua.P{
Fn: handler,
NRet: 1,
// Protect: true,
}, luar.New(l, upload), luar.New(l, post), lua.LString(board), lua.LString(filePath), lua.LString(thumbPath), lua.LString(catalogThumbPath))
errRet := l.CheckAny(-1)
if errRet != nil && errRet.Type() != lua.LTNil {
return errors.New(errRet.String())
}
return nil
lState.PreloadModule("config", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"system_critical_config": func(l *lua.LState) int {
l.Push(luar.New(l, config.GetSystemCriticalConfig()))
return 1
},
"site_config": func(l *lua.LState) int {
l.Push(luar.New(l, config.GetSiteConfig()))
return 1
},
"board_config": func(l *lua.LState) int {
numArgs := l.GetTop()
board := ""
if numArgs > 0 {
board = l.CheckString(1)
}
l.Push(luar.New(l, config.GetBoardConfig(board)))
return 1
},
})
return 0
})
lState.Register("get_thumbnail_ext", func(l *lua.LState) int {
fileExt := l.CheckString(1)
l.Push(luar.New(l, uploads.GetThumbnailExtension(fileExt)))
return 1
})
lState.Register("set_thumbnail_ext", func(l *lua.LState) int {
fileExt := l.CheckString(1)
thumbExt := l.CheckString(2)
uploads.SetThumbnailExtension(fileExt, thumbExt)
return 0
})
lState.Register("system_critical_config", func(l *lua.LState) int {
l.Push(luar.New(l, config.GetSystemCriticalConfig()))
return 1
})
lState.Register("site_config", func(l *lua.LState) int {
l.Push(luar.New(l, config.GetSiteConfig()))
return 1
})
lState.Register("board_config", func(l *lua.LState) int {
l.Push(luar.New(l, config.GetBoardConfig(l.CheckString(1))))
l.Push(t)
return 1
})
lState.Register("db_query", func(l *lua.LState) int {
queryStr := l.CheckString(1)
queryArgsL := l.CheckAny(2)
var queryArgs []any
if queryArgsL.Type() != lua.LTNil {
table := queryArgsL.(*lua.LTable)
table.ForEach(func(_ lua.LValue, val lua.LValue) {
arg := lvalueToInterface(l, val)
queryArgs = append(queryArgs, arg)
})
}
rows, err := gcsql.QuerySQL(queryStr, queryArgs...)
l.Push(luar.New(l, rows))
l.Push(luar.New(l, err))
return 2
})
lState.Register("db_scan_rows", func(l *lua.LState) int {
rows := l.CheckUserData(1).Value.(*sql.Rows)
table := l.CheckTable(2)
var scanners []any
colNames, err := rows.Columns()
if err != nil {
l.Push(luar.New(l, err))
return 1
}
for range colNames {
scanners = append(scanners, &lvalueScanner{state: l})
}
if err = rows.Scan(scanners...); err != nil {
l.Push(luar.New(l, err))
return 1
}
for i, name := range colNames {
table.RawSetString(name, scanners[i].(*lvalueScanner).val)
}
l.Push(lua.LNil)
return 1
})
lState.Register("event_register", func(l *lua.LState) int {
table := l.CheckTable(-2)
var triggers []string
table.ForEach(func(i, val lua.LValue) {
triggers = append(triggers, val.String())
lState.PreloadModule("events", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"register_event": func(l *lua.LState) int {
table := l.CheckTable(-2)
var triggers []string
table.ForEach(func(i, val lua.LValue) {
triggers = append(triggers, val.String())
})
fn := l.CheckFunction(-1)
events.RegisterEvent(triggers, luaEventRegisterHandlerAdapter(l, fn))
return 0
},
"trigger_event": func(l *lua.LState) int {
trigger := l.CheckString(1)
numArgs := l.GetTop()
var data []interface{}
for i := 2; i <= numArgs; i++ {
v := l.CheckAny(i)
data = append(data, lvalueToInterface(l, v))
}
events.TriggerEvent(trigger, data...)
return 0
},
})
fn := l.CheckFunction(-1)
events.RegisterEvent(triggers, luaEventRegisterHandlerAdapter(l, fn))
return 0
})
lState.Register("event_trigger", func(l *lua.LState) int {
trigger := l.CheckString(1)
numArgs := l.GetTop()
var data []interface{}
for i := 2; i <= numArgs; i++ {
v := l.CheckAny(i)
data = append(data, lvalueToInterface(l, v))
}
events.TriggerEvent(trigger, data...)
return 0
})
lState.Register("register_manage_page", func(l *lua.LState) int {
actionID := l.CheckString(1)
actionTitle := l.CheckString(2)
actionPerms := l.CheckInt(3)
actionJSON := l.CheckInt(4)
fn := l.CheckFunction(5)
actionHandler := func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, infoEv *zerolog.Event, errEv *zerolog.Event) (output interface{}, err error) {
if err = l.CallByParam(lua.P{
Fn: fn,
NRet: 2,
// Protect: true,
}, luar.New(l, writer), luar.New(l, request), luar.New(l, staff), lua.LBool(wantsJSON), luar.New(l, infoEv), luar.New(l, errEv)); err != nil {
return "", err
}
out := lua.LVAsString(l.Get(-2))
errStr := lua.LVAsString(l.Get(-1))
if errStr != "" {
err = errors.New(errStr)
}
return out, err
}
manage.RegisterManagePage(actionID, actionTitle, actionPerms, actionJSON, actionHandler)
return 0
})
lState.Register("load_template", func(l *lua.LState) int {
var tmplPaths []string
for i := 0; i < l.GetTop(); i++ {
tmplPaths = append(tmplPaths, l.CheckString(i+1))
}
tmpl, err := gctemplates.LoadTemplate(tmplPaths...)
l.Push(luar.New(l, tmpl))
l.Push(luar.New(l, err))
return 2
})
lState.Register("parse_template", func(l *lua.LState) int {
tmplName := l.CheckString(1)
tmplData := l.CheckString(2)
tmpl, err := gctemplates.ParseTemplate(tmplName, tmplData)
l.Push(luar.New(l, tmpl))
l.Push(luar.New(l, err))
return 2
})
lState.Register("minify_template", func(l *lua.LState) int {
tmplUD := l.CheckUserData(1)
tmpl := tmplUD.Value.(*template.Template)
dataTable := l.CheckTable(2)
data := map[string]interface{}{}
dataTable.ForEach(func(l1, l2 lua.LValue) {
data[l1.String()] = lvalueToInterface(l, l2)
})
writer := l.CheckUserData(3).Value.(io.Writer)
mediaType := l.CheckString(4)
err := serverutil.MinifyTemplate(tmpl, data, writer, mediaType)
l.Push(luar.New(l, err))
l.Push(t)
return 1
})
lState.PreloadModule("gclog", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"info_log": createLuaLogFunc("info"),
"warn_log": createLuaLogFunc("warn"),
"error_log": createLuaLogFunc("error"),
})
l.Push(t)
return 1
})
lState.PreloadModule("gcsql", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"query_rows": func(l *lua.LState) int {
queryStr := l.CheckString(1)
queryArgsL := l.CheckAny(2)
var queryArgs []any
if queryArgsL.Type() != lua.LTNil {
table := queryArgsL.(*lua.LTable)
table.ForEach(func(_ lua.LValue, val lua.LValue) {
arg := lvalueToInterface(l, val)
queryArgs = append(queryArgs, arg)
})
}
rows, err := gcsql.QuerySQL(queryStr, queryArgs...)
l.Push(luar.New(l, rows))
l.Push(luar.New(l, err))
return 2
},
"execute_sql": func(l *lua.LState) int {
execStr := l.CheckString(1)
execArgsL := l.CheckAny(2)
var execArgs []any
if execArgsL.Type() != lua.LTNil {
table := execArgsL.(*lua.LTable)
table.ForEach(func(_, val lua.LValue) {
arg := lvalueToInterface(l, val)
execArgs = append(execArgs, arg)
})
}
result, err := gcsql.ExecSQL(execStr)
l.Push(luar.New(l, result))
l.Push(luar.New(l, err))
return 2
},
"scan_rows": func(l *lua.LState) int {
rows := l.CheckUserData(1).Value.(*sql.Rows)
table := l.CheckTable(2)
var scanners []any
colNames, err := rows.Columns()
if err != nil {
l.Push(luar.New(l, err))
return 1
}
for range colNames {
scanners = append(scanners, &lvalueScanner{state: l})
}
if err = rows.Scan(scanners...); err != nil {
l.Push(luar.New(l, err))
return 1
}
for i, name := range colNames {
table.RawSetString(name, scanners[i].(*lvalueScanner).val)
}
l.Push(lua.LNil)
return 1
},
})
l.Push(t)
return 1
})
lState.PreloadModule("gctemplates", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"load_template": func(l *lua.LState) int {
var tmplPaths []string
for i := 0; i < l.GetTop(); i++ {
tmplPaths = append(tmplPaths, l.CheckString(i+1))
}
tmpl, err := gctemplates.LoadTemplate(tmplPaths...)
l.Push(luar.New(l, tmpl))
l.Push(luar.New(l, err))
return 2
},
"minify_template": func(l *lua.LState) int {
tmplUD := l.CheckUserData(1)
tmpl := tmplUD.Value.(*template.Template)
dataTable := l.CheckTable(2)
data := map[string]interface{}{}
dataTable.ForEach(func(l1, l2 lua.LValue) {
data[l1.String()] = lvalueToInterface(l, l2)
})
writer := l.CheckUserData(3).Value.(io.Writer)
mediaType := l.CheckString(4)
err := serverutil.MinifyTemplate(tmpl, data, writer, mediaType)
l.Push(luar.New(l, err))
return 1
},
"parse_template": func(l *lua.LState) int {
tmplName := l.CheckString(1)
tmplData := l.CheckString(2)
tmpl, err := gctemplates.ParseTemplate(tmplName, tmplData)
l.Push(luar.New(l, tmpl))
l.Push(luar.New(l, err))
return 2
},
})
l.Push(t)
return 1
})
lState.PreloadModule("manage", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"register_manage_page": func(l *lua.LState) int {
actionID := l.CheckString(1)
actionTitle := l.CheckString(2)
actionPerms := l.CheckInt(3)
actionJSON := l.CheckInt(4)
fn := l.CheckFunction(5)
actionHandler := func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, infoEv *zerolog.Event, errEv *zerolog.Event) (output interface{}, err error) {
if err = l.CallByParam(lua.P{
Fn: fn,
NRet: 2,
// Protect: true,
}, luar.New(l, writer), luar.New(l, request), luar.New(l, staff), lua.LBool(wantsJSON), luar.New(l, infoEv), luar.New(l, errEv)); err != nil {
return "", err
}
out := lua.LVAsString(l.Get(-2))
errStr := lua.LVAsString(l.Get(-1))
if errStr != "" {
err = errors.New(errStr)
}
return out, err
}
manage.RegisterManagePage(actionID, actionTitle, actionPerms, actionJSON, actionHandler)
return 0
},
})
l.Push(t)
return 1
})
lState.PreloadModule("uploads", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"register_handler": func(l *lua.LState) int {
ext := l.CheckString(1)
handler := l.CheckFunction(2)
uploads.RegisterUploadHandler(ext, func(upload *gcsql.Upload, post *gcsql.Post, board, filePath, thumbPath, catalogThumbPath string, infoEv, accessEv, errEv *zerolog.Event) error {
l.CallByParam(lua.P{
Fn: handler,
NRet: 1,
// Protect: true,
}, luar.New(l, upload), luar.New(l, post), lua.LString(board), lua.LString(filePath), lua.LString(thumbPath), lua.LString(catalogThumbPath))
errRet := l.CheckAny(-1)
if errRet != nil && errRet.Type() != lua.LTNil {
return errors.New(errRet.String())
}
return nil
})
return 0
},
"get_thumbnail_ext": func(l *lua.LState) int {
fileExt := l.CheckString(1)
l.Push(luar.New(l, uploads.GetThumbnailExtension(fileExt)))
return 1
},
"set_thumbnail_ext": func(l *lua.LState) int {
fileExt := l.CheckString(1)
thumbExt := l.CheckString(2)
uploads.SetThumbnailExtension(fileExt, thumbExt)
return 0
},
})
l.Push(t)
return 1
})
lState.SetGlobal("_GOCHAN_VERSION", lua.LString(config.GetVersion().String()))
}

View file

@ -19,14 +19,21 @@ post.MessageRaw = "Message modified by a plugin\n"
post.Message = "Message modified by a plugin<br />"
print(string.format("Modified message text: %q", post.MessageText))`
eventsTestingStr = `event_register({"newPost"}, function(tr, ...)
eventsTestingStr = `local events = require("events")
events.register_event({"newPost"}, function(tr, ...)
print("newPost triggered :D")
for i, v in ipairs(arg) do
print(i .. ": " .. tostring(v))
end
end)
event_trigger("newPost", "blah", 16, 3.14, true, nil)`
events.trigger_event("newPost", "blah", 16, 3.14, true, nil)`
configTestingStr = `local config = require("config")
local system_critical_cfg = config.system_critical_config()
local site_cfg = config.site_config()
local board_cfg = config.board_config()
return { ListenIP = system_critical_cfg.ListenIP, SiteSlogan = site_cfg.SiteSlogan, DefaultStyle = board_cfg.DefaultStyle }`
)
func initPluginTests() {
@ -37,7 +44,7 @@ func initPluginTests() {
func TestVersionFunction(t *testing.T) {
initPluginTests()
err := lState.DoString(versionStr)
assert.Nil(t, err)
assert.NoError(t, err)
testingVersionStr := lState.Get(-1).(lua.LString)
assert.EqualValues(t, config.GetVersion().String(), testingVersionStr)
}
@ -52,15 +59,26 @@ func TestStructPassing(t *testing.T) {
}
lState.SetGlobal("post", luar.New(lState, p))
err := lState.DoString(structPassingStr)
assert.Nil(t, err)
assert.NoError(t, err)
t.Logf("Modified message text after Lua: %q", p.MessageRaw)
if p.MessageRaw != "Message modified by a plugin\n" || p.Message != "Message modified by a plugin<br />" {
t.Fatal("message was not properly modified by plugin")
}
}
func TestEventPlugins(t *testing.T) {
func TestEventModule(t *testing.T) {
initPluginTests()
err := lState.DoString(eventsTestingStr)
assert.Nil(t, err)
assert.NoError(t, err)
}
func TestConfigModule(t *testing.T) {
config.InitConfig(config.GetVersion().String())
initPluginTests()
err := lState.DoString(configTestingStr)
assert.NoError(t, err)
returnTable := lState.CheckTable(-1)
assert.Equal(t, "127.0.0.1", returnTable.RawGetString("ListenIP").(lua.LString).String())
assert.Equal(t, "Gochan testing", returnTable.RawGetString("SiteSlogan").(lua.LString).String())
assert.Equal(t, "test.css", returnTable.RawGetString("DefaultStyle").(lua.LString).String())
}

View file

@ -68,7 +68,7 @@ func InitLog(logPath string, debug bool) (err error) {
}
if debug {
logger = zerolog.New(io.MultiWriter(logFile, os.Stdout)).Hook(&logHook{})
logger = zerolog.New(io.MultiWriter(logFile, zerolog.NewConsoleWriter())).Hook(&logHook{})
} else {
logger = zerolog.New(logFile).Hook(&logHook{})
}

View file

@ -2,57 +2,57 @@
- **_GOCHAN_VERSION**
- The version string of the running Gochan server
# Functions
- **error_log([error_message string])**
- Creates and returns a zerolog [Event](https://pkg.go.dev/github.com/rs/zerolog) object for the error log. If a string is used as the argument, it is used as the error message.
- **info_log()**
- Creates and returns a zerolog [Event](https://pkg.go.dev/github.com/rs/zerolog) object with an info level.
- **warn_log()**
- Creates and returns a zerolog [Event](https://pkg.go.dev/github.com/rs/zerolog) object with a warning level.
- **register_upload_handler(ext string, function(upload, post, board, filePath, thumbPath, catalogThumbPath, infoEv, accessEv, errEv))**
- Registers a function to be called for handling uploaded files with the given extension. See [pdf_thumbnail.lua](./sample-plugins/pdf_thumbnail.lua) for a usage example.
- **get_thumbnail_ext(upload_ext string)**
- Returns the configured (or built-in) thumbnail file extension to be used for the given upload extension
- **set_thumbnail_ext(upload_ext string, thumbnail_ext string)**
- Sets the thumbnail extension to be used for the given upload extension
- **system_critical_config()**
- Returns the [SystemCriticalConfig](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/config#SystemCriticalConfig)
- **site_config()**
# Modules
The following are modules that can be loaded via `require("modulename")`. See [./examples/plugins/](./examples/plugins/) for usage examples
## config
- **config.system_critical_config()**
- Returns the [SystemCriticalConfig](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/config#SystemCriticalConfig)
- **config.site_config()**
- Returns the [SiteConfig](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/config#SiteConfig)
- **board_config(board string)**
- **config.board_config(board string)**
- Returns the [BoardConfig](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/config#BoardConfig) for the given board, or the default BoardConfig if `board` is an empty string
- **db_query(query string)**
- Returns a [Rows](https://pkg.go.dev/database/sql#Rows) object for the given SQL query and an error if any occured, or nil if there were no errors.
- **db_scan_rows(rows, scan_table)**
- scans the value of the current row into `scan_table` and returns an error if any occured, or nil if there were no errors.
- **event_register(events_table, handler_func)**
## events
- **events.register_event(events_table, handler_func)**
- Registers `handler_func` for the events in `events_table`. If any arguments are passed to the event when it is triggered, it will be sent to `handler_func`.
- **event_trigger(event_name string, data...)**
- **events.trigger_event(event_name string, data...)**
- Triggers the event registered to `event_name` and passes `data` (if set) to the event handler.
- **register_manage_page(action string, title string, perms int, wants_json int, handler func(writer, request, staff, wants_json, info_ev, err_ev))**
- Registers the manage page accessible at /manage/`action` to be handled by `handler`. See [manage.RegisterManagePage](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/manage#RegisterManagePage) for info on how `handler` should be used, or [registermgmtpage.lua](./sample-plugins/registermgmtpage.lua) for an example
## gclog
- **gclog.info_log()**
- Creates and returns a zerolog [Event](https://pkg.go.dev/github.com/rs/zerolog) object with an info level.
- **gclog.warn_log()**
- Creates and returns a zerolog [Event](https://pkg.go.dev/github.com/rs/zerolog) object with a warning level.
- **gclog.error_log([error_message string])**
- Creates and returns a zerolog [Event](https://pkg.go.dev/github.com/rs/zerolog) object for the error log. If a string is used as the argument, it is used as the error message.
- **load_template(files...)**
## gcsql
- **gcsql.query_rows(query string, args...)**
- Returns a [Rows](https://pkg.go.dev/database/sql#Rows) object for the given SQL query and an error if any occured, or nil if there were no errors. `args` if given will be used for a parameterized query.
- **gcsql.execute_sql(query string, args...)**
- Executes the SQL string `query` with the optional `args` as parameters and returns a [Result](https://pkg.go.dev/database/sql#Result) object and an error (or nil if there were no errors)
- **gcsql.scan_rows(rows, scan_table)**
- scans the value of the current row into `scan_table` and returns an error if any occured, or nil if there were no errors.
## gctemplates
- **gctemplates.load_template(files...)**
- Calls [gctemplates.LoadTemplate](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/gctemplates#LoadTemplate) using the given `files` and returns a [Template](https://pkg.go.dev/html/template#Template) and an error object (or nil if there were no errors).
- **parse_template(template_name string, template_data string)**
- **gctemplates.minify_template(template, data_table, writer, media_type)**
- Calls [serverutil.MinifyTemplate](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/server/serverutil#MinifyTemplate) with the given `template` object, `data_table` (as variables passed to the template), `writer`, and `media_type`. See [registermgmtpage.lua](./examples/plugins/registermgmtpage.lua) for an example
- **gctemplates.parse_template(template_name string, template_data string)**
- Calls [gctemplates.ParseTemplate](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/gctemplates#ParseTemplate) with the given template name and Go template data, and returns a [Template](https://pkg.go.dev/html/template#Template) and an error object (or nil if there were no errors).
- **minify_template(template, data_table, writer, media_type)**
- Calls [serverutil.MinifyTemplate](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/server/serverutil#MinifyTemplate) with the given `template` object, `data_table` (as variables passed to the template), `writer`, and `media_type`. See [registermgmtpage.lua](./sample-plugins/registermgmtpage.lua) for an example
## manage
- **manage.register_manage_page(action string, title string, perms int, wants_json int, handler func(writer, request, staff, wants_json, info_ev, err_ev))**
- Registers the manage page accessible at /manage/`action` to be handled by `handler`. See [manage.RegisterManagePage](https://pkg.go.dev/github.com/gochan-org/gochan/pkg/manage#RegisterManagePage) for info on how `handler` should be used, or [registermgmtpage.lua](./examples/plugins/registermgmtpage.lua) for an example
## uploads
- **uploads.register_handler(ext string, function(upload, post, board, filePath, thumbPath, catalogThumbPath, infoEv, accessEv, errEv))**
- Registers a function to be called for handling uploaded files with the given extension. See [pdf_thumbnail.lua](./examples//plugins/pdf_thumbnail.lua) for a usage example.
- **uploads.get_thumbnail_ext(upload_ext string)**
- Returns the configured (or built-in) thumbnail file extension to be used for the given upload extension
- **uploads.set_thumbnail_ext(upload_ext string, thumbnail_ext string)**
- Sets the thumbnail extension to be used for the given upload extension
# Events

View file

@ -1,18 +0,0 @@
event_register({"incoming-upload"}, function(tr, upload)
rows, err = db_query("SELECT COUNT(*) FROM DBPREFIXfiles WHERE original_filename = ?", {upload.OriginalFilename})
if(err ~= nil) then
return err:Error()
end
while rows:Next() do
rows_table = {}
err = db_scan_rows(rows, rows_table)
if(err ~= nil) then
rows:Close()
return err:Error()
end
if(rows_table["COUNT(*)"] > 0) then
rows:Close()
return "a file with that filename has already been uploaded"
end
end
end)