Added the forum cache. The rest of the code now uses it instead than firing queries on every request.

Added the ability for admins to delete forums.
Added the ability for admins to edit forums.
Added the Uncategorised Forum for topics with an ID of 0. Possibly for deleted forums in the future?
Added the uncategorised forum visibility switch.
Creating forums now has an anti-CSRF session check. The same is true with the newly implemented forum deletion and modification features.
Cleaned up some of the error code.
This commit is contained in:
Azareal 2016-12-06 10:26:48 +00:00
parent 84cbb3a5d5
commit 30ecdf8d93
14 changed files with 281 additions and 96 deletions

View File

@ -1 +1,2 @@
go build
go build
pause

View File

@ -12,4 +12,5 @@ var max_request_size = 5 * megabyte
// Misc
var default_route = route_topics
var staff_css = "background-color: #ffeaff;background-position: left;"
var staff_css = "background-color: #ffeaff;background-position: left;"
var uncategorised_forum_visible = true

View File

@ -120,6 +120,26 @@ func LoginRequiredJSQ(w http.ResponseWriter, r *http.Request, user User, is_js s
}
}
func SecurityError(w http.ResponseWriter, r *http.Request, user User) {
errmsg := "There was a security issue with your request."
pi := Page{"Security Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(403)
fmt.Fprintln(w,errpage)
}
func NotFound(w http.ResponseWriter, r *http.Request, user User) {
errmsg := "The requested page doesn't exist."
pi := Page{"Not Found","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(404)
fmt.Fprintln(w,errpage)
}
func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User, is_js string) {
if is_js == "0" {
pi := Page{errtitle,"error",user,tList,errmsg}

View File

@ -4,6 +4,7 @@ type Forum struct
{
ID int
Name string
Active bool
LastTopic string
LastTopicID int
LastReplyer string

2
grosolo-linux Normal file
View File

@ -0,0 +1,2 @@
go build
./Grosolo

Binary file not shown.

BIN
grosolo.exe~ Normal file

Binary file not shown.

56
main.go
View File

@ -42,12 +42,15 @@ var register_stmt *sql.Stmt
var username_exists_stmt *sql.Stmt
var create_forum_stmt *sql.Stmt
var delete_forum_stmt *sql.Stmt
var update_forum_stmt *sql.Stmt
var custom_pages map[string]string = make(map[string]string)
var templates = template.Must(template.ParseGlob("templates/*"))
var no_css_tmpl = template.CSS("")
var staff_css_tmpl = template.CSS(staff_css)
var groups map[int]Group = make(map[int]Group)
var forums map[int]Forum = make(map[int]Forum)
var static_files map[string]SFile = make(map[string]SFile)
func init_database(err error) {
@ -188,8 +191,20 @@ func init_database(err error) {
log.Fatal(err)
}
log.Print("Preparing delete_forum statement.")
delete_forum_stmt, err = db.Prepare("DELETE FROM forums WHERE fid = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing update_forum statement.")
update_forum_stmt, err = db.Prepare("UPDATE forums SET name = ? WHERE fid = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Loading the usergroups.")
rows, err := db.Query("select gid,name,permissions,is_admin,is_banned from users_groups")
rows, err := db.Query("SELECT gid,name,permissions,is_admin,is_banned FROM users_groups")
if err != nil {
log.Fatal(err)
}
@ -207,6 +222,42 @@ func init_database(err error) {
if err != nil {
log.Fatal(err)
}
log.Print("Loading the forums.")
rows, err = db.Query("SELECT fid, name, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime FROM forums")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
forum := Forum{0,"",true,"",0,"",0,""}
err := rows.Scan(&forum.ID, &forum.Name, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
if err != nil {
log.Fatal(err)
}
if forum.LastTopicID != 0 {
forum.LastTopicTime, err = relative_time(forum.LastTopicTime)
if err != nil {
log.Fatal(err)
}
} else {
forum.LastTopic = "None"
forum.LastTopicTime = ""
}
forums[forum.ID] = forum
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
log.Print("Adding the uncategorised forum")
forums[0] = Forum{0,"Uncategorised",uncategorised_forum_visible,"",0,"",0,""}
log.Print("Adding the reports forum")
forums[-1] = Forum{-1,"Reports",false,"",0,"",0,""}
}
func main(){
@ -288,6 +339,9 @@ func main(){
// Admin
http.HandleFunc("/panel/forums/", route_panel_forums)
http.HandleFunc("/panel/forums/create/", route_panel_forums_create_submit)
http.HandleFunc("/panel/forums/delete/", route_panel_forums_delete)
http.HandleFunc("/panel/forums/delete/submit/", route_panel_forums_delete_submit)
http.HandleFunc("/panel/forums/edit/submit/", route_panel_forums_edit_submit)
http.HandleFunc("/", default_route)

View File

@ -21,6 +21,12 @@ type PageSimple struct
Something interface{}
}
type AreYouSure struct
{
URL string
Message string
}
func add_custom_page(path string, f os.FileInfo, err error) error {
if err != nil {
return err

View File

@ -83,4 +83,30 @@ $(document).ready(function(){
});
});
});
$(".edit_field").click(function(event)
{
event.preventDefault();
var block_parent = $(this).closest('.editable_parent');
var block = block_parent.find('.editable_block').eq(0);
block.html("<input name='edit_field' value='" + block.text() + "' type='text'/><a href='" + $(this).closest('a').attr("href") + "'><button class='submit_edit' type='submit'>Update</button></a>");
$(".submit_edit").click(function(event)
{
event.preventDefault();
var block_parent = $(this).closest('.editable_parent');
var block = block_parent.find('.editable_block').eq(0);
var newContent = block.find('input').eq(0).val();
block.html(newContent);
var form_action = $(this).closest('a').attr("href");
console.log("Form Action: " + form_action);
$.ajax({
url: form_action + "?session=" + session,
type: "POST",
dataType: "json",
data: {is_js: "1",edit_item: newContent}
});
});
});
});

235
routes.go
View File

@ -1,5 +1,6 @@
package main
import "errors"
import "log"
import "fmt"
import "strconv"
@ -58,14 +59,7 @@ func route_custom_page(w http.ResponseWriter, r *http.Request){
pi := Page{"Page","page",user,tList,val}
templates.ExecuteTemplate(w,"custom_page.html", pi)
} else {
errmsg := "The requested page doesn't exist."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(404)
fmt.Fprintln(w,errpage)
NotFound(w,r,user)
}
}
@ -133,7 +127,6 @@ func route_forum(w http.ResponseWriter, r *http.Request){
var(
topicList map[int]interface{}
currentID int
fname string
tid int
title string
@ -156,18 +149,9 @@ func route_forum(w http.ResponseWriter, r *http.Request){
return
}
err = db.QueryRow("select name from forums where fid = ?", fid).Scan(&fname)
if err == sql.ErrNoRows {
pi := Page{"Error","error",user,tList,"The requested forum doesn't exist."}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(404)
fmt.Fprintln(w,errpage)
return
} else if err != nil {
InternalError(err,w,r,user)
_, ok := forums[fid]
if !ok {
NotFound(w,r,user)
return
}
@ -202,7 +186,7 @@ func route_forum(w http.ResponseWriter, r *http.Request){
InternalError(err,w,r,user)
return
}
pi := Page{fname,"forum",user,topicList,0}
pi := Page{forums[fid].Name,"forum",user,topicList,0}
err = templates.ExecuteTemplate(w,"forum.html", pi)
if err != nil {
InternalError(err, w, r, user)
@ -211,46 +195,23 @@ func route_forum(w http.ResponseWriter, r *http.Request){
func route_forums(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
var forumList map[int]interface{}
forumList = make(map[int]interface{})
var forumList map[int]interface{} = make(map[int]interface{})
currentID := 0
rows, err := db.Query("select fid, name, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime from forums")
if err != nil {
InternalError(err,w,r,user)
return
for _, forum := range forums {
if forum.Active {
forumList[currentID] = forum
currentID++
}
}
defer rows.Close()
for rows.Next() {
forum := Forum{0,"","",0,"",0,""}
err := rows.Scan(&forum.ID, &forum.Name, &forum.LastTopic,&forum.LastTopicID,&forum.LastReplyer,&forum.LastReplyerID,&forum.LastTopicTime)
if err != nil {
InternalError(err,w,r,user)
return
}
if forum.LastTopicID != 0 {
forum.LastTopicTime, err = relative_time(forum.LastTopicTime)
if err != nil {
InternalError(err,w,r,user)
return
}
} else {
forum.LastTopic = "None"
forum.LastTopicTime = ""
}
forumList[currentID] = forum
currentID++
}
err = rows.Err()
if err != nil {
InternalError(err,w,r,user)
if len(forums) == 0 {
InternalError(errors.New("No forums"),w,r,user)
return
}
pi := Page{"Forum List","forums",user,forumList,0}
err = templates.ExecuteTemplate(w,"forums.html", pi)
err := templates.ExecuteTemplate(w,"forums.html", pi)
if err != nil {
InternalError(err, w, r, user)
}
@ -290,14 +251,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
//err = db.QueryRow("select title, content, createdBy, status, is_closed from topics where tid = ?", tid).Scan(&title, &content, &createdBy, &status, &is_closed)
err = db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, users.name, users.avatar, users.is_super_admin, users.group from topics left join users ON topics.createdBy = users.uid where tid = ?", topic.ID).Scan(&topic.Title, &content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.CreatedByName, &topic.Avatar, &is_super_admin, &group)
if err == sql.ErrNoRows {
errmsg := "The requested topic doesn't exist."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(404)
fmt.Fprintln(w,errpage)
NotFound(w,r,user)
return
} else if err != nil {
InternalError(err,w,r,user)
@ -1036,9 +990,9 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
var salt string
var session string
username := html.EscapeString(r.PostFormValue("username"))
log.Print("Username: " + username)
//log.Print("Username: " + username)
password := r.PostFormValue("password")
log.Print("Password: " + password)
//log.Print("Password: " + password)
err = login_stmt.QueryRow(username).Scan(&uid, &username, &real_password, &salt)
if err == sql.ErrNoRows {
@ -1112,8 +1066,8 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
return
}
log.Print("Successful Login")
log.Print("Session: " + session)
//log.Print("Successful Login")
//log.Print("Session: " + session)
cookie := http.Cookie{Name: "uid",Value: strconv.Itoa(uid),Path: "/",MaxAge: year}
http.SetCookie(w,&cookie)
cookie = http.Cookie{Name: "session",Value: session,Path: "/",MaxAge: year}
@ -1230,28 +1184,11 @@ func route_panel_forums(w http.ResponseWriter, r *http.Request){
var forumList map[int]interface{} = make(map[int]interface{})
currentID := 0
rows, err := db.Query("select fid, name from forums")
if err != nil {
InternalError(err,w,r,user)
return
}
defer rows.Close()
for rows.Next() {
forum := ForumSimple{0,""}
err := rows.Scan(&forum.ID, &forum.Name)
if err != nil {
InternalError(err,w,r,user)
return
for _, forum := range forums {
if forum.ID > -1 {
forumList[currentID] = forum
currentID++
}
forumList[currentID] = forum
currentID++
}
err = rows.Err()
if err != nil {
InternalError(err,w,r,user)
return
}
pi := Page{"Forum Manager","panel-forums",user,forumList,0}
@ -1264,17 +1201,135 @@ func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request){
NoPermissions(w,r,user)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
if r.FormValue("session") != user.Session {
SecurityError(w,r,user)
return
}
_, err = create_forum_stmt.Exec(r.PostFormValue("forum-name"))
fname := r.PostFormValue("forum-name")
res, err := create_forum_stmt.Exec(fname)
if err != nil {
InternalError(err,w,r,user)
return
}
http.Redirect(w,r, "/panel/forums/", http.StatusSeeOther)
lastId, err := res.LastInsertId()
if err != nil {
InternalError(err,w,r,user)
return
}
forums[int(lastId)] = Forum{int(lastId),fname,true,"",0,"",0,""}
http.Redirect(w,r,"/panel/forums/",http.StatusSeeOther)
}
func route_panel_forums_delete(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
if r.FormValue("session") != user.Session {
SecurityError(w,r,user)
return
}
fid, err := strconv.Atoi(r.URL.Path[len("/panel/forums/delete/"):])
if err != nil {
LocalError("The provided Forum ID is not a valid number.",w,r,user)
return
}
_, ok := forums[fid];
if !ok {
LocalError("The forum you're trying to delete doesn't exist.",w,r,user)
return
}
confirm_msg := "Are you sure you want to delete the '" + forums[fid].Name + "' forum?"
yousure := AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid),confirm_msg}
pi := Page{"Delete Forum","panel-forums-delete",user,tList,yousure}
templates.ExecuteTemplate(w,"areyousure.html", pi)
}
func route_panel_forums_delete_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
if r.FormValue("session") != user.Session {
SecurityError(w,r,user)
return
}
fid, err := strconv.Atoi(r.URL.Path[len("/panel/forums/delete/submit/"):])
if err != nil {
LocalError("The provided Forum ID is not a valid number.",w,r,user)
return
}
_, ok := forums[fid];
if !ok {
LocalError("The forum you're trying to delete doesn't exist.",w,r,user)
return
}
_, err = delete_forum_stmt.Exec(fid)
if err != nil {
InternalError(err,w,r,user)
return
}
// Remove this forum from the forum cache
delete(forums,fid);
http.Redirect(w,r,"/panel/forums/",http.StatusSeeOther)
}
func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
if r.FormValue("session") != user.Session {
SecurityError(w,r,user)
return
}
fid, err := strconv.Atoi(r.URL.Path[len("/panel/forums/edit/submit/"):])
if err != nil {
LocalError("The provided Forum ID is not a valid number.",w,r,user)
return
}
forum_name := r.PostFormValue("edit_item")
forum, ok := forums[fid];
if !ok {
LocalError("The forum you're trying to edit doesn't exist.",w,r,user)
return
}
_, err = update_forum_stmt.Exec(forum_name, fid)
if err != nil {
InternalError(err,w,r,user)
return
}
forum.Name = forum_name
forums[fid] = forum
http.Redirect(w,r,"/panel/forums/",http.StatusSeeOther)
}

10
templates/areyousure.html Normal file
View File

@ -0,0 +1,10 @@
{{template "header.html" . }}
<div class="rowblock">
<div class="rowitem"><a>Are you sure?</a></div>
</div>
<div class="rowblock">
<div class="rowitem passive">{{.Something.Message}}<br /><br />
<a class="username" href="{{.Something.URL}}?session={{.CurrentUser.Session}}">Continue</a>
</div>
</div>
{{template "footer.html" . }}

View File

@ -4,6 +4,9 @@
<title>{{.Title}}</title>
<link href="/static/main.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="/static/jquery-1.12.3.min.js"></script>
<script type="text/javascript">
var session = "{{.CurrentUser.Session}}";
</script>
<script type="text/javascript" src="/static/global.js"></script>
</head>
<body>

View File

@ -5,18 +5,24 @@
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a href="/forum/-1">Reports</a></div>
</div>
<div class="colblock_right">
{{range .ItemList}}
<div class="rowitem"><a style="font-size: 20px;position:relative;top: -2px;font-weight: normal;text-transform: none;">{{.Name}}</a></div>
<div class="rowitem editable_parent" style="font-weight: normal;">
<a class="editable_block" style="font-size: 20px;position:relative;top: -2px;text-transform: none;">{{.Name}}</a>
<span style="float: right;">
<a href="/panel/forums/edit/submit/{{.ID}}" class="username edit_field">Edit</a>
<a href="/panel/forums/delete/{{.ID}}?session={{$.CurrentUser.Session}}" class="username">Delete</a>
</span>
</div>
{{end}}
</div><br />
<div class="colblock_right">
<div class="rowitem"><a>Add Forum</a></div>
</div>
<div class="colblock_right">
<form action="/panel/forums/create/" method="post">
<form action="/panel/forums/create/?session={{.CurrentUser.Session}}" method="post">
<div class="formrow">
<div class="formitem"><a>Forum Name</a></div>
<div class="formitem"><input name="forum-name" type="text" /></div>