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:
parent
6d17e08605
commit
a906f17470
6
data.sql
6
data.sql
|
@ -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');
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
1
main.go
1
main.go
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
4
mysql.go
4
mysql.go
|
@ -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)
|
||||
}
|
||||
|
|
16
pages.go
16
pages.go
|
@ -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
|
||||
|
|
128
panel_routes.go
128
panel_routes.go
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
37
routes.go
37
routes.go
|
@ -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 = "🔓︎"
|
||||
case "stick":
|
||||
replyItem.ActionType = "This topic has been pinned by <a href='" + build_profile_url(replyItem.CreatedBy) + "'>" + replyItem.CreatedByName + "</a>"
|
||||
replyItem.ActionIcon = "📌︎"
|
||||
case "unstick":
|
||||
replyItem.ActionType = "This topic has been unpinned by <a href='" + build_profile_url(replyItem.CreatedBy) + "'>" + replyItem.CreatedByName + "</a>"
|
||||
replyItem.ActionIcon = "📌︎"
|
||||
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 {
|
||||
|
|
|
@ -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>
|
|
@ -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" . }}
|
Loading…
Reference in New Issue