Admins now show up in purple on posts.

Added a .gitignore file.
Added group caching.
Improved the forum list.
Added the Member group.
Opening posts are now parsed properly.
Fixed a bug where the avatar of the opening poster was applied to everyone.
Only admins can see moderation options now.
This commit is contained in:
Azareal 2016-12-04 06:16:59 +00:00
parent 771af02674
commit 1f6e585296
19 changed files with 81 additions and 20 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
src/uploads/*
bin/*

View File

@ -33,7 +33,7 @@ Set the password column of your user account in the database to what you want yo
# Run the program
go run errors.go main.go pages.go reply.go routes.go topic.go user.go utils.go forum.go config.go
go run errors.go main.go pages.go reply.go routes.go topic.go user.go utils.go forum.go group.go config.go
Alternatively, you could run the run.bat batch file on Windows.
@ -65,4 +65,4 @@ Tweak the CSS to make it responsive.
Add a forum cache.
Add a group cache.
Cache the static files in memory.

BIN
forum-list.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

Before

Width:  |  Height:  |  Size: 312 KiB

After

Width:  |  Height:  |  Size: 312 KiB

BIN
ren6.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -11,4 +11,5 @@ var dbport = "3306" // You probably won't need to change this
var max_request_size = 5 * megabyte
// Misc
var default_route = route_topics
var default_route = route_topics
var staff_css = "background-color: #ffeaff;background-position: left;"

View File

@ -64,6 +64,7 @@ CREATE TABLE `replies`(
INSERT INTO users(`name`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`)
VALUES ('Admin',1,1,NOW(),NOW());
INSERT INTO users_groups(`name`,`permissions`,`is_admin`) VALUES ('Administrator','{}',1);
INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{}');
INSERT INTO forums(`name`,`lastTopicTime`) VALUES ('General',NOW());
INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`)
VALUES ('Test Topic','A topic automatically generated by the software.',NOW(),NOW(),1,1);

10
src/group.go Normal file
View File

@ -0,0 +1,10 @@
package main
type Group struct
{
ID int
Name string
Permissions string
Is_Admin bool
Is_Banned bool
}

View File

@ -17,6 +17,7 @@ const kilobyte int = 1024
const megabyte int = 1024 * 1024
const saltLength int = 32
const sessionLength int = 80
var db *sql.DB
var get_session_stmt *sql.Stmt
var create_topic_stmt *sql.Stmt
@ -37,8 +38,12 @@ var set_avatar_stmt *sql.Stmt
var set_username_stmt *sql.Stmt
var register_stmt *sql.Stmt
var username_exists_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)
func init_database(err error) {
if(dbpassword != ""){
@ -161,7 +166,7 @@ func init_database(err error) {
// create_account_stmt, err = db.Prepare("INSERT INTO
log.Print("Preparing register statement.")
register_stmt, err = db.Prepare("INSERT INTO users(`name`,`password`,`salt`,`group`,`is_super_admin`,`session`) VALUES(?,?,?,0,0,?)")
register_stmt, err = db.Prepare("INSERT INTO users(`name`,`password`,`salt`,`group`,`is_super_admin`,`session`) VALUES(?,?,?,2,0,?)")
if err != nil {
log.Fatal(err)
}
@ -171,6 +176,26 @@ func init_database(err error) {
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")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
group := Group{0,"","",false,false}
err := rows.Scan(&group.ID, &group.Name, &group.Permissions, &group.Is_Admin, &group.Is_Banned)
if err != nil {
log.Fatal(err)
}
groups[group.ID] = group
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
func main(){

View File

@ -251,11 +251,17 @@ button
padding-top: 2px;
padding-bottom: 2px;
color: #505050; /* 80,80,80 */
background-color: #FFFFFF;
border-style: dotted;
border-color: #505050; /* 232,232,232. All three RGB colours being the same seems to create a shade of gray */
border-width: 1px;
font-size: 15px;
}
button.username
{
position: relative;
top: -0.25px;
}
.show_on_edit
{

BIN
src/public/white-dot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

View File

@ -13,4 +13,5 @@ type Reply struct
LastEdit int
LastEditBy int
Avatar string
Css template.CSS
}

View File

@ -92,7 +92,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){
avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar
}
topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar}
topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar, ""}
currentID++
}
err = rows.Err()
@ -173,7 +173,7 @@ func route_forum(w http.ResponseWriter, r *http.Request){
avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar
}
topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar}
topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar, ""}
currentID++
}
err = rows.Err()
@ -243,13 +243,16 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
replyLastEdit int
replyLastEditBy int
replyAvatar string
replyCss template.CSS
is_super_admin bool
group int
currentID int
replyList map[int]interface{}
)
replyList = make(map[int]interface{})
currentID = 0
topic := TopicUser{0,"","",0,false,false,"",0,"","",""}
topic := TopicUser{0,"","",0,false,false,"",0,"","","",no_css_tmpl}
topic.ID, err = strconv.Atoi(r.URL.Path[len("/topic/"):])
if err != nil {
@ -259,7 +262,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
// Get the topic..
//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 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)
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}
@ -275,7 +278,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
return
}
topic.Content = template.HTML(content)
topic.Content = template.HTML(parse_message(content))
if topic.Is_Closed {
topic.Status = "closed"
} else {
@ -284,10 +287,13 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
if topic.Avatar != "" && topic.Avatar[0] == '.' {
topic.Avatar = "/uploads/avatar_" + strconv.Itoa(topic.CreatedBy) + topic.Avatar
}
if is_super_admin || groups[group].Is_Admin {
topic.Css = staff_css_tmpl
}
// Get the replies..
//rows, err := db.Query("select rid, content, createdBy, createdAt from replies where tid = ?", tid)
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name from replies left join users ON replies.createdBy = users.uid where tid = ?", topic.ID)
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group from replies left join users ON replies.createdBy = users.uid where tid = ?", topic.ID)
if err != nil {
InternalError(err,w,r,user)
return
@ -295,17 +301,22 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
defer rows.Close()
for rows.Next() {
err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName)
err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName, &is_super_admin, &group)
if err != nil {
InternalError(err,w,r,user)
return
}
if is_super_admin || groups[group].Is_Admin {
replyCss = staff_css_tmpl
} else {
replyCss = no_css_tmpl
}
if replyAvatar != "" && replyAvatar[0] == '.' {
replyAvatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + replyAvatar
replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar
}
replyList[currentID] = Reply{rid,topic.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar}
replyList[currentID] = Reply{rid,topic.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss}
currentID++
}
err = rows.Err()

View File

@ -1,2 +1,2 @@
go run errors.go main.go pages.go reply.go routes.go topic.go user.go utils.go config.go forum.go
go run errors.go main.go pages.go reply.go routes.go topic.go user.go utils.go config.go forum.go group.go
pause

View File

@ -1,7 +1,7 @@
{{template "header.html" . }}
<div class="rowblock">
{{range .ItemList}}<div class="rowitem">
<a href="/forum/{{.ID}}">{{.Name}}</a>
<a href="/forum/{{.ID}}" style="font-size: 20px;position:relative;top: -2px;font-weight: normal;text-transform: none;">{{.Name}}</a>
<a href="/topic/{{.LastTopicID}}" style="font-weight: normal;text-transform: none;float: right;">{{.LastTopic}} <small style="font-size: 12px;">{{.LastTopicTime}}</small></a>
</div>{{end}}
</div>

View File

@ -4,6 +4,7 @@
<div class="rowitem"{{ if .Something.Sticky }}style="background-color: #FFFFEA;"{{end}}>
<a class='topic_name hide_on_edit'>{{.Something.Title}}</a>
<span class='topic_status topic_status_e topic_status_{{.Something.Status}} hide_on_edit'>{{.Something.Status}}</span>
{{if .CurrentUser.Is_Admin}}
<a href='/topic/edit/{{.Something.ID}}' class="username hide_on_edit open_edit" style="font-weight: normal;">Edit</a>
<a href='/topic/delete/submit/{{.Something.ID}}' class="username" style="font-weight: normal;">Delete</a>
{{ if .Something.Sticky }}<a href='/topic/unstick/submit/{{.Something.ID}}' class="username" style="font-weight: normal;">Unpin</a>{{else}}<a href='/topic/stick/submit/{{.Something.ID}}' class="username" style="font-weight: normal;">Pin</a>{{end}}
@ -14,11 +15,12 @@
<option>closed</option>
</select>
<button name="topic-button" class="formbutton show_on_edit submit_edit">Update</button>
{{end}}
</div>
</form>
</div>
<div class="rowblock">
<div class="rowitem passive editable_parent" style="border-bottom: none;{{ if .Something.Avatar }}background-image: url({{ .Something.Avatar }});background-position: left;background-repeat: no-repeat;background-size: 128px;padding-left: 136px;{{end}}">
<div class="rowitem passive editable_parent" style="border-bottom: none;{{ if .Something.Avatar }}background-image: url({{ .Something.Avatar }}), url(/static/white-dot.jpg);background-position: top left;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{.Something.Css}}{{end}}">
<span class="hide_on_edit topic_content">{{.Something.Content}}</span>
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Something.Content}}</textarea>
<br /><br />
@ -27,12 +29,12 @@
</div><br />
<div class="rowblock" style="overflow: hidden;">
{{range $index, $element := .ItemList}}
<div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{ $element.Avatar }});background-position: left;background-repeat: no-repeat;background-size: 128px;padding-left: 136px;{{end}}">
<div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{$element.Avatar}}), url(/static/white-dot.jpg);background-position: top left;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{$element.Css}}{{end}}">
<span class="editable_block">{{$element.ContentHtml}}</span>
<br /><br />
<a class="username">{{$element.CreatedByName}}<a/>
<a href="/reply/edit/submit/{{$element.ID}}"><button class="username edit_item">Edit</button></a>
<a href="/reply/delete/submit/{{$element.ID}}"><button class="username delete_item">Delete</button></a>
<a class="username">{{$element.CreatedByName}}</a>
{{if $.CurrentUser.Is_Admin}}<a href="/reply/edit/submit/{{$element.ID}}"><button class="username edit_item">Edit</button></a>
<a href="/reply/delete/submit/{{$element.ID}}"><button class="username delete_item">Delete</button></a>{{end}}
</div>{{ end }}
</div>
<div class="rowblock">

View File

@ -1,4 +1,5 @@
package main
import "html/template"
type Topic struct
{
@ -27,4 +28,5 @@ type TopicUser struct
CreatedByName string
Avatar string
Css template.CSS
}

BIN
staff-posts.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB