Fixed a bug where it would use the wrong templates for Tempra Simple, Tempra Cursive, and Shadow

Likes are now done over AJAX.
Posts you have liked are now visually differentiated from those which you have not.
Added support for OR to the where parser.
|| and && now get translated to OR and AND in the where parser.
Added support for ( and ) in the where parser.
Added an adapter and builder method for getting the database version.
Multiple wheres can now be chained with the micro and accumulator builders.
Added the In method to the accumulator select builder.
Added the GetConn method to the builder.
/uploads/ files should now get cached properly.
Added more tooltips for topic titles and usernames.

Fixed a bug in the runners where old stale templates would be served.
Fixed a bug where liking topics didn't work.
Began moving the database initialisation logic out of {adapter}.go and into querygen.
Tweaked the alert direction to show the newest alerts rather than the oldest.
Tweaked the WS JS to have it handle messages more efficiently.
Partially fixed an issue where inline edited posts would lack newlines until the page is refreshed.
Used arrow functions in a few places in global.js to save a few characters.

Schema:
Added the liked, oldestItemLikedCreatedAt and lastLiked columns to the users table.
Added the createdAt column to the likes table.

MySQL Update Queries:
ALTER TABLE `users` ADD COLUMN `liked` INT NOT NULL DEFAULT '0' AFTER `topics`;
ALTER TABLE `users` ADD COLUMN `oldestItemLikedCreatedAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `liked`;
ALTER TABLE `users` ADD COLUMN `lastLiked` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `oldestItemLikedCreatedAt`;
ALTER TABLE `likes` ADD COLUMN `createdAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `sentBy`;
delete from `likes`;
delete from `activity_stream` where `event` = 'like';
delete from `activity_stream_matches` where `asid` not in(select `asid` from `activity_stream`);
update `topics` set `likeCount` = 0;
update `replies` set `likeCount` = 0;
This commit is contained in:
Azareal 2018-03-31 06:25:27 +01:00
parent 2b86a17478
commit 74e09efb63
59 changed files with 1789 additions and 1225 deletions

1
.gitignore vendored
View File

@ -20,3 +20,4 @@ logs/*
*.log *.log
.DS_Store .DS_Store
.vscode/launch.json .vscode/launch.json
config.go

View File

@ -44,6 +44,11 @@ We recommend changing the root password (that is the password for the user 'root
It's entirely possible that your host might already have MySQL, so you might be able to skip this step, particularly if it's a managed VPS or a shared host (contrary to popular belief, it is possible, although the ecosystem in this regard is extremely immature). Or they might have a quicker and easier method of setting up MySQL. It's entirely possible that your host might already have MySQL, so you might be able to skip this step, particularly if it's a managed VPS or a shared host (contrary to popular belief, it is possible, although the ecosystem in this regard is extremely immature). Or they might have a quicker and easier method of setting up MySQL.
# Downloading
At some point, we'll have releases which you can download, but right now, you'll have to use the `git clone` command as mentioned down in the advanced setup section to download a copy of Gosora.
# Installation Instructions # Installation Instructions
*Linux* *Linux*

View File

@ -1,2 +1,11 @@
echo Building the templates
gosora.exe -build-templates gosora.exe -build-templates
echo Rebuilding the executable
go build -o gosora.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
pause pause

View File

@ -104,6 +104,10 @@ func (reply *Reply) Like(uid int) (err error) {
return err return err
} }
_, err = replyStmts.addLikesToReply.Exec(1, reply.ID) _, err = replyStmts.addLikesToReply.Exec(1, reply.ID)
if err != nil {
return err
}
_, err = userStmts.incrementLiked.Exec(1, uid)
return err return err
} }

View File

@ -132,10 +132,10 @@ func CompileTemplates() error {
// Schemas to train the template compiler on what to expect // Schemas to train the template compiler on what to expect
// TODO: Add support for interface{}s // TODO: Add support for interface{}s
user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, BuildAvatar(62, ""), "", "", "", "", 0, 0, "0.0.0.0.0", 0} user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, BuildAvatar(62, ""), "", "", "", "", 0, 0, 0, "0.0.0.0.0", 0}
// TODO: Do a more accurate level calculation for this? // TODO: Do a more accurate level calculation for this?
user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, BuildAvatar(1, ""), "", "", "", "", 58, 1000, "127.0.0.1", 0} user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, BuildAvatar(1, ""), "", "", "", "", 58, 1000, 0, "127.0.0.1", 0}
user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, BuildAvatar(2, ""), "", "", "", "", 42, 900, "::1", 0} user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, BuildAvatar(2, ""), "", "", "", "", 42, 900, 0, "::1", 0}
headerVars := &HeaderVars{ headerVars := &HeaderVars{
Site: Site, Site: Site,
Settings: SettingBox.Load().(SettingMap), Settings: SettingBox.Load().(SettingMap),
@ -373,6 +373,10 @@ func InitTemplates() error {
return GetTmplPhrase(phraseName) // TODO: Log non-existent phrases? return GetTmplPhrase(phraseName) // TODO: Log non-existent phrases?
} }
fmap["scope"] = func(name interface{}) interface{} {
return ""
}
// The interpreted templates... // The interpreted templates...
DebugLog("Loading the template files...") DebugLog("Loading the template files...")
Templates.Funcs(fmap) Templates.Funcs(fmap)

View File

@ -90,6 +90,7 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
"divide": true, "divide": true,
"dock": true, "dock": true,
"lang": true, "lang": true,
"scope": true,
} }
c.importMap = map[string]string{ c.importMap = map[string]string{
@ -687,6 +688,9 @@ ArgLoop:
out = "w.Write(phrases[" + strconv.Itoa(len(c.langIndexToName)-1) + "])\n" out = "w.Write(phrases[" + strconv.Itoa(len(c.langIndexToName)-1) + "])\n"
literal = true literal = true
break ArgLoop break ArgLoop
case "scope":
literal = true
break ArgLoop
default: default:
c.detail("Variable!") c.detail("Variable!")
if len(node.Args) > (pos + 1) { if len(node.Args) > (pos + 1) {

View File

@ -141,7 +141,7 @@ func init() {
stick: acc.Update("topics").Set("sticky = 1").Where("tid = ?").Prepare(), stick: acc.Update("topics").Set("sticky = 1").Where("tid = ?").Prepare(),
unstick: acc.Update("topics").Set("sticky = 0").Where("tid = ?").Prepare(), unstick: acc.Update("topics").Set("sticky = 0").Where("tid = ?").Prepare(),
hasLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy = ? and targetItem = ? and targetType = 'topics'").Prepare(), hasLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy = ? and targetItem = ? and targetType = 'topics'").Prepare(),
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy").Fields("?,?,?,?").Prepare(), createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy, createdAt").Fields("?,?,?,?,UTC_TIMESTAMP()").Prepare(),
addLikesToTopic: acc.Update("topics").Set("likeCount = likeCount + ?").Where("tid = ?").Prepare(), addLikesToTopic: acc.Update("topics").Set("likeCount = likeCount + ?").Where("tid = ?").Prepare(),
delete: acc.Delete("topics").Where("tid = ?").Prepare(), delete: acc.Delete("topics").Where("tid = ?").Prepare(),
edit: acc.Update("topics").Set("title = ?, content = ?, parsed_content = ?").Where("tid = ?").Prepare(), // TODO: Only run the content update bits on non-polls, does this matter? edit: acc.Update("topics").Set("title = ?, content = ?, parsed_content = ?").Where("tid = ?").Prepare(), // TODO: Only run the content update bits on non-polls, does this matter?
@ -205,20 +205,24 @@ func (topic *Topic) Unstick() (err error) {
// TODO: Test this // TODO: Test this
// TODO: Use a transaction for this // TODO: Use a transaction for this
func (topic *Topic) Like(score int, uid int) (err error) { func (topic *Topic) Like(score int, uid int) (err error) {
var tid int // Unused var disp int // Unused
err = topicStmts.hasLikedTopic.QueryRow(uid, topic.ID).Scan(&tid) err = topicStmts.hasLikedTopic.QueryRow(uid, topic.ID).Scan(&disp)
if err != nil && err != ErrNoRows { if err != nil && err != ErrNoRows {
return err return err
} else if err != ErrNoRows { } else if err != ErrNoRows {
return ErrAlreadyLiked return ErrAlreadyLiked
} }
_, err = topicStmts.createLike.Exec(score, tid, "topics", uid) _, err = topicStmts.createLike.Exec(score, topic.ID, "topics", uid)
if err != nil { if err != nil {
return err return err
} }
_, err = topicStmts.addLikesToTopic.Exec(1, tid) _, err = topicStmts.addLikesToTopic.Exec(1, topic.ID)
if err != nil {
return err
}
_, err = userStmts.incrementLiked.Exec(1, uid)
topic.cacheRemove() topic.cacheRemove()
return err return err
} }

View File

@ -54,6 +54,7 @@ type User struct {
Tag string Tag string
Level int Level int
Score int Score int
Liked int
LastIP string // ! This part of the UserCache data might fall out of date LastIP string // ! This part of the UserCache data might fall out of date
TempGroup int TempGroup int
} }
@ -71,6 +72,8 @@ type UserStmts struct {
incrementPosts *sql.Stmt incrementPosts *sql.Stmt
incrementBigposts *sql.Stmt incrementBigposts *sql.Stmt
incrementMegaposts *sql.Stmt incrementMegaposts *sql.Stmt
incrementLiked *sql.Stmt
decrementLiked *sql.Stmt
updateLastIP *sql.Stmt updateLastIP *sql.Stmt
setPassword *sql.Stmt setPassword *sql.Stmt
@ -92,6 +95,9 @@ func init() {
incrementPosts: acc.SimpleUpdate("users", "posts = posts + ?", "uid = ?"), incrementPosts: acc.SimpleUpdate("users", "posts = posts + ?", "uid = ?"),
incrementBigposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?", "uid = ?"), incrementBigposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?", "uid = ?"),
incrementMegaposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?", "uid = ?"), incrementMegaposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?", "uid = ?"),
incrementLiked: acc.SimpleUpdate("users", "liked = liked + ?, lastLiked = UTC_TIMESTAMP()", "uid = ?"),
decrementLiked: acc.SimpleUpdate("users", "liked = liked - ?", "uid = ?"),
//recalcLastLiked: acc...
updateLastIP: acc.SimpleUpdate("users", "last_ip = ?", "uid = ?"), updateLastIP: acc.SimpleUpdate("users", "last_ip = ?", "uid = ?"),
setPassword: acc.SimpleUpdate("users", "password = ?, salt = ?", "uid = ?"), setPassword: acc.SimpleUpdate("users", "password = ?, salt = ?", "uid = ?"),

View File

@ -50,7 +50,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
// TODO: Add an admin version of registerStmt with more flexibility? // TODO: Add an admin version of registerStmt with more flexibility?
return &DefaultUserStore{ return &DefaultUserStore{
cache: cache, cache: cache,
get: acc.SimpleSelect("users", "name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", ""), get: acc.SimpleSelect("users", "name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, liked, last_ip, temp_group", "uid = ?", "", ""),
exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""), exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""),
register: acc.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()"), // TODO: Implement user_count on users_groups here register: acc.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()"), // TODO: Implement user_count on users_groups here
usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""), usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""),
@ -65,7 +65,7 @@ func (mus *DefaultUserStore) DirtyGet(id int) *User {
} }
user = &User{ID: id, Loggedin: true} user = &User{ID: id, Loggedin: true}
err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup) err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup)
user.Init() user.Init()
if err == nil { if err == nil {
@ -83,7 +83,7 @@ func (mus *DefaultUserStore) Get(id int) (*User, error) {
} }
user = &User{ID: id, Loggedin: true} user = &User{ID: id, Loggedin: true}
err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup) err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup)
user.Init() user.Init()
if err == nil { if err == nil {
@ -127,14 +127,14 @@ func (mus *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err erro
qlist = qlist[0 : len(qlist)-1] qlist = qlist[0 : len(qlist)-1]
acc := qgen.Builder.Accumulator() acc := qgen.Builder.Accumulator()
rows, err := acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(uidList...) rows, err := acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, liked, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(uidList...)
if err != nil { if err != nil {
return list, err return list, err
} }
for rows.Next() { for rows.Next() {
user := &User{Loggedin: true} user := &User{Loggedin: true}
err := rows.Scan(&user.ID, &user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup) err := rows.Scan(&user.ID, &user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup)
if err != nil { if err != nil {
return list, err return list, err
} }
@ -175,7 +175,7 @@ func (mus *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err erro
func (mus *DefaultUserStore) BypassGet(id int) (*User, error) { func (mus *DefaultUserStore) BypassGet(id int) (*User, error) {
user := &User{ID: id, Loggedin: true} user := &User{ID: id, Loggedin: true}
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup) err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup)
user.Init() user.Init()
return user, err return user, err
@ -183,7 +183,7 @@ func (mus *DefaultUserStore) BypassGet(id int) (*User, error) {
func (mus *DefaultUserStore) Reload(id int) error { func (mus *DefaultUserStore) Reload(id int) error {
user := &User{ID: id, Loggedin: true} user := &User{ID: id, Loggedin: true}
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup) err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Liked, &user.LastIP, &user.TempGroup)
if err != nil { if err != nil {
mus.cache.Remove(id) mus.cache.Remove(id)
return err return err

View File

@ -10,7 +10,6 @@ import (
var stmts *Stmts var stmts *Stmts
var db *sql.DB var db *sql.DB
var dbVersion string
var dbAdapter string var dbAdapter string
// ErrNoRows is an alias of sql.ErrNoRows, just in case we end up with non-database/sql datastores // ErrNoRows is an alias of sql.ErrNoRows, just in case we end up with non-database/sql datastores

View File

@ -462,6 +462,32 @@ func init() {
counters.SetReverseOSMapEnum(reverseOSMapEnum) counters.SetReverseOSMapEnum(reverseOSMapEnum)
} }
type WriterIntercept struct {
w http.ResponseWriter
code int
}
func NewWriterIntercept(w http.ResponseWriter) *WriterIntercept {
return &WriterIntercept{w:w,code:200}
}
func (writ *WriterIntercept) Header() http.Header {
return writ.w.Header()
}
func (writ *WriterIntercept) Write(pieces []byte) (int, error) {
return writ.w.Write(pieces)
}
func (writ *WriterIntercept) WriteHeader(code int) {
writ.w.WriteHeader(code)
writ.code = code
}
func (writ *WriterIntercept) GetCode() int {
return writ.code
}
type GenRouter struct { type GenRouter struct {
UploadHandler func(http.ResponseWriter, *http.Request) UploadHandler func(http.ResponseWriter, *http.Request)
extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError
@ -471,7 +497,14 @@ type GenRouter struct {
func NewGenRouter(uploads http.Handler) *GenRouter { func NewGenRouter(uploads http.Handler) *GenRouter {
return &GenRouter{ return &GenRouter{
UploadHandler: http.StripPrefix("/uploads/",uploads).ServeHTTP, UploadHandler: func(w http.ResponseWriter, req *http.Request) {
writ := NewWriterIntercept(w)
http.StripPrefix("/uploads/",uploads).ServeHTTP(writ,req)
if writ.GetCode() == 200 {
w.Header().Set("Cache-Control", "max-age=" + strconv.Itoa(common.Day))
w.Header().Set("Vary", "Accept-Encoding")
}
},
extraRoutes: make(map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError), extraRoutes: make(map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError),
} }
} }
@ -1514,6 +1547,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
err = common.ParseForm(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
counters.RouteViewCounter.Bump(83) counters.RouteViewCounter.Bump(83)
err = routeLikeTopicSubmit(w,req,user,extraData) err = routeLikeTopicSubmit(w,req,user,extraData)
default: default:
@ -1588,6 +1627,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
err = common.ParseForm(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
counters.RouteViewCounter.Bump(88) counters.RouteViewCounter.Bump(88)
err = routeReplyLikeSubmit(w,req,user,extraData) err = routeReplyLikeSubmit(w,req,user,extraData)
} }

View File

@ -16,16 +16,17 @@ import (
// TODO: Refactor this // TODO: Refactor this
func routeLikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError { func routeLikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
isJs := (r.PostFormValue("isJs") == "1")
tid, err := strconv.Atoi(stid) tid, err := strconv.Atoi(stid)
if err != nil { if err != nil {
return common.PreError("Topic IDs can only ever be numbers.", w, r) return common.PreErrorJSQ("Topic IDs can only ever be numbers.", w, r, isJs)
} }
topic, err := common.Topics.Get(tid) topic, err := common.Topics.Get(tid)
if err == ErrNoRows { if err == ErrNoRows {
return common.PreError("The requested topic doesn't exist.", w, r) return common.PreErrorJSQ("The requested topic doesn't exist.", w, r, isJs)
} else if err != nil { } else if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
// TODO: Add hooks to make use of headerLite // TODO: Add hooks to make use of headerLite
@ -34,55 +35,62 @@ func routeLikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.Us
return ferr return ferr
} }
if !user.Perms.ViewTopic || !user.Perms.LikeItem { if !user.Perms.ViewTopic || !user.Perms.LikeItem {
return common.NoPermissions(w, r, user) return common.NoPermissionsJSQ(w, r, user, isJs)
} }
if topic.CreatedBy == user.ID { if topic.CreatedBy == user.ID {
return common.LocalError("You can't like your own topics", w, r, user) return common.LocalErrorJSQ("You can't like your own topics", w, r, user, isJs)
} }
_, err = common.Users.Get(topic.CreatedBy) _, err = common.Users.Get(topic.CreatedBy)
if err != nil && err == ErrNoRows { if err != nil && err == ErrNoRows {
return common.LocalError("The target user doesn't exist", w, r, user) return common.LocalErrorJSQ("The target user doesn't exist", w, r, user, isJs)
} else if err != nil { } else if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
score := 1 score := 1
err = topic.Like(score, user.ID) err = topic.Like(score, user.ID)
//log.Print("likeErr: ", err)
if err == common.ErrAlreadyLiked { if err == common.ErrAlreadyLiked {
return common.LocalError("You already liked this", w, r, user) return common.LocalErrorJSQ("You already liked this", w, r, user, isJs)
} else if err != nil { } else if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
err = common.AddActivityAndNotifyTarget(user.ID, topic.CreatedBy, "like", "topic", tid) err = common.AddActivityAndNotifyTarget(user.ID, topic.CreatedBy, "like", "topic", tid)
if err != nil { if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
} else {
_, _ = w.Write(successJSONBytes)
}
return nil return nil
} }
func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError { func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
isJs := (r.PostFormValue("isJs") == "1")
rid, err := strconv.Atoi(srid) rid, err := strconv.Atoi(srid)
if err != nil { if err != nil {
return common.PreError("The provided Reply ID is not a valid number.", w, r) return common.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
} }
reply, err := common.Rstore.Get(rid) reply, err := common.Rstore.Get(rid)
if err == ErrNoRows { if err == ErrNoRows {
return common.PreError("You can't like something which doesn't exist!", w, r) return common.PreErrorJSQ("You can't like something which doesn't exist!", w, r, isJs)
} else if err != nil { } else if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
var fid int var fid int
err = stmts.getTopicFID.QueryRow(reply.ParentID).Scan(&fid) err = stmts.getTopicFID.QueryRow(reply.ParentID).Scan(&fid)
if err == ErrNoRows { if err == ErrNoRows {
return common.PreError("The parent topic doesn't exist.", w, r) return common.PreErrorJSQ("The parent topic doesn't exist.", w, r, isJs)
} else if err != nil { } else if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
// TODO: Add hooks to make use of headerLite // TODO: Add hooks to make use of headerLite
@ -91,32 +99,36 @@ func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.Us
return ferr return ferr
} }
if !user.Perms.ViewTopic || !user.Perms.LikeItem { if !user.Perms.ViewTopic || !user.Perms.LikeItem {
return common.NoPermissions(w, r, user) return common.NoPermissionsJSQ(w, r, user, isJs)
} }
if reply.CreatedBy == user.ID { if reply.CreatedBy == user.ID {
return common.LocalError("You can't like your own replies", w, r, user) return common.LocalErrorJSQ("You can't like your own replies", w, r, user, isJs)
} }
_, err = common.Users.Get(reply.CreatedBy) _, err = common.Users.Get(reply.CreatedBy)
if err != nil && err != ErrNoRows { if err != nil && err != ErrNoRows {
return common.LocalError("The target user doesn't exist", w, r, user) return common.LocalErrorJSQ("The target user doesn't exist", w, r, user, isJs)
} else if err != nil { } else if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
err = reply.Like(user.ID) err = reply.Like(user.ID)
if err == common.ErrAlreadyLiked { if err == common.ErrAlreadyLiked {
return common.LocalError("You've already liked this!", w, r, user) return common.LocalErrorJSQ("You've already liked this!", w, r, user, isJs)
} else if err != nil { } else if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
err = common.AddActivityAndNotifyTarget(user.ID, reply.CreatedBy, "like", "post", rid) err = common.AddActivityAndNotifyTarget(user.ID, reply.CreatedBy, "like", "post", rid)
if err != nil { if err != nil {
return common.InternalError(err, w, r) return common.InternalErrorJSQ(err, w, r, isJs)
} }
if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(reply.ParentID), http.StatusSeeOther) http.Redirect(w, r, "/topic/"+strconv.Itoa(reply.ParentID), http.StatusSeeOther)
} else {
_, _ = w.Write(successJSONBytes)
}
return nil return nil
} }

View File

@ -47,8 +47,6 @@ func initMSSQL() (err error) {
return err return err
} }
// TODO: Fetch the database version
// Set the number of max open connections // Set the number of max open connections
db.SetMaxOpenConns(64) db.SetMaxOpenConns(64)
db.SetMaxIdleConns(32) db.SetMaxIdleConns(32)
@ -76,7 +74,7 @@ func initMSSQL() (err error) {
// TODO: Is there a less noisy way of doing this for tests? // TODO: Is there a less noisy way of doing this for tests?
log.Print("Preparing getActivityFeedByWatcher statement.") log.Print("Preparing getActivityFeedByWatcher statement.")
getActivityFeedByWatcherStmt, 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] INNER JOIN [activity_stream] ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE [watcher] = ? ORDER BY activity_stream.asid ASC OFFSET 0 ROWS FETCH NEXT 8 ROWS ONLY") getActivityFeedByWatcherStmt, 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] INNER JOIN [activity_stream] ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE [watcher] = ? ORDER BY activity_stream.asid DESC OFFSET 0 ROWS FETCH NEXT 8 ROWS ONLY")
if err != nil { if err != nil {
return err return err
} }

View File

@ -9,11 +9,8 @@
package main package main
import ( import (
"database/sql"
"log" "log"
//import "time"
"./common" "./common"
"./query_gen/lib" "./query_gen/lib"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
@ -27,28 +24,20 @@ func init() {
} }
func initMySQL() (err error) { func initMySQL() (err error) {
var _dbpassword string err = qgen.Builder.Init("mysql", map[string]string{
if common.DbConfig.Password != "" { "host": common.DbConfig.Host,
_dbpassword = ":" + common.DbConfig.Password "port": common.DbConfig.Port,
} "name": common.DbConfig.Dbname,
"username": common.DbConfig.Username,
// TODO: Move this bit to the query gen lib "password": common.DbConfig.Password,
// Open the database connection "collation": dbCollation,
db, err = sql.Open("mysql", common.DbConfig.Username+_dbpassword+"@tcp("+common.DbConfig.Host+":"+common.DbConfig.Port+")/"+common.DbConfig.Dbname+"?collation="+dbCollation+"&parseTime=true") })
if err != nil { if err != nil {
return err return err
} }
// Make sure that the connection is alive
err = db.Ping()
if err != nil {
return err
}
// Fetch the database version
db.QueryRow("SELECT VERSION()").Scan(&dbVersion)
// Set the number of max open connections // Set the number of max open connections
db = qgen.Builder.GetConn()
db.SetMaxOpenConns(64) db.SetMaxOpenConns(64)
db.SetMaxIdleConns(32) db.SetMaxIdleConns(32)
@ -70,7 +59,7 @@ func initMySQL() (err error) {
// TODO: Is there a less noisy way of doing this for tests? // TODO: Is there a less noisy way of doing this for tests?
log.Print("Preparing getActivityFeedByWatcher statement.") log.Print("Preparing getActivityFeedByWatcher statement.")
stmts.getActivityFeedByWatcher, 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` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid ASC LIMIT 8") stmts.getActivityFeedByWatcher, 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` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid DESC LIMIT 8")
if err != nil { if err != nil {
return err return err
} }

88
patcher/main.go Normal file
View File

@ -0,0 +1,88 @@
package main
import (
"bufio"
"fmt"
"os"
"runtime/debug"
"../query_gen/lib"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows
defer func() {
r := recover()
if r != nil {
fmt.Println(r)
debug.PrintStack()
pressAnyKey(scanner)
return
}
}()
err := patcher(scanner)
if err != nil {
fmt.Println(err)
}
}
func pressAnyKey(scanner *bufio.Scanner) {
fmt.Println("Please press enter to exit...")
for scanner.Scan() {
_ = scanner.Text()
return
}
}
func patcher(scanner *bufio.Scanner) error {
return nil
}
/*func eachUserQuick(handle func(int)) error {
stmt, err := qgen.Builder.Select("users").Orderby("uid desc").Limit(1).Prepare()
if err != nil {
return err
}
var topID int
err := stmt.QueryRow(topID)
if err != nil {
return err
}
for i := 1; i <= topID; i++ {
err = handle(i)
if err != nil {
return err
}
}
}*/
func eachUser(handle func(int)) error {
stmt, err := qgen.Builder.Select("users").Prepare()
if err != nil {
return err
}
rows, err := stmt.Query()
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var uid int
err := rows.Scan(&uid)
if err != nil {
return err
}
err = handle(uid)
if err != nil {
return err
}
}
return rows.Err()
}

View File

@ -14,10 +14,10 @@ import (
) )
// TODO: Add support for SSL for all database drivers, not just pgsql // TODO: Add support for SSL for all database drivers, not just pgsql
var db_sslmode = "disable" // verify-full var dbSslmode = "disable" // verify-full
func init() { func init() {
db_adapter = "pgsql" dbAdapter = "pgsql"
_initDatabase = initPgsql _initDatabase = initPgsql
} }
@ -28,7 +28,7 @@ func initPgsql() (err error) {
_dbpassword = " password='" + _escape_bit(common.DbConfig.Password) + "'" _dbpassword = " password='" + _escape_bit(common.DbConfig.Password) + "'"
} }
// TODO: Move this bit to the query gen lib // TODO: Move this bit to the query gen lib
db, err = sql.Open("postgres", "host='"+_escape_bit(common.DbConfig.Host)+"' port='"+_escape_bit(common.DbConfig.Port)+"' user='"+_escape_bit(common.DbConfig.Username)+"' dbname='"+_escape_bit(common.Config.Dbname)+"'"+_dbpassword+" sslmode='"+db_sslmode+"'") db, err = sql.Open("postgres", "host='"+_escape_bit(common.DbConfig.Host)+"' port='"+_escape_bit(common.DbConfig.Port)+"' user='"+_escape_bit(common.DbConfig.Username)+"' dbname='"+_escape_bit(common.Config.Dbname)+"'"+_dbpassword+" sslmode='"+dbSslmode+"'")
if err != nil { if err != nil {
return err return err
} }
@ -39,9 +39,6 @@ func initPgsql() (err error) {
return err return err
} }
// Fetch the database version
db.QueryRow("SELECT VERSION()").Scan(&db_version)
// Set the number of max open connections. How many do we need? Might need to do some tests. // Set the number of max open connections. How many do we need? Might need to do some tests.
db.SetMaxOpenConns(64) db.SetMaxOpenConns(64)
db.SetMaxIdleConns(32) db.SetMaxIdleConns(32)

View File

@ -153,7 +153,7 @@ function runWebSockets() {
console.log("The WebSockets connection was closed"); console.log("The WebSockets connection was closed");
} }
conn.onmessage = function(event) { conn.onmessage = function(event) {
//console.log("WS_Message:", event.data); //console.log("WSMessage:", event.data);
if(event.data[0] == "{") { if(event.data[0] == "{") {
try { try {
var data = JSON.parse(event.data); var data = JSON.parse(event.data);
@ -200,14 +200,15 @@ function runWebSockets() {
var messages = event.data.split('\r'); var messages = event.data.split('\r');
for(var i = 0; i < messages.length; i++) { for(var i = 0; i < messages.length; i++) {
//console.log("Message: ",messages[i]); let message = messages[i];
if(messages[i].startsWith("set ")) { //console.log("Message: ",message);
//msgblocks = messages[i].split(' ',3); if(message.startsWith("set ")) {
let msgblocks = SplitN(messages[i]," ",3); //msgblocks = message.split(' ',3);
let msgblocks = SplitN(message," ",3);
if(msgblocks.length < 3) continue; if(msgblocks.length < 3) continue;
document.querySelector(msgblocks[1]).innerHTML = msgblocks[2]; document.querySelector(msgblocks[1]).innerHTML = msgblocks[2];
} else if(messages[i].startsWith("set-class ")) { } else if(message.startsWith("set-class ")) {
let msgblocks = SplitN(messages[i]," ",3); let msgblocks = SplitN(message," ",3);
if(msgblocks.length < 3) continue; if(msgblocks.length < 3) continue;
document.querySelector(msgblocks[1]).className = msgblocks[2]; document.querySelector(msgblocks[1]).className = msgblocks[2];
} }
@ -220,8 +221,45 @@ $(document).ready(function(){
if(window["WebSocket"]) runWebSockets(); if(window["WebSocket"]) runWebSockets();
else conn = false; else conn = false;
$(".open_edit").click(function(event){ $(".add_like").click(function(event) {
//console.log("clicked on .open_edit"); event.preventDefault();
let likeButton = this;
let target = this.closest("a").getAttribute("href");
console.log("target: ", target);
likeButton.classList.remove("add_like");
likeButton.classList.add("remove_like");
let controls = likeButton.closest(".controls");
let hadLikes = controls.classList.contains("has_likes");
if(!hadLikes) controls.classList.add("has_likes");
let likeCountNode = controls.getElementsByClassName("like_count")[0];
console.log("likeCountNode",likeCountNode);
likeCountNode.innerHTML = parseInt(likeCountNode.innerHTML) + 1;
$.ajax({
url: target,
type: "POST",
dataType: "json",
data: { isJs: 1 },
error: ajaxError,
success: function (data, status, xhr) {
if("success" in data) {
if(data["success"] == "1") {
return;
}
}
// addNotice("Failed to add a like: {err}")
likeButton.classList.add("add_like");
likeButton.classList.remove("remove_like");
if(!hadLikes) controls.classList.remove("has_likes");
likeCountNode.innerHTML = parseInt(likeCountNode.innerHTML) - 1;
console.log("data", data);
console.log("status", status);
console.log("xhr", xhr);
}
});
});
$(".open_edit").click((event) => {
event.preventDefault(); event.preventDefault();
$(".hide_on_edit").hide(); $(".hide_on_edit").hide();
$(".show_on_edit").show(); $(".show_on_edit").show();
@ -229,17 +267,17 @@ $(document).ready(function(){
$(".topic_item .submit_edit").click(function(event){ $(".topic_item .submit_edit").click(function(event){
event.preventDefault(); event.preventDefault();
//console.log("clicked on .topic_item .submit_edit"); let topicNameInput = $(".topic_name_input").val();
$(".topic_name").html($(".topic_name_input").val()); $(".topic_name").html(topicNameInput);
$(".topic_content").html($(".topic_content_input").val()); $(".topic_name").attr(topicNameInput);
$(".topic_status_e:not(.open_edit)").html($(".topic_status_input").val()); let topicContentInput = $('.topic_content_input').val();
$(".topic_content").html(topicContentInput.replace(/(\n)+/g,"<br />"));
let topicStatusInput = $('.topic_status_input').val();
$(".topic_status_e:not(.open_edit)").html(topicStatusInput);
$(".hide_on_edit").show(); $(".hide_on_edit").show();
$(".show_on_edit").hide(); $(".show_on_edit").hide();
let topicNameInput = $('.topic_name_input').val();
let topicStatusInput = $('.topic_status_input').val();
let topicContentInput = $('.topic_content_input').val();
let formAction = this.form.getAttribute("action"); let formAction = this.form.getAttribute("action");
//console.log("New Topic Name: ", topicNameInput); //console.log("New Topic Name: ", topicNameInput);
//console.log("New Topic Status: ", topicStatusInput); //console.log("New Topic Status: ", topicStatusInput);
@ -284,8 +322,7 @@ $(document).ready(function(){
}); });
}); });
$(".edit_field").click(function(event) $(".edit_field").click(function(event) {
{
event.preventDefault(); event.preventDefault();
let blockParent = $(this).closest('.editable_parent'); let blockParent = $(this).closest('.editable_parent');
let block = blockParent.find('.editable_block').eq(0); let block = blockParent.find('.editable_block').eq(0);
@ -395,7 +432,7 @@ $(document).ready(function(){
} }
}); });
$(this).click(function() { $(this).click(() => {
$(".selectedAlert").removeClass("selectedAlert"); $(".selectedAlert").removeClass("selectedAlert");
$("#back").removeClass("alertActive"); $("#back").removeClass("alertActive");
}); });
@ -421,9 +458,7 @@ $(document).ready(function(){
document.getElementById("back").className += " alertActive" document.getElementById("back").className += " alertActive"
}); });
$("input,textarea,select,option").keyup(function(event){ $("input,textarea,select,option").keyup(event => event.stopPropagation())
event.stopPropagation();
})
$(".create_topic_link").click((event) => { $(".create_topic_link").click((event) => {
event.preventDefault(); event.preventDefault();

View File

@ -1,6 +1,9 @@
package qgen package qgen
import "database/sql" import (
"database/sql"
"strconv"
)
type accDeleteBuilder struct { type accDeleteBuilder struct {
table string table string
@ -10,7 +13,10 @@ type accDeleteBuilder struct {
} }
func (delete *accDeleteBuilder) Where(where string) *accDeleteBuilder { func (delete *accDeleteBuilder) Where(where string) *accDeleteBuilder {
delete.where = where if delete.where != "" {
delete.where += " AND "
}
delete.where += where
return delete return delete
} }
@ -32,7 +38,10 @@ func (update *accUpdateBuilder) Set(set string) *accUpdateBuilder {
} }
func (update *accUpdateBuilder) Where(where string) *accUpdateBuilder { func (update *accUpdateBuilder) Where(where string) *accUpdateBuilder {
update.where = where if update.where != "" {
update.where += " AND "
}
update.where += where
return update return update
} }
@ -59,6 +68,28 @@ func (selectItem *accSelectBuilder) Columns(columns string) *accSelectBuilder {
} }
func (selectItem *accSelectBuilder) Where(where string) *accSelectBuilder { func (selectItem *accSelectBuilder) Where(where string) *accSelectBuilder {
if selectItem.where != "" {
selectItem.where += " AND "
}
selectItem.where += where
return selectItem
}
// TODO: Don't implement the SQL at the accumulator level but the adapter level
func (selectItem *accSelectBuilder) In(column string, inList []int) *accSelectBuilder {
if len(inList) == 0 {
return selectItem
}
var where = column + " IN("
for _, item := range inList {
where += strconv.Itoa(item) + ","
}
where = where[:len(where)-1] + ")"
if selectItem.where != "" {
where += " AND " + selectItem.where
}
selectItem.where = where selectItem.where = where
return selectItem return selectItem
} }
@ -140,7 +171,10 @@ type accCountBuilder struct {
} }
func (count *accCountBuilder) Where(where string) *accCountBuilder { func (count *accCountBuilder) Where(where string) *accCountBuilder {
count.where = where if count.where != "" {
count.where += " AND "
}
count.where += where
return count return count
} }

View File

@ -19,10 +19,25 @@ func (build *builder) Accumulator() *Accumulator {
return &Accumulator{build.conn, build.adapter, nil} return &Accumulator{build.conn, build.adapter, nil}
} }
// TODO: Move this method out of builder?
func (build *builder) Init(adapter string, config map[string]string) error {
err := build.SetAdapter(adapter)
if err != nil {
return err
}
conn, err := build.adapter.BuildConn(config)
build.conn = conn
return err
}
func (build *builder) SetConn(conn *sql.DB) { func (build *builder) SetConn(conn *sql.DB) {
build.conn = conn build.conn = conn
} }
func (build *builder) GetConn() *sql.DB {
return build.conn
}
func (build *builder) SetAdapter(name string) error { func (build *builder) SetAdapter(name string) error {
adap, err := GetAdapter(name) adap, err := GetAdapter(name)
if err != nil { if err != nil {
@ -36,6 +51,11 @@ func (build *builder) GetAdapter() Adapter {
return build.adapter return build.adapter
} }
func (build *builder) DbVersion() (dbVersion string) {
build.conn.QueryRow(build.adapter.DbVersion()).Scan(&dbVersion)
return dbVersion
}
func (build *builder) Begin() (*sql.Tx, error) { func (build *builder) Begin() (*sql.Tx, error) {
return build.conn.Begin() return build.conn.Begin()
} }

View File

@ -44,7 +44,10 @@ func (delete *deletePrebuilder) Table(table string) *deletePrebuilder {
} }
func (delete *deletePrebuilder) Where(where string) *deletePrebuilder { func (delete *deletePrebuilder) Where(where string) *deletePrebuilder {
delete.where = where if delete.where != "" {
delete.where += " AND "
}
delete.where += where
return delete return delete
} }
@ -76,7 +79,10 @@ func (update *updatePrebuilder) Set(set string) *updatePrebuilder {
} }
func (update *updatePrebuilder) Where(where string) *updatePrebuilder { func (update *updatePrebuilder) Where(where string) *updatePrebuilder {
update.where = where if update.where != "" {
update.where += " AND "
}
update.where += where
return update return update
} }
@ -113,7 +119,10 @@ func (selectItem *selectPrebuilder) Columns(columns string) *selectPrebuilder {
} }
func (selectItem *selectPrebuilder) Where(where string) *selectPrebuilder { func (selectItem *selectPrebuilder) Where(where string) *selectPrebuilder {
selectItem.where = where if selectItem.where != "" {
selectItem.where += " AND "
}
selectItem.where += where
return selectItem return selectItem
} }
@ -205,7 +214,10 @@ func (count *countPrebuilder) Table(table string) *countPrebuilder {
} }
func (count *countPrebuilder) Where(where string) *countPrebuilder { func (count *countPrebuilder) Where(where string) *countPrebuilder {
count.where = where if count.where != "" {
count.where += " AND "
}
count.where += where
return count return count
} }

View File

@ -2,6 +2,7 @@
package qgen package qgen
import ( import (
"database/sql"
"errors" "errors"
"log" "log"
"strconv" "strconv"
@ -34,6 +35,15 @@ func (adapter *MssqlAdapter) GetStmts() map[string]DBStmt {
return adapter.Buffer return adapter.Buffer
} }
// TODO: Implement this
func (adapter *MssqlAdapter) BuildConn(config map[string]string) (*sql.DB, error) {
return nil, nil
}
func (adapter *MssqlAdapter) DbVersion() string {
return "SELECT CONCAT(SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'))"
}
// TODO: Convert any remaining stringy types to nvarchar // TODO: Convert any remaining stringy types to nvarchar
// We may need to change the CreateTable API to better suit Mssql and the other database drivers which are coming up // We may need to change the CreateTable API to better suit Mssql and the other database drivers which are coming up
func (adapter *MssqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) { func (adapter *MssqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
@ -249,7 +259,7 @@ func (adapter *MssqlAdapter) SimpleUpsert(name string, table string, columns str
switch token.Type { switch token.Type {
case "substitute": case "substitute":
querystr += " ?" querystr += " ?"
case "function", "operator", "number": case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
@ -314,7 +324,7 @@ func (adapter *MssqlAdapter) SimpleUpdate(name string, table string, set string,
switch token.Type { switch token.Type {
case "substitute": case "substitute":
querystr += " ?" querystr += " ?"
case "function", "operator", "number": case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
@ -339,7 +349,7 @@ func (adapter *MssqlAdapter) SimpleUpdate(name string, table string, set string,
for _, loc := range processWhere(where) { for _, loc := range processWhere(where) {
for _, token := range loc.Expr { for _, token := range loc.Expr {
switch token.Type { switch token.Type {
case "function", "operator", "number", "substitute": case "function", "operator", "number", "substitute", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
@ -381,7 +391,7 @@ func (adapter *MssqlAdapter) SimpleDelete(name string, table string, where strin
switch token.Type { switch token.Type {
case "substitute": case "substitute":
querystr += " ?" querystr += " ?"
case "function", "operator", "number": case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
@ -451,7 +461,7 @@ func (adapter *MssqlAdapter) SimpleSelect(name string, table string, columns str
case "substitute": case "substitute":
substituteCount++ substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount) querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number": case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
// MSSQL seems to convert the formats? so we'll compare it with a regular date. Do this with the other methods too? // MSSQL seems to convert the formats? so we'll compare it with a regular date. Do this with the other methods too?
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
@ -576,7 +586,7 @@ func (adapter *MssqlAdapter) SimpleLeftJoin(name string, table1 string, table2 s
case "substitute": case "substitute":
substituteCount++ substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount) querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number": case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
@ -706,7 +716,7 @@ func (adapter *MssqlAdapter) SimpleInnerJoin(name string, table1 string, table2
case "substitute": case "substitute":
substituteCount++ substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount) querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number": case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
@ -827,7 +837,7 @@ func (adapter *MssqlAdapter) SimpleInsertSelect(name string, ins DBInsert, sel D
case "substitute": case "substitute":
substituteCount++ substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount) querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number": case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
@ -951,7 +961,7 @@ func (adapter *MssqlAdapter) simpleJoin(name string, ins DBInsert, sel DBJoin, j
case "substitute": case "substitute":
substituteCount++ substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount) querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number": case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up // TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
@ -1046,7 +1056,7 @@ func (adapter *MssqlAdapter) SimpleCount(name string, table string, where string
for _, loc := range processWhere(where) { for _, loc := range processWhere(where) {
for _, token := range loc.Expr { for _, token := range loc.Expr {
switch token.Type { switch token.Type {
case "function", "operator", "number", "substitute": case "function", "operator", "number", "substitute", "or":
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()" token.Contents = "GETUTCDATE()"
} }

View File

@ -1,13 +1,17 @@
/* WIP Under Construction */ /* WIP Under Construction */
package qgen package qgen
//import "fmt"
import ( import (
"database/sql"
"errors" "errors"
"strconv" "strconv"
"strings" "strings"
_ "github.com/go-sql-driver/mysql"
) )
var ErrNoCollation = errors.New("You didn't provide a collation")
func init() { func init() {
Registry = append(Registry, Registry = append(Registry,
&MysqlAdapter{Name: "mysql", Buffer: make(map[string]DBStmt)}, &MysqlAdapter{Name: "mysql", Buffer: make(map[string]DBStmt)},
@ -33,6 +37,30 @@ func (adapter *MysqlAdapter) GetStmts() map[string]DBStmt {
return adapter.Buffer return adapter.Buffer
} }
func (adapter *MysqlAdapter) BuildConn(config map[string]string) (*sql.DB, error) {
dbCollation, ok := config["collation"]
if !ok {
return nil, ErrNoCollation
}
var dbpassword string
if config["password"] != "" {
dbpassword = ":" + config["password"]
}
// Open the database connection
db, err := sql.Open("mysql", config["username"]+dbpassword+"@tcp("+config["host"]+":"+config["port"]+")/"+config["name"]+"?collation="+dbCollation+"&parseTime=true")
if err != nil {
return db, err
}
// Make sure that the connection is alive
return db, db.Ping()
}
func (adapter *MysqlAdapter) DbVersion() string {
return "SELECT VERSION()"
}
func (adapter *MysqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) { func (adapter *MysqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
if name == "" { if name == "" {
return "", errors.New("You need a name for this statement") return "", errors.New("You need a name for this statement")
@ -239,7 +267,7 @@ func (adapter *MysqlAdapter) SimpleUpdate(name string, table string, set string,
querystr += "`" + item.Column + "` =" querystr += "`" + item.Column + "` ="
for _, token := range item.Expr { for _, token := range item.Expr {
switch token.Type { switch token.Type {
case "function", "operator", "number", "substitute": case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents querystr += " " + token.Contents
case "column": case "column":
querystr += " `" + token.Contents + "`" querystr += " `" + token.Contents + "`"
@ -278,7 +306,7 @@ func (adapter *MysqlAdapter) SimpleDelete(name string, table string, where strin
for _, loc := range processWhere(where) { for _, loc := range processWhere(where) {
for _, token := range loc.Expr { for _, token := range loc.Expr {
switch token.Type { switch token.Type {
case "function", "operator", "number", "substitute": case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents querystr += " " + token.Contents
case "column": case "column":
querystr += " `" + token.Contents + "`" querystr += " `" + token.Contents + "`"
@ -316,7 +344,7 @@ func (adapter *MysqlAdapter) buildWhere(where string) (querystr string, err erro
for _, loc := range processWhere(where) { for _, loc := range processWhere(where) {
for _, token := range loc.Expr { for _, token := range loc.Expr {
switch token.Type { switch token.Type {
case "function", "operator", "number", "substitute": case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents querystr += " " + token.Contents
case "column": case "column":
querystr += " `" + token.Contents + "`" querystr += " `" + token.Contents + "`"
@ -344,7 +372,7 @@ func (adapter *MysqlAdapter) buildFlexiWhere(where string, dateCutoff *dateCutof
for _, loc := range processWhere(where) { for _, loc := range processWhere(where) {
for _, token := range loc.Expr { for _, token := range loc.Expr {
switch token.Type { switch token.Type {
case "function", "operator", "number", "substitute": case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents querystr += " " + token.Contents
case "column": case "column":
querystr += " `" + token.Contents + "`" querystr += " `" + token.Contents + "`"
@ -544,7 +572,7 @@ func (adapter *MysqlAdapter) buildJoinWhere(where string) (querystr string, err
for _, loc := range processWhere(where) { for _, loc := range processWhere(where) {
for _, token := range loc.Expr { for _, token := range loc.Expr {
switch token.Type { switch token.Type {
case "function", "operator", "number", "substitute": case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents querystr += " " + token.Contents
case "column": case "column":
halves := strings.Split(token.Contents, ".") halves := strings.Split(token.Contents, ".")

View File

@ -1,9 +1,12 @@
/* WIP Under Really Heavy Construction */ /* WIP Under Really Heavy Construction */
package qgen package qgen
import "strings" import (
import "strconv" "database/sql"
import "errors" "errors"
"strconv"
"strings"
)
func init() { func init() {
Registry = append(Registry, Registry = append(Registry,
@ -30,6 +33,16 @@ func (adapter *PgsqlAdapter) GetStmts() map[string]DBStmt {
return adapter.Buffer return adapter.Buffer
} }
// TODO: Implement this
func (adapter *PgsqlAdapter) BuildConn(config map[string]string) (*sql.DB, error) {
return nil, nil
}
// TODO: Implement this
func (adapter *PgsqlAdapter) DbVersion() string {
return ""
}
// TODO: Implement this // TODO: Implement this
// We may need to change the CreateTable API to better suit PGSQL and the other database drivers which are coming up // We may need to change the CreateTable API to better suit PGSQL and the other database drivers which are coming up
func (adapter *PgsqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) { func (adapter *PgsqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
@ -167,7 +180,7 @@ func (adapter *PgsqlAdapter) SimpleUpdate(name string, table string, set string,
token.Contents = "LOCALTIMESTAMP()" token.Contents = "LOCALTIMESTAMP()"
} }
querystr += " " + token.Contents querystr += " " + token.Contents
case "operator", "number", "substitute": case "operator", "number", "substitute", "or":
querystr += " " + token.Contents querystr += " " + token.Contents
case "column": case "column":
querystr += " `" + token.Contents + "`" querystr += " `" + token.Contents + "`"
@ -193,7 +206,7 @@ func (adapter *PgsqlAdapter) SimpleUpdate(name string, table string, set string,
token.Contents = "LOCALTIMESTAMP()" token.Contents = "LOCALTIMESTAMP()"
} }
querystr += " " + token.Contents querystr += " " + token.Contents
case "operator", "number", "substitute": case "operator", "number", "substitute", "or":
querystr += " " + token.Contents querystr += " " + token.Contents
case "column": case "column":
querystr += " `" + token.Contents + "`" querystr += " `" + token.Contents + "`"

View File

@ -98,6 +98,9 @@ type DBStmt struct {
type Adapter interface { type Adapter interface {
GetName() string GetName() string
BuildConn(config map[string]string) (*sql.DB, error)
DbVersion() string
CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error)
SimpleInsert(name string, table string, columns string, fields string) (string, error) SimpleInsert(name string, table string, columns string, fields string) (string, error)
SimpleUpdate(name string, table string, set string, where string) (string, error) SimpleUpdate(name string, table string, set string, where string) (string, error)

View File

@ -166,7 +166,12 @@ func (where *DBWhere) parseOperator(segment string, i int) int {
// TODO: Make this case insensitive // TODO: Make this case insensitive
func normalizeAnd(in string) string { func normalizeAnd(in string) string {
return strings.Replace(in, " and ", " AND ", -1) in = strings.Replace(in, " and ", " AND ", -1)
return strings.Replace(in, " && ", " AND ", -1)
}
func normalizeOr(in string) string {
in = strings.Replace(in, " or ", " OR ", -1)
return strings.Replace(in, " || ", " OR ", -1)
} }
// TODO: Write tests for this // TODO: Write tests for this
@ -175,6 +180,7 @@ func processWhere(wherestr string) (where []DBWhere) {
return where return where
} }
wherestr = normalizeAnd(wherestr) wherestr = normalizeAnd(wherestr)
wherestr = normalizeOr(wherestr)
for _, segment := range strings.Split(wherestr, " AND ") { for _, segment := range strings.Split(wherestr, " AND ") {
var tmpWhere = &DBWhere{[]DBToken{}} var tmpWhere = &DBWhere{[]DBToken{}}
@ -184,10 +190,16 @@ func processWhere(wherestr string) (where []DBWhere) {
switch { switch {
case '0' <= char && char <= '9': case '0' <= char && char <= '9':
i = tmpWhere.parseNumber(segment, i) i = tmpWhere.parseNumber(segment, i)
// TODO: Sniff the third byte offset from char or it's non-existent to avoid matching uppercase strings which start with OR
case char == 'O' && (i+1) < len(segment) && segment[i+1] == 'R':
tmpWhere.Expr = append(tmpWhere.Expr, DBToken{"OR", "or"})
i += 1
case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_': case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_':
i = tmpWhere.parseColumn(segment, i) i = tmpWhere.parseColumn(segment, i)
case char == '\'': case char == '\'':
i = tmpWhere.parseString(segment, i) i = tmpWhere.parseString(segment, i)
case char == ')' && i < (len(segment)-1):
tmpWhere.Expr = append(tmpWhere.Expr, DBToken{")", "operator"})
case isOpByte(char): case isOpByte(char):
i = tmpWhere.parseOperator(segment, i) i = tmpWhere.parseOperator(segment, i)
case char == '?': case char == '?':
@ -335,11 +347,11 @@ func processLimit(limitstr string) (limiter DBLimit) {
} }
func isOpByte(char byte) bool { func isOpByte(char byte) bool {
return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' || char == '(' || char == ')'
} }
func isOpRune(char rune) bool { func isOpRune(char rune) bool {
return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' || char == '(' || char == ')'
} }
func processFields(fieldstr string) (fields []DBField) { func processFields(fieldstr string) (fields []DBField) {

View File

@ -1,4 +1,3 @@
/* WIP Under Construction */
package main package main
import "./lib" import "./lib"
@ -11,7 +10,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"password", "varchar", 100, false, false, ""}, qgen.DBTableColumn{"password", "varchar", 100, false, false, ""},
qgen.DBTableColumn{"salt", "varchar", 80, false, false, "''"}, qgen.DBTableColumn{"salt", "varchar", 80, false, false, "''"},
qgen.DBTableColumn{"group", "int", 0, false, false, ""}, qgen.DBTableColumn{"group", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"active", "boolean", 0, false, false, "0"}, qgen.DBTableColumn{"active", "boolean", 0, false, false, "0"},
qgen.DBTableColumn{"is_super_admin", "boolean", 0, false, false, "0"}, qgen.DBTableColumn{"is_super_admin", "boolean", 0, false, false, "0"},
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""}, qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
@ -30,6 +29,12 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"bigposts", "int", 0, false, false, "0"}, qgen.DBTableColumn{"bigposts", "int", 0, false, false, "0"},
qgen.DBTableColumn{"megaposts", "int", 0, false, false, "0"}, qgen.DBTableColumn{"megaposts", "int", 0, false, false, "0"},
qgen.DBTableColumn{"topics", "int", 0, false, false, "0"}, qgen.DBTableColumn{"topics", "int", 0, false, false, "0"},
qgen.DBTableColumn{"liked", "int", 0, false, false, "0"},
// These two are to bound liked queries with little bits of information we know about the user to reduce the server load
qgen.DBTableColumn{"oldestItemLikedCreatedAt", "datetime", 0, false, false, ""}, // For internal use only, semantics may change
qgen.DBTableColumn{"lastLiked", "datetime", 0, false, false, ""}, // For internal use only, semantics may change
//qgen.DBTableColumn{"penalty_count","int",0,false,false,"0"}, //qgen.DBTableColumn{"penalty_count","int",0,false,false,"0"},
qgen.DBTableColumn{"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect qgen.DBTableColumn{"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect
}, },
@ -106,7 +111,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("emails", "", "", qgen.Install.CreateTable("emails", "", "",
[]qgen.DBTableColumn{ []qgen.DBTableColumn{
qgen.DBTableColumn{"email", "varchar", 200, false, false, ""}, qgen.DBTableColumn{"email", "varchar", 200, false, false, ""},
qgen.DBTableColumn{"uid", "int", 0, false, false, ""}, qgen.DBTableColumn{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"validated", "boolean", 0, false, false, "0"}, qgen.DBTableColumn{"validated", "boolean", 0, false, false, "0"},
qgen.DBTableColumn{"token", "varchar", 200, false, false, "''"}, qgen.DBTableColumn{"token", "varchar", 200, false, false, "''"},
}, },
@ -153,7 +158,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""}, qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
qgen.DBTableColumn{"lastReplyAt", "datetime", 0, false, false, ""}, qgen.DBTableColumn{"lastReplyAt", "datetime", 0, false, false, ""},
qgen.DBTableColumn{"lastReplyBy", "int", 0, false, false, ""}, qgen.DBTableColumn{"lastReplyBy", "int", 0, false, false, ""},
qgen.DBTableColumn{"createdBy", "int", 0, false, false, ""}, qgen.DBTableColumn{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"is_closed", "boolean", 0, false, false, "0"}, qgen.DBTableColumn{"is_closed", "boolean", 0, false, false, "0"},
qgen.DBTableColumn{"sticky", "boolean", 0, false, false, "0"}, qgen.DBTableColumn{"sticky", "boolean", 0, false, false, "0"},
qgen.DBTableColumn{"parentID", "int", 0, false, false, "2"}, qgen.DBTableColumn{"parentID", "int", 0, false, false, "2"},
@ -178,7 +183,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"content", "text", 0, false, false, ""}, qgen.DBTableColumn{"content", "text", 0, false, false, ""},
qgen.DBTableColumn{"parsed_content", "text", 0, false, false, ""}, qgen.DBTableColumn{"parsed_content", "text", 0, false, false, ""},
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""}, qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
qgen.DBTableColumn{"createdBy", "int", 0, false, false, ""}, qgen.DBTableColumn{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"lastEdit", "int", 0, false, false, "0"}, qgen.DBTableColumn{"lastEdit", "int", 0, false, false, "0"},
qgen.DBTableColumn{"lastEditBy", "int", 0, false, false, "0"}, qgen.DBTableColumn{"lastEditBy", "int", 0, false, false, "0"},
qgen.DBTableColumn{"lastUpdated", "datetime", 0, false, false, ""}, qgen.DBTableColumn{"lastUpdated", "datetime", 0, false, false, ""},
@ -200,7 +205,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"sectionTable", "varchar", 200, false, false, "forums"}, qgen.DBTableColumn{"sectionTable", "varchar", 200, false, false, "forums"},
qgen.DBTableColumn{"originID", "int", 0, false, false, ""}, qgen.DBTableColumn{"originID", "int", 0, false, false, ""},
qgen.DBTableColumn{"originTable", "varchar", 200, false, false, "replies"}, qgen.DBTableColumn{"originTable", "varchar", 200, false, false, "replies"},
qgen.DBTableColumn{"uploadedBy", "int", 0, false, false, ""}, qgen.DBTableColumn{"uploadedBy", "int", 0, false, false, ""}, // TODO; Make this a foreign key
qgen.DBTableColumn{"path", "varchar", 200, false, false, ""}, qgen.DBTableColumn{"path", "varchar", 200, false, false, ""},
}, },
[]qgen.DBTableKey{ []qgen.DBTableKey{
@ -215,6 +220,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"contentID", "int", 0, false, false, ""}, qgen.DBTableColumn{"contentID", "int", 0, false, false, ""},
qgen.DBTableColumn{"contentType", "varchar", 100, false, false, "replies"}, qgen.DBTableColumn{"contentType", "varchar", 100, false, false, "replies"},
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""}, qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
// TODO: Add a createdBy column?
}, },
[]qgen.DBTableKey{ []qgen.DBTableKey{
qgen.DBTableKey{"reviseID", "primary"}, qgen.DBTableKey{"reviseID", "primary"},
@ -247,7 +253,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("polls_votes", "utf8mb4", "utf8mb4_general_ci", qgen.Install.CreateTable("polls_votes", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DBTableColumn{ []qgen.DBTableColumn{
qgen.DBTableColumn{"pollID", "int", 0, false, false, ""}, qgen.DBTableColumn{"pollID", "int", 0, false, false, ""},
qgen.DBTableColumn{"uid", "int", 0, false, false, ""}, qgen.DBTableColumn{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"option", "int", 0, false, false, "0"}, qgen.DBTableColumn{"option", "int", 0, false, false, "0"},
qgen.DBTableColumn{"castAt", "createdAt", 0, false, false, ""}, qgen.DBTableColumn{"castAt", "createdAt", 0, false, false, ""},
qgen.DBTableColumn{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"}, qgen.DBTableColumn{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
@ -258,11 +264,11 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("users_replies", "utf8mb4", "utf8mb4_general_ci", qgen.Install.CreateTable("users_replies", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DBTableColumn{ []qgen.DBTableColumn{
qgen.DBTableColumn{"rid", "int", 0, false, true, ""}, qgen.DBTableColumn{"rid", "int", 0, false, true, ""},
qgen.DBTableColumn{"uid", "int", 0, false, false, ""}, qgen.DBTableColumn{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"content", "text", 0, false, false, ""}, qgen.DBTableColumn{"content", "text", 0, false, false, ""},
qgen.DBTableColumn{"parsed_content", "text", 0, false, false, ""}, qgen.DBTableColumn{"parsed_content", "text", 0, false, false, ""},
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""}, qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
qgen.DBTableColumn{"createdBy", "int", 0, false, false, ""}, qgen.DBTableColumn{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"lastEdit", "int", 0, false, false, ""}, qgen.DBTableColumn{"lastEdit", "int", 0, false, false, ""},
qgen.DBTableColumn{"lastEditBy", "int", 0, false, false, ""}, qgen.DBTableColumn{"lastEditBy", "int", 0, false, false, ""},
qgen.DBTableColumn{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"}, qgen.DBTableColumn{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
@ -277,7 +283,8 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"weight", "tinyint", 0, false, false, "1"}, qgen.DBTableColumn{"weight", "tinyint", 0, false, false, "1"},
qgen.DBTableColumn{"targetItem", "int", 0, false, false, ""}, qgen.DBTableColumn{"targetItem", "int", 0, false, false, ""},
qgen.DBTableColumn{"targetType", "varchar", 50, false, false, "replies"}, qgen.DBTableColumn{"targetType", "varchar", 50, false, false, "replies"},
qgen.DBTableColumn{"sentBy", "int", 0, false, false, ""}, qgen.DBTableColumn{"sentBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
qgen.DBTableColumn{"recalc", "tinyint", 0, false, false, "0"}, qgen.DBTableColumn{"recalc", "tinyint", 0, false, false, "0"},
}, },
[]qgen.DBTableKey{}, []qgen.DBTableKey{},
@ -285,8 +292,8 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("activity_stream_matches", "", "", qgen.Install.CreateTable("activity_stream_matches", "", "",
[]qgen.DBTableColumn{ []qgen.DBTableColumn{
qgen.DBTableColumn{"watcher", "int", 0, false, false, ""}, qgen.DBTableColumn{"watcher", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"asid", "int", 0, false, false, ""}, qgen.DBTableColumn{"asid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
}, },
[]qgen.DBTableKey{}, []qgen.DBTableKey{},
) )
@ -294,7 +301,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("activity_stream", "", "", qgen.Install.CreateTable("activity_stream", "", "",
[]qgen.DBTableColumn{ []qgen.DBTableColumn{
qgen.DBTableColumn{"asid", "int", 0, false, true, ""}, qgen.DBTableColumn{"asid", "int", 0, false, true, ""},
qgen.DBTableColumn{"actor", "int", 0, false, false, ""}, /* the one doing the act */ qgen.DBTableColumn{"actor", "int", 0, false, false, ""}, /* the one doing the act */ // TODO: Make this a foreign key
qgen.DBTableColumn{"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */ qgen.DBTableColumn{"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */
qgen.DBTableColumn{"event", "varchar", 50, false, false, ""}, /* 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 */ qgen.DBTableColumn{"event", "varchar", 50, false, false, ""}, /* 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 */
qgen.DBTableColumn{"elementType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ qgen.DBTableColumn{"elementType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
@ -307,7 +314,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("activity_subscriptions", "", "", qgen.Install.CreateTable("activity_subscriptions", "", "",
[]qgen.DBTableColumn{ []qgen.DBTableColumn{
qgen.DBTableColumn{"user", "int", 0, false, false, ""}, qgen.DBTableColumn{"user", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"targetID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ qgen.DBTableColumn{"targetID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
qgen.DBTableColumn{"targetType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ qgen.DBTableColumn{"targetType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
qgen.DBTableColumn{"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/ qgen.DBTableColumn{"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/
@ -378,7 +385,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"elementID", "int", 0, false, false, ""}, qgen.DBTableColumn{"elementID", "int", 0, false, false, ""},
qgen.DBTableColumn{"elementType", "varchar", 100, false, false, ""}, qgen.DBTableColumn{"elementType", "varchar", 100, false, false, ""},
qgen.DBTableColumn{"ipaddress", "varchar", 200, false, false, ""}, qgen.DBTableColumn{"ipaddress", "varchar", 200, false, false, ""},
qgen.DBTableColumn{"actorID", "int", 0, false, false, ""}, qgen.DBTableColumn{"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"doneAt", "datetime", 0, false, false, ""}, qgen.DBTableColumn{"doneAt", "datetime", 0, false, false, ""},
}, },
[]qgen.DBTableKey{}, []qgen.DBTableKey{},
@ -390,7 +397,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"elementID", "int", 0, false, false, ""}, qgen.DBTableColumn{"elementID", "int", 0, false, false, ""},
qgen.DBTableColumn{"elementType", "varchar", 100, false, false, ""}, qgen.DBTableColumn{"elementType", "varchar", 100, false, false, ""},
qgen.DBTableColumn{"ipaddress", "varchar", 200, false, false, ""}, qgen.DBTableColumn{"ipaddress", "varchar", 200, false, false, ""},
qgen.DBTableColumn{"actorID", "int", 0, false, false, ""}, qgen.DBTableColumn{"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"doneAt", "datetime", 0, false, false, ""}, qgen.DBTableColumn{"doneAt", "datetime", 0, false, false, ""},
}, },
[]qgen.DBTableKey{}, []qgen.DBTableKey{},

View File

@ -302,6 +302,32 @@ func init() {
counters.SetReverseOSMapEnum(reverseOSMapEnum) counters.SetReverseOSMapEnum(reverseOSMapEnum)
} }
type WriterIntercept struct {
w http.ResponseWriter
code int
}
func NewWriterIntercept(w http.ResponseWriter) *WriterIntercept {
return &WriterIntercept{w:w,code:200}
}
func (writ *WriterIntercept) Header() http.Header {
return writ.w.Header()
}
func (writ *WriterIntercept) Write(pieces []byte) (int, error) {
return writ.w.Write(pieces)
}
func (writ *WriterIntercept) WriteHeader(code int) {
writ.w.WriteHeader(code)
writ.code = code
}
func (writ *WriterIntercept) GetCode() int {
return writ.code
}
type GenRouter struct { type GenRouter struct {
UploadHandler func(http.ResponseWriter, *http.Request) UploadHandler func(http.ResponseWriter, *http.Request)
extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError
@ -311,7 +337,14 @@ type GenRouter struct {
func NewGenRouter(uploads http.Handler) *GenRouter { func NewGenRouter(uploads http.Handler) *GenRouter {
return &GenRouter{ return &GenRouter{
UploadHandler: http.StripPrefix("/uploads/",uploads).ServeHTTP, UploadHandler: func(w http.ResponseWriter, req *http.Request) {
writ := NewWriterIntercept(w)
http.StripPrefix("/uploads/",uploads).ServeHTTP(writ,req)
if writ.GetCode() == 200 {
w.Header().Set("Cache-Control", "max-age=" + strconv.Itoa(common.Day))
w.Header().Set("Vary", "Accept-Encoding")
}
},
extraRoutes: make(map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError), extraRoutes: make(map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError),
} }
} }

View File

@ -74,7 +74,7 @@ func buildTopicRoutes() {
Action("routes.LockTopicSubmit", "/topic/lock/submit/").LitBefore("req.URL.Path += extraData"), Action("routes.LockTopicSubmit", "/topic/lock/submit/").LitBefore("req.URL.Path += extraData"),
Action("routes.UnlockTopicSubmit", "/topic/unlock/submit/", "extraData"), Action("routes.UnlockTopicSubmit", "/topic/unlock/submit/", "extraData"),
Action("routes.MoveTopicSubmit", "/topic/move/submit/", "extraData"), Action("routes.MoveTopicSubmit", "/topic/move/submit/", "extraData"),
Action("routeLikeTopicSubmit", "/topic/like/submit/", "extraData"), Action("routeLikeTopicSubmit", "/topic/like/submit/", "extraData").Before("ParseForm"),
) )
addRouteGroup(topicGroup) addRouteGroup(topicGroup)
} }
@ -88,7 +88,7 @@ func buildReplyRoutes() {
UploadAction("routes.CreateReplySubmit", "/reply/create/").MaxSizeVar("common.Config.MaxRequestSize"), // TODO: Rename the route so it's /reply/create/submit/ UploadAction("routes.CreateReplySubmit", "/reply/create/").MaxSizeVar("common.Config.MaxRequestSize"), // TODO: Rename the route so it's /reply/create/submit/
Action("routes.ReplyEditSubmit", "/reply/edit/submit/", "extraData"), Action("routes.ReplyEditSubmit", "/reply/edit/submit/", "extraData"),
Action("routes.ReplyDeleteSubmit", "/reply/delete/submit/", "extraData"), Action("routes.ReplyDeleteSubmit", "/reply/delete/submit/", "extraData"),
Action("routeReplyLikeSubmit", "/reply/like/submit/", "extraData"), Action("routeReplyLikeSubmit", "/reply/like/submit/", "extraData").Before("ParseForm"),
) )
addRouteGroup(replyGroup) addRouteGroup(replyGroup)
} }

View File

@ -21,6 +21,7 @@ import (
type TopicStmts struct { type TopicStmts struct {
getReplies *sql.Stmt getReplies *sql.Stmt
getLikedTopic *sql.Stmt
} }
var topicStmts TopicStmts var topicStmts TopicStmts
@ -30,6 +31,7 @@ func init() {
common.DbInits.Add(func(acc *qgen.Accumulator) error { common.DbInits.Add(func(acc *qgen.Accumulator) error {
topicStmts = TopicStmts{ topicStmts = TopicStmts{
getReplies: acc.SimpleLeftJoin("replies", "users", "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, replies.likeCount, replies.actionType", "replies.createdBy = users.uid", "replies.tid = ?", "replies.rid ASC", "?,?"), getReplies: acc.SimpleLeftJoin("replies", "users", "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, replies.likeCount, replies.actionType", "replies.createdBy = users.uid", "replies.tid = ?", "replies.rid ASC", "?,?"),
getLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy = ? && targetItem = ? && targetType = 'topics'").Prepare(),
} }
return acc.FirstError() return acc.FirstError()
}) })
@ -107,12 +109,25 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
poll = pPoll.Copy() poll = pPoll.Copy()
} }
if topic.LikeCount > 0 && user.Liked > 0 {
var disp int // Discard this value
err = topicStmts.getLikedTopic.QueryRow(user.ID, topic.ID).Scan(&disp)
if err == nil {
topic.Liked = true
} else if err != nil && err != sql.ErrNoRows {
return common.InternalError(err, w, r)
}
}
// Calculate the offset // Calculate the offset
offset, page, lastPage := common.PageOffset(topic.PostCount, page, common.Config.ItemsPerPage) offset, page, lastPage := common.PageOffset(topic.PostCount, page, common.Config.ItemsPerPage)
tpage := common.TopicPage{topic.Title, user, headerVars, []common.ReplyUser{}, topic, poll, page, lastPage} tpage := common.TopicPage{topic.Title, user, headerVars, []common.ReplyUser{}, topic, poll, page, lastPage}
// Get the replies if we have any... // Get the replies if we have any...
if topic.PostCount > 0 { if topic.PostCount > 0 {
var likedMap = make(map[int]int)
var likedQueryList = []int{user.ID}
rows, err := topicStmts.getReplies.Query(topic.ID, offset, common.Config.ItemsPerPage) rows, err := topicStmts.getReplies.Query(topic.ID, offset, common.Config.ItemsPerPage)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return common.LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.", w, r, user) return common.LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.", w, r, user)
@ -171,7 +186,11 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
replyItem.ActionIcon = "" replyItem.ActionIcon = ""
} }
} }
replyItem.Liked = false
if replyItem.LikeCount > 0 {
likedMap[replyItem.ID] = len(tpage.ItemList)
likedQueryList = append(likedQueryList, replyItem.ID)
}
common.RunVhook("topic_reply_row_assign", &tpage, &replyItem) common.RunVhook("topic_reply_row_assign", &tpage, &replyItem)
// TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis? // TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis?
@ -181,6 +200,28 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
if err != nil { if err != nil {
return common.InternalError(err, w, r) return common.InternalError(err, w, r)
} }
// TODO: Add a config setting to disable the liked query for a burst of extra speed
if user.Liked > 0 && len(likedQueryList) > 1 /*&& user.LastLiked <= time.Now()*/ {
rows, err := qgen.Builder.Accumulator().Select("likes").Columns("targetItem").Where("sentBy = ? AND targetType = 'replies'").In("targetItem", likedQueryList[1:]).Query(user.ID)
if err != nil && err != sql.ErrNoRows {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
var likeRid int
err := rows.Scan(&likeRid)
if err != nil {
return common.InternalError(err, w, r)
}
tpage.ItemList[likedMap[likeRid]].Liked = true
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
}
} }
if common.RunPreRenderHook("pre_render_view_topic", w, r, &user, &tpage) { if common.RunPreRenderHook("pre_render_view_topic", w, r, &user, &tpage) {

View File

@ -1,8 +1,14 @@
echo "Generating the dynamic code" echo "Generating the dynamic code"
go generate go generate
echo "Building Gosora" echo "Building Gosora"
go build -o Gosora go build -o Gosora
echo "Building the templates" echo "Building the templates"
./Gosora -build-templates ./Gosora -build-templates
echo "Building Gosora... Again"
go build -o Gosora
echo "Running Gosora" echo "Running Gosora"
./Gosora ./Gosora

View File

@ -1,8 +1,14 @@
echo "Generating the dynamic code" echo "Generating the dynamic code"
go generate go generate
echo "Building Gosora" echo "Building Gosora"
go build -o Gosora -tags no_ws go build -o Gosora -tags no_ws
echo "Building the templates" echo "Building the templates"
./Gosora -build-templates ./Gosora -build-templates
echo "Building Gosora... Again"
go build -o Gosora -tags no_ws
echo "Running Gosora" echo "Running Gosora"
./Gosora ./Gosora

View File

@ -46,6 +46,13 @@ if %errorlevel% neq 0 (
exit /b %errorlevel% exit /b %errorlevel%
) )
echo Building the executable... again
go build -o gosora.exe -tags no_ws
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Running Gosora echo Running Gosora
gosora.exe gosora.exe
pause pause

View File

@ -46,6 +46,13 @@ if %errorlevel% neq 0 (
exit /b %errorlevel% exit /b %errorlevel%
) )
echo Building the executable... again
go build -o gosora.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Running Gosora echo Running Gosora
gosora.exe gosora.exe
rem Or you could redirect the output to a file rem Or you could redirect the output to a file

View File

@ -46,6 +46,13 @@ if %errorlevel% neq 0 (
exit /b %errorlevel% exit /b %errorlevel%
) )
echo Building the executable... again
go build -o gosora.exe -tags mssql
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Running Gosora echo Running Gosora
gosora.exe gosora.exe
pause pause

View File

@ -3,5 +3,6 @@ CREATE TABLE [likes] (
[targetItem] int not null, [targetItem] int not null,
[targetType] nvarchar (50) DEFAULT 'replies' not null, [targetType] nvarchar (50) DEFAULT 'replies' not null,
[sentBy] int not null, [sentBy] int not null,
[createdAt] datetime not null,
[recalc] tinyint DEFAULT 0 not null [recalc] tinyint DEFAULT 0 not null
); );

View File

@ -21,6 +21,9 @@ CREATE TABLE [users] (
[bigposts] int DEFAULT 0 not null, [bigposts] int DEFAULT 0 not null,
[megaposts] int DEFAULT 0 not null, [megaposts] int DEFAULT 0 not null,
[topics] int DEFAULT 0 not null, [topics] int DEFAULT 0 not null,
[liked] int DEFAULT 0 not null,
[oldestItemLikedCreatedAt] datetime not null,
[lastLiked] datetime not null,
[temp_group] int DEFAULT 0 not null, [temp_group] int DEFAULT 0 not null,
primary key([uid]), primary key([uid]),
unique([name]) unique([name])

View File

@ -3,5 +3,6 @@ CREATE TABLE `likes` (
`targetItem` int not null, `targetItem` int not null,
`targetType` varchar(50) DEFAULT 'replies' not null, `targetType` varchar(50) DEFAULT 'replies' not null,
`sentBy` int not null, `sentBy` int not null,
`createdAt` datetime not null,
`recalc` tinyint DEFAULT 0 not null `recalc` tinyint DEFAULT 0 not null
); );

View File

@ -21,6 +21,9 @@ CREATE TABLE `users` (
`bigposts` int DEFAULT 0 not null, `bigposts` int DEFAULT 0 not null,
`megaposts` int DEFAULT 0 not null, `megaposts` int DEFAULT 0 not null,
`topics` int DEFAULT 0 not null, `topics` int DEFAULT 0 not null,
`liked` int DEFAULT 0 not null,
`oldestItemLikedCreatedAt` datetime not null,
`lastLiked` datetime not null,
`temp_group` int DEFAULT 0 not null, `temp_group` int DEFAULT 0 not null,
primary key(`uid`), primary key(`uid`),
unique(`name`) unique(`name`)

View File

@ -3,5 +3,6 @@ CREATE TABLE `likes` (
`targetItem` int not null, `targetItem` int not null,
`targetType` varchar (50) DEFAULT 'replies' not null, `targetType` varchar (50) DEFAULT 'replies' not null,
`sentBy` int not null, `sentBy` int not null,
`createdAt` timestamp not null,
`recalc` tinyint DEFAULT 0 not null `recalc` tinyint DEFAULT 0 not null
); );

View File

@ -21,6 +21,9 @@ CREATE TABLE `users` (
`bigposts` int DEFAULT 0 not null, `bigposts` int DEFAULT 0 not null,
`megaposts` int DEFAULT 0 not null, `megaposts` int DEFAULT 0 not null,
`topics` int DEFAULT 0 not null, `topics` int DEFAULT 0 not null,
`liked` int DEFAULT 0 not null,
`oldestItemLikedCreatedAt` timestamp not null,
`lastLiked` timestamp not null,
`temp_group` int DEFAULT 0 not null, `temp_group` int DEFAULT 0 not null,
primary key(`uid`), primary key(`uid`),
unique(`name`) unique(`name`)

View File

@ -1,3 +1,4 @@
{ {
"Version":"0" "DBVersion":"0",
"DynamicFileVersion":"0"
} }

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below: // Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main package main
import "strconv"
import "net/http" import "net/http"
import "./common" import "./common"
import "strconv"
var forum_tmpl_phrase_id int var forum_tmpl_phrase_id int
@ -307,61 +307,67 @@ w.Write([]byte(item.Link))
w.Write(forum_frags[58]) w.Write(forum_frags[58])
w.Write([]byte(item.Title)) w.Write([]byte(item.Title))
w.Write(forum_frags[59]) w.Write(forum_frags[59])
w.Write([]byte(item.Creator.Link)) w.Write([]byte(item.Title))
w.Write(forum_frags[60]) w.Write(forum_frags[60])
w.Write([]byte(item.Creator.Name)) w.Write([]byte(item.Creator.Link))
w.Write(forum_frags[61]) w.Write(forum_frags[61])
if item.IsClosed { w.Write([]byte(item.Creator.Name))
w.Write(forum_frags[62]) w.Write(forum_frags[62])
w.Write(phrases[44]) w.Write([]byte(item.Creator.Name))
w.Write(forum_frags[63]) w.Write(forum_frags[63])
} if item.IsClosed {
if item.Sticky {
w.Write(forum_frags[64]) w.Write(forum_frags[64])
w.Write(phrases[45]) w.Write(phrases[44])
w.Write(forum_frags[65]) w.Write(forum_frags[65])
} }
w.Write(forum_frags[66])
w.Write([]byte(strconv.Itoa(item.PostCount)))
w.Write(forum_frags[67])
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(forum_frags[68])
if item.Sticky { if item.Sticky {
w.Write(forum_frags[66])
w.Write(phrases[45])
w.Write(forum_frags[67])
}
w.Write(forum_frags[68])
w.Write([]byte(strconv.Itoa(item.PostCount)))
w.Write(forum_frags[69]) w.Write(forum_frags[69])
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(forum_frags[70])
if item.Sticky {
w.Write(forum_frags[71])
} else { } else {
if item.IsClosed { if item.IsClosed {
w.Write(forum_frags[70])
}
}
w.Write(forum_frags[71])
w.Write([]byte(item.LastUser.Link))
w.Write(forum_frags[72]) w.Write(forum_frags[72])
w.Write([]byte(item.LastUser.Avatar)) }
}
w.Write(forum_frags[73]) w.Write(forum_frags[73])
w.Write([]byte(item.LastUser.Name))
w.Write(forum_frags[74])
w.Write([]byte(item.LastUser.Name))
w.Write(forum_frags[75])
w.Write([]byte(item.LastUser.Link)) w.Write([]byte(item.LastUser.Link))
w.Write(forum_frags[74])
w.Write([]byte(item.LastUser.Avatar))
w.Write(forum_frags[75])
w.Write([]byte(item.LastUser.Name))
w.Write(forum_frags[76]) w.Write(forum_frags[76])
w.Write([]byte(item.LastUser.Name)) w.Write([]byte(item.LastUser.Name))
w.Write(forum_frags[77]) w.Write(forum_frags[77])
w.Write([]byte(item.RelativeLastReplyAt)) w.Write([]byte(item.LastUser.Link))
w.Write(forum_frags[78]) w.Write(forum_frags[78])
w.Write([]byte(item.LastUser.Name))
w.Write(forum_frags[79])
w.Write([]byte(item.LastUser.Name))
w.Write(forum_frags[80])
w.Write([]byte(item.RelativeLastReplyAt))
w.Write(forum_frags[81])
} }
} else { } else {
w.Write(forum_frags[79]) w.Write(forum_frags[82])
w.Write(phrases[46]) w.Write(phrases[46])
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic { if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
w.Write(forum_frags[80])
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
w.Write(forum_frags[81])
w.Write(phrases[47])
w.Write(forum_frags[82])
}
w.Write(forum_frags[83]) w.Write(forum_frags[83])
} w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
w.Write(forum_frags[84]) w.Write(forum_frags[84])
w.Write(phrases[47])
w.Write(forum_frags[85])
}
w.Write(forum_frags[86])
}
w.Write(forum_frags[87])
if tmpl_forum_vars.LastPage > 1 { if tmpl_forum_vars.LastPage > 1 {
w.Write(paginator_frags[0]) w.Write(paginator_frags[0])
if tmpl_forum_vars.Page > 1 { if tmpl_forum_vars.Page > 1 {
@ -397,7 +403,7 @@ w.Write(paginator_frags[13])
} }
w.Write(paginator_frags[14]) w.Write(paginator_frags[14])
} }
w.Write(forum_frags[85]) w.Write(forum_frags[88])
w.Write(footer_frags[0]) w.Write(footer_frags[0])
w.Write([]byte(common.BuildWidget("footer",tmpl_forum_vars.Header))) w.Write([]byte(common.BuildWidget("footer",tmpl_forum_vars.Header)))
w.Write(footer_frags[1]) w.Write(footer_frags[1])

File diff suppressed because it is too large Load Diff

View File

@ -187,73 +187,77 @@ w.Write([]byte(tmpl_profile_vars.ProfileOwner.Name))
w.Write(profile_frags[3]) w.Write(profile_frags[3])
w.Write([]byte(tmpl_profile_vars.ProfileOwner.Name)) w.Write([]byte(tmpl_profile_vars.ProfileOwner.Name))
w.Write(profile_frags[4]) w.Write(profile_frags[4])
if tmpl_profile_vars.ProfileOwner.Tag != "" { w.Write([]byte(tmpl_profile_vars.ProfileOwner.Name))
w.Write(profile_frags[5]) w.Write(profile_frags[5])
w.Write([]byte(tmpl_profile_vars.ProfileOwner.Tag)) if tmpl_profile_vars.ProfileOwner.Tag != "" {
w.Write(profile_frags[6]) w.Write(profile_frags[6])
} w.Write([]byte(tmpl_profile_vars.ProfileOwner.Tag))
w.Write(profile_frags[7]) w.Write(profile_frags[7])
if !tmpl_profile_vars.CurrentUser.Loggedin { w.Write([]byte(tmpl_profile_vars.ProfileOwner.Tag))
w.Write(profile_frags[8]) w.Write(profile_frags[8])
w.Write(phrases[19]) }
w.Write(profile_frags[9]) w.Write(profile_frags[9])
} else { if !tmpl_profile_vars.CurrentUser.Loggedin {
w.Write(profile_frags[10]) w.Write(profile_frags[10])
w.Write(phrases[20]) w.Write(phrases[19])
w.Write(profile_frags[11]) w.Write(profile_frags[11])
if tmpl_profile_vars.CurrentUser.IsSuperMod && !tmpl_profile_vars.ProfileOwner.IsSuperMod {
w.Write(profile_frags[12])
if tmpl_profile_vars.ProfileOwner.IsBanned {
w.Write(profile_frags[13])
w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID)))
w.Write(profile_frags[14])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_frags[15])
w.Write(phrases[21])
w.Write(profile_frags[16])
} else { } else {
w.Write(profile_frags[12])
w.Write(phrases[20])
w.Write(profile_frags[13])
if tmpl_profile_vars.CurrentUser.IsSuperMod && !tmpl_profile_vars.ProfileOwner.IsSuperMod {
w.Write(profile_frags[14])
if tmpl_profile_vars.ProfileOwner.IsBanned {
w.Write(profile_frags[15])
w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID)))
w.Write(profile_frags[16])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_frags[17]) w.Write(profile_frags[17])
w.Write(phrases[22]) w.Write(phrases[21])
w.Write(profile_frags[18]) w.Write(profile_frags[18])
} } else {
w.Write(profile_frags[19]) w.Write(profile_frags[19])
} w.Write(phrases[22])
w.Write(profile_frags[20]) w.Write(profile_frags[20])
w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID))) }
w.Write(profile_frags[21]) w.Write(profile_frags[21])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session)) }
w.Write(profile_frags[22]) w.Write(profile_frags[22])
w.Write(phrases[23])
w.Write(profile_frags[23])
w.Write(phrases[24])
w.Write(profile_frags[24])
}
w.Write(profile_frags[25])
if tmpl_profile_vars.CurrentUser.Perms.BanUsers {
w.Write(profile_frags[26])
w.Write(phrases[25])
w.Write(profile_frags[27])
w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID))) w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID)))
w.Write(profile_frags[28]) w.Write(profile_frags[23])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session)) w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_frags[29]) w.Write(profile_frags[24])
w.Write(profile_frags[30]) w.Write(phrases[23])
w.Write(phrases[26]) w.Write(profile_frags[25])
w.Write(profile_frags[31]) w.Write(phrases[24])
w.Write(phrases[27]) w.Write(profile_frags[26])
w.Write(profile_frags[32])
w.Write(phrases[28])
w.Write(profile_frags[33])
w.Write(phrases[29])
w.Write(profile_frags[34])
w.Write(phrases[30])
w.Write(profile_frags[35])
w.Write(phrases[31])
w.Write(profile_frags[36])
} }
w.Write(profile_frags[27])
if tmpl_profile_vars.CurrentUser.Perms.BanUsers {
w.Write(profile_frags[28])
w.Write(phrases[25])
w.Write(profile_frags[29])
w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID)))
w.Write(profile_frags[30])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_frags[31])
w.Write(profile_frags[32])
w.Write(phrases[26])
w.Write(profile_frags[33])
w.Write(phrases[27])
w.Write(profile_frags[34])
w.Write(phrases[28])
w.Write(profile_frags[35])
w.Write(phrases[29])
w.Write(profile_frags[36])
w.Write(phrases[30])
w.Write(profile_frags[37]) w.Write(profile_frags[37])
w.Write(phrases[32]) w.Write(phrases[31])
w.Write(profile_frags[38]) w.Write(profile_frags[38])
}
w.Write(profile_frags[39])
w.Write(phrases[32])
w.Write(profile_frags[40])
if tmpl_profile_vars.Header.Theme.BgAvatars { if tmpl_profile_vars.Header.Theme.BgAvatars {
if len(tmpl_profile_vars.ItemList) != 0 { if len(tmpl_profile_vars.ItemList) != 0 {
for _, item := range tmpl_profile_vars.ItemList { for _, item := range tmpl_profile_vars.ItemList {
@ -363,20 +367,20 @@ w.Write(profile_comments_row_frags[49])
} }
} }
} }
w.Write(profile_frags[39])
if !tmpl_profile_vars.CurrentUser.IsBanned {
w.Write(profile_frags[40])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_frags[41]) w.Write(profile_frags[41])
w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID))) if !tmpl_profile_vars.CurrentUser.IsBanned {
w.Write(profile_frags[42]) w.Write(profile_frags[42])
w.Write(phrases[45]) w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_frags[43]) w.Write(profile_frags[43])
w.Write(phrases[46]) w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID)))
w.Write(profile_frags[44]) w.Write(profile_frags[44])
} w.Write(phrases[45])
w.Write(profile_frags[45]) w.Write(profile_frags[45])
w.Write(phrases[46])
w.Write(profile_frags[46]) w.Write(profile_frags[46])
}
w.Write(profile_frags[47])
w.Write(profile_frags[48])
w.Write(footer_frags[0]) w.Write(footer_frags[0])
w.Write([]byte(common.BuildWidget("footer",tmpl_profile_vars.Header))) w.Write([]byte(common.BuildWidget("footer",tmpl_profile_vars.Header)))
w.Write(footer_frags[1]) w.Write(footer_frags[1])

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below: // Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main package main
import "strconv"
import "net/http" import "net/http"
import "./common" import "./common"
import "strconv"
var topic_tmpl_phrase_id int var topic_tmpl_phrase_id int
@ -245,376 +245,385 @@ w.Write(phrases[22])
w.Write(topic_frags[16]) w.Write(topic_frags[16])
} }
w.Write(topic_frags[17]) w.Write(topic_frags[17])
w.Write(phrases[23])
w.Write(topic_frags[18]) w.Write(topic_frags[18])
if tmpl_topic_vars.Topic.Sticky { w.Write(phrases[23])
w.Write(topic_frags[19]) w.Write(topic_frags[19])
if tmpl_topic_vars.Topic.Sticky {
w.Write(topic_frags[20])
} else { } else {
if tmpl_topic_vars.Topic.IsClosed { if tmpl_topic_vars.Topic.IsClosed {
w.Write(topic_frags[20])
}
}
w.Write(topic_frags[21]) w.Write(topic_frags[21])
w.Write([]byte(tmpl_topic_vars.Topic.Title)) }
}
w.Write(topic_frags[22]) w.Write(topic_frags[22])
if tmpl_topic_vars.Topic.IsClosed { w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_frags[23]) w.Write(topic_frags[23])
w.Write(phrases[24]) w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_frags[24]) w.Write(topic_frags[24])
w.Write(phrases[25]) if tmpl_topic_vars.Topic.IsClosed {
w.Write(topic_frags[25]) w.Write(topic_frags[25])
w.Write(phrases[24])
w.Write(topic_frags[26])
w.Write(phrases[25])
w.Write(topic_frags[27])
} }
if tmpl_topic_vars.CurrentUser.Perms.EditTopic { if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_frags[26])
w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_frags[27])
w.Write(phrases[26])
w.Write(topic_frags[28]) w.Write(topic_frags[28])
w.Write(phrases[27]) w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_frags[29]) w.Write(topic_frags[29])
} w.Write(phrases[26])
w.Write(topic_frags[30]) w.Write(topic_frags[30])
if tmpl_topic_vars.Poll.ID > 0 { w.Write(phrases[27])
w.Write(topic_frags[31]) w.Write(topic_frags[31])
w.Write(phrases[28])
w.Write(topic_frags[32])
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
w.Write(topic_frags[33])
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
w.Write(topic_frags[34])
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write(topic_frags[35])
if tmpl_topic_vars.Topic.ContentLines <= 5 {
w.Write(topic_frags[36])
} }
w.Write(topic_frags[32])
if tmpl_topic_vars.Poll.ID > 0 {
w.Write(topic_frags[33])
w.Write(phrases[28])
w.Write(topic_frags[34])
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
w.Write(topic_frags[35])
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
w.Write(topic_frags[36])
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write(topic_frags[37]) w.Write(topic_frags[37])
if tmpl_topic_vars.Topic.ContentLines <= 5 {
w.Write(topic_frags[38])
}
w.Write(topic_frags[39])
if len(tmpl_topic_vars.Poll.QuickOptions) != 0 { if len(tmpl_topic_vars.Poll.QuickOptions) != 0 {
for _, item := range tmpl_topic_vars.Poll.QuickOptions { for _, item := range tmpl_topic_vars.Poll.QuickOptions {
w.Write(topic_frags[38])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
w.Write(topic_frags[39])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[40]) w.Write(topic_frags[40])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
w.Write(topic_frags[41]) w.Write(topic_frags[41])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[42]) w.Write(topic_frags[42])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[43]) w.Write(topic_frags[43])
w.Write([]byte(item.Value)) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[44]) w.Write(topic_frags[44])
} w.Write([]byte(strconv.Itoa(item.ID)))
}
w.Write(topic_frags[45]) w.Write(topic_frags[45])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID))) w.Write([]byte(item.Value))
w.Write(topic_frags[46]) w.Write(topic_frags[46])
w.Write(phrases[29]) }
}
w.Write(topic_frags[47]) w.Write(topic_frags[47])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
w.Write(topic_frags[48]) w.Write(topic_frags[48])
w.Write(phrases[30]) w.Write(phrases[29])
w.Write(topic_frags[49]) w.Write(topic_frags[49])
w.Write(phrases[31])
w.Write(topic_frags[50])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
w.Write(topic_frags[50])
w.Write(phrases[30])
w.Write(topic_frags[51]) w.Write(topic_frags[51])
} w.Write(phrases[31])
w.Write(topic_frags[52]) w.Write(topic_frags[52])
w.Write(phrases[32]) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
w.Write(topic_frags[53]) w.Write(topic_frags[53])
w.Write([]byte(tmpl_topic_vars.Topic.ClassName)) }
w.Write(topic_frags[54]) w.Write(topic_frags[54])
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
w.Write(topic_frags[55]) w.Write(topic_frags[55])
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name)) w.Write(phrases[32])
w.Write(topic_frags[56]) w.Write(topic_frags[56])
if tmpl_topic_vars.Topic.ContentLines <= 5 { w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
w.Write(topic_frags[57]) w.Write(topic_frags[57])
} w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
w.Write(topic_frags[58]) w.Write(topic_frags[58])
w.Write([]byte(tmpl_topic_vars.Topic.ContentHTML)) w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write(topic_frags[59]) w.Write(topic_frags[59])
w.Write([]byte(tmpl_topic_vars.Topic.Content)) if tmpl_topic_vars.Topic.ContentLines <= 5 {
w.Write(topic_frags[60]) w.Write(topic_frags[60])
w.Write(phrases[33]) }
w.Write(topic_frags[61]) w.Write(topic_frags[61])
w.Write([]byte(tmpl_topic_vars.Topic.UserLink)) w.Write([]byte(tmpl_topic_vars.Topic.ContentHTML))
w.Write(topic_frags[62]) w.Write(topic_frags[62])
w.Write([]byte(tmpl_topic_vars.Topic.CreatedByName)) w.Write([]byte(tmpl_topic_vars.Topic.Content))
w.Write(topic_frags[63]) w.Write(topic_frags[63])
if tmpl_topic_vars.CurrentUser.Perms.LikeItem { if tmpl_topic_vars.Topic.LikeCount > 0 {
w.Write(topic_frags[64]) w.Write(topic_frags[64])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID))) }
w.Write(topic_frags[65]) w.Write(topic_frags[65])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session)) w.Write(phrases[33])
w.Write(topic_frags[66]) w.Write(topic_frags[66])
if tmpl_topic_vars.Topic.Liked { w.Write([]byte(tmpl_topic_vars.Topic.UserLink))
w.Write(topic_frags[67]) w.Write(topic_frags[67])
w.Write(phrases[34]) w.Write([]byte(tmpl_topic_vars.Topic.CreatedByName))
w.Write(topic_frags[68]) w.Write(topic_frags[68])
w.Write(phrases[35]) if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_frags[69]) w.Write(topic_frags[69])
} else { w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[70]) w.Write(topic_frags[70])
w.Write(phrases[36]) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[71]) w.Write(topic_frags[71])
w.Write(phrases[37])
w.Write(topic_frags[72])
}
w.Write(topic_frags[73])
if tmpl_topic_vars.Topic.Liked { if tmpl_topic_vars.Topic.Liked {
w.Write(topic_frags[72])
w.Write(phrases[34])
w.Write(topic_frags[73])
w.Write(phrases[35])
w.Write(topic_frags[74]) w.Write(topic_frags[74])
} } else {
w.Write(topic_frags[75]) w.Write(topic_frags[75])
w.Write(phrases[36])
w.Write(topic_frags[76])
w.Write(phrases[37])
w.Write(topic_frags[77])
}
w.Write(topic_frags[78])
if tmpl_topic_vars.Topic.Liked {
w.Write(topic_frags[79])
} else {
w.Write(topic_frags[80])
}
w.Write(topic_frags[81])
} }
if tmpl_topic_vars.CurrentUser.Perms.EditTopic { if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_frags[76]) w.Write(topic_frags[82])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[77]) w.Write(topic_frags[83])
w.Write(phrases[38]) w.Write(phrases[38])
w.Write(topic_frags[78]) w.Write(topic_frags[84])
w.Write(phrases[39]) w.Write(phrases[39])
w.Write(topic_frags[79]) w.Write(topic_frags[85])
} }
if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic { if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
w.Write(topic_frags[80]) w.Write(topic_frags[86])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[81]) w.Write(topic_frags[87])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[82]) w.Write(topic_frags[88])
w.Write(phrases[40]) w.Write(phrases[40])
w.Write(topic_frags[83]) w.Write(topic_frags[89])
w.Write(phrases[41]) w.Write(phrases[41])
w.Write(topic_frags[84]) w.Write(topic_frags[90])
} }
if tmpl_topic_vars.CurrentUser.Perms.CloseTopic { if tmpl_topic_vars.CurrentUser.Perms.CloseTopic {
if tmpl_topic_vars.Topic.IsClosed { if tmpl_topic_vars.Topic.IsClosed {
w.Write(topic_frags[85])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[86])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[87])
w.Write(phrases[42])
w.Write(topic_frags[88])
w.Write(phrases[43])
w.Write(topic_frags[89])
} else {
w.Write(topic_frags[90])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[91]) w.Write(topic_frags[91])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session)) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[92]) w.Write(topic_frags[92])
w.Write(phrases[44]) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[93]) w.Write(topic_frags[93])
w.Write(phrases[45]) w.Write(phrases[42])
w.Write(topic_frags[94]) w.Write(topic_frags[94])
w.Write(phrases[43])
w.Write(topic_frags[95])
} else {
w.Write(topic_frags[96])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[97])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[98])
w.Write(phrases[44])
w.Write(topic_frags[99])
w.Write(phrases[45])
w.Write(topic_frags[100])
} }
} }
if tmpl_topic_vars.CurrentUser.Perms.PinTopic { if tmpl_topic_vars.CurrentUser.Perms.PinTopic {
if tmpl_topic_vars.Topic.Sticky { if tmpl_topic_vars.Topic.Sticky {
w.Write(topic_frags[95])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[96])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[97])
w.Write(phrases[46])
w.Write(topic_frags[98])
w.Write(phrases[47])
w.Write(topic_frags[99])
} else {
w.Write(topic_frags[100])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[101]) w.Write(topic_frags[101])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session)) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[102]) w.Write(topic_frags[102])
w.Write(phrases[48]) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[103]) w.Write(topic_frags[103])
w.Write(phrases[49]) w.Write(phrases[46])
w.Write(topic_frags[104]) w.Write(topic_frags[104])
w.Write(phrases[47])
w.Write(topic_frags[105])
} else {
w.Write(topic_frags[106])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[107])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[108])
w.Write(phrases[48])
w.Write(topic_frags[109])
w.Write(phrases[49])
w.Write(topic_frags[110])
} }
} }
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs { if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_frags[105])
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_frags[106])
w.Write(phrases[50])
w.Write(topic_frags[107])
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_frags[108])
}
w.Write(topic_frags[109])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[110])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[111]) w.Write(topic_frags[111])
w.Write(phrases[51]) w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_frags[112]) w.Write(topic_frags[112])
w.Write(phrases[52]) w.Write(phrases[50])
w.Write(topic_frags[113]) w.Write(topic_frags[113])
if tmpl_topic_vars.Topic.LikeCount > 0 { w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_frags[114]) w.Write(topic_frags[114])
w.Write(phrases[53]) }
w.Write(topic_frags[115]) w.Write(topic_frags[115])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount))) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[116]) w.Write(topic_frags[116])
w.Write(phrases[54]) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[117]) w.Write(topic_frags[117])
} w.Write(phrases[51])
if tmpl_topic_vars.Topic.Tag != "" {
w.Write(topic_frags[118]) w.Write(topic_frags[118])
w.Write([]byte(tmpl_topic_vars.Topic.Tag)) w.Write(phrases[52])
w.Write(topic_frags[119]) w.Write(topic_frags[119])
} else { w.Write(phrases[53])
w.Write(topic_frags[120]) w.Write(topic_frags[120])
w.Write(phrases[55]) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
w.Write(topic_frags[121]) w.Write(topic_frags[121])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level))) w.Write(phrases[54])
w.Write(topic_frags[122]) w.Write(topic_frags[122])
w.Write(phrases[56]) if tmpl_topic_vars.Topic.Tag != "" {
w.Write(topic_frags[123]) w.Write(topic_frags[123])
} w.Write([]byte(tmpl_topic_vars.Topic.Tag))
w.Write(topic_frags[124]) w.Write(topic_frags[124])
w.Write(phrases[57]) } else {
w.Write(topic_frags[125]) w.Write(topic_frags[125])
w.Write(phrases[55])
w.Write(topic_frags[126])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
w.Write(topic_frags[127])
w.Write(phrases[56])
w.Write(topic_frags[128])
}
w.Write(topic_frags[129])
w.Write(phrases[57])
w.Write(topic_frags[130])
if len(tmpl_topic_vars.ItemList) != 0 { if len(tmpl_topic_vars.ItemList) != 0 {
for _, item := range tmpl_topic_vars.ItemList { for _, item := range tmpl_topic_vars.ItemList {
if item.ActionType != "" { if item.ActionType != "" {
w.Write(topic_frags[126])
w.Write([]byte(item.ActionIcon))
w.Write(topic_frags[127])
w.Write([]byte(item.ActionType))
w.Write(topic_frags[128])
} else {
w.Write(topic_frags[129])
w.Write([]byte(item.ClassName))
w.Write(topic_frags[130])
w.Write([]byte(item.Avatar))
w.Write(topic_frags[131]) w.Write(topic_frags[131])
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name)) w.Write([]byte(item.ActionIcon))
w.Write(topic_frags[132]) w.Write(topic_frags[132])
if item.ContentLines <= 5 { w.Write([]byte(item.ActionType))
w.Write(topic_frags[133]) w.Write(topic_frags[133])
} } else {
w.Write(topic_frags[134]) w.Write(topic_frags[134])
w.Write(topic_frags[135]) w.Write(topic_frags[135])
w.Write([]byte(item.ContentHtml)) w.Write([]byte(item.ClassName))
w.Write(topic_frags[136]) w.Write(topic_frags[136])
w.Write([]byte(item.UserLink)) w.Write([]byte(item.Avatar))
w.Write(topic_frags[137]) w.Write(topic_frags[137])
w.Write([]byte(item.CreatedByName)) w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write(topic_frags[138]) w.Write(topic_frags[138])
if item.ContentLines <= 5 {
w.Write(topic_frags[139])
}
w.Write(topic_frags[140])
w.Write(topic_frags[141])
w.Write([]byte(item.ContentHtml))
w.Write(topic_frags[142])
if item.LikeCount > 0 {
w.Write(topic_frags[143])
}
w.Write(topic_frags[144])
w.Write([]byte(item.UserLink))
w.Write(topic_frags[145])
w.Write([]byte(item.CreatedByName))
w.Write(topic_frags[146])
if tmpl_topic_vars.CurrentUser.Perms.LikeItem { if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
if item.Liked { if item.Liked {
w.Write(topic_frags[139])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[140])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[141])
w.Write(phrases[58])
w.Write(topic_frags[142])
w.Write(phrases[59])
w.Write(topic_frags[143])
} else {
w.Write(topic_frags[144])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[145])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[146])
w.Write(phrases[60])
w.Write(topic_frags[147]) w.Write(topic_frags[147])
w.Write(phrases[61]) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[148]) w.Write(topic_frags[148])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[149])
w.Write(phrases[58])
w.Write(topic_frags[150])
w.Write(phrases[59])
w.Write(topic_frags[151])
} else {
w.Write(topic_frags[152])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[153])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[154])
w.Write(phrases[60])
w.Write(topic_frags[155])
w.Write(phrases[61])
w.Write(topic_frags[156])
} }
} }
if tmpl_topic_vars.CurrentUser.Perms.EditReply { if tmpl_topic_vars.CurrentUser.Perms.EditReply {
w.Write(topic_frags[149]) w.Write(topic_frags[157])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[150]) w.Write(topic_frags[158])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[151]) w.Write(topic_frags[159])
w.Write(phrases[62]) w.Write(phrases[62])
w.Write(topic_frags[152]) w.Write(topic_frags[160])
w.Write(phrases[63]) w.Write(phrases[63])
w.Write(topic_frags[153]) w.Write(topic_frags[161])
} }
if tmpl_topic_vars.CurrentUser.Perms.DeleteReply { if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_frags[154]) w.Write(topic_frags[162])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[155]) w.Write(topic_frags[163])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[156]) w.Write(topic_frags[164])
w.Write(phrases[64]) w.Write(phrases[64])
w.Write(topic_frags[157]) w.Write(topic_frags[165])
w.Write(phrases[65]) w.Write(phrases[65])
w.Write(topic_frags[158]) w.Write(topic_frags[166])
} }
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs { if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_frags[159])
w.Write([]byte(item.IPAddress))
w.Write(topic_frags[160])
w.Write(phrases[66])
w.Write(topic_frags[161])
w.Write([]byte(item.IPAddress))
w.Write(topic_frags[162])
}
w.Write(topic_frags[163])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[164])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[165])
w.Write(phrases[67])
w.Write(topic_frags[166])
w.Write(phrases[68])
w.Write(topic_frags[167]) w.Write(topic_frags[167])
if item.LikeCount > 0 { w.Write([]byte(item.IPAddress))
w.Write(topic_frags[168]) w.Write(topic_frags[168])
w.Write([]byte(strconv.Itoa(item.LikeCount))) w.Write(phrases[66])
w.Write(topic_frags[169]) w.Write(topic_frags[169])
w.Write(phrases[69]) w.Write([]byte(item.IPAddress))
w.Write(topic_frags[170]) w.Write(topic_frags[170])
} }
if item.Tag != "" {
w.Write(topic_frags[171]) w.Write(topic_frags[171])
w.Write([]byte(item.Tag)) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[172]) w.Write(topic_frags[172])
} else {
w.Write(topic_frags[173])
w.Write(phrases[70])
w.Write(topic_frags[174])
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_frags[175])
w.Write(phrases[71])
w.Write(topic_frags[176])
}
w.Write(topic_frags[177])
}
}
}
w.Write(topic_frags[178])
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_frags[179])
w.Write(phrases[72])
w.Write(topic_frags[180])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[173])
w.Write(phrases[67])
w.Write(topic_frags[174])
w.Write(phrases[68])
w.Write(topic_frags[175])
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topic_frags[176])
w.Write(phrases[69])
w.Write(topic_frags[177])
if item.Tag != "" {
w.Write(topic_frags[178])
w.Write([]byte(item.Tag))
w.Write(topic_frags[179])
} else {
w.Write(topic_frags[180])
w.Write(phrases[70])
w.Write(topic_frags[181]) w.Write(topic_frags[181])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID))) w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_frags[182]) w.Write(topic_frags[182])
w.Write(phrases[73]) w.Write(phrases[71])
w.Write(topic_frags[183]) w.Write(topic_frags[183])
w.Write(phrases[74]) }
w.Write(topic_frags[184]) w.Write(topic_frags[184])
w.Write(phrases[75]) }
}
}
w.Write(topic_frags[185]) w.Write(topic_frags[185])
w.Write(phrases[76]) if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_frags[186]) w.Write(topic_frags[186])
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles { w.Write(phrases[72])
w.Write(topic_frags[187]) w.Write(topic_frags[187])
w.Write(phrases[77]) w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[188]) w.Write(topic_frags[188])
} w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[189]) w.Write(topic_frags[189])
} w.Write(phrases[73])
w.Write(topic_frags[190]) w.Write(topic_frags[190])
w.Write(phrases[74])
w.Write(topic_frags[191])
w.Write(phrases[75])
w.Write(topic_frags[192])
w.Write(phrases[76])
w.Write(topic_frags[193])
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
w.Write(topic_frags[194])
w.Write(phrases[77])
w.Write(topic_frags[195])
}
w.Write(topic_frags[196])
}
w.Write(topic_frags[197])
w.Write(footer_frags[0]) w.Write(footer_frags[0])
w.Write([]byte(common.BuildWidget("footer",tmpl_topic_vars.Header))) w.Write([]byte(common.BuildWidget("footer",tmpl_topic_vars.Header)))
w.Write(footer_frags[1]) w.Write(footer_frags[1])

View File

@ -227,178 +227,173 @@ w.Write(phrases[22])
w.Write(topic_alt_frags[13]) w.Write(topic_alt_frags[13])
} }
w.Write(topic_alt_frags[14]) w.Write(topic_alt_frags[14])
w.Write(phrases[23])
w.Write(topic_alt_frags[15]) w.Write(topic_alt_frags[15])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) w.Write(phrases[23])
w.Write(topic_alt_frags[16]) w.Write(topic_alt_frags[16])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[17]) w.Write(topic_alt_frags[17])
if tmpl_topic_alt_vars.Topic.Sticky { w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[18]) w.Write(topic_alt_frags[18])
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write(topic_alt_frags[19])
} else { } else {
if tmpl_topic_alt_vars.Topic.IsClosed { if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_frags[19])
}
}
w.Write(topic_alt_frags[20]) w.Write(topic_alt_frags[20])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title)) }
}
w.Write(topic_alt_frags[21]) w.Write(topic_alt_frags[21])
if tmpl_topic_alt_vars.Topic.IsClosed { w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_frags[22]) w.Write(topic_alt_frags[22])
w.Write(phrases[24]) w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_frags[23]) w.Write(topic_alt_frags[23])
w.Write(phrases[25]) if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_frags[24]) w.Write(topic_alt_frags[24])
w.Write(phrases[24])
w.Write(topic_alt_frags[25])
w.Write(phrases[25])
w.Write(topic_alt_frags[26])
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic { if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_alt_frags[25])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_frags[26])
w.Write(phrases[26])
w.Write(topic_alt_frags[27]) w.Write(topic_alt_frags[27])
w.Write(phrases[27]) w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_frags[28]) w.Write(topic_alt_frags[28])
} w.Write(phrases[26])
w.Write(topic_alt_frags[29]) w.Write(topic_alt_frags[29])
if tmpl_topic_alt_vars.Poll.ID > 0 { w.Write(phrases[27])
w.Write(topic_alt_frags[30]) w.Write(topic_alt_frags[30])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID))) }
w.Write(topic_alt_frags[31]) w.Write(topic_alt_frags[31])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID))) if tmpl_topic_alt_vars.Poll.ID > 0 {
w.Write(topic_alt_frags[32]) w.Write(topic_alt_frags[32])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[33]) w.Write(topic_alt_frags[33])
w.Write(phrases[28]) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[34]) w.Write(topic_alt_frags[34])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Avatar)) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[35]) w.Write(topic_alt_frags[35])
w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink)) w.Write(phrases[28])
w.Write(topic_alt_frags[36]) w.Write(topic_alt_frags[36])
w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedByName)) w.Write([]byte(tmpl_topic_alt_vars.Topic.Avatar))
w.Write(topic_alt_frags[37]) w.Write(topic_alt_frags[37])
if tmpl_topic_alt_vars.Topic.Tag != "" { w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink))
w.Write(topic_alt_frags[38]) w.Write(topic_alt_frags[38])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Tag)) w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedByName))
w.Write(topic_alt_frags[39]) w.Write(topic_alt_frags[39])
} else { if tmpl_topic_alt_vars.Topic.Tag != "" {
w.Write(topic_alt_frags[40]) w.Write(topic_alt_frags[40])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Tag))
w.Write(topic_alt_frags[41])
} else {
w.Write(topic_alt_frags[42])
w.Write(phrases[29]) w.Write(phrases[29])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.Level))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.Level)))
w.Write(topic_alt_frags[41])
}
w.Write(topic_alt_frags[42])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[43]) w.Write(topic_alt_frags[43])
if len(tmpl_topic_alt_vars.Poll.QuickOptions) != 0 { }
for _, item := range tmpl_topic_alt_vars.Poll.QuickOptions {
w.Write(topic_alt_frags[44]) w.Write(topic_alt_frags[44])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[45]) w.Write(topic_alt_frags[45])
w.Write([]byte(strconv.Itoa(item.ID))) if len(tmpl_topic_alt_vars.Poll.QuickOptions) != 0 {
for _, item := range tmpl_topic_alt_vars.Poll.QuickOptions {
w.Write(topic_alt_frags[46]) w.Write(topic_alt_frags[46])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[47]) w.Write(topic_alt_frags[47])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[48]) w.Write(topic_alt_frags[48])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[49]) w.Write(topic_alt_frags[49])
w.Write([]byte(item.Value)) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[50]) w.Write(topic_alt_frags[50])
} w.Write([]byte(strconv.Itoa(item.ID)))
}
w.Write(topic_alt_frags[51]) w.Write(topic_alt_frags[51])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID))) w.Write([]byte(item.Value))
w.Write(topic_alt_frags[52]) w.Write(topic_alt_frags[52])
w.Write(phrases[30]) }
}
w.Write(topic_alt_frags[53]) w.Write(topic_alt_frags[53])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[54]) w.Write(topic_alt_frags[54])
w.Write(phrases[31]) w.Write(phrases[30])
w.Write(topic_alt_frags[55]) w.Write(topic_alt_frags[55])
w.Write(phrases[32])
w.Write(topic_alt_frags[56])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[56])
w.Write(phrases[31])
w.Write(topic_alt_frags[57]) w.Write(topic_alt_frags[57])
} w.Write(phrases[32])
w.Write(topic_alt_frags[58]) w.Write(topic_alt_frags[58])
w.Write(phrases[33]) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[59]) w.Write(topic_alt_frags[59])
w.Write(phrases[34]) }
w.Write(topic_alt_frags[60]) w.Write(topic_alt_frags[60])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Avatar))
w.Write(topic_alt_frags[61]) w.Write(topic_alt_frags[61])
w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink)) w.Write(phrases[33])
w.Write(topic_alt_frags[62]) w.Write(topic_alt_frags[62])
w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedByName)) w.Write(phrases[34])
w.Write(topic_alt_frags[63]) w.Write(topic_alt_frags[63])
if tmpl_topic_alt_vars.Topic.Tag != "" { w.Write([]byte(tmpl_topic_alt_vars.Topic.Avatar))
w.Write(topic_alt_frags[64]) w.Write(topic_alt_frags[64])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Tag)) w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink))
w.Write(topic_alt_frags[65]) w.Write(topic_alt_frags[65])
} else { w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedByName))
w.Write(topic_alt_frags[66]) w.Write(topic_alt_frags[66])
if tmpl_topic_alt_vars.Topic.Tag != "" {
w.Write(topic_alt_frags[67])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Tag))
w.Write(topic_alt_frags[68])
} else {
w.Write(topic_alt_frags[69])
w.Write(phrases[35]) w.Write(phrases[35])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.Level))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.Level)))
w.Write(topic_alt_frags[67])
}
w.Write(topic_alt_frags[68])
w.Write([]byte(tmpl_topic_alt_vars.Topic.ContentHTML))
w.Write(topic_alt_frags[69])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_frags[70]) w.Write(topic_alt_frags[70])
if tmpl_topic_alt_vars.CurrentUser.Loggedin { }
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_frags[71]) w.Write(topic_alt_frags[71])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) w.Write([]byte(tmpl_topic_alt_vars.Topic.ContentHTML))
w.Write(topic_alt_frags[72]) w.Write(topic_alt_frags[72])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_frags[73]) w.Write(topic_alt_frags[73])
w.Write(phrases[36]) if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_frags[74]) w.Write(topic_alt_frags[74])
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_alt_frags[75]) w.Write(topic_alt_frags[75])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_frags[76]) w.Write(topic_alt_frags[76])
w.Write(phrases[37]) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[77]) w.Write(topic_alt_frags[77])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[78])
if tmpl_topic_alt_vars.Topic.Liked {
w.Write(topic_alt_frags[79])
} else {
w.Write(topic_alt_frags[80])
}
w.Write(topic_alt_frags[81])
w.Write(phrases[36])
w.Write(topic_alt_frags[82])
}
if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_alt_frags[83])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[84])
w.Write(phrases[37])
w.Write(topic_alt_frags[85])
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteTopic { if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteTopic {
w.Write(topic_alt_frags[78])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[79])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[80])
w.Write(phrases[38])
w.Write(topic_alt_frags[81])
}
if tmpl_topic_alt_vars.CurrentUser.Perms.CloseTopic {
if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_frags[82])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[83])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[84])
w.Write(phrases[39])
w.Write(topic_alt_frags[85])
} else {
w.Write(topic_alt_frags[86]) w.Write(topic_alt_frags[86])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[87]) w.Write(topic_alt_frags[87])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[88]) w.Write(topic_alt_frags[88])
w.Write(phrases[40]) w.Write(phrases[38])
w.Write(topic_alt_frags[89]) w.Write(topic_alt_frags[89])
} }
} if tmpl_topic_alt_vars.CurrentUser.Perms.CloseTopic {
if tmpl_topic_alt_vars.CurrentUser.Perms.PinTopic { if tmpl_topic_alt_vars.Topic.IsClosed {
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write(topic_alt_frags[90]) w.Write(topic_alt_frags[90])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[91]) w.Write(topic_alt_frags[91])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[92]) w.Write(topic_alt_frags[92])
w.Write(phrases[41]) w.Write(phrases[39])
w.Write(topic_alt_frags[93]) w.Write(topic_alt_frags[93])
} else { } else {
w.Write(topic_alt_frags[94]) w.Write(topic_alt_frags[94])
@ -406,208 +401,222 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[95]) w.Write(topic_alt_frags[95])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[96]) w.Write(topic_alt_frags[96])
w.Write(phrases[42]) w.Write(phrases[40])
w.Write(topic_alt_frags[97]) w.Write(topic_alt_frags[97])
} }
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs { if tmpl_topic_alt_vars.CurrentUser.Perms.PinTopic {
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write(topic_alt_frags[98]) w.Write(topic_alt_frags[98])
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress)) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[99]) w.Write(topic_alt_frags[99])
w.Write(phrases[43]) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[100]) w.Write(topic_alt_frags[100])
w.Write(phrases[44]) w.Write(phrases[41])
w.Write(topic_alt_frags[101]) w.Write(topic_alt_frags[101])
} } else {
w.Write(topic_alt_frags[102]) w.Write(topic_alt_frags[102])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[103]) w.Write(topic_alt_frags[103])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[104]) w.Write(topic_alt_frags[104])
w.Write(phrases[45]) w.Write(phrases[42])
w.Write(topic_alt_frags[105]) w.Write(topic_alt_frags[105])
} }
w.Write(topic_alt_frags[106])
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_frags[107])
} }
w.Write(topic_alt_frags[108])
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_frags[109])
w.Write(phrases[46])
w.Write(topic_alt_frags[110])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
w.Write(topic_alt_frags[111])
}
w.Write(topic_alt_frags[112])
w.Write([]byte(tmpl_topic_alt_vars.Topic.RelativeCreatedAt))
w.Write(topic_alt_frags[113])
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs { if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_frags[114]) w.Write(topic_alt_frags[106])
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress)) w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_frags[115]) w.Write(topic_alt_frags[107])
w.Write(phrases[47]) w.Write(phrases[43])
w.Write(topic_alt_frags[116]) w.Write(topic_alt_frags[108])
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress)) w.Write(phrases[44])
w.Write(topic_alt_frags[117]) w.Write(topic_alt_frags[109])
} }
w.Write(topic_alt_frags[110])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[111])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[112])
w.Write(phrases[45])
w.Write(topic_alt_frags[113])
}
w.Write(topic_alt_frags[114])
w.Write(phrases[46])
w.Write(topic_alt_frags[115])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
w.Write(topic_alt_frags[116])
w.Write([]byte(tmpl_topic_alt_vars.Topic.RelativeCreatedAt))
w.Write(topic_alt_frags[117])
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_frags[118]) w.Write(topic_alt_frags[118])
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_frags[119])
w.Write(phrases[47])
w.Write(topic_alt_frags[120])
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_frags[121])
}
w.Write(topic_alt_frags[122])
if len(tmpl_topic_alt_vars.ItemList) != 0 { if len(tmpl_topic_alt_vars.ItemList) != 0 {
for _, item := range tmpl_topic_alt_vars.ItemList { for _, item := range tmpl_topic_alt_vars.ItemList {
w.Write(topic_alt_frags[119])
if item.ActionType != "" {
w.Write(topic_alt_frags[120])
}
w.Write(topic_alt_frags[121])
w.Write(phrases[48])
w.Write(topic_alt_frags[122])
w.Write([]byte(item.Avatar))
w.Write(topic_alt_frags[123]) w.Write(topic_alt_frags[123])
w.Write([]byte(item.UserLink))
w.Write(topic_alt_frags[124]) w.Write(topic_alt_frags[124])
w.Write([]byte(item.CreatedByName)) if item.ActionType != "" {
w.Write(topic_alt_frags[125]) w.Write(topic_alt_frags[125])
if item.Tag != "" { }
w.Write(topic_alt_frags[126]) w.Write(topic_alt_frags[126])
w.Write([]byte(item.Tag)) w.Write(phrases[48])
w.Write(topic_alt_frags[127]) w.Write(topic_alt_frags[127])
} else { w.Write([]byte(item.Avatar))
w.Write(topic_alt_frags[128]) w.Write(topic_alt_frags[128])
w.Write([]byte(item.UserLink))
w.Write(topic_alt_frags[129])
w.Write([]byte(item.CreatedByName))
w.Write(topic_alt_frags[130])
if item.Tag != "" {
w.Write(topic_alt_frags[131])
w.Write([]byte(item.Tag))
w.Write(topic_alt_frags[132])
} else {
w.Write(topic_alt_frags[133])
w.Write(phrases[49]) w.Write(phrases[49])
w.Write([]byte(strconv.Itoa(item.Level))) w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_alt_frags[129])
}
w.Write(topic_alt_frags[130])
if item.ActionType != "" {
w.Write(topic_alt_frags[131])
}
w.Write(topic_alt_frags[132])
if item.ActionType != "" {
w.Write(topic_alt_frags[133])
w.Write([]byte(item.ActionIcon))
w.Write(topic_alt_frags[134]) w.Write(topic_alt_frags[134])
w.Write([]byte(item.ActionType)) }
w.Write(topic_alt_frags[135]) w.Write(topic_alt_frags[135])
} else { if item.ActionType != "" {
w.Write(topic_alt_frags[136]) w.Write(topic_alt_frags[136])
w.Write([]byte(item.ContentHtml)) }
w.Write(topic_alt_frags[137]) w.Write(topic_alt_frags[137])
if item.ActionType != "" {
w.Write(topic_alt_frags[138])
w.Write([]byte(item.ActionIcon))
w.Write(topic_alt_frags[139])
w.Write([]byte(item.ActionType))
w.Write(topic_alt_frags[140])
} else {
w.Write(topic_alt_frags[141])
w.Write([]byte(item.ContentHtml))
w.Write(topic_alt_frags[142])
if item.LikeCount > 0 {
w.Write(topic_alt_frags[143])
}
w.Write(topic_alt_frags[144])
if tmpl_topic_alt_vars.CurrentUser.Loggedin { if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem { if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_frags[138])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[139])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[140])
w.Write(phrases[50])
w.Write(topic_alt_frags[141])
}
if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write(topic_alt_frags[142])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[143])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[144])
w.Write(phrases[51])
w.Write(topic_alt_frags[145]) w.Write(topic_alt_frags[145])
}
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_frags[146])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[147]) w.Write(topic_alt_frags[146])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[147])
if item.Liked {
w.Write(topic_alt_frags[148]) w.Write(topic_alt_frags[148])
w.Write(phrases[52]) } else {
w.Write(topic_alt_frags[149]) w.Write(topic_alt_frags[149])
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_frags[150]) w.Write(topic_alt_frags[150])
w.Write([]byte(item.IPAddress)) w.Write(phrases[50])
w.Write(topic_alt_frags[151]) w.Write(topic_alt_frags[151])
w.Write(phrases[53]) }
if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write(topic_alt_frags[152]) w.Write(topic_alt_frags[152])
w.Write(phrases[54])
w.Write(topic_alt_frags[153])
}
w.Write(topic_alt_frags[154])
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[155]) w.Write(topic_alt_frags[153])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[156]) w.Write(topic_alt_frags[154])
w.Write(phrases[55]) w.Write(phrases[51])
w.Write(topic_alt_frags[157]) w.Write(topic_alt_frags[155])
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_frags[156])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[157])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[158]) w.Write(topic_alt_frags[158])
if item.LikeCount > 0 { w.Write(phrases[52])
w.Write(topic_alt_frags[159]) w.Write(topic_alt_frags[159])
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_frags[160]) w.Write(topic_alt_frags[160])
if item.LikeCount > 0 { w.Write([]byte(item.IPAddress))
w.Write(topic_alt_frags[161]) w.Write(topic_alt_frags[161])
w.Write(phrases[56]) w.Write(phrases[53])
w.Write(topic_alt_frags[162]) w.Write(topic_alt_frags[162])
w.Write([]byte(strconv.Itoa(item.LikeCount))) w.Write(phrases[54])
w.Write(topic_alt_frags[163]) w.Write(topic_alt_frags[163])
} }
w.Write(topic_alt_frags[164]) w.Write(topic_alt_frags[164])
w.Write([]byte(item.RelativeCreatedAt)) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[165]) w.Write(topic_alt_frags[165])
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs { w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[166]) w.Write(topic_alt_frags[166])
w.Write([]byte(item.IPAddress)) w.Write(phrases[55])
w.Write(topic_alt_frags[167]) w.Write(topic_alt_frags[167])
w.Write([]byte(item.IPAddress)) }
w.Write(topic_alt_frags[168]) w.Write(topic_alt_frags[168])
} w.Write(phrases[56])
w.Write(topic_alt_frags[169]) w.Write(topic_alt_frags[169])
} w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topic_alt_frags[170]) w.Write(topic_alt_frags[170])
} w.Write([]byte(item.RelativeCreatedAt))
}
w.Write(topic_alt_frags[171]) w.Write(topic_alt_frags[171])
if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply { if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_frags[172]) w.Write(topic_alt_frags[172])
w.Write(phrases[57]) w.Write([]byte(item.IPAddress))
w.Write(topic_alt_frags[173]) w.Write(topic_alt_frags[173])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Avatar)) w.Write([]byte(item.IPAddress))
w.Write(topic_alt_frags[174]) w.Write(topic_alt_frags[174])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Link)) }
w.Write(topic_alt_frags[175]) w.Write(topic_alt_frags[175])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Name)) }
w.Write(topic_alt_frags[176]) w.Write(topic_alt_frags[176])
if tmpl_topic_alt_vars.CurrentUser.Tag != "" { }
}
w.Write(topic_alt_frags[177]) w.Write(topic_alt_frags[177])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Tag)) if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_alt_frags[178]) w.Write(topic_alt_frags[178])
} else { w.Write(phrases[57])
w.Write(topic_alt_frags[179]) w.Write(topic_alt_frags[179])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Avatar))
w.Write(topic_alt_frags[180])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Link))
w.Write(topic_alt_frags[181])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Name))
w.Write(topic_alt_frags[182])
if tmpl_topic_alt_vars.CurrentUser.Tag != "" {
w.Write(topic_alt_frags[183])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Tag))
w.Write(topic_alt_frags[184])
} else {
w.Write(topic_alt_frags[185])
w.Write(phrases[58]) w.Write(phrases[58])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.CurrentUser.Level))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.CurrentUser.Level)))
w.Write(topic_alt_frags[180])
}
w.Write(topic_alt_frags[181])
w.Write(phrases[59])
w.Write(topic_alt_frags[182])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[183])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[184])
w.Write(phrases[60])
w.Write(topic_alt_frags[185])
w.Write(phrases[61])
w.Write(topic_alt_frags[186]) w.Write(topic_alt_frags[186])
w.Write(phrases[62]) }
w.Write(topic_alt_frags[187]) w.Write(topic_alt_frags[187])
w.Write(phrases[63]) w.Write(phrases[59])
w.Write(topic_alt_frags[188]) w.Write(topic_alt_frags[188])
if tmpl_topic_alt_vars.CurrentUser.Perms.UploadFiles { w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[189]) w.Write(topic_alt_frags[189])
w.Write(phrases[64]) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[190]) w.Write(topic_alt_frags[190])
} w.Write(phrases[60])
w.Write(topic_alt_frags[191]) w.Write(topic_alt_frags[191])
} w.Write(phrases[61])
w.Write(topic_alt_frags[192]) w.Write(topic_alt_frags[192])
w.Write(phrases[62])
w.Write(topic_alt_frags[193])
w.Write(phrases[63])
w.Write(topic_alt_frags[194])
if tmpl_topic_alt_vars.CurrentUser.Perms.UploadFiles {
w.Write(topic_alt_frags[195])
w.Write(phrases[64])
w.Write(topic_alt_frags[196])
}
w.Write(topic_alt_frags[197])
}
w.Write(topic_alt_frags[198])
w.Write(footer_frags[0]) w.Write(footer_frags[0])
w.Write([]byte(common.BuildWidget("footer",tmpl_topic_alt_vars.Header))) w.Write([]byte(common.BuildWidget("footer",tmpl_topic_alt_vars.Header)))
w.Write(footer_frags[1]) w.Write(footer_frags[1])

View File

@ -317,67 +317,75 @@ w.Write([]byte(item.Link))
w.Write(topics_frags[62]) w.Write(topics_frags[62])
w.Write([]byte(item.Title)) w.Write([]byte(item.Title))
w.Write(topics_frags[63]) w.Write(topics_frags[63])
if item.ForumName != "" { w.Write([]byte(item.Title))
w.Write(topics_frags[64]) w.Write(topics_frags[64])
w.Write([]byte(item.ForumLink)) if item.ForumName != "" {
w.Write(topics_frags[65]) w.Write(topics_frags[65])
w.Write([]byte(item.ForumName)) w.Write([]byte(item.ForumLink))
w.Write(topics_frags[66]) w.Write(topics_frags[66])
} w.Write([]byte(item.ForumName))
w.Write(topics_frags[67]) w.Write(topics_frags[67])
w.Write([]byte(item.Creator.Link)) w.Write([]byte(item.ForumName))
w.Write(topics_frags[68]) w.Write(topics_frags[68])
w.Write([]byte(item.Creator.Name)) }
w.Write(topics_frags[69]) w.Write(topics_frags[69])
if item.IsClosed { w.Write([]byte(item.Creator.Link))
w.Write(topics_frags[70]) w.Write(topics_frags[70])
w.Write(phrases[44]) w.Write([]byte(item.Creator.Name))
w.Write(topics_frags[71]) w.Write(topics_frags[71])
} w.Write([]byte(item.Creator.Name))
if item.Sticky {
w.Write(topics_frags[72]) w.Write(topics_frags[72])
w.Write(phrases[45]) if item.IsClosed {
w.Write(topics_frags[73]) w.Write(topics_frags[73])
} w.Write(phrases[44])
w.Write(topics_frags[74]) w.Write(topics_frags[74])
w.Write([]byte(strconv.Itoa(item.PostCount))) }
w.Write(topics_frags[75])
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topics_frags[76])
if item.Sticky { if item.Sticky {
w.Write(topics_frags[75])
w.Write(phrases[45])
w.Write(topics_frags[76])
}
w.Write(topics_frags[77]) w.Write(topics_frags[77])
w.Write([]byte(strconv.Itoa(item.PostCount)))
w.Write(topics_frags[78])
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topics_frags[79])
if item.Sticky {
w.Write(topics_frags[80])
} else { } else {
if item.IsClosed { if item.IsClosed {
w.Write(topics_frags[78])
}
}
w.Write(topics_frags[79])
w.Write([]byte(item.LastUser.Link))
w.Write(topics_frags[80])
w.Write([]byte(item.LastUser.Avatar))
w.Write(topics_frags[81]) w.Write(topics_frags[81])
w.Write([]byte(item.LastUser.Name)) }
}
w.Write(topics_frags[82]) w.Write(topics_frags[82])
w.Write([]byte(item.LastUser.Name))
w.Write(topics_frags[83])
w.Write([]byte(item.LastUser.Link)) w.Write([]byte(item.LastUser.Link))
w.Write(topics_frags[83])
w.Write([]byte(item.LastUser.Avatar))
w.Write(topics_frags[84]) w.Write(topics_frags[84])
w.Write([]byte(item.LastUser.Name)) w.Write([]byte(item.LastUser.Name))
w.Write(topics_frags[85]) w.Write(topics_frags[85])
w.Write([]byte(item.RelativeLastReplyAt)) w.Write([]byte(item.LastUser.Name))
w.Write(topics_frags[86]) w.Write(topics_frags[86])
} w.Write([]byte(item.LastUser.Link))
} else {
w.Write(topics_frags[87]) w.Write(topics_frags[87])
w.Write(phrases[46]) w.Write([]byte(item.LastUser.Name))
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
w.Write(topics_frags[88]) w.Write(topics_frags[88])
w.Write(phrases[47]) w.Write([]byte(item.LastUser.Name))
w.Write(topics_frags[89]) w.Write(topics_frags[89])
} w.Write([]byte(item.RelativeLastReplyAt))
w.Write(topics_frags[90]) w.Write(topics_frags[90])
} }
} else {
w.Write(topics_frags[91]) w.Write(topics_frags[91])
w.Write(phrases[46])
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
w.Write(topics_frags[92])
w.Write(phrases[47])
w.Write(topics_frags[93])
}
w.Write(topics_frags[94])
}
w.Write(topics_frags[95])
if tmpl_topics_vars.LastPage > 1 { if tmpl_topics_vars.LastPage > 1 {
w.Write(paginator_frags[0]) w.Write(paginator_frags[0])
if tmpl_topics_vars.Page > 1 { if tmpl_topics_vars.Page > 1 {
@ -413,7 +421,7 @@ w.Write(paginator_frags[13])
} }
w.Write(paginator_frags[14]) w.Write(paginator_frags[14])
} }
w.Write(topics_frags[92]) w.Write(topics_frags[96])
w.Write(footer_frags[0]) w.Write(footer_frags[0])
w.Write([]byte(common.BuildWidget("footer",tmpl_topics_vars.Header))) w.Write([]byte(common.BuildWidget("footer",tmpl_topics_vars.Header)))
w.Write(footer_frags[1]) w.Write(footer_frags[1])

View File

@ -82,8 +82,8 @@
<span class="selector"></span> <span class="selector"></span>
<a href="{{.Creator.Link}}"><img src="{{.Creator.Avatar}}" height="64" alt="{{.Creator.Name}}'s Avatar" title="{{.Creator.Name}}'s Avatar" /></a> <a href="{{.Creator.Link}}"><img src="{{.Creator.Avatar}}" height="64" alt="{{.Creator.Name}}'s Avatar" title="{{.Creator.Name}}'s Avatar" /></a>
<span class="topic_inner_left"> <span class="topic_inner_left">
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement"><span>{{.Title}}</span></a> <a class="rowtopic" href="{{.Link}}" itemprop="itemListElement" title="{{.Title}}"><span>{{.Title}}</span></a>
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a> <br /><a class="rowsmall starter" href="{{.Creator.Link}}" title="{{.Creator.Name}}">{{.Creator.Name}}</a>
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}} {{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="{{lang "status_closed_tooltip"}}"> | &#x1F512;&#xFE0E</span>{{end}} {{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="{{lang "status_closed_tooltip"}}"> | &#x1F512;&#xFE0E</span>{{end}}
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="{{lang "status_pinned_tooltip"}}"> | &#x1F4CD;&#xFE0E</span>{{end}} {{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="{{lang "status_pinned_tooltip"}}"> | &#x1F4CD;&#xFE0E</span>{{end}}
@ -96,7 +96,7 @@
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}"> <div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
<a href="{{.LastUser.Link}}"><img src="{{.LastUser.Avatar}}" height="64" alt="{{.LastUser.Name}}'s Avatar" title="{{.LastUser.Name}}'s Avatar" /></a> <a href="{{.LastUser.Link}}"><img src="{{.LastUser.Avatar}}" height="64" alt="{{.LastUser.Name}}'s Avatar" title="{{.LastUser.Name}}'s Avatar" /></a>
<span> <span>
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br> <a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;" title="{{.LastUser.Name}}">{{.LastUser.Name}}</a><br>
<span class="rowsmall lastReplyAt">{{.RelativeLastReplyAt}}</span> <span class="rowsmall lastReplyAt">{{.RelativeLastReplyAt}}</span>
</span> </span>
</div> </div>

View File

@ -9,7 +9,7 @@
<img src="{{.ProfileOwner.Avatar}}" class="avatar" alt="{{.ProfileOwner.Name}}'s Avatar" title="{{.ProfileOwner.Name}}'s Avatar" /> <img src="{{.ProfileOwner.Avatar}}" class="avatar" alt="{{.ProfileOwner.Name}}'s Avatar" title="{{.ProfileOwner.Name}}'s Avatar" />
</div> </div>
<div class="rowitem nameRow"> <div class="rowitem nameRow">
<span class="profileName">{{.ProfileOwner.Name}}</span>{{if .ProfileOwner.Tag}}<span class="username">{{.ProfileOwner.Tag}}</span>{{end}} <span class="profileName" title="{{.ProfileOwner.Name}}">{{.ProfileOwner.Name}}</span>{{if .ProfileOwner.Tag}}<span class="username" title="{{.ProfileOwner.Tag}}">{{.ProfileOwner.Tag}}</span>{{end}}
</div> </div>
</div> </div>
<div class="passiveBlock"> <div class="passiveBlock">

View File

@ -11,9 +11,9 @@
<main> <main>
<div class="rowblock rowhead topic_block" aria-label="{{lang "topic_opening_post_aria"}}"> <div {{scope "topic_title_block"}} class="rowblock rowhead topic_block" aria-label="{{lang "topic_opening_post_aria"}}">
<div class="rowitem topic_item{{if .Topic.Sticky}} topic_sticky_head{{else if .Topic.IsClosed}} topic_closed_head{{end}}"> <div class="rowitem topic_item{{if .Topic.Sticky}} topic_sticky_head{{else if .Topic.IsClosed}} topic_closed_head{{end}}">
<h1 class='topic_name hide_on_edit'>{{.Topic.Title}}</h1> <h1 class='topic_name hide_on_edit' title='{{.Topic.Title}}'>{{.Topic.Title}}</h1>
{{if .Topic.IsClosed}}<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='{{lang "status_closed_tooltip"}}' aria-label='{{lang "topic_status_closed_aria"}}'>&#x1F512;&#xFE0E</span>{{end}} {{if .Topic.IsClosed}}<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='{{lang "status_closed_tooltip"}}' aria-label='{{lang "topic_status_closed_aria"}}'>&#x1F512;&#xFE0E</span>{{end}}
{{if .CurrentUser.Perms.EditTopic}} {{if .CurrentUser.Perms.EditTopic}}
<input form='edit_topic_form' class='show_on_edit topic_name_input' name="topic_name" value='{{.Topic.Title}}' type="text" aria-label="{{lang "topic_title_input_aria"}}" /> <input form='edit_topic_form' class='show_on_edit topic_name_input' name="topic_name" value='{{.Topic.Title}}' type="text" aria-label="{{lang "topic_title_input_aria"}}" />
@ -47,16 +47,16 @@
</article> </article>
{{end}} {{end}}
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="{{lang "topic_opening_post_aria"}}"> <article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="{{lang "topic_opening_post_aria"}}">
<div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;"> <div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
<p class="hide_on_edit topic_content user_content" itemprop="text" style="margin:0;padding:0;">{{.Topic.ContentHTML}}</p> <p class="hide_on_edit topic_content user_content" itemprop="text" style="margin:0;padding:0;">{{.Topic.ContentHTML}}</p>
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea> <textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
<span class="controls" aria-label="{{lang "topic_post_controls_aria"}}"> <span class="controls{{if .Topic.LikeCount}} has_likes{{end}}" aria-label="{{lang "topic_post_controls_aria"}}">
<a href="{{.Topic.UserLink}}" class="username real_username" rel="author">{{.Topic.CreatedByName}}</a>&nbsp;&nbsp; <a href="{{.Topic.UserLink}}" class="username real_username" rel="author">{{.Topic.CreatedByName}}</a>&nbsp;&nbsp;
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="mod_button"{{if .Topic.Liked}} title="{{lang "topic_unlike_tooltip"}}" aria-label="{{lang "topic_unlike_aria"}}"{{else}} title="{{lang "topic_like_tooltip"}}" aria-label="{{lang "topic_like_aria"}}"{{end}} style="color:#202020;"> {{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="mod_button"{{if .Topic.Liked}} title="{{lang "topic_unlike_tooltip"}}" aria-label="{{lang "topic_unlike_aria"}}"{{else}} title="{{lang "topic_like_tooltip"}}" aria-label="{{lang "topic_like_aria"}}"{{end}} style="color:#202020;">
<button class="username like_label"{{if .Topic.Liked}} style="background-color:#D6FFD6;"{{end}}></button></a>{{end}} <button class="username like_label {{if .Topic.Liked}}remove_like{{else}}add_like{{end}}"></button></a>{{end}}
{{if .CurrentUser.Perms.EditTopic}}<a href='/topic/edit/{{.Topic.ID}}' class="mod_button open_edit" style="font-weight:normal;" title="{{lang "topic_edit_tooltip"}}" aria-label="{{lang "topic_edit_aria"}}"><button class="username edit_label"></button></a>{{end}} {{if .CurrentUser.Perms.EditTopic}}<a href='/topic/edit/{{.Topic.ID}}' class="mod_button open_edit" style="font-weight:normal;" title="{{lang "topic_edit_tooltip"}}" aria-label="{{lang "topic_edit_aria"}}"><button class="username edit_label"></button></a>{{end}}
@ -68,7 +68,7 @@
{{if .CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.Topic.IPAddress}}' style="font-weight:normal;" title="{{lang "topic_ip_tooltip"}}" aria-label="The poster's IP is {{.Topic.IPAddress}}"><button class="username ip_label"></button></a>{{end}} {{if .CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.Topic.IPAddress}}' style="font-weight:normal;" title="{{lang "topic_ip_tooltip"}}" aria-label="The poster's IP is {{.Topic.IPAddress}}"><button class="username ip_label"></button></a>{{end}}
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="{{lang "topic_flag_tooltip"}}" aria-label="{{lang "topic_flag_aria"}}" rel="nofollow"><button class="username flag_label"></button></a> <a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="{{lang "topic_flag_tooltip"}}" aria-label="{{lang "topic_flag_aria"}}" rel="nofollow"><button class="username flag_label"></button></a>
{{if .Topic.LikeCount}}<a class="username hide_on_micro like_count" aria-label="{{lang "topic_like_count_aria"}}">{{.Topic.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="{{lang "topic_like_count_tooltip"}}"></a>{{end}} <a class="username hide_on_micro like_count" aria-label="{{lang "topic_like_count_aria"}}">{{.Topic.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="{{lang "topic_like_count_tooltip"}}"></a>
{{if .Topic.Tag}}<a class="username hide_on_micro user_tag">{{.Topic.Tag}}</a>{{else}}<a class="username hide_on_micro level" aria-label="{{lang "topic_level_aria"}}">{{.Topic.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="{{lang "topic_level_tooltip"}}"></a>{{end}} {{if .Topic.Tag}}<a class="username hide_on_micro user_tag">{{.Topic.Tag}}</a>{{else}}<a class="username hide_on_micro level" aria-label="{{lang "topic_level_aria"}}">{{.Topic.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="{{lang "topic_level_tooltip"}}"></a>{{end}}
@ -82,14 +82,14 @@
<span itemprop="text">{{.ActionType}}</span> <span itemprop="text">{{.ActionType}}</span>
</article> </article>
{{else}} {{else}}
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item {{.ClassName}}" style="background-image: url({{.Avatar}}), url(/static/{{$.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;"> <article {{scope "post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item {{.ClassName}}" style="background-image: url({{.Avatar}}), url(/static/{{$.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
{{/** TODO: We might end up with <br>s in the inline editor, fix this **/}} {{/** TODO: We might end up with <br>s in the inline editor, fix this **/}}
<p class="editable_block user_content" itemprop="text" style="margin:0;padding:0;">{{.ContentHtml}}</p> <p class="editable_block user_content" itemprop="text" style="margin:0;padding:0;">{{.ContentHtml}}</p>
<span class="controls"> <span class="controls{{if .LikeCount}} has_likes{{end}}">
<a href="{{.UserLink}}" class="username real_username" rel="author">{{.CreatedByName}}</a>&nbsp;&nbsp; <a href="{{.UserLink}}" class="username real_username" rel="author">{{.CreatedByName}}</a>&nbsp;&nbsp;
{{if $.CurrentUser.Perms.LikeItem}}{{if .Liked}}<a href="/reply/like/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic_post_like_tooltip"}}" aria-label="{{lang "topic_post_like_aria"}}" style="color:#202020;"><button class="username like_label" style="background-color:#D6FFD6;"></button></a>{{else}}<a href="/reply/like/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic_post_unlike_tooltip"}}" aria-label="{{lang "topic_post_unlike_aria"}}" style="color:#202020;"><button class="username like_label"></button></a>{{end}}{{end}} {{if $.CurrentUser.Perms.LikeItem}}{{if .Liked}}<a href="/reply/like/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic_post_like_tooltip"}}" aria-label="{{lang "topic_post_like_aria"}}" style="color:#202020;"><button class="username like_label remove_like"></button></a>{{else}}<a href="/reply/like/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic_post_unlike_tooltip"}}" aria-label="{{lang "topic_post_unlike_aria"}}" style="color:#202020;"><button class="username like_label add_like"></button></a>{{end}}{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic_post_edit_tooltip"}}" aria-label="{{lang "topic_post_edit_aria"}}"><button class="username edit_item edit_label"></button></a>{{end}} {{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic_post_edit_tooltip"}}" aria-label="{{lang "topic_post_edit_aria"}}"><button class="username edit_item edit_label"></button></a>{{end}}
@ -97,7 +97,7 @@
{{if $.CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.IPAddress}}' style="font-weight:normal;" title="{{lang "topic_post_ip_tooltip"}}" aria-label="The poster's IP is {{.IPAddress}}"><button class="username ip_label"></button></a>{{end}} {{if $.CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.IPAddress}}' style="font-weight:normal;" title="{{lang "topic_post_ip_tooltip"}}" aria-label="The poster's IP is {{.IPAddress}}"><button class="username ip_label"></button></a>{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button report_item" title="{{lang "topic_post_flag_tooltip"}}" aria-label="{{lang "topic_post_flag_aria"}}" rel="nofollow"><button class="username report_item flag_label"></button></a> <a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button report_item" title="{{lang "topic_post_flag_tooltip"}}" aria-label="{{lang "topic_post_flag_aria"}}" rel="nofollow"><button class="username report_item flag_label"></button></a>
{{if .LikeCount}}<a class="username hide_on_micro like_count">{{.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="{{lang "topic_post_like_count_tooltip"}}"></a>{{end}} <a class="username hide_on_micro like_count">{{.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="{{lang "topic_post_like_count_tooltip"}}"></a>
{{if .Tag}}<a class="username hide_on_micro user_tag">{{.Tag}}</a>{{else}}<a class="username hide_on_micro level" aria-label="{{lang "topic_post_level_aria"}}">{{.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="{{lang "topic_post_level_tooltip"}}"></a>{{end}} {{if .Tag}}<a class="username hide_on_micro user_tag">{{.Tag}}</a>{{else}}<a class="username hide_on_micro level" aria-label="{{lang "topic_post_level_aria"}}">{{.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="{{lang "topic_post_level_tooltip"}}"></a>{{end}}

View File

@ -8,10 +8,10 @@
<main> <main>
<div class="rowblock rowhead topic_block" aria-label="{{lang "topic_opening_post_aria"}}"> <div {{scope "topic_title_block"}} class="rowblock rowhead topic_block" aria-label="{{lang "topic_opening_post_aria"}}">
<form action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post"> <form action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post">
<div class="rowitem topic_item{{if .Topic.Sticky}} topic_sticky_head{{else if .Topic.IsClosed}} topic_closed_head{{end}}"> <div class="rowitem topic_item{{if .Topic.Sticky}} topic_sticky_head{{else if .Topic.IsClosed}} topic_closed_head{{end}}">
<h1 class='topic_name hide_on_edit'>{{.Topic.Title}}</h1> <h1 class='topic_name hide_on_edit' title='{{.Topic.Title}}'>{{.Topic.Title}}</h1>
{{/** TODO: Inline this CSS **/}} {{/** TODO: Inline this CSS **/}}
{{if .Topic.IsClosed}}<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='{{lang "status_closed_tooltip"}}' aria-label='{{lang "topic_status_closed_aria"}}' style="font-weight:normal;float: right;position:relative;top:-5px;">&#x1F512;&#xFE0E</span>{{end}} {{if .Topic.IsClosed}}<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='{{lang "status_closed_tooltip"}}' aria-label='{{lang "topic_status_closed_aria"}}' style="font-weight:normal;float: right;position:relative;top:-5px;">&#x1F512;&#xFE0E</span>{{end}}
{{if .CurrentUser.Perms.EditTopic}} {{if .CurrentUser.Perms.EditTopic}}
@ -54,7 +54,7 @@
</div> </div>
</article> </article>
{{end}} {{end}}
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item top_post" aria-label="{{lang "topic_opening_post_aria"}}"> <article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item top_post" aria-label="{{lang "topic_opening_post_aria"}}">
<div class="userinfo" aria-label="{{lang "topic_userinfo_aria"}}"> <div class="userinfo" aria-label="{{lang "topic_userinfo_aria"}}">
<div class="avatar_item" style="background-image: url({{.Topic.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div> <div class="avatar_item" style="background-image: url({{.Topic.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="{{.Topic.UserLink}}" class="the_name" rel="author">{{.Topic.CreatedByName}}</a> <a href="{{.Topic.UserLink}}" class="the_name" rel="author">{{.Topic.CreatedByName}}</a>
@ -63,9 +63,9 @@
<div class="content_container"> <div class="content_container">
<div class="hide_on_edit topic_content user_content" itemprop="text">{{.Topic.ContentHTML}}</div> <div class="hide_on_edit topic_content user_content" itemprop="text">{{.Topic.ContentHTML}}</div>
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea> <textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
<div class="button_container"> <div class="controls button_container{{if .Topic.LikeCount}} has_likes{{end}}">
{{if .CurrentUser.Loggedin}} {{if .CurrentUser.Loggedin}}
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="action_button like_item add_like" aria-label="{{lang "topic_like_aria"}}" data-action="like"></a>{{end}} {{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="action_button like_item {{if .Topic.Liked}}remove_like{{else}}add_like{{end}}" aria-label="{{lang "topic_like_aria"}}" data-action="like"></a>{{end}}
{{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit" aria-label="{{lang "topic_edit_aria"}}" data-action="edit"></a>{{end}} {{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit" aria-label="{{lang "topic_edit_aria"}}" data-action="edit"></a>{{end}}
{{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="action_button delete_item" aria-label="{{lang "topic_delete_aria"}}" data-action="delete"></a>{{end}} {{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="action_button delete_item" aria-label="{{lang "topic_delete_aria"}}" data-action="delete"></a>{{end}}
{{if .CurrentUser.Perms.CloseTopic}} {{if .CurrentUser.Perms.CloseTopic}}
@ -76,8 +76,8 @@
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="action_button report_item" aria-label="{{lang "topic_report_aria"}}" data-action="report"></a> <a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="action_button report_item" aria-label="{{lang "topic_report_aria"}}" data-action="report"></a>
<a href="#" class="action_button button_menu"></a> <a href="#" class="action_button button_menu"></a>
{{end}} {{end}}
<div class="action_button_right{{if .Topic.LikeCount}} has_likes{{end}}"> <div class="action_button_right">
{{if .Topic.LikeCount}}<a class="action_button like_count hide_on_micro" aria-label="{{lang "topic_like_count_aria"}}">{{.Topic.LikeCount}}</a>{{end}} <a class="action_button like_count hide_on_micro" aria-label="{{lang "topic_like_count_aria"}}">{{.Topic.LikeCount}}</a>
<a class="action_button created_at hide_on_mobile">{{.Topic.RelativeCreatedAt}}</a> <a class="action_button created_at hide_on_mobile">{{.Topic.RelativeCreatedAt}}</a>
{{if .CurrentUser.Perms.ViewIPs}}<a href="/users/ips/?ip={{.Topic.IPAddress}}" title="{{lang "topic_ip_full_tooltip"}}" class="action_button ip_item hide_on_mobile" aria-hidden="true">{{.Topic.IPAddress}}</a>{{end}} {{if .CurrentUser.Perms.ViewIPs}}<a href="/users/ips/?ip={{.Topic.IPAddress}}" title="{{lang "topic_ip_full_tooltip"}}" class="action_button ip_item hide_on_mobile" aria-hidden="true">{{.Topic.IPAddress}}</a>{{end}}
</div> </div>
@ -86,7 +86,7 @@
</article> </article>
{{range .ItemList}} {{range .ItemList}}
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item {{if .ActionType}}action_item{{end}}"> <article {{scope "post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item {{if .ActionType}}action_item{{end}}">
<div class="userinfo" aria-label="{{lang "topic_userinfo_aria"}}"> <div class="userinfo" aria-label="{{lang "topic_userinfo_aria"}}">
<div class="avatar_item" style="background-image: url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div> <div class="avatar_item" style="background-image: url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="{{.UserLink}}" class="the_name" rel="author">{{.CreatedByName}}</a> <a href="{{.UserLink}}" class="the_name" rel="author">{{.CreatedByName}}</a>
@ -99,17 +99,17 @@
{{else}} {{else}}
{{/** TODO: We might end up with <br>s in the inline editor, fix this **/}} {{/** TODO: We might end up with <br>s in the inline editor, fix this **/}}
<div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div> <div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div>
<div class="button_container"> <div class="controls button_container{{if .LikeCount}} has_likes{{end}}">
{{if $.CurrentUser.Loggedin}} {{if $.CurrentUser.Loggedin}}
{{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button like_item add_like" aria-label="{{lang "topic_post_like_aria"}}" data-action="like"></a>{{end}} {{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button like_item {{if .Liked}}remove_like{{else}}add_like{{end}}" aria-label="{{lang "topic_post_like_aria"}}" data-action="like"></a>{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button edit_item" aria-label="{{lang "topic_post_edit_aria"}}" data-action="edit"></a>{{end}} {{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button edit_item" aria-label="{{lang "topic_post_edit_aria"}}" data-action="edit"></a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button delete_item" aria-label="{{lang "topic_post_delete_aria"}}" data-action="delete"></a>{{end}} {{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button delete_item" aria-label="{{lang "topic_post_delete_aria"}}" data-action="delete"></a>{{end}}
{{if $.CurrentUser.Perms.ViewIPs}}<a href="/users/ips/?ip={{.IPAddress}}" title="{{lang "topic_ip_full_tooltip"}}" class="action_button ip_item_button hide_on_big" aria-label="{{lang "topic_ip_full_aria"}}" data-action="ip"></a>{{end}} {{if $.CurrentUser.Perms.ViewIPs}}<a href="/users/ips/?ip={{.IPAddress}}" title="{{lang "topic_ip_full_tooltip"}}" class="action_button ip_item_button hide_on_big" aria-label="{{lang "topic_ip_full_aria"}}" data-action="ip"></a>{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item" aria-label="{{lang "topic_report_aria"}}" data-action="report"></a> <a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item" aria-label="{{lang "topic_report_aria"}}" data-action="report"></a>
<a href="#" class="action_button button_menu"></a> <a href="#" class="action_button button_menu"></a>
{{end}} {{end}}
<div class="action_button_right{{if .LikeCount}} has_likes{{end}}"> <div class="action_button_right">
{{if .LikeCount}}<a class="action_button like_count hide_on_micro" aria-label="{{lang "topic_post_like_count_tooltip"}}">{{.LikeCount}}</a>{{end}} <a class="action_button like_count hide_on_micro" aria-label="{{lang "topic_post_like_count_tooltip"}}">{{.LikeCount}}</a>
<a class="action_button created_at hide_on_mobile">{{.RelativeCreatedAt}}</a> <a class="action_button created_at hide_on_mobile">{{.RelativeCreatedAt}}</a>
{{if $.CurrentUser.Perms.ViewIPs}}<a href="/users/ips/?ip={{.IPAddress}}" title="IP Address" class="action_button ip_item hide_on_mobile" aria-hidden="true">{{.IPAddress}}</a>{{end}} {{if $.CurrentUser.Perms.ViewIPs}}<a href="/users/ips/?ip={{.IPAddress}}" title="IP Address" class="action_button ip_item hide_on_mobile" aria-hidden="true">{{.IPAddress}}</a>{{end}}
</div> </div>

View File

@ -107,8 +107,8 @@
<span class="selector"></span> <span class="selector"></span>
<a href="{{.Creator.Link}}"><img src="{{.Creator.Avatar}}" height="64" alt="{{.Creator.Name}}'s Avatar" title="{{.Creator.Name}}'s Avatar" /></a> <a href="{{.Creator.Link}}"><img src="{{.Creator.Avatar}}" height="64" alt="{{.Creator.Name}}'s Avatar" title="{{.Creator.Name}}'s Avatar" /></a>
<span class="topic_inner_left"> <span class="topic_inner_left">
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement"><span>{{.Title}}</span></a> {{if .ForumName}}<a class="rowsmall parent_forum" href="{{.ForumLink}}">{{.ForumName}}</a>{{end}} <a class="rowtopic" href="{{.Link}}" itemprop="itemListElement" title="{{.Title}}"><span>{{.Title}}</span></a> {{if .ForumName}}<a class="rowsmall parent_forum" href="{{.ForumLink}}" title="{{.ForumName}}">{{.ForumName}}</a>{{end}}
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a> <br /><a class="rowsmall starter" href="{{.Creator.Link}}" title="{{.Creator.Name}}">{{.Creator.Name}}</a>
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}} {{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="{{lang "status_closed_tooltip"}}"> | &#x1F512;&#xFE0E</span>{{end}} {{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="{{lang "status_closed_tooltip"}}"> | &#x1F512;&#xFE0E</span>{{end}}
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="{{lang "status_pinned_tooltip"}}"> | &#x1F4CD;&#xFE0E</span>{{end}} {{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="{{lang "status_pinned_tooltip"}}"> | &#x1F4CD;&#xFE0E</span>{{end}}
@ -121,7 +121,7 @@
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}"> <div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
<a href="{{.LastUser.Link}}"><img src="{{.LastUser.Avatar}}" height="64" alt="{{.LastUser.Name}}'s Avatar" title="{{.LastUser.Name}}'s Avatar" /></a> <a href="{{.LastUser.Link}}"><img src="{{.LastUser.Avatar}}" height="64" alt="{{.LastUser.Name}}'s Avatar" title="{{.LastUser.Name}}'s Avatar" /></a>
<span> <span>
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br> <a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;" title="{{.LastUser.Name}}">{{.LastUser.Name}}</a><br>
<span class="rowsmall lastReplyAt">{{.RelativeLastReplyAt}}</span> <span class="rowsmall lastReplyAt">{{.RelativeLastReplyAt}}</span>
</span> </span>
</div> </div>

View File

@ -1002,12 +1002,18 @@ textarea {
display: inline-flex; display: inline-flex;
margin-left: auto; margin-left: auto;
} }
.like_count {
display: none;
}
.has_likes .like_count {
display: block;
}
.like_count:after { .like_count:after {
content: "{{index .Phrases "topic_like_count_suffix"}}"; content: "{{index .Phrases "topic_like_count_suffix"}}";
margin-right: 6px; margin-right: 6px;
} }
.post_item .add_like:after, .post_item .add_like:after, .post_item .remove_like:after,
.created_at:before, .created_at:before,
.ip_item:before { .ip_item:before {
border-left: 1px solid var(--element-border-color); border-left: 1px solid var(--element-border-color);
@ -1018,15 +1024,19 @@ textarea {
.created_at:before, .ip_item:before { .created_at:before, .ip_item:before {
margin-right: 10px; margin-right: 10px;
} }
.post_item .add_like:after { .post_item .add_like:after, .post_item .remove_like:after {
margin-left: 10px; margin-left: 10px;
margin-right: 5px; margin-right: 5px;
} }
/* TODO: Use a less bold bold */
.post_item .remove_like:before {
font-weight: bold;
}
.created_at { .created_at {
margin-right: 10px; margin-right: 10px;
} }
.add_like:before { .add_like:before, .remove_like:before {
content: "{{index .Phrases "topic_plus_one"}}"; content: "{{index .Phrases "topic_plus_one"}}";
} }
.button_container .open_edit:after, .edit_item:after{ .button_container .open_edit:after, .edit_item:after{
@ -1651,10 +1661,10 @@ textarea {
padding-top: 15px; padding-top: 15px;
font-size: 12px; font-size: 12px;
} }
.action_button:not(.add_like) { .action_button:not(.add_like):not(.remove_like) {
font: normal normal normal 14px/1 FontAwesome; font: normal normal normal 14px/1 FontAwesome;
} }
.action_button_right.has_likes { .has_likes .action_button_right {
margin-left: 0px; margin-left: 0px;
width: 100%; width: 100%;
} }
@ -1664,7 +1674,7 @@ textarea {
.post_item:not(.top_post) .like_item { .post_item:not(.top_post) .like_item {
border-bottom: 1px solid var(--element-border-color); border-bottom: 1px solid var(--element-border-color);
} }
.post_item .add_like:after { .post_item .add_like:after, .post_item .remove_like:after {
border-left: none; border-left: none;
margin: inherit; margin: inherit;
} }
@ -1810,7 +1820,7 @@ textarea {
.button_menu_pane .action_button:nth-last-child(-n+8) { .button_menu_pane .action_button:nth-last-child(-n+8) {
border-bottom: none; border-bottom: none;
} }
.button_menu_pane .action_button:after, .button_menu_pane .add_like:before { .button_menu_pane .action_button:after, .button_menu_pane .add_like:before, .button_menu_pane .remove_like:before {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }

View File

@ -336,16 +336,19 @@ a {
content: "{{index .Phrases "topic_level"}}"; content: "{{index .Phrases "topic_level"}}";
} }
.like_count_label, .like_count {
display: none;
}
.like_count_label:before { .like_count_label:before {
content: "{{index .Phrases "topics_likes_suffix"}}"; content: "{{index .Phrases "topics_likes_suffix"}}";
} }
.like_count_label { .has_likes .like_count_label {
font-size: 12px; font-size: 12px;
display: block; display: block;
float: left; float: left;
line-height: 19px; line-height: 19px;
} }
.like_count { .has_likes .like_count {
font-size: 12px; font-size: 12px;
display: block; display: block;
float: left; float: left;

View File

@ -6,6 +6,12 @@
"FullImage": "shadow.png", "FullImage": "shadow.png",
"URL": "github.com/Azareal/Gosora", "URL": "github.com/Azareal/Gosora",
"BgAvatars":true, "BgAvatars":true,
"Templates": [
{
"Name": "topic",
"Source": "topic"
}
],
"Resources": [ "Resources": [
{ {
"Name":"shadow/misc.js", "Name":"shadow/misc.js",

View File

@ -716,6 +716,12 @@ button.username {
margin-right: auto; margin-right: auto;
} }
.like_count {
display: none;
}
.has_likes .like_count {
display: block;
}
.like_label:before { .like_label:before {
content: "😀"; content: "😀";
} }
@ -961,7 +967,7 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
margin-bottom: 6px !important; margin-bottom: 6px !important;
} }
.add_like:before { .add_like:before, .remove_like:before {
content: "{{index .Phrases "topic_plus_one"}}"; content: "{{index .Phrases "topic_plus_one"}}";
} }
.button_container .open_edit:after, .edit_item:after { .button_container .open_edit:after, .edit_item:after {

View File

@ -9,5 +9,11 @@
"HideFromThemes": true, "HideFromThemes": true,
"BgAvatars":true, "BgAvatars":true,
"URL": "github.com/Azareal/Gosora", "URL": "github.com/Azareal/Gosora",
"Templates": [
{
"Name": "topic",
"Source": "topic"
}
],
"Docks":["rightSidebar"] "Docks":["rightSidebar"]
} }

View File

@ -647,6 +647,12 @@ button.username {
.mod_button { .mod_button {
margin-right: 4px; margin-right: 4px;
} }
.like_count_label, .like_count {
display: none;
}
.has_likes .like_count_label, .has_likes .like_count {
display: block;
}
.like_label:before, .like_count_label:before { .like_label:before, .like_count_label:before {
content: "😀"; content: "😀";
} }
@ -674,7 +680,7 @@ button.username {
.pin_label:before, .unpin_label:before { .pin_label:before, .unpin_label:before {
content: "📌"; content: "📌";
} }
.unpin_label, .unlock_label { .remove_like, .unpin_label, .unlock_label {
background-color: #D6FFD6; background-color: #D6FFD6;
} }
.lock_label:before, .unlock_label:before { .lock_label:before, .unlock_label:before {

View File

@ -8,6 +8,12 @@
"URL": "github.com/Azareal/Gosora", "URL": "github.com/Azareal/Gosora",
"BgAvatars":true, "BgAvatars":true,
"Docks":["rightSidebar"], "Docks":["rightSidebar"],
"Templates": [
{
"Name": "topic",
"Source": "topic"
}
],
"Resources": [ "Resources": [
{ {
"Name":"tempra-simple/misc.js", "Name":"tempra-simple/misc.js",