Added support for modlogs.

Added support for more mod-action posts in topics.

Fixed an easter egg.
Fixed a few redirects.
Fixed an issue with users not being loaded if they weren't already in the cache in the User Editor.
This commit is contained in:
Azareal 2017-04-06 18:37:32 +01:00
parent 6d17e08605
commit a906f17470
10 changed files with 253 additions and 55 deletions

View File

@ -174,7 +174,8 @@ CREATE TABLE `moderation_logs`(
`elementID` int not null,
`elementType` varchar(100) not null,
`ipaddress` varchar(200) not null,
`actorID` int not null
`actorID` int not null,
`doneAt` datetime not null
);
CREATE TABLE `administration_logs`(
@ -182,7 +183,8 @@ CREATE TABLE `administration_logs`(
`elementID` int not null,
`elementType` varchar(100) not null,
`ipaddress` varchar(200) not null,
`actorID` int not null
`actorID` int not null,
`doneAt` datetime not null
);
INSERT INTO settings(`name`,`content`,`type`) VALUES ('url_tags','1','bool');

BIN
images/modlogs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

@ -264,6 +264,7 @@ func main(){
router.HandleFunc("/panel/groups/edit/submit/", route_panel_groups_edit_submit)
router.HandleFunc("/panel/groups/edit/perms/submit/", route_panel_groups_edit_perms_submit)
router.HandleFunc("/panel/groups/create/", route_panel_groups_create_submit)
router.HandleFunc("/panel/logs/mod/", route_panel_logs_mod)
router.HandleFunc("/api/", route_api)
//router.HandleFunc("/exit/", route_exit)

View File

@ -209,6 +209,23 @@ func route_stick_topic(w http.ResponseWriter, r *http.Request) {
return
}
//topic.Sticky = true
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
LocalError("Bad IP",w,r,user)
return
}
err = addModLog("stick",tid,"topic",ipaddress,user.ID)
if err != nil {
InternalError(err,w,r)
return
}
_, err = create_action_reply_stmt.Exec(tid,"stick",ipaddress,user.ID)
if err != nil {
InternalError(err,w,r)
return
}
err = topics.Load(tid)
if err != nil {
LocalError("This topic doesn't exist!",w,r,user)
@ -248,6 +265,23 @@ func route_unstick_topic(w http.ResponseWriter, r *http.Request) {
return
}
//topic.Sticky = false
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
LocalError("Bad IP",w,r,user)
return
}
err = addModLog("unstick",tid,"topic",ipaddress,user.ID)
if err != nil {
InternalError(err,w,r)
return
}
_, err = create_action_reply_stmt.Exec(tid,"unstick",ipaddress,user.ID)
if err != nil {
InternalError(err,w,r)
return
}
err = topics.Load(tid)
if err != nil {
LocalError("This topic doesn't exist!",w,r,user)
@ -385,6 +419,17 @@ func route_reply_delete_submit(w http.ResponseWriter, r *http.Request) {
InternalErrorJSQ(err,w,r,is_js)
}
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
LocalError("Bad IP",w,r,user)
return
}
err = addModLog("delete",tid,"reply",ipaddress,user.ID)
if err != nil {
InternalError(err,w,r)
return
}
err = topics.Load(tid)
if err != nil {
LocalError("This topic no longer exists!",w,r,user)
@ -435,7 +480,7 @@ func route_profile_reply_edit_submit(w http.ResponseWriter, r *http.Request) {
}
if is_js == "0" {
http.Redirect(w,r, "/user/" + strconv.Itoa(uid) + "#reply-" + strconv.Itoa(rid), http.StatusSeeOther)
http.Redirect(w,r,"/user/" + strconv.Itoa(uid) + "#reply-" + strconv.Itoa(rid), http.StatusSeeOther)
} else {
fmt.Fprintf(w,`{"success":"1"}`)
}
@ -544,6 +589,10 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request) {
LocalError("The provided User ID is not a valid number.",w,r,user)
return
}
if uid == -2 {
LocalError("Sigh, are you really trying to ban me? Do you despise so much? Despite all of our adventures over at /arcane-tower/...?",w,r,user)
return
}
var group int
var is_super_admin bool
@ -561,11 +610,7 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request) {
return
}
if uid == user.ID {
LocalError("You may not ban yourself.",w,r,user)
return
}
if uid == -2 {
LocalError("You may not ban me. Fine, I will offer up some guidance unto thee. Come to my lair, young one. /arcane-tower/",w,r,user)
LocalError("Why are you trying to ban yourself? Stop that.",w,r,user)
return
}
@ -580,12 +625,23 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request) {
return
}
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
LocalError("Bad IP",w,r,user)
return
}
err = addModLog("ban",uid,"user",ipaddress,user.ID)
if err != nil {
InternalError(err,w,r)
return
}
err = users.Load(uid)
if err != nil {
LocalError("This user no longer exists!",w,r,user)
return
}
http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther)
http.Redirect(w,r,"/user/" + strconv.Itoa(uid),http.StatusSeeOther)
}
func route_unban(w http.ResponseWriter, r *http.Request) {
@ -630,12 +686,23 @@ func route_unban(w http.ResponseWriter, r *http.Request) {
return
}
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
LocalError("Bad IP",w,r,user)
return
}
err = addModLog("unban",uid,"user",ipaddress,user.ID)
if err != nil {
InternalError(err,w,r)
return
}
err = users.Load(uid)
if err != nil {
LocalError("This user no longer exists!",w,r,user)
return
}
http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther)
http.Redirect(w,r,"/user/" + strconv.Itoa(uid),http.StatusSeeOther)
}
func route_activate(w http.ResponseWriter, r *http.Request) {
@ -685,10 +752,21 @@ func route_activate(w http.ResponseWriter, r *http.Request) {
return
}
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
LocalError("Bad IP",w,r,user)
return
}
err = addModLog("activate",uid,"user",ipaddress,user.ID)
if err != nil {
InternalError(err,w,r)
return
}
err = users.Load(uid)
if err != nil {
LocalError("This user no longer exists!",w,r,user)
return
}
http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther)
http.Redirect(w,r,"/user/" + strconv.Itoa(uid),http.StatusSeeOther)
}

View File

@ -597,13 +597,13 @@ func init_database(err error) {
}
log.Print("Preparing add_modlog_entry statement.")
add_modlog_entry_stmt, err = db.Prepare("INSERT INTO moderation_logs(action,elementID,elementType,ipaddress,actorID) VALUES(?,?,?,?,?)")
add_modlog_entry_stmt, err = db.Prepare("INSERT INTO moderation_logs(action,elementID,elementType,ipaddress,actorID,doneAt) VALUES(?,?,?,?,?,NOW())")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing add_adminlog_entry statement.")
add_adminlog_entry_stmt, err = db.Prepare("INSERT INTO moderation_logs(action,elementID,elementType,actorID) VALUES(?,?,?,?)")
add_adminlog_entry_stmt, err = db.Prepare("INSERT INTO moderation_logs(action,elementID,elementType,ipaddress,actorID,doneAt) VALUES(?,?,?,?,?,NOW())")
if err != nil {
log.Fatal(err)
}

View File

@ -4,6 +4,7 @@ import "bytes"
import "strings"
import "strconv"
import "regexp"
import "html/template"
type Page struct
{
@ -124,6 +125,21 @@ type EditGroupPermsPage struct
ExtData interface{}
}
type Log struct {
Action template.HTML
IPAddress string
DoneAt string
}
type LogsPage struct
{
Title string
CurrentUser User
NoticeList []string
Logs []Log
ExtData interface{}
}
type PageSimple struct
{
Title string

View File

@ -7,6 +7,7 @@ import "strconv"
import "html"
import "encoding/json"
import "net/http"
import "html/template"
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
@ -571,14 +572,7 @@ func route_panel_users(w http.ResponseWriter, r *http.Request){
return
}
puser.Is_Admin = puser.Is_Super_Admin || groups[puser.Group].Is_Admin
puser.Is_Super_Mod = puser.Is_Admin || groups[puser.Group].Is_Mod
puser.Is_Mod = puser.Is_Super_Mod
puser.Is_Banned = groups[puser.Group].Is_Banned
if puser.Is_Banned && puser.Is_Super_Mod {
puser.Is_Banned = false
}
init_user_perms(&puser)
if puser.Avatar != "" {
if puser.Avatar[0] == '.' {
puser.Avatar = "/uploads/avatar_" + strconv.Itoa(puser.ID) + puser.Avatar
@ -625,7 +619,7 @@ func route_panel_users_edit(w http.ResponseWriter, r *http.Request){
return
}
targetUser, err := users.Get(uid)
targetUser, err := users.CascadeGet(uid)
if err == sql.ErrNoRows {
LocalError("The user you're trying to edit doesn't exist.",w,r,user)
return
@ -677,7 +671,7 @@ func route_panel_users_edit_submit(w http.ResponseWriter, r *http.Request){
return
}
targetUser, err := users.Get(tid)
targetUser, err := users.CascadeGet(tid)
if err == sql.ErrNoRows {
LocalError("The user you're trying to edit doesn't exist.",w,r,user)
return
@ -786,11 +780,7 @@ func route_panel_groups(w http.ResponseWriter, r *http.Request){
rank_emoji = "👪"
}
if user.Perms.EditGroup && (!group.Is_Admin || user.Perms.EditGroupAdmin) && (!group.Is_Mod || user.Perms.EditGroupSuperMod) {
can_edit = true
} else {
can_edit = false
}
can_edit = user.Perms.EditGroup && (!group.Is_Admin || user.Perms.EditGroupAdmin) && (!group.Is_Mod || user.Perms.EditGroupSuperMod)
groupList = append(groupList, GroupAdmin{group.ID,group.Name,rank,rank_emoji,can_edit,can_delete})
}
@ -845,10 +835,7 @@ func route_panel_groups_edit(w http.ResponseWriter, r *http.Request){
rank = "Member"
}
var disable_rank bool
if !user.Perms.EditGroupGlobalPerms || (group.ID == 6) {
disable_rank = true
}
disable_rank := !user.Perms.EditGroupGlobalPerms || (group.ID == 6)
pi := EditGroupPage{"Group Editor",user,noticeList,group.ID,group.Name,group.Tag,rank,disable_rank,nil}
err = templates.ExecuteTemplate(w,"panel-group-edit.html",pi)
@ -1305,3 +1292,106 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request){
http.Redirect(w,r,"/panel/themes/",http.StatusSeeOther)
}
func route_panel_logs_mod(w http.ResponseWriter, r *http.Request){
user, noticeList, ok := SessionCheck(w,r)
if !ok {
return
}
if !user.Is_Super_Mod || !user.Perms.ManageThemes {
NoPermissions(w,r,user)
return
}
rows, err := db.Query("select action, elementID, elementType, ipaddress, actorID, doneAt from moderation_logs")
if err != nil {
InternalError(err,w,r)
return
}
defer rows.Close()
var logs []Log
var action, elementType, ipaddress, doneAt string
var elementID, actorID int
for rows.Next() {
err := rows.Scan(&action,&elementID,&elementType, &ipaddress, &actorID, &doneAt)
if err != nil {
InternalError(err,w,r)
return
}
actor, err := users.CascadeGet(actorID)
if err != nil {
actor = &User{Name:"Unknown"}
}
switch(action) {
case "lock":
topic, err := topics.CascadeGet(elementID)
if err != nil {
topic = &Topic{Title:"Unknown"}
}
action = "<a href='" + build_topic_url(elementID) + "'>" + topic.Title + "</a> was locked by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
case "unlock":
topic, err := topics.CascadeGet(elementID)
if err != nil {
topic = &Topic{Title:"Unknown"}
}
action = "<a href='" + build_topic_url(elementID) + "'>" + topic.Title + "</a> was reopened by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
case "stick":
topic, err := topics.CascadeGet(elementID)
if err != nil {
topic = &Topic{Title:"Unknown"}
}
action = "<a href='" + build_topic_url(elementID) + "'>" + topic.Title + "</a> was pinned by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
case "unstick":
topic, err := topics.CascadeGet(elementID)
if err != nil {
topic = &Topic{Title:"Unknown"}
}
action = "<a href='" + build_topic_url(elementID) + "'>" + topic.Title + "</a> was unpinned by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
case "delete":
if elementType == "topic" {
action = "Topic #" + strconv.Itoa(elementID) + " was deleted by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
} else {
topic, err := get_topic_by_reply(elementID)
if err != nil {
topic = &Topic{Title:"Unknown"}
}
action = "A reply in <a href='" + build_topic_url(elementID) + "'>" + topic.Title + "</a> was deleted by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
}
case "ban":
targetUser, err := users.CascadeGet(elementID)
if err != nil {
targetUser = &User{Name:"Unknown"}
}
action = "<a href='" + build_profile_url(elementID) + "'>" + targetUser.Name + "</a> was banned by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
case "unban":
targetUser, err := users.CascadeGet(elementID)
if err != nil {
targetUser = &User{Name:"Unknown"}
}
action = "<a href='" + build_profile_url(elementID) + "'>" + targetUser.Name + "</a> was unbanned by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
case "activate":
targetUser, err := users.CascadeGet(elementID)
if err != nil {
targetUser = &User{Name:"Unknown"}
}
action = "<a href='" + build_profile_url(elementID) + "'>" + targetUser.Name + "</a> was activated by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
default:
action = "Unknown action '" + action + "' by <a href='" + build_profile_url(actorID) + "'>"+actor.Name+"</a>"
}
logs = append(logs, Log{Action:template.HTML(action),IPAddress:ipaddress,DoneAt:doneAt})
}
err = rows.Err()
if err != nil {
InternalError(err,w,r)
return
}
pi := LogsPage{"Moderation Logs",user,noticeList,logs,nil}
err = templates.ExecuteTemplate(w,"panel-modlogs.html",pi)
if err != nil {
log.Print(err)
}
}

View File

@ -288,12 +288,9 @@ func route_forums(w http.ResponseWriter, r *http.Request){
}
func route_topic_id(w http.ResponseWriter, r *http.Request){
var(
err error
page int
offset int
replyList []Reply
)
var err error
var page, offset int
var replyList []Reply
page, _ = strconv.Atoi(r.FormValue("page"))
tid, err := strconv.Atoi(r.URL.Path[len("/topic/"):])
@ -416,6 +413,12 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
case "unlock":
replyItem.ActionType = "This topic has been reopened by <a href='" + build_profile_url(replyItem.CreatedBy) + "'>" + replyItem.CreatedByName + "</a>"
replyItem.ActionIcon = "&#x1F513;&#xFE0E"
case "stick":
replyItem.ActionType = "This topic has been pinned by <a href='" + build_profile_url(replyItem.CreatedBy) + "'>" + replyItem.CreatedByName + "</a>"
replyItem.ActionIcon = "&#x1F4CC;&#xFE0E"
case "unstick":
replyItem.ActionType = "This topic has been unpinned by <a href='" + build_profile_url(replyItem.CreatedBy) + "'>" + replyItem.CreatedByName + "</a>"
replyItem.ActionIcon = "&#x1F4CC;&#xFE0E"
default:
replyItem.ActionType = replyItem.ActionType + " has happened"
replyItem.ActionIcon = ""
@ -452,23 +455,11 @@ func route_profile(w http.ResponseWriter, r *http.Request){
return
}
var(
err error
rid int
replyContent string
replyCreatedBy int
replyCreatedByName string
replyCreatedAt string
replyLastEdit int
replyLastEditBy int
replyAvatar string
replyCss template.CSS
replyLines int
replyTag string
replyGroup int
replyList []Reply
)
var err error
var replyContent, replyCreatedByName, replyCreatedAt, replyAvatar, replyTag string
var rid, replyCreatedBy, replyLastEdit, replyLastEditBy, replyLines, replyGroup int
var replyCss template.CSS
var replyList []Reply
pid, err := strconv.Atoi(r.URL.Path[len("/user/"):])
if err != nil {

View File

@ -9,4 +9,5 @@
{{if .CurrentUser.Perms.ManageThemes}}<div class="rowitem passive"><a href="/panel/themes/">Themes</a></div>{{end}}
{{if .CurrentUser.Perms.ManagePlugins}}<div class="rowitem passive"><a href="/panel/plugins/">Plugins</a></div>{{end}}
<div class="rowitem passive"><a href="/forum/1">Reports</a></div>
<div class="rowitem passive"><a href="/panel/logs/mod/">Logs</a></div>
</div>

View File

@ -0,0 +1,19 @@
{{template "header.html" . }}
{{template "panel-menu.html" . }}
<div class="colstack_right">
<div class="colstack_item colstack_head">
<div class="rowitem rowhead"><a>Moderation Logs</a></div>
</div>
<div class="colstack_item">
{{range .Logs}}
<div class="rowitem" style="font-weight: normal;text-transform: none;">
<a style="font-size: 17px;">{{.Action}}</a><br />
<small style="margin-left: 2px;">IP: {{.IPAddress}}</small>
<span style="float: right;">
<span style="font-size: 16px;position:relative;top: -2px;">{{.DoneAt}}</span>
</span>
</div>
{{end}}
</div>
</div>
{{template "footer.html" . }}