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
.DS_Store
.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.
# 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
*Linux*

View File

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

View File

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

View File

@ -132,10 +132,10 @@ func CompileTemplates() error {
// Schemas to train the template compiler on what to expect
// 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?
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}
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}
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, 0, "::1", 0}
headerVars := &HeaderVars{
Site: Site,
Settings: SettingBox.Load().(SettingMap),
@ -373,6 +373,10 @@ func InitTemplates() error {
return GetTmplPhrase(phraseName) // TODO: Log non-existent phrases?
}
fmap["scope"] = func(name interface{}) interface{} {
return ""
}
// The interpreted templates...
DebugLog("Loading the template files...")
Templates.Funcs(fmap)

View File

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

View File

@ -141,7 +141,7 @@ func init() {
stick: acc.Update("topics").Set("sticky = 1").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(),
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(),
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?
@ -205,20 +205,24 @@ func (topic *Topic) Unstick() (err error) {
// TODO: Test this
// TODO: Use a transaction for this
func (topic *Topic) Like(score int, uid int) (err error) {
var tid int // Unused
err = topicStmts.hasLikedTopic.QueryRow(uid, topic.ID).Scan(&tid)
var disp int // Unused
err = topicStmts.hasLikedTopic.QueryRow(uid, topic.ID).Scan(&disp)
if err != nil && err != ErrNoRows {
return err
} else if err != ErrNoRows {
return ErrAlreadyLiked
}
_, err = topicStmts.createLike.Exec(score, tid, "topics", uid)
_, err = topicStmts.createLike.Exec(score, topic.ID, "topics", uid)
if err != nil {
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()
return err
}

View File

@ -54,6 +54,7 @@ type User struct {
Tag string
Level int
Score int
Liked int
LastIP string // ! This part of the UserCache data might fall out of date
TempGroup int
}
@ -71,6 +72,8 @@ type UserStmts struct {
incrementPosts *sql.Stmt
incrementBigposts *sql.Stmt
incrementMegaposts *sql.Stmt
incrementLiked *sql.Stmt
decrementLiked *sql.Stmt
updateLastIP *sql.Stmt
setPassword *sql.Stmt
@ -92,6 +95,9 @@ func init() {
incrementPosts: acc.SimpleUpdate("users", "posts = posts + ?", "uid = ?"),
incrementBigposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?", "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 = ?"),
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?
return &DefaultUserStore{
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 = ?", "", ""),
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 = ?", "", ""),
@ -65,7 +65,7 @@ func (mus *DefaultUserStore) DirtyGet(id int) *User {
}
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()
if err == nil {
@ -83,7 +83,7 @@ func (mus *DefaultUserStore) Get(id int) (*User, error) {
}
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()
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]
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 {
return list, err
}
for rows.Next() {
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 {
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) {
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()
return user, err
@ -183,7 +183,7 @@ func (mus *DefaultUserStore) BypassGet(id int) (*User, error) {
func (mus *DefaultUserStore) Reload(id int) error {
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 {
mus.cache.Remove(id)
return err

View File

@ -10,7 +10,6 @@ import (
var stmts *Stmts
var db *sql.DB
var dbVersion string
var dbAdapter string
// 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)
}
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 {
UploadHandler func(http.ResponseWriter, *http.Request)
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 {
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),
}
}
@ -1514,6 +1547,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
err = common.ParseForm(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
counters.RouteViewCounter.Bump(83)
err = routeLikeTopicSubmit(w,req,user,extraData)
default:
@ -1588,6 +1627,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
err = common.ParseForm(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
counters.RouteViewCounter.Bump(88)
err = routeReplyLikeSubmit(w,req,user,extraData)
}

View File

@ -16,16 +16,17 @@ import (
// TODO: Refactor this
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)
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)
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 {
return common.InternalError(err, w, r)
return common.InternalErrorJSQ(err, w, r, isJs)
}
// 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
}
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 {
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)
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 {
return common.InternalError(err, w, r)
return common.InternalErrorJSQ(err, w, r, isJs)
}
score := 1
err = topic.Like(score, user.ID)
//log.Print("likeErr: ", err)
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 {
return common.InternalError(err, w, r)
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.AddActivityAndNotifyTarget(user.ID, topic.CreatedBy, "like", "topic", tid)
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)
} else {
_, _ = w.Write(successJSONBytes)
}
return nil
}
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)
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)
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 {
return common.InternalError(err, w, r)
return common.InternalErrorJSQ(err, w, r, isJs)
}
var fid int
err = stmts.getTopicFID.QueryRow(reply.ParentID).Scan(&fid)
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 {
return common.InternalError(err, w, r)
return common.InternalErrorJSQ(err, w, r, isJs)
}
// 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
}
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 {
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)
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 {
return common.InternalError(err, w, r)
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = reply.Like(user.ID)
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 {
return common.InternalError(err, w, r)
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.AddActivityAndNotifyTarget(user.ID, reply.CreatedBy, "like", "post", rid)
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)
} else {
_, _ = w.Write(successJSONBytes)
}
return nil
}

View File

@ -47,8 +47,6 @@ func initMSSQL() (err error) {
return err
}
// TODO: Fetch the database version
// Set the number of max open connections
db.SetMaxOpenConns(64)
db.SetMaxIdleConns(32)
@ -76,7 +74,7 @@ func initMSSQL() (err error) {
// TODO: Is there a less noisy way of doing this for tests?
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 {
return err
}

View File

@ -9,11 +9,8 @@
package main
import (
"database/sql"
"log"
//import "time"
"./common"
"./query_gen/lib"
_ "github.com/go-sql-driver/mysql"
@ -27,28 +24,20 @@ func init() {
}
func initMySQL() (err error) {
var _dbpassword string
if common.DbConfig.Password != "" {
_dbpassword = ":" + common.DbConfig.Password
}
// TODO: Move this bit to the query gen lib
// Open the database connection
db, err = sql.Open("mysql", common.DbConfig.Username+_dbpassword+"@tcp("+common.DbConfig.Host+":"+common.DbConfig.Port+")/"+common.DbConfig.Dbname+"?collation="+dbCollation+"&parseTime=true")
err = qgen.Builder.Init("mysql", map[string]string{
"host": common.DbConfig.Host,
"port": common.DbConfig.Port,
"name": common.DbConfig.Dbname,
"username": common.DbConfig.Username,
"password": common.DbConfig.Password,
"collation": dbCollation,
})
if err != nil {
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
db = qgen.Builder.GetConn()
db.SetMaxOpenConns(64)
db.SetMaxIdleConns(32)
@ -70,7 +59,7 @@ func initMySQL() (err error) {
// TODO: Is there a less noisy way of doing this for tests?
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 {
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
var db_sslmode = "disable" // verify-full
var dbSslmode = "disable" // verify-full
func init() {
db_adapter = "pgsql"
dbAdapter = "pgsql"
_initDatabase = initPgsql
}
@ -28,7 +28,7 @@ func initPgsql() (err error) {
_dbpassword = " password='" + _escape_bit(common.DbConfig.Password) + "'"
}
// 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 {
return err
}
@ -39,9 +39,6 @@ func initPgsql() (err error) {
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.
db.SetMaxOpenConns(64)
db.SetMaxIdleConns(32)

View File

@ -153,7 +153,7 @@ function runWebSockets() {
console.log("The WebSockets connection was closed");
}
conn.onmessage = function(event) {
//console.log("WS_Message:", event.data);
//console.log("WSMessage:", event.data);
if(event.data[0] == "{") {
try {
var data = JSON.parse(event.data);
@ -200,14 +200,15 @@ function runWebSockets() {
var messages = event.data.split('\r');
for(var i = 0; i < messages.length; i++) {
//console.log("Message: ",messages[i]);
if(messages[i].startsWith("set ")) {
//msgblocks = messages[i].split(' ',3);
let msgblocks = SplitN(messages[i]," ",3);
let message = messages[i];
//console.log("Message: ",message);
if(message.startsWith("set ")) {
//msgblocks = message.split(' ',3);
let msgblocks = SplitN(message," ",3);
if(msgblocks.length < 3) continue;
document.querySelector(msgblocks[1]).innerHTML = msgblocks[2];
} else if(messages[i].startsWith("set-class ")) {
let msgblocks = SplitN(messages[i]," ",3);
} else if(message.startsWith("set-class ")) {
let msgblocks = SplitN(message," ",3);
if(msgblocks.length < 3) continue;
document.querySelector(msgblocks[1]).className = msgblocks[2];
}
@ -220,8 +221,45 @@ $(document).ready(function(){
if(window["WebSocket"]) runWebSockets();
else conn = false;
$(".open_edit").click(function(event){
//console.log("clicked on .open_edit");
$(".add_like").click(function(event) {
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();
$(".hide_on_edit").hide();
$(".show_on_edit").show();
@ -229,17 +267,17 @@ $(document).ready(function(){
$(".topic_item .submit_edit").click(function(event){
event.preventDefault();
//console.log("clicked on .topic_item .submit_edit");
$(".topic_name").html($(".topic_name_input").val());
$(".topic_content").html($(".topic_content_input").val());
$(".topic_status_e:not(.open_edit)").html($(".topic_status_input").val());
let topicNameInput = $(".topic_name_input").val();
$(".topic_name").html(topicNameInput);
$(".topic_name").attr(topicNameInput);
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();
$(".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");
//console.log("New Topic Name: ", topicNameInput);
//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();
let blockParent = $(this).closest('.editable_parent');
let block = blockParent.find('.editable_block').eq(0);
@ -395,7 +432,7 @@ $(document).ready(function(){
}
});
$(this).click(function() {
$(this).click(() => {
$(".selectedAlert").removeClass("selectedAlert");
$("#back").removeClass("alertActive");
});
@ -421,9 +458,7 @@ $(document).ready(function(){
document.getElementById("back").className += " alertActive"
});
$("input,textarea,select,option").keyup(function(event){
event.stopPropagation();
})
$("input,textarea,select,option").keyup(event => event.stopPropagation())
$(".create_topic_link").click((event) => {
event.preventDefault();

View File

@ -1,6 +1,9 @@
package qgen
import "database/sql"
import (
"database/sql"
"strconv"
)
type accDeleteBuilder struct {
table string
@ -10,7 +13,10 @@ type accDeleteBuilder struct {
}
func (delete *accDeleteBuilder) Where(where string) *accDeleteBuilder {
delete.where = where
if delete.where != "" {
delete.where += " AND "
}
delete.where += where
return delete
}
@ -32,7 +38,10 @@ func (update *accUpdateBuilder) Set(set string) *accUpdateBuilder {
}
func (update *accUpdateBuilder) Where(where string) *accUpdateBuilder {
update.where = where
if update.where != "" {
update.where += " AND "
}
update.where += where
return update
}
@ -59,6 +68,28 @@ func (selectItem *accSelectBuilder) Columns(columns 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
return selectItem
}
@ -140,7 +171,10 @@ type accCountBuilder struct {
}
func (count *accCountBuilder) Where(where string) *accCountBuilder {
count.where = where
if count.where != "" {
count.where += " AND "
}
count.where += where
return count
}

View File

@ -19,10 +19,25 @@ func (build *builder) Accumulator() *Accumulator {
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) {
build.conn = conn
}
func (build *builder) GetConn() *sql.DB {
return build.conn
}
func (build *builder) SetAdapter(name string) error {
adap, err := GetAdapter(name)
if err != nil {
@ -36,6 +51,11 @@ func (build *builder) GetAdapter() 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) {
return build.conn.Begin()
}

View File

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

View File

@ -2,6 +2,7 @@
package qgen
import (
"database/sql"
"errors"
"log"
"strconv"
@ -34,6 +35,15 @@ func (adapter *MssqlAdapter) GetStmts() map[string]DBStmt {
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
// 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) {
@ -249,7 +259,7 @@ func (adapter *MssqlAdapter) SimpleUpsert(name string, table string, columns str
switch token.Type {
case "substitute":
querystr += " ?"
case "function", "operator", "number":
case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
@ -314,7 +324,7 @@ func (adapter *MssqlAdapter) SimpleUpdate(name string, table string, set string,
switch token.Type {
case "substitute":
querystr += " ?"
case "function", "operator", "number":
case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
@ -339,7 +349,7 @@ func (adapter *MssqlAdapter) SimpleUpdate(name string, table string, set string,
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
case "function", "operator", "number", "substitute", "or":
// TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
@ -381,7 +391,7 @@ func (adapter *MssqlAdapter) SimpleDelete(name string, table string, where strin
switch token.Type {
case "substitute":
querystr += " ?"
case "function", "operator", "number":
case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
@ -451,7 +461,7 @@ func (adapter *MssqlAdapter) SimpleSelect(name string, table string, columns str
case "substitute":
substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number":
case "function", "operator", "number", "or":
// 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?
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
@ -576,7 +586,7 @@ func (adapter *MssqlAdapter) SimpleLeftJoin(name string, table1 string, table2 s
case "substitute":
substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number":
case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
@ -706,7 +716,7 @@ func (adapter *MssqlAdapter) SimpleInnerJoin(name string, table1 string, table2
case "substitute":
substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number":
case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
@ -827,7 +837,7 @@ func (adapter *MssqlAdapter) SimpleInsertSelect(name string, ins DBInsert, sel D
case "substitute":
substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number":
case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
@ -951,7 +961,7 @@ func (adapter *MssqlAdapter) simpleJoin(name string, ins DBInsert, sel DBJoin, j
case "substitute":
substituteCount++
querystr += " ?" + strconv.Itoa(substituteCount)
case "function", "operator", "number":
case "function", "operator", "number", "or":
// TODO: Split the function case off to speed things up
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
@ -1046,7 +1056,7 @@ func (adapter *MssqlAdapter) SimpleCount(name string, table string, where string
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
case "function", "operator", "number", "substitute", "or":
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
token.Contents = "GETUTCDATE()"
}

View File

@ -1,13 +1,17 @@
/* WIP Under Construction */
package qgen
//import "fmt"
import (
"database/sql"
"errors"
"strconv"
"strings"
_ "github.com/go-sql-driver/mysql"
)
var ErrNoCollation = errors.New("You didn't provide a collation")
func init() {
Registry = append(Registry,
&MysqlAdapter{Name: "mysql", Buffer: make(map[string]DBStmt)},
@ -33,6 +37,30 @@ func (adapter *MysqlAdapter) GetStmts() map[string]DBStmt {
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) {
if name == "" {
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 + "` ="
for _, token := range item.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"
@ -278,7 +306,7 @@ func (adapter *MysqlAdapter) SimpleDelete(name string, table string, where strin
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"
@ -316,7 +344,7 @@ func (adapter *MysqlAdapter) buildWhere(where string) (querystr string, err erro
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"
@ -344,7 +372,7 @@ func (adapter *MysqlAdapter) buildFlexiWhere(where string, dateCutoff *dateCutof
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"
@ -544,7 +572,7 @@ func (adapter *MysqlAdapter) buildJoinWhere(where string) (querystr string, err
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
case "function", "operator", "number", "substitute", "or":
querystr += " " + token.Contents
case "column":
halves := strings.Split(token.Contents, ".")

View File

@ -1,9 +1,12 @@
/* WIP Under Really Heavy Construction */
package qgen
import "strings"
import "strconv"
import "errors"
import (
"database/sql"
"errors"
"strconv"
"strings"
)
func init() {
Registry = append(Registry,
@ -30,6 +33,16 @@ func (adapter *PgsqlAdapter) GetStmts() map[string]DBStmt {
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
// 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) {
@ -167,7 +180,7 @@ func (adapter *PgsqlAdapter) SimpleUpdate(name string, table string, set string,
token.Contents = "LOCALTIMESTAMP()"
}
querystr += " " + token.Contents
case "operator", "number", "substitute":
case "operator", "number", "substitute", "or":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"
@ -193,7 +206,7 @@ func (adapter *PgsqlAdapter) SimpleUpdate(name string, table string, set string,
token.Contents = "LOCALTIMESTAMP()"
}
querystr += " " + token.Contents
case "operator", "number", "substitute":
case "operator", "number", "substitute", "or":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"

View File

@ -98,6 +98,9 @@ type DBStmt struct {
type Adapter interface {
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)
SimpleInsert(name string, table string, columns string, fields 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
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
@ -175,6 +180,7 @@ func processWhere(wherestr string) (where []DBWhere) {
return where
}
wherestr = normalizeAnd(wherestr)
wherestr = normalizeOr(wherestr)
for _, segment := range strings.Split(wherestr, " AND ") {
var tmpWhere = &DBWhere{[]DBToken{}}
@ -184,10 +190,16 @@ func processWhere(wherestr string) (where []DBWhere) {
switch {
case '0' <= char && char <= '9':
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 == '_':
i = tmpWhere.parseColumn(segment, i)
case char == '\'':
i = tmpWhere.parseString(segment, i)
case char == ')' && i < (len(segment)-1):
tmpWhere.Expr = append(tmpWhere.Expr, DBToken{")", "operator"})
case isOpByte(char):
i = tmpWhere.parseOperator(segment, i)
case char == '?':
@ -335,11 +347,11 @@ func processLimit(limitstr string) (limiter DBLimit) {
}
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 {
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) {

View File

@ -1,4 +1,3 @@
/* WIP Under Construction */
package main
import "./lib"
@ -11,7 +10,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"password", "varchar", 100, 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{"is_super_admin", "boolean", 0, false, false, "0"},
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{"megaposts", "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{"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.DBTableColumn{
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{"token", "varchar", 200, false, false, "''"},
},
@ -153,7 +158,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
qgen.DBTableColumn{"lastReplyAt", "datetime", 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{"sticky", "boolean", 0, false, false, "0"},
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{"parsed_content", "text", 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{"lastEditBy", "int", 0, false, false, "0"},
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{"originID", "int", 0, false, false, ""},
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.DBTableKey{
@ -215,6 +220,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"contentID", "int", 0, false, false, ""},
qgen.DBTableColumn{"contentType", "varchar", 100, false, false, "replies"},
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
// TODO: Add a createdBy column?
},
[]qgen.DBTableKey{
qgen.DBTableKey{"reviseID", "primary"},
@ -247,7 +253,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("polls_votes", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DBTableColumn{
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{"castAt", "createdAt", 0, false, false, ""},
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.DBTableColumn{
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{"parsed_content", "text", 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{"lastEditBy", "int", 0, false, false, ""},
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{"targetItem", "int", 0, false, false, ""},
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.DBTableKey{},
@ -285,8 +292,8 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("activity_stream_matches", "", "",
[]qgen.DBTableColumn{
qgen.DBTableColumn{"watcher", "int", 0, false, false, ""},
qgen.DBTableColumn{"asid", "int", 0, false, false, ""},
qgen.DBTableColumn{"watcher", "int", 0, false, false, ""}, // TODO: Make this a foreign key
qgen.DBTableColumn{"asid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
},
[]qgen.DBTableKey{},
)
@ -294,7 +301,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("activity_stream", "", "",
[]qgen.DBTableColumn{
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{"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 */
@ -307,7 +314,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("activity_subscriptions", "", "",
[]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{"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*/
@ -378,7 +385,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"elementID", "int", 0, false, false, ""},
qgen.DBTableColumn{"elementType", "varchar", 100, 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.DBTableKey{},
@ -390,7 +397,7 @@ func createTables(adapter qgen.Adapter) error {
qgen.DBTableColumn{"elementID", "int", 0, false, false, ""},
qgen.DBTableColumn{"elementType", "varchar", 100, 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.DBTableKey{},

View File

@ -302,6 +302,32 @@ func init() {
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 {
UploadHandler func(http.ResponseWriter, *http.Request)
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 {
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),
}
}

View File

@ -74,7 +74,7 @@ func buildTopicRoutes() {
Action("routes.LockTopicSubmit", "/topic/lock/submit/").LitBefore("req.URL.Path += extraData"),
Action("routes.UnlockTopicSubmit", "/topic/unlock/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)
}
@ -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/
Action("routes.ReplyEditSubmit", "/reply/edit/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)
}

View File

@ -21,6 +21,7 @@ import (
type TopicStmts struct {
getReplies *sql.Stmt
getLikedTopic *sql.Stmt
}
var topicStmts TopicStmts
@ -30,6 +31,7 @@ func init() {
common.DbInits.Add(func(acc *qgen.Accumulator) error {
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", "?,?"),
getLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy = ? && targetItem = ? && targetType = 'topics'").Prepare(),
}
return acc.FirstError()
})
@ -107,12 +109,25 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
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
offset, page, lastPage := common.PageOffset(topic.PostCount, page, common.Config.ItemsPerPage)
tpage := common.TopicPage{topic.Title, user, headerVars, []common.ReplyUser{}, topic, poll, page, lastPage}
// Get the replies if we have any...
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)
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)
@ -171,7 +186,11 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
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)
// 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 {
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) {

View File

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

View File

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

View File

@ -46,6 +46,13 @@ if %errorlevel% neq 0 (
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
gosora.exe
pause

View File

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

View File

@ -46,6 +46,13 @@ if %errorlevel% neq 0 (
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
gosora.exe
pause

View File

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

View File

@ -21,6 +21,9 @@ CREATE TABLE [users] (
[bigposts] int DEFAULT 0 not null,
[megaposts] 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,
primary key([uid]),
unique([name])

View File

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

View File

@ -21,6 +21,9 @@ CREATE TABLE `users` (
`bigposts` int DEFAULT 0 not null,
`megaposts` 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,
primary key(`uid`),
unique(`name`)

View File

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

View File

@ -21,6 +21,9 @@ CREATE TABLE `users` (
`bigposts` int DEFAULT 0 not null,
`megaposts` 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,
primary key(`uid`),
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:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "strconv"
import "net/http"
import "./common"
import "strconv"
var forum_tmpl_phrase_id int
@ -307,61 +307,67 @@ w.Write([]byte(item.Link))
w.Write(forum_frags[58])
w.Write([]byte(item.Title))
w.Write(forum_frags[59])
w.Write([]byte(item.Creator.Link))
w.Write([]byte(item.Title))
w.Write(forum_frags[60])
w.Write([]byte(item.Creator.Name))
w.Write([]byte(item.Creator.Link))
w.Write(forum_frags[61])
if item.IsClosed {
w.Write([]byte(item.Creator.Name))
w.Write(forum_frags[62])
w.Write(phrases[44])
w.Write([]byte(item.Creator.Name))
w.Write(forum_frags[63])
}
if item.Sticky {
if item.IsClosed {
w.Write(forum_frags[64])
w.Write(phrases[45])
w.Write(phrases[44])
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 {
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([]byte(strconv.Itoa(item.LikeCount)))
w.Write(forum_frags[70])
if item.Sticky {
w.Write(forum_frags[71])
} else {
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([]byte(item.LastUser.Avatar))
}
}
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(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([]byte(item.LastUser.Name))
w.Write(forum_frags[77])
w.Write([]byte(item.RelativeLastReplyAt))
w.Write([]byte(item.LastUser.Link))
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 {
w.Write(forum_frags[79])
w.Write(forum_frags[82])
w.Write(phrases[46])
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([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
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 {
w.Write(paginator_frags[0])
if tmpl_forum_vars.Page > 1 {
@ -397,7 +403,7 @@ w.Write(paginator_frags[13])
}
w.Write(paginator_frags[14])
}
w.Write(forum_frags[85])
w.Write(forum_frags[88])
w.Write(footer_frags[0])
w.Write([]byte(common.BuildWidget("footer",tmpl_forum_vars.Header)))
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([]byte(tmpl_profile_vars.ProfileOwner.Name))
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([]byte(tmpl_profile_vars.ProfileOwner.Tag))
if tmpl_profile_vars.ProfileOwner.Tag != "" {
w.Write(profile_frags[6])
}
w.Write([]byte(tmpl_profile_vars.ProfileOwner.Tag))
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(phrases[19])
}
w.Write(profile_frags[9])
} else {
if !tmpl_profile_vars.CurrentUser.Loggedin {
w.Write(profile_frags[10])
w.Write(phrases[20])
w.Write(phrases[19])
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 {
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(phrases[22])
w.Write(phrases[21])
w.Write(profile_frags[18])
}
} else {
w.Write(profile_frags[19])
}
w.Write(phrases[22])
w.Write(profile_frags[20])
w.Write([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID)))
}
w.Write(profile_frags[21])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
}
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(profile_frags[28])
w.Write(profile_frags[23])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_frags[29])
w.Write(profile_frags[30])
w.Write(phrases[26])
w.Write(profile_frags[31])
w.Write(phrases[27])
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[24])
w.Write(phrases[23])
w.Write(profile_frags[25])
w.Write(phrases[24])
w.Write(profile_frags[26])
}
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(phrases[32])
w.Write(phrases[31])
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 len(tmpl_profile_vars.ItemList) != 0 {
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([]byte(strconv.Itoa(tmpl_profile_vars.ProfileOwner.ID)))
if !tmpl_profile_vars.CurrentUser.IsBanned {
w.Write(profile_frags[42])
w.Write(phrases[45])
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
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(phrases[45])
w.Write(profile_frags[45])
w.Write(phrases[46])
w.Write(profile_frags[46])
}
w.Write(profile_frags[47])
w.Write(profile_frags[48])
w.Write(footer_frags[0])
w.Write([]byte(common.BuildWidget("footer",tmpl_profile_vars.Header)))
w.Write(footer_frags[1])

View File

@ -3,9 +3,9 @@
// 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. */
package main
import "strconv"
import "net/http"
import "./common"
import "strconv"
var topic_tmpl_phrase_id int
@ -245,376 +245,385 @@ w.Write(phrases[22])
w.Write(topic_frags[16])
}
w.Write(topic_frags[17])
w.Write(phrases[23])
w.Write(topic_frags[18])
if tmpl_topic_vars.Topic.Sticky {
w.Write(phrases[23])
w.Write(topic_frags[19])
if tmpl_topic_vars.Topic.Sticky {
w.Write(topic_frags[20])
} else {
if tmpl_topic_vars.Topic.IsClosed {
w.Write(topic_frags[20])
}
}
w.Write(topic_frags[21])
w.Write([]byte(tmpl_topic_vars.Topic.Title))
}
}
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(phrases[24])
w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_frags[24])
w.Write(phrases[25])
if tmpl_topic_vars.Topic.IsClosed {
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 {
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(phrases[27])
w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_frags[29])
}
w.Write(phrases[26])
w.Write(topic_frags[30])
if tmpl_topic_vars.Poll.ID > 0 {
w.Write(phrases[27])
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])
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 {
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([]byte(strconv.Itoa(item.ID)))
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
w.Write(topic_frags[41])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[42])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[43])
w.Write([]byte(item.Value))
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_frags[44])
}
}
w.Write([]byte(strconv.Itoa(item.ID)))
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(phrases[29])
}
}
w.Write(topic_frags[47])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
w.Write(topic_frags[48])
w.Write(phrases[30])
w.Write(phrases[29])
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(topic_frags[50])
w.Write(phrases[30])
w.Write(topic_frags[51])
}
w.Write(phrases[31])
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([]byte(tmpl_topic_vars.Topic.ClassName))
}
w.Write(topic_frags[54])
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
w.Write(topic_frags[55])
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write(phrases[32])
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([]byte(tmpl_topic_vars.Topic.Avatar))
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([]byte(tmpl_topic_vars.Topic.Content))
if tmpl_topic_vars.Topic.ContentLines <= 5 {
w.Write(topic_frags[60])
w.Write(phrases[33])
}
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([]byte(tmpl_topic_vars.Topic.CreatedByName))
w.Write([]byte(tmpl_topic_vars.Topic.Content))
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([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
}
w.Write(topic_frags[65])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(phrases[33])
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(phrases[34])
w.Write([]byte(tmpl_topic_vars.Topic.CreatedByName))
w.Write(topic_frags[68])
w.Write(phrases[35])
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_frags[69])
} else {
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[70])
w.Write(phrases[36])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
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 {
w.Write(topic_frags[72])
w.Write(phrases[34])
w.Write(topic_frags[73])
w.Write(phrases[35])
w.Write(topic_frags[74])
}
} else {
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 {
w.Write(topic_frags[76])
w.Write(topic_frags[82])
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(topic_frags[78])
w.Write(topic_frags[84])
w.Write(phrases[39])
w.Write(topic_frags[79])
w.Write(topic_frags[85])
}
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(topic_frags[81])
w.Write(topic_frags[87])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[82])
w.Write(topic_frags[88])
w.Write(phrases[40])
w.Write(topic_frags[83])
w.Write(topic_frags[89])
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.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([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[92])
w.Write(phrases[44])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[93])
w.Write(phrases[45])
w.Write(phrases[42])
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.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([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[102])
w.Write(phrases[48])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[103])
w.Write(phrases[49])
w.Write(phrases[46])
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 {
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(phrases[51])
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_frags[112])
w.Write(phrases[52])
w.Write(phrases[50])
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(phrases[53])
}
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(phrases[54])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[117])
}
if tmpl_topic_vars.Topic.Tag != "" {
w.Write(phrases[51])
w.Write(topic_frags[118])
w.Write([]byte(tmpl_topic_vars.Topic.Tag))
w.Write(phrases[52])
w.Write(topic_frags[119])
} else {
w.Write(phrases[53])
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([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
w.Write(phrases[54])
w.Write(topic_frags[122])
w.Write(phrases[56])
if tmpl_topic_vars.Topic.Tag != "" {
w.Write(topic_frags[123])
}
w.Write([]byte(tmpl_topic_vars.Topic.Tag))
w.Write(topic_frags[124])
w.Write(phrases[57])
} else {
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 {
for _, item := range tmpl_topic_vars.ItemList {
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([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write([]byte(item.ActionIcon))
w.Write(topic_frags[132])
if item.ContentLines <= 5 {
w.Write([]byte(item.ActionType))
w.Write(topic_frags[133])
}
} else {
w.Write(topic_frags[134])
w.Write(topic_frags[135])
w.Write([]byte(item.ContentHtml))
w.Write([]byte(item.ClassName))
w.Write(topic_frags[136])
w.Write([]byte(item.UserLink))
w.Write([]byte(item.Avatar))
w.Write(topic_frags[137])
w.Write([]byte(item.CreatedByName))
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
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 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(phrases[61])
w.Write([]byte(strconv.Itoa(item.ID)))
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 {
w.Write(topic_frags[149])
w.Write(topic_frags[157])
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(topic_frags[151])
w.Write(topic_frags[159])
w.Write(phrases[62])
w.Write(topic_frags[152])
w.Write(topic_frags[160])
w.Write(phrases[63])
w.Write(topic_frags[153])
w.Write(topic_frags[161])
}
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(topic_frags[155])
w.Write(topic_frags[163])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[156])
w.Write(topic_frags[164])
w.Write(phrases[64])
w.Write(topic_frags[157])
w.Write(topic_frags[165])
w.Write(phrases[65])
w.Write(topic_frags[158])
w.Write(topic_frags[166])
}
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])
if item.LikeCount > 0 {
w.Write([]byte(item.IPAddress))
w.Write(topic_frags[168])
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(phrases[66])
w.Write(topic_frags[169])
w.Write(phrases[69])
w.Write([]byte(item.IPAddress))
w.Write(topic_frags[170])
}
if item.Tag != "" {
w.Write(topic_frags[171])
w.Write([]byte(item.Tag))
w.Write([]byte(strconv.Itoa(item.ID)))
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(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([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_frags[182])
w.Write(phrases[73])
w.Write(phrases[71])
w.Write(topic_frags[183])
w.Write(phrases[74])
}
w.Write(topic_frags[184])
w.Write(phrases[75])
}
}
}
w.Write(topic_frags[185])
w.Write(phrases[76])
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_frags[186])
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
w.Write(phrases[72])
w.Write(topic_frags[187])
w.Write(phrases[77])
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_frags[188])
}
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[189])
}
w.Write(phrases[73])
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([]byte(common.BuildWidget("footer",tmpl_topic_vars.Header)))
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[14])
w.Write(phrases[23])
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([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
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])
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write(topic_alt_frags[19])
} else {
if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_frags[19])
}
}
w.Write(topic_alt_frags[20])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
}
}
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(phrases[24])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
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(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 {
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(phrases[27])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_frags[28])
}
w.Write(phrases[26])
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([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
}
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([]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(phrases[28])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
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([]byte(tmpl_topic_alt_vars.Topic.UserLink))
w.Write(phrases[28])
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])
if tmpl_topic_alt_vars.Topic.Tag != "" {
w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink))
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])
} else {
if tmpl_topic_alt_vars.Topic.Tag != "" {
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([]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])
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([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
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([]byte(strconv.Itoa(item.ID)))
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[47])
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_frags[48])
w.Write([]byte(strconv.Itoa(item.ID)))
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([]byte(strconv.Itoa(item.ID)))
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(phrases[30])
}
}
w.Write(topic_alt_frags[53])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Poll.ID)))
w.Write(topic_alt_frags[54])
w.Write(phrases[31])
w.Write(phrases[30])
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(topic_alt_frags[56])
w.Write(phrases[31])
w.Write(topic_alt_frags[57])
}
w.Write(phrases[32])
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(phrases[34])
}
w.Write(topic_alt_frags[60])
w.Write([]byte(tmpl_topic_alt_vars.Topic.Avatar))
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([]byte(tmpl_topic_alt_vars.Topic.CreatedByName))
w.Write(phrases[34])
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([]byte(tmpl_topic_alt_vars.Topic.Tag))
w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink))
w.Write(topic_alt_frags[65])
} else {
w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedByName))
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([]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])
if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
}
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([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_frags[73])
w.Write(phrases[36])
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_frags[74])
}
if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
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(phrases[37])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
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 {
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([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[87])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[88])
w.Write(phrases[40])
w.Write(phrases[38])
w.Write(topic_alt_frags[89])
}
}
if tmpl_topic_alt_vars.CurrentUser.Perms.PinTopic {
if tmpl_topic_alt_vars.Topic.Sticky {
if tmpl_topic_alt_vars.CurrentUser.Perms.CloseTopic {
if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_frags[90])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[91])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[92])
w.Write(phrases[41])
w.Write(phrases[39])
w.Write(topic_alt_frags[93])
} else {
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([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[96])
w.Write(phrases[42])
w.Write(phrases[40])
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([]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(phrases[43])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_frags[100])
w.Write(phrases[44])
w.Write(phrases[41])
w.Write(topic_alt_frags[101])
}
} else {
w.Write(topic_alt_frags[102])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[103])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
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[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 {
w.Write(topic_alt_frags[114])
w.Write(topic_alt_frags[106])
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_frags[115])
w.Write(phrases[47])
w.Write(topic_alt_frags[116])
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_frags[117])
w.Write(topic_alt_frags[107])
w.Write(phrases[43])
w.Write(topic_alt_frags[108])
w.Write(phrases[44])
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([]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 {
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([]byte(item.UserLink))
w.Write(topic_alt_frags[124])
w.Write([]byte(item.CreatedByName))
if item.ActionType != "" {
w.Write(topic_alt_frags[125])
if item.Tag != "" {
}
w.Write(topic_alt_frags[126])
w.Write([]byte(item.Tag))
w.Write(phrases[48])
w.Write(topic_alt_frags[127])
} else {
w.Write([]byte(item.Avatar))
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([]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([]byte(item.ActionType))
}
w.Write(topic_alt_frags[135])
} else {
if item.ActionType != "" {
w.Write(topic_alt_frags[136])
w.Write([]byte(item.ContentHtml))
}
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.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])
}
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_frags[146])
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(topic_alt_frags[147])
if item.Liked {
w.Write(topic_alt_frags[148])
w.Write(phrases[52])
} else {
w.Write(topic_alt_frags[149])
}
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_frags[150])
w.Write([]byte(item.IPAddress))
w.Write(phrases[50])
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(phrases[54])
w.Write(topic_alt_frags[153])
}
w.Write(topic_alt_frags[154])
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(topic_alt_frags[156])
w.Write(phrases[55])
w.Write(topic_alt_frags[157])
w.Write(topic_alt_frags[154])
w.Write(phrases[51])
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])
if item.LikeCount > 0 {
w.Write(phrases[52])
w.Write(topic_alt_frags[159])
}
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_frags[160])
if item.LikeCount > 0 {
w.Write([]byte(item.IPAddress))
w.Write(topic_alt_frags[161])
w.Write(phrases[56])
w.Write(phrases[53])
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[164])
w.Write([]byte(item.RelativeCreatedAt))
w.Write([]byte(strconv.Itoa(item.ID)))
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([]byte(item.IPAddress))
w.Write(phrases[55])
w.Write(topic_alt_frags[167])
w.Write([]byte(item.IPAddress))
}
w.Write(topic_alt_frags[168])
}
w.Write(phrases[56])
w.Write(topic_alt_frags[169])
}
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topic_alt_frags[170])
}
}
w.Write([]byte(item.RelativeCreatedAt))
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(phrases[57])
w.Write([]byte(item.IPAddress))
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([]byte(tmpl_topic_alt_vars.CurrentUser.Link))
}
w.Write(topic_alt_frags[175])
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Name))
}
w.Write(topic_alt_frags[176])
if tmpl_topic_alt_vars.CurrentUser.Tag != "" {
}
}
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])
} else {
w.Write(phrases[57])
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([]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(phrases[62])
}
w.Write(topic_alt_frags[187])
w.Write(phrases[63])
w.Write(phrases[59])
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(phrases[64])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_frags[190])
}
w.Write(phrases[60])
w.Write(topic_alt_frags[191])
}
w.Write(phrases[61])
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([]byte(common.BuildWidget("footer",tmpl_topic_alt_vars.Header)))
w.Write(footer_frags[1])

View File

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

View File

@ -82,8 +82,8 @@
<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>
<span class="topic_inner_left">
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement"><span>{{.Title}}</span></a>
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a>
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement" title="{{.Title}}"><span>{{.Title}}</span></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 **/}}
{{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}}
@ -96,7 +96,7 @@
<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>
<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>
</div>

View File

@ -9,7 +9,7 @@
<img src="{{.ProfileOwner.Avatar}}" class="avatar" alt="{{.ProfileOwner.Name}}'s Avatar" title="{{.ProfileOwner.Name}}'s Avatar" />
</div>
<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 class="passiveBlock">

View File

@ -11,9 +11,9 @@
<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}}">
<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 .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"}}" />
@ -47,16 +47,16 @@
</article>
{{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;">
<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>
<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;
{{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}}
@ -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}}
<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}}
@ -82,14 +82,14 @@
<span itemprop="text">{{.ActionType}}</span>
</article>
{{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 **/}}
<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;
{{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}}
@ -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}}
<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}}

View File

@ -8,10 +8,10 @@
<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">
<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 **/}}
{{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}}
@ -54,7 +54,7 @@
</div>
</article>
{{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="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>
@ -63,9 +63,9 @@
<div class="content_container">
<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>
<div class="button_container">
<div class="controls button_container{{if .Topic.LikeCount}} has_likes{{end}}">
{{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.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}}
@ -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="#" class="action_button button_menu"></a>
{{end}}
<div class="action_button_right{{if .Topic.LikeCount}} has_likes{{end}}">
{{if .Topic.LikeCount}}<a class="action_button like_count hide_on_micro" aria-label="{{lang "topic_like_count_aria"}}">{{.Topic.LikeCount}}</a>{{end}}
<div class="action_button_right">
<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>
{{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>
@ -86,7 +86,7 @@
</article>
{{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="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>
@ -99,17 +99,17 @@
{{else}}
{{/** 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="button_container">
<div class="controls button_container{{if .LikeCount}} has_likes{{end}}">
{{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.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}}
<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>
{{end}}
<div class="action_button_right{{if .LikeCount}} has_likes{{end}}">
{{if .LikeCount}}<a class="action_button like_count hide_on_micro" aria-label="{{lang "topic_post_like_count_tooltip"}}">{{.LikeCount}}</a>{{end}}
<div class="action_button_right">
<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>
{{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>

View File

@ -107,8 +107,8 @@
<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>
<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}}
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a>
<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}}" 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 **/}}
{{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}}
@ -121,7 +121,7 @@
<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>
<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>
</div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -647,6 +647,12 @@ button.username {
.mod_button {
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 {
content: "😀";
}
@ -674,7 +680,7 @@ button.username {
.pin_label:before, .unpin_label:before {
content: "📌";
}
.unpin_label, .unlock_label {
.remove_like, .unpin_label, .unlock_label {
background-color: #D6FFD6;
}
.lock_label:before, .unlock_label:before {

View File

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