Added the InternalErrorJS and LocalErrorJS error handler functions.

Hid the themes which aren't under construction yet from the Theme Manager.
Fixed a bug in the BBCode parser where every post had ten spaces appended to them.
Added the get_reply() internal API function for plugins to make use of.
Added the bell to the theme.
Fixed some bits of the Cosmo theme where the rowhead wasn't appearing.
Added a "Don't have an account?" link to the login page.

I began work on the alerts system, I took a little break, so it's a little further behind than you might expect, but it shouldn't take too long for me to finish it up.
I haven't finished the back-end portions of it yet, so there's not much to see yet!
This commit is contained in:
Azareal 2017-02-28 09:27:28 +00:00
parent 1d85224dbc
commit 9fce51a3d7
33 changed files with 453 additions and 73 deletions

View File

@ -112,13 +112,35 @@ CREATE TABLE `users_replies`(
CREATE TABLE `likes`(
`weight` tinyint DEFAULT 1 not null,
/*`type` tinyint not null, /* Regular Post = 1, Big Post = 2, Mega Post = 3, etc.*/
/*`type` tinyint not null, /* Regular Post: 1, Big Post: 2, Mega Post: 3, etc.*/
`targetItem` int not null,
`targetType` varchar(50) DEFAULT 'replies' not null,
`sentBy` int not null,
`recalc` tinyint DEFAULT 0 not null
);
CREATE TABLE `activity_stream_matches`(
`watcher` int not null,
`asid` int not null
);
CREATE TABLE `activity_stream`(
`asid` int not null AUTO_INCREMENT,
`actor` int not null, /* the one doing the act */
`targetUser` int not null, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */
`event` varchar(50) not null, /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */
`elementType` varchar(50) not null, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
`elementID` int not null, /* the ID of the element being acted upon */
primary key(`asid`)
);
CREATE TABLE `activity_subscriptions`(
`user` int not null,
`targetID` int not null,
`targetType` varchar(50) not null,
`level` tinyint DEFAULT 0 not null /* 0: Mentions (aka the global default for any post), 1: Replies, 2: Everyone*/
);
CREATE TABLE `settings`(
`name` varchar(200) not null,
`content` varchar(250) not null,

View File

@ -41,6 +41,12 @@ func InternalErrorJSQ(err error, w http.ResponseWriter, r *http.Request, is_js s
log.Fatal(err)
}
func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
w.Write([]byte(`{'errmsg': 'A problem has occured in the system.'}`))
log.Fatal(err)
}
func PreError(errmsg string, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
user := User{ID:0,Group:6,Perms:GuestPerms,}
@ -91,6 +97,11 @@ func LocalErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, user U
}
}
func LocalErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
w.Write([]byte(`{'errmsg': '` + errmsg + `'}`))
}
func NoPermissions(w http.ResponseWriter, r *http.Request, user User) {
w.WriteHeader(403)
pi := Page{"Local Error",user,nList,tList,"You don't have permission to do that."}

View File

@ -10,12 +10,17 @@ import "net/http"
import "net/http/httptest"
import "io/ioutil"
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
//import _ "github.com/go-sql-driver/mysql"
//import "github.com/erikstmartin/go-testdb"
//import "github.com/husobee/vestigo"
import "runtime/pprof"
var db_test *sql.DB
var db_prod *sql.DB
var gloinited bool = false
func gloinit() {
var err error
debug = false
nogrouplog = true
@ -24,10 +29,15 @@ func gloinit() {
//log.SetOutput(discard)
init_themes()
var err error
init_database(err)
db_prod = db
//db_test, err = sql.Open("testdb","")
//if err != nil {
// log.Fatal(err)
//}
init_templates()
db.SetMaxOpenConns(64)
db_prod.SetMaxOpenConns(64)
err = init_errors()
if err != nil {
log.Fatal(err)
@ -1368,6 +1378,35 @@ func TestForumGuestRoute(t *testing.T) {
fmt.Println("No problems found in the forum-guest route!")
}
/*func TestAlerts(t *testing.T) {
if !gloinited {
gloinit()
}
if !plugins_inited {
init_plugins()
}
db = db_test
alert_w := httptest.NewRecorder()
alert_req := httptest.NewRequest("get","/api/?action=get&module=alerts",bytes.NewReader(nil))
alert_handler := http.HandlerFunc(route_api)
//testdb.StubQuery()
testdb.SetQueryFunc(func(query string) (result sql.Rows, err error) {
cols := []string{"asid","actor","targetUser","event","elementType","elementID"}
rows := `1,1,0,like,post,5
1,1,0,friend_invite,user,2`
return testdb.RowsFromCSVString(cols,rows), nil
})
alert_handler.ServeHTTP(alert_w,alert_req)
fmt.Println(alert_w.Body)
if alert_w.Code != 200 {
panic("HTTP Error!")
}
fmt.Println("No problems found in the alert handler!")
db = db_prod
}*/
/*func TestRoute(t *testing.T) {
}*/

View File

@ -184,8 +184,7 @@ func main(){
init_plugins()
router := NewRouter()
router.HandleFunc("/static/", route_static) // In a directory to stop it clashing with the other paths
router.HandleFunc("/static/", route_static)
fs_u := http.FileServer(http.Dir("./uploads"))
router.Handle("/uploads/", http.StripPrefix("/uploads/",fs_u))
@ -260,6 +259,7 @@ func main(){
router.HandleFunc("/panel/users/edit/", route_panel_users_edit)
router.HandleFunc("/panel/users/edit/submit/", route_panel_users_edit_submit)
router.HandleFunc("/panel/groups/", route_panel_groups)
router.HandleFunc("/api/", route_api)
//router.HandleFunc("/exit/", route_exit)
router.HandleFunc("/", default_route)

View File

@ -1412,6 +1412,9 @@ func route_panel_themes(w http.ResponseWriter, r *http.Request){
var themeList []interface{}
for _, theme := range themes {
if theme.HideFromThemes {
continue
}
themeList = append(themeList,theme)
}

View File

@ -16,8 +16,10 @@ var get_full_user_stmt *sql.Stmt
var get_topic_list_stmt *sql.Stmt
var get_topic_user_stmt *sql.Stmt
var get_topic_stmt *sql.Stmt
var get_topic_by_reply_stmt *sql.Stmt
var get_topic_replies_stmt *sql.Stmt
var get_topic_replies_offset_stmt *sql.Stmt
var get_post_stmt *sql.Stmt
var get_forum_topics_stmt *sql.Stmt
var get_forum_topics_offset_stmt *sql.Stmt
var create_topic_stmt *sql.Stmt
@ -37,6 +39,7 @@ var delete_reply_stmt *sql.Stmt
var delete_topic_stmt *sql.Stmt
var stick_topic_stmt *sql.Stmt
var unstick_topic_stmt *sql.Stmt
var get_activity_feed_by_watcher_stmt *sql.Stmt
var update_last_ip_stmt *sql.Stmt
var login_stmt *sql.Stmt
var update_session_stmt *sql.Stmt
@ -133,6 +136,12 @@ func init_database(err error) {
log.Fatal(err)
}
log.Print("Preparing get_topic_by_reply statement.")
get_topic_by_reply_stmt, err = db.Prepare("select topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount from replies left join topics on replies.tid = topics.tid where rid = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing get_topic_replies statement.")
get_topic_replies_stmt, err = db.Prepare("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?")
if err != nil {
@ -145,6 +154,12 @@ func init_database(err error) {
log.Fatal(err)
}
log.Print("Preparing get_post statement.")
get_post_stmt, err = db.Prepare("select content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount from replies where rid = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing get_forum_topics statement.")
get_forum_topics_stmt, err = db.Prepare("select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid where topics.parentID = ? order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc")
if err != nil {
@ -259,6 +274,12 @@ func init_database(err error) {
log.Fatal(err)
}
log.Print("Preparing get_activity_feed_by_watcher statement.")
get_activity_feed_by_watcher_stmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM `activity_stream_matches` LEFT JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid WHERE `watcher` = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing update_last_ip statement.")
update_last_ip_stmt, err = db.Prepare("UPDATE users SET last_ip = ? WHERE uid = ?")
if err != nil {
@ -266,7 +287,7 @@ func init_database(err error) {
}
log.Print("Preparing login statement.")
login_stmt, err = db.Prepare("SELECT `uid`, `name`, `password`, `salt` FROM `users` WHERE `name` = ?")
login_stmt, err = db.Prepare("SELECT `uid`,`name`,`password`,`salt` FROM `users` WHERE `name` = ?")
if err != nil {
log.Fatal(err)
}
@ -290,7 +311,7 @@ func init_database(err error) {
}
log.Print("Preparing get_password statement.")
get_password_stmt, err = db.Prepare("SELECT `password`, `salt` FROM `users` WHERE `uid` = ?")
get_password_stmt, err = db.Prepare("SELECT `password`,`salt` FROM `users` WHERE `uid` = ?")
if err != nil {
log.Fatal(err)
}
@ -444,19 +465,19 @@ func init_database(err error) {
}
log.Print("Preparing add_forum_perms_to_forum_admins statement.")
add_forum_perms_to_forum_admins_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset, ? AS permissions FROM users_groups WHERE is_admin = 1")
add_forum_perms_to_forum_admins_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 1")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing add_forum_perms_to_forum_staff statement.")
add_forum_perms_to_forum_staff_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset, ? AS permissions FROM users_groups WHERE is_admin = 0 AND is_mod = 1")
add_forum_perms_to_forum_staff_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 0 AND is_mod = 1")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing add_forum_perms_to_forum_members statement.")
add_forum_perms_to_forum_members_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset, ? AS permissions FROM users_groups WHERE is_admin = 0 AND is_mod = 0 AND is_banned = 0")
add_forum_perms_to_forum_members_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 0 AND is_mod = 0 AND is_banned = 0")
if err != nil {
log.Fatal(err)
}

View File

@ -364,21 +364,20 @@ func bbcode_full_parse(data interface{}) interface{} {
}
}
}
//fmt.Println(outbytes)
//fmt.Println(string(outbytes))
if lastTag != i {
outbytes = append(outbytes, msgbytes[lastTag:len(msgbytes) - 10]...)
outbytes = append(outbytes, msgbytes[lastTag:]...)
}
if len(outbytes) != 0 {
return string(outbytes)
return string(outbytes[0:len(msgbytes) - 10])
}
msg = string(msgbytes)
msg = string(msgbytes[0:len(msgbytes) - 10])
//msg = bbcode_url.ReplaceAllString(msg,"<a href=\"$1$2//$3\" rel=\"nofollow\">$1$2//$3</i>")
msg = bbcode_url_label.ReplaceAllString(msg,"<a href=\"$1$2//$3\" rel=\"nofollow\">$4</i>")
// Convert [code] into class="codequotes"
} else {
msg = string(msgbytes)
msg = string(msgbytes[0:len(msgbytes) - 10])
}
return msg
}

View File

@ -2,7 +2,7 @@
package main
import "html/template"
type Reply struct
type Reply struct /* Should probably rename this to ReplyUser and rename ReplyShort to Reply */
{
ID int
ParentID int
@ -27,3 +27,24 @@ type Reply struct
LikeCount int
}
type ReplyShort struct
{
ID int
ParentID int
Content string
CreatedBy int
Group int
CreatedAt string
LastEdit int
LastEditBy int
ContentLines int
IpAddress string
Liked bool
LikeCount int
}
func get_reply(id int) (*ReplyShort, error) {
reply := ReplyShort{ID:id}
err := get_post_stmt.QueryRow(id).Scan(&reply.Content, &reply.CreatedBy, &reply.CreatedAt, &reply.LastEdit, &reply.LastEditBy, &reply.IpAddress, &reply.LikeCount)
return &reply, err
}

184
routes.go
View File

@ -1605,3 +1605,187 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w,&cookie)
http.Redirect(w,r, "/", http.StatusSeeOther)
}
func route_api(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
is_js := r.PostFormValue("js")
if is_js == "" {
is_js = "0"
}
if err != nil {
PreErrorJSQ("Bad Form",w,r,is_js)
return
}
user, ok := SimpleSessionCheck(w,r)
if !ok {
return
}
action := r.FormValue("action")
if action != "get" && action != "set" {
PreErrorJSQ("Invalid Action",w,r,is_js)
return
}
module := r.FormValue("module")
switch(module) {
case "alerts": // A feed of events tailored for a specific user
w.Header().Set("Content-Type","application/json")
if !user.Loggedin {
w.Write([]byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`))
return
}
var msglist string
var asid int
var actor_id int
var targetUser_id int
var event string
var elementType string
var elementID int
//---
var targetUser *User
rows, err := get_activity_feed_by_watcher_stmt.Query(user.ID)
if err != nil {
InternalErrorJS(err,w,r)
return
}
for rows.Next() {
err = rows.Scan(&asid,&actor_id,&targetUser_id,&event,&elementType,&elementID)
if err != nil {
InternalErrorJS(err,w,r)
return
}
actor, err := users.CascadeGet(actor_id)
if err != nil {
LocalErrorJS("Unable to find the actor",w,r)
return
}
/*if elementType != "forum" {
targetUser, err = users.CascadeGet(targetUser_id)
if err != nil {
LocalErrorJS("Unable to find the target user",w,r)
return
}
}*/
if event == "friend_invite" {
msglist += `{"msg":"You received a friend invite from {0}","sub":["` + actor.Name + `"],"path":"/user/`+strconv.Itoa(actor.ID)+`"},`
continue
}
/*
"You received a friend invite from {user}"
"{x}{mentioned you on}{user}{'s profile}"
"{x}{mentioned you in}{topic}"
"{x}{likes}{you}"
"{x}{liked}{your topic}{topic}"
"{x}{liked}{your post on}{user}{'s profile}" todo
"{x}{liked}{your post in}{topic}"
"{x}{replied to}{your post in}{topic}" todo
"{x}{created a new topic}{topic}"
*/
var act string
var post_act string
var url string
var area string
var start_frag string
var end_frag string
switch(elementType) {
case "forum":
if event == "reply" {
act = "created a new topic"
topic, err := topics.CascadeGet(elementID)
if err != nil {
LocalErrorJS("Unable to find the linked topic",w,r)
return
}
url = build_topic_url(elementID)
area = topic.Title
// Store the forum ID in the targetUser column instead of making a new one? o.O
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now..
} else {
act = "did something in a forum"
}
case "topic":
topic, err := topics.CascadeGet(elementID)
if err != nil {
LocalErrorJS("Unable to find the linked topic",w,r)
return
}
url = build_topic_url(elementID)
area = topic.Title
if targetUser_id == user.ID {
post_act = " your topic"
}
case "user":
targetUser, err = users.CascadeGet(elementID)
if err != nil {
LocalErrorJS("Unable to find the target user",w,r)
return
}
area = targetUser.Name
end_frag = "'s profile"
url = build_profile_url(elementID)
case "post":
topic, err := get_topic_by_reply(elementID)
if err != nil {
LocalErrorJS("Unable to find the target reply or parent topic",w,r)
return
}
url = build_topic_url(elementID)
area = topic.Title
if targetUser_id == user.ID {
post_act = " your post in"
}
default:
LocalErrorJS("Invalid elementType",w,r)
}
if event == "like" {
if elementType == "user" {
act = "likes"
end_frag = ""
if targetUser.ID == user.ID {
area = "you"
}
} else {
act = "liked"
}
} else if event == "mention" {
if elementType == "user" {
act = "mentioned you on"
} else {
act = "mentioned you in"
post_act = ""
}
}
msglist += `{"msg":"{0} ` + start_frag + act + post_act + ` {1}` + end_frag + `","sub":["` + actor.Name + `","` + area + `"],"path":"` + url + `"},`
}
err = rows.Err()
if err != nil {
InternalErrorJS(err,w,r)
return
}
rows.Close()
if len(msglist) != 0 {
msglist = msglist[0:len(msglist)-1]
}
w.Write([]byte(`{"msgs":[`+msglist+`]}`))
//case "topics":
//case "forums":
//case "users":
//case "pages":
default:
PreErrorJSQ("Invalid Module",w,r,is_js)
}
}

View File

@ -32,6 +32,10 @@ w.Write(menu_5)
w.Write(menu_6)
}
w.Write(menu_7)
if !tmpl_forum_vars.CurrentUser.Loggedin {
w.Write(menu_8)
}
w.Write(menu_9)
w.Write(header_3)
if len(tmpl_forum_vars.NoticeList) != 0 {
for _, item := range tmpl_forum_vars.NoticeList {

View File

@ -32,6 +32,10 @@ w.Write(menu_5)
w.Write(menu_6)
}
w.Write(menu_7)
if !tmpl_forums_vars.CurrentUser.Loggedin {
w.Write(menu_8)
}
w.Write(menu_9)
w.Write(header_3)
if len(tmpl_forums_vars.NoticeList) != 0 {
for _, item := range tmpl_forums_vars.NoticeList {

View File

@ -40,6 +40,9 @@ var menu_6 []byte = []byte(`
<li class="menu_left menu_login"><a href="/accounts/login/">Login</a></li>
`)
var menu_7 []byte = []byte(`
<li class="menu_right menu_alerts">🔔<div class="alert_counter">`)
var menu_8 []byte = []byte(`1`)
var menu_9 []byte = []byte(`</div></li>
</ul>
</div>
</div>

View File

@ -32,6 +32,10 @@ w.Write(menu_5)
w.Write(menu_6)
}
w.Write(menu_7)
if !tmpl_profile_vars.CurrentUser.Loggedin {
w.Write(menu_8)
}
w.Write(menu_9)
w.Write(header_3)
if len(tmpl_profile_vars.NoticeList) != 0 {
for _, item := range tmpl_profile_vars.NoticeList {

View File

@ -32,6 +32,10 @@ w.Write(menu_5)
w.Write(menu_6)
}
w.Write(menu_7)
if !tmpl_topic_vars.CurrentUser.Loggedin {
w.Write(menu_8)
}
w.Write(menu_9)
w.Write(header_3)
if len(tmpl_topic_vars.NoticeList) != 0 {
for _, item := range tmpl_topic_vars.NoticeList {

View File

@ -32,6 +32,10 @@ w.Write(menu_5)
w.Write(menu_6)
}
w.Write(menu_7)
if !tmpl_topic_alt_vars.CurrentUser.Loggedin {
w.Write(menu_8)
}
w.Write(menu_9)
w.Write(header_3)
if len(tmpl_topic_alt_vars.NoticeList) != 0 {
for _, item := range tmpl_topic_alt_vars.NoticeList {

View File

@ -1,7 +1,7 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "strconv"
import "io"
import "strconv"
func init() {
template_topics_handle = template_topics
@ -32,6 +32,10 @@ w.Write(menu_5)
w.Write(menu_6)
}
w.Write(menu_7)
if !tmpl_topics_vars.CurrentUser.Loggedin {
w.Write(menu_8)
}
w.Write(menu_9)
w.Write(header_3)
if len(tmpl_topics_vars.NoticeList) != 0 {
for _, item := range tmpl_topics_vars.NoticeList {

View File

@ -1,5 +1,5 @@
<div class="colblock_left">
<div class="rowitem"><a>My Account</a></div>
<div class="rowitem rowhead"><a>My Account</a></div>
<div class="rowitem passive"><a href="/user/edit/avatar/">Change Avatar</a></div>
<div class="rowitem passive"><a href="/user/edit/username/">Change Username</a></div>
<div class="rowitem passive"><a href="/user/edit/critical/">Change Password</a></div>

View File

@ -1,7 +1,7 @@
{{template "header.html" . }}
{{template "account-menu.html" . }}
<div class="colblock_right">
<div class="rowitem"><a>Edit Avatar</a></div>
<div class="rowitem rowhead"><a>Edit Avatar</a></div>
</div>
{{ if .CurrentUser.Avatar }}
<div class="colblock_right">

View File

@ -1,7 +1,7 @@
{{template "header.html" . }}
{{template "account-menu.html" . }}
<div class="colblock_right">
<div class="rowitem"><a>Emails</a></div>
<div class="rowitem rowhead"><a>Emails</a></div>
</div>
<div class="colblock_right">
{{range .ItemList}}

View File

@ -1,7 +1,7 @@
{{template "header.html" . }}
{{template "account-menu.html" . }}
<div class="colblock_right">
<div class="rowitem"><a>Edit Username</a></div>
<div class="rowitem rowhead"><a>Edit Username</a></div>
</div>
<div class="colblock_right">
<form action="/user/edit/username/submit/" method="post">

View File

@ -1,7 +1,7 @@
{{template "header.html" . }}
{{template "account-menu.html" . }}
<div class="colblock_right">
<div class="rowitem"><a>Edit Password</a></div>
<div class="rowitem rowhead"><a>Edit Password</a></div>
</div>
<div class="colblock_right">
<form action="/user/edit/critical/submit/" method="post">

View File

@ -1,6 +1,6 @@
{{template "header.html" . }}
<div class="rowblock">
<div class="rowitem"><a>Create Topic</a></div>
<div class="rowitem rowhead"><a>Create Topic</a></div>
</div>
<div class="rowblock">
<form action="/topic/create/submit/" method="post">

View File

@ -1,6 +1,6 @@
{{template "header.html" . }}
<div class="rowblock">
<div class="rowitem"><a>Login</a></div>
<div class="rowitem rowhead"><a>Login</a></div>
</div>
<div class="rowblock">
<form action="/accounts/login/submit/" method="post">
@ -13,7 +13,8 @@
<div class="formitem"><input name="password" type="password" autocomplete="current-password" placeholder="*****" /></div>
</div>
<div class="formrow">
<div class="formitem"><button name="login-button" class="formbutton">Login</div></div>
<div class="formitem"><button name="login-button" class="formbutton">Login</div>
<div class="formitem" style="color: #505050; font-size: 12px; font-weight: normal; float: right;">Don't have an account?</div>
</div>
</form>
</div>

View File

@ -9,12 +9,13 @@
{{if .CurrentUser.Loggedin}}
<li class="menu_left menu_account"><a href="/user/edit/critical/">Account</a></li>
<li class="menu_left menu_profile"><a href="/user/{{.CurrentUser.ID}}">Profile</a></li>
{{ if .CurrentUser.Is_Super_Mod}}<li class="menu_left menu_account"><a href="/panel/">Panel</a></li>{{end}}
{{if .CurrentUser.Is_Super_Mod}}<li class="menu_left menu_account"><a href="/panel/">Panel</a></li>{{end}}
<li class="menu_left menu_logout"><a href="/accounts/logout?session={{.CurrentUser.Session}}">Logout</a></li>
{{else}}
<li class="menu_left menu_register"><a href="/accounts/create/">Register</a></li>
<li class="menu_left menu_login"><a href="/accounts/login/">Login</a></li>
{{end}}
<li class="menu_right menu_alerts">🔔︎<div class="alert_counter">{{if not .CurrentUser.Loggedin}}1{{end}}</div></li>
</ul>
</div>
</div>

View File

@ -1,6 +1,6 @@
{{template "header.html" . }}
<div class="rowblock">
<div class="rowitem"><a>Create Account</a></div>
<div class="rowitem rowhead"><a>Create Account</a></div>
</div>
<div class="rowblock">
<form action="/accounts/create/submit/" method="post">

View File

@ -26,6 +26,7 @@ type Theme struct
FullImage string
MobileFriendly bool
Disabled bool
HideFromThemes bool
Tag string
Settings map[string]ThemeSetting
Templates []TemplateMapping
@ -141,21 +142,7 @@ func map_theme_templates(theme Theme) {
case *func(TopicPage,io.Writer):
//overriden_templates[themeTmpl.Name] = d_tmpl_ptr
overriden_templates[themeTmpl.Name] = true
//log.Print("Topic Handle")
//fmt.Println(template_topic_handle)
//log.Print("Before")
//fmt.Println(d_tmpl_ptr)
//fmt.Println(*d_tmpl_ptr)
//log.Print("Source")
//fmt.Println(s_tmpl_ptr)
//fmt.Println(*s_tmpl_ptr)
*d_tmpl_ptr = *s_tmpl_ptr
//log.Print("After")
//fmt.Println(d_tmpl_ptr)
//fmt.Println(*d_tmpl_ptr)
//log.Print("Source")
//fmt.Println(s_tmpl_ptr)
//fmt.Println(*s_tmpl_ptr)
default:
log.Fatal("The source and destination templates are incompatible")
}
@ -236,19 +223,7 @@ func reset_template_overrides() {
case func(TopicPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(TopicPage,io.Writer):
//log.Print("Topic Handle")
//fmt.Println(template_topic_handle)
//log.Print("Before")
//fmt.Println(d_ptr)
//fmt.Println(*d_ptr)
//log.Print("Origin")
//fmt.Println(o_ptr)
*d_ptr = o_ptr
//log.Print("After")
//fmt.Println(d_ptr)
//fmt.Println(*d_ptr)
//log.Print("Origin")
//fmt.Println(o_ptr)
default:
log.Fatal("The origin and destination templates are incompatible")
}

View File

@ -4,6 +4,7 @@
"Version": "Coming Soon",
"Creator": "Azareal",
"Disabled": true,
"HideFromThemes": true,
"Tag": "WIP",
"Templates": [
{

View File

@ -50,10 +50,7 @@ li
padding-left: 1px;
padding-right: 1px;
border: 1px solid #7a7a7a;
border-top: none;
border-bottom: none;
border-left: none;
border-right: 1px solid #7a7a7a;
font-weight: normal;
color: white;
}
@ -63,16 +60,10 @@ li a
color: white;
text-decoration: none;
}
li:hover a, li a:hover, li a:link, li a:visited
{
color: white;
text-decoration: none;
}
li:hover
{
background: rgba(10,10,10,0.5);
font-weight: normal;
color: white;
}
.menu_right
@ -85,17 +76,28 @@ li:hover
padding-right: 10px;
height: 38px;
text-align: center;
border-left: 1px solid #7a7a7a;
}
.menu_right:hover
{
border: #282828 1px solid;
background: #282828;
padding-left: 10px;
padding-right: 10px;
height: 38px;
.menu_alerts .alert_counter {
position: relative;
font-size: 9px;
top: -24px;
background-color: rgb(140,0,0);
color: white;
padding: 3px;
width: 14px;
left: 10px;
line-height: 8px;
border-radius: 20px;
padding-top: 2.5px;
height: 14px;
opacity: 0.8;
text-align: center;
}
.menu_alerts .alert_counter:empty {
display: none;
}
#footer
{

8
themes/shadow/theme.json Normal file
View File

@ -0,0 +1,8 @@
{
"Name": "shadow",
"FriendlyName": "Shadow",
"Version": "0.0.1",
"Creator": "Azareal",
"Disabled": true,
"HideFromThemes": true
}

View File

@ -57,6 +57,31 @@ li a
padding-right: 10px;
}
.menu_alerts {
padding-left: 7px;
padding-top: 2px;
color: rgb(80,80,80);
}
.menu_alerts .alert_counter {
position:relative;
font-size: 9px;
top: -24px;
background-color: rgb(140,0,0);
color: white;
padding: 3px;
width: 14px;
left: 10px;
line-height: 8px;
border-radius: 20px;
padding-top: 2.5px;
height: 14px;
opacity: 0.8;
text-align: center;
}
.menu_alerts .alert_counter:empty {
display: none;
}
.container
{
width: 90%;

View File

@ -55,6 +55,31 @@ li a
padding-right: 10px;
}
.menu_alerts {
padding-left: 7px;
padding-top: 2px;
color: rgb(80,80,80);
}
.menu_alerts .alert_counter {
position:relative;
font-size: 9px;
top: -24px;
background-color: rgb(140,0,0);
color: white;
padding: 3px;
width: 14px;
left: 10px;
line-height: 8px;
border-radius: 20px;
padding-top: 2.5px;
height: 14px;
opacity: 0.8;
text-align: center;
}
.menu_alerts .alert_counter:empty {
display: none;
}
.container
{
width: 90%;

View File

@ -1,6 +1,7 @@
package main
//import "fmt"
import "sync"
import "strconv"
import "html/template"
import "database/sql"
@ -341,4 +342,14 @@ func copy_topic_to_topicuser(topic *Topic, user *User) (tu TopicUser) {
tu.PostCount = topic.PostCount
tu.LikeCount = topic.LikeCount
return tu
}
}
func get_topic_by_reply(rid int) (*Topic, error) {
topic := Topic{ID:0}
err := get_topic_by_reply_stmt.QueryRow(rid).Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
return &topic, err
}
func build_topic_url(tid int) string {
return "/topic/" + strconv.Itoa(tid)
}

View File

@ -525,4 +525,8 @@ func init_user_perms(user *User) {
if user.Is_Banned && user.Is_Super_Mod {
user.Is_Banned = false
}
}
}
func build_profile_url(uid int) string {
return "/user/" + strconv.Itoa(uid)
}