Continued the work on Cosora.
Trying to add support for Travis CI. Added the NullUserStore for tests. Moved more text out of the templates. Removed an unnecessary dependency for Cosora. Fixed a few more bugs in the MSSQL Adapter. Disabled upserts for now, as it isn't working on MySQL. Refactored the scheduled group logic to reduce the number of potential bugs and to stop using upserts. Fixed many bugs in the Markdown Plugin. Added the ability to escape Markdown. Fixed a number of tests, commented unneccesary ones, and added new ones to probe for new problems. Added the wink smiley. Cleaned up some of the JavaScript. Refactored the permissions logic to use transactions and avoid duplicating logic. Added the ChangeRank method to the Group struct. Added the ChangeGroup method to the User struct. Added the currently unused LogWarning function. Added the transaction versions of the builder methods. Better ones coming up soon! The IsBanned flag is always set to false on mod and admin groups now. Refactored the group creation logic to use transactions. Fixed a bug in the group creator where new groups aren't visible to Exists(). The installer now drops tables for MySQL Databases, if they already exist to make it easier for us to run automated tests. Added more ARIA Attributes.
This commit is contained in:
parent
da6ae8d7d4
commit
f7942b42ac
|
@ -0,0 +1,10 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.9
|
||||||
|
- master
|
||||||
|
before_install:
|
||||||
|
- chmod 755 ./install-linux
|
||||||
|
- chmod 755 ./run-linux
|
||||||
|
script: go test
|
||||||
|
addons:
|
||||||
|
mariadb: '10.0'
|
17
auth.go
17
auth.go
|
@ -20,8 +20,12 @@ var auth Auth
|
||||||
// ErrMismatchedHashAndPassword is thrown whenever a hash doesn't match it's unhashed password
|
// ErrMismatchedHashAndPassword is thrown whenever a hash doesn't match it's unhashed password
|
||||||
var ErrMismatchedHashAndPassword = bcrypt.ErrMismatchedHashAndPassword
|
var ErrMismatchedHashAndPassword = bcrypt.ErrMismatchedHashAndPassword
|
||||||
|
|
||||||
|
// nolint
|
||||||
// ErrPasswordTooLong is silly, but we don't want bcrypt to bork on us
|
// ErrPasswordTooLong is silly, but we don't want bcrypt to bork on us
|
||||||
var ErrPasswordTooLong = errors.New("The password you selected is too long")
|
var ErrPasswordTooLong = errors.New("The password you selected is too long")
|
||||||
|
var ErrWrongPassword = errors.New("That's not the correct password.")
|
||||||
|
var ErrSecretError = errors.New("There was a glitch in the system. Please contact your local administrator.")
|
||||||
|
var ErrNoUserByName = errors.New("We couldn't find an account with that username.")
|
||||||
|
|
||||||
// Auth is the main authentication interface.
|
// Auth is the main authentication interface.
|
||||||
type Auth interface {
|
type Auth interface {
|
||||||
|
@ -61,24 +65,24 @@ func (auth *DefaultAuth) Authenticate(username string, password string) (uid int
|
||||||
var realPassword, salt string
|
var realPassword, salt string
|
||||||
err = auth.login.QueryRow(username).Scan(&uid, &realPassword, &salt)
|
err = auth.login.QueryRow(username).Scan(&uid, &realPassword, &salt)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return 0, errors.New("We couldn't find an account with that username.") // nolint
|
return 0, ErrNoUserByName
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
return 0, errors.New("There was a glitch in the system. Please contact your local administrator.") // nolint
|
return 0, ErrSecretError
|
||||||
}
|
}
|
||||||
|
|
||||||
if salt == "" {
|
if salt == "" {
|
||||||
// Send an email to admin for this?
|
// Send an email to admin for this?
|
||||||
LogError(errors.New("Missing salt for user #" + strconv.Itoa(uid) + ". Potential security breach."))
|
LogError(errors.New("Missing salt for user #" + strconv.Itoa(uid) + ". Potential security breach."))
|
||||||
return 0, errors.New("There was a glitch in the system. Please contact your local administrator")
|
return 0, ErrSecretError
|
||||||
}
|
}
|
||||||
|
|
||||||
err = CheckPassword(realPassword, password, salt)
|
err = CheckPassword(realPassword, password, salt)
|
||||||
if err == ErrMismatchedHashAndPassword {
|
if err == ErrMismatchedHashAndPassword {
|
||||||
return 0, errors.New("That's not the correct password.")
|
return 0, ErrWrongPassword
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
return 0, errors.New("There was a glitch in the system. Please contact your local administrator.")
|
return 0, ErrSecretError
|
||||||
}
|
}
|
||||||
|
|
||||||
return uid, nil
|
return uid, nil
|
||||||
|
@ -89,7 +93,7 @@ func (auth *DefaultAuth) ForceLogout(uid int) error {
|
||||||
_, err := auth.logout.Exec(uid)
|
_, err := auth.logout.Exec(uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
return errors.New("There was a glitch in the system. Please contact your local administrator.")
|
return ErrSecretError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush the user out of the cache
|
// Flush the user out of the cache
|
||||||
|
@ -110,6 +114,7 @@ func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Set the cookie domain
|
// TODO: Set the cookie domain
|
||||||
|
// SetCookies sets the two cookies required for the current user to be recognised as a specific user in future requests
|
||||||
func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) {
|
func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) {
|
||||||
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: year}
|
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: year}
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
|
|
4
cache.go
4
cache.go
|
@ -12,8 +12,8 @@ const CACHE_SQL int = 2
|
||||||
// ErrCacheDesync is thrown whenever a piece of data, for instance, a user is out of sync with the database. Currently unused.
|
// ErrCacheDesync is thrown whenever a piece of data, for instance, a user is out of sync with the database. Currently unused.
|
||||||
var ErrCacheDesync = errors.New("The cache is out of sync with the database.") // TODO: A cross-server synchronisation mechanism
|
var ErrCacheDesync = errors.New("The cache is out of sync with the database.") // TODO: A cross-server synchronisation mechanism
|
||||||
|
|
||||||
// ErrStoreCapacityOverflow is thrown whenever a datastore reaches it's maximum hard capacity. I'm not sure if this error is actually used.
|
// ErrStoreCapacityOverflow is thrown whenever a datastore reaches it's maximum hard capacity. I'm not sure if this error is actually used. It might be, we should check
|
||||||
var ErrStoreCapacityOverflow = errors.New("This datastore has reached it's maximum capacity.")
|
var ErrStoreCapacityOverflow = errors.New("This datastore has reached it's maximum capacity.") // nolint
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
type DataStore interface {
|
type DataStore interface {
|
||||||
|
|
|
@ -14,14 +14,18 @@ var errorBuffer []error
|
||||||
//var notfoundCountPerSecond int
|
//var notfoundCountPerSecond int
|
||||||
//var nopermsCountPerSecond int
|
//var nopermsCountPerSecond int
|
||||||
|
|
||||||
// LogError logs internal handler errors which can't be handled with InternalError() as a wrapper for log.Fatal(), we might do more with it in the future
|
// LogError logs internal handler errors which can't be handled with InternalError() as a wrapper for log.Fatal(), we might do more with it in the future.
|
||||||
func LogError(err error) {
|
func LogError(err error) {
|
||||||
|
LogWarning(err)
|
||||||
|
log.Fatal("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogWarning(err error) {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
debug.PrintStack()
|
debug.PrintStack()
|
||||||
errorBufferMutex.Lock()
|
errorBufferMutex.Lock()
|
||||||
defer errorBufferMutex.Unlock()
|
defer errorBufferMutex.Unlock()
|
||||||
errorBuffer = append(errorBuffer, err)
|
errorBuffer = append(errorBuffer, err)
|
||||||
log.Fatal("")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InternalError is the main function for handling internal errors, while simultaneously printing out a page for the end-user to let them know that *something* has gone wrong
|
// InternalError is the main function for handling internal errors, while simultaneously printing out a page for the end-user to let them know that *something* has gone wrong
|
||||||
|
|
|
@ -405,3 +405,5 @@ func (mfs *MemoryForumStore) GlobalCount() (fcount int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Work on SqlForumStore
|
// TODO: Work on SqlForumStore
|
||||||
|
|
||||||
|
// TODO: Work on the NullForumStore
|
||||||
|
|
68
gen_mssql.go
68
gen_mssql.go
|
@ -65,13 +65,10 @@ var createForumStmt *sql.Stmt
|
||||||
var addForumPermsToForumStmt *sql.Stmt
|
var addForumPermsToForumStmt *sql.Stmt
|
||||||
var addPluginStmt *sql.Stmt
|
var addPluginStmt *sql.Stmt
|
||||||
var addThemeStmt *sql.Stmt
|
var addThemeStmt *sql.Stmt
|
||||||
var createGroupStmt *sql.Stmt
|
|
||||||
var addModlogEntryStmt *sql.Stmt
|
var addModlogEntryStmt *sql.Stmt
|
||||||
var addAdminlogEntryStmt *sql.Stmt
|
var addAdminlogEntryStmt *sql.Stmt
|
||||||
var addAttachmentStmt *sql.Stmt
|
var addAttachmentStmt *sql.Stmt
|
||||||
var createWordFilterStmt *sql.Stmt
|
var createWordFilterStmt *sql.Stmt
|
||||||
var addForumPermsToGroupStmt *sql.Stmt
|
|
||||||
var replaceScheduleGroupStmt *sql.Stmt
|
|
||||||
var addRepliesToTopicStmt *sql.Stmt
|
var addRepliesToTopicStmt *sql.Stmt
|
||||||
var removeRepliesFromTopicStmt *sql.Stmt
|
var removeRepliesFromTopicStmt *sql.Stmt
|
||||||
var addTopicsToForumStmt *sql.Stmt
|
var addTopicsToForumStmt *sql.Stmt
|
||||||
|
@ -105,6 +102,7 @@ var updatePluginStmt *sql.Stmt
|
||||||
var updatePluginInstallStmt *sql.Stmt
|
var updatePluginInstallStmt *sql.Stmt
|
||||||
var updateThemeStmt *sql.Stmt
|
var updateThemeStmt *sql.Stmt
|
||||||
var updateUserStmt *sql.Stmt
|
var updateUserStmt *sql.Stmt
|
||||||
|
var updateUserGroupStmt *sql.Stmt
|
||||||
var updateGroupPermsStmt *sql.Stmt
|
var updateGroupPermsStmt *sql.Stmt
|
||||||
var updateGroupRankStmt *sql.Stmt
|
var updateGroupRankStmt *sql.Stmt
|
||||||
var updateGroupStmt *sql.Stmt
|
var updateGroupStmt *sql.Stmt
|
||||||
|
@ -116,15 +114,11 @@ var bumpSyncStmt *sql.Stmt
|
||||||
var deleteUserStmt *sql.Stmt
|
var deleteUserStmt *sql.Stmt
|
||||||
var deleteReplyStmt *sql.Stmt
|
var deleteReplyStmt *sql.Stmt
|
||||||
var deleteProfileReplyStmt *sql.Stmt
|
var deleteProfileReplyStmt *sql.Stmt
|
||||||
var deleteForumPermsByForumStmt *sql.Stmt
|
|
||||||
var deleteActivityStreamMatchStmt *sql.Stmt
|
var deleteActivityStreamMatchStmt *sql.Stmt
|
||||||
var deleteWordFilterStmt *sql.Stmt
|
var deleteWordFilterStmt *sql.Stmt
|
||||||
var reportExistsStmt *sql.Stmt
|
var reportExistsStmt *sql.Stmt
|
||||||
var groupCountStmt *sql.Stmt
|
var groupCountStmt *sql.Stmt
|
||||||
var modlogCountStmt *sql.Stmt
|
var modlogCountStmt *sql.Stmt
|
||||||
var addForumPermsToForumAdminsStmt *sql.Stmt
|
|
||||||
var addForumPermsToForumStaffStmt *sql.Stmt
|
|
||||||
var addForumPermsToForumMembersStmt *sql.Stmt
|
|
||||||
var notifyWatchersStmt *sql.Stmt
|
var notifyWatchersStmt *sql.Stmt
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
|
@ -365,9 +359,9 @@ func _gen_mssql() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing getExpiredScheduledGroups statement.")
|
log.Print("Preparing getExpiredScheduledGroups statement.")
|
||||||
getExpiredScheduledGroupsStmt, err = db.Prepare("SELECT [uid] FROM [users_groups_scheduler] WHERE GETUTCDATE() > [revert_at] AND [temporary] = 1")
|
getExpiredScheduledGroupsStmt, err = db.Prepare("SELECT [uid] FROM [users_groups_scheduler] WHERE GETDATE() > [revert_at] AND [temporary] = 1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("Bad Query: ","SELECT [uid] FROM [users_groups_scheduler] WHERE GETUTCDATE() > [revert_at] AND [temporary] = 1")
|
log.Print("Bad Query: ","SELECT [uid] FROM [users_groups_scheduler] WHERE GETDATE() > [revert_at] AND [temporary] = 1")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,13 +533,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing createGroup statement.")
|
|
||||||
createGroupStmt, err = db.Prepare("INSERT INTO [users_groups] ([name],[tag],[is_admin],[is_mod],[is_banned],[permissions]) VALUES (?,?,?,?,?,?)")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","INSERT INTO [users_groups] ([name],[tag],[is_admin],[is_mod],[is_banned],[permissions]) VALUES (?,?,?,?,?,?)")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing addModlogEntry statement.")
|
log.Print("Preparing addModlogEntry statement.")
|
||||||
addModlogEntryStmt, err = db.Prepare("INSERT INTO [moderation_logs] ([action],[elementID],[elementType],[ipaddress],[actorID],[doneAt]) VALUES (?,?,?,?,?,GETUTCDATE())")
|
addModlogEntryStmt, err = db.Prepare("INSERT INTO [moderation_logs] ([action],[elementID],[elementType],[ipaddress],[actorID],[doneAt]) VALUES (?,?,?,?,?,GETUTCDATE())")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -574,20 +561,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing addForumPermsToGroup statement.")
|
|
||||||
addForumPermsToGroupStmt, err = db.Prepare("MERGE [forums_permissions] WITH(HOLDLOCK) as t1 USING (VALUES(?,?,?,?)) AS updates (f0,f1,f2,f3) ON [gid] = ? [fid] = ? WHEN MATCHED THEN UPDATE SET [gid] = f0,[fid] = f1,[preset] = f2,[permissions] = f3 WHEN NOT MATCHED THEN INSERT([gid],[fid],[preset],[permissions]) VALUES (f0,f1,f2,f3);")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","MERGE [forums_permissions] WITH(HOLDLOCK) as t1 USING (VALUES(?,?,?,?)) AS updates (f0,f1,f2,f3) ON [gid] = ? [fid] = ? WHEN MATCHED THEN UPDATE SET [gid] = f0,[fid] = f1,[preset] = f2,[permissions] = f3 WHEN NOT MATCHED THEN INSERT([gid],[fid],[preset],[permissions]) VALUES (f0,f1,f2,f3);")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing replaceScheduleGroup statement.")
|
|
||||||
replaceScheduleGroupStmt, err = db.Prepare("MERGE [users_groups_scheduler] WITH(HOLDLOCK) as t1 USING (VALUES(?,?,?,GETUTCDATE(),?,?)) AS updates (f0,f1,f2,f3,f4,f5) ON [uid] = ? WHEN MATCHED THEN UPDATE SET [uid] = f0,[set_group] = f1,[issued_by] = f2,[issued_at] = f3,[revert_at] = f4,[temporary] = f5 WHEN NOT MATCHED THEN INSERT([uid],[set_group],[issued_by],[issued_at],[revert_at],[temporary]) VALUES (f0,f1,f2,f3,f4,f5);")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","MERGE [users_groups_scheduler] WITH(HOLDLOCK) as t1 USING (VALUES(?,?,?,GETUTCDATE(),?,?)) AS updates (f0,f1,f2,f3,f4,f5) ON [uid] = ? WHEN MATCHED THEN UPDATE SET [uid] = f0,[set_group] = f1,[issued_by] = f2,[issued_at] = f3,[revert_at] = f4,[temporary] = f5 WHEN NOT MATCHED THEN INSERT([uid],[set_group],[issued_by],[issued_at],[revert_at],[temporary]) VALUES (f0,f1,f2,f3,f4,f5);")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing addRepliesToTopic statement.")
|
log.Print("Preparing addRepliesToTopic statement.")
|
||||||
addRepliesToTopicStmt, err = db.Prepare("UPDATE [topics] SET [postCount] = [postCount] + ?,[lastReplyBy] = ?,[lastReplyAt] = GETUTCDATE() WHERE [tid] = ?")
|
addRepliesToTopicStmt, err = db.Prepare("UPDATE [topics] SET [postCount] = [postCount] + ?,[lastReplyBy] = ?,[lastReplyAt] = GETUTCDATE() WHERE [tid] = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -819,6 +792,13 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Print("Preparing updateUserGroup statement.")
|
||||||
|
updateUserGroupStmt, err = db.Prepare("UPDATE [users] SET [group] = ? WHERE [uid] = ?")
|
||||||
|
if err != nil {
|
||||||
|
log.Print("Bad Query: ","UPDATE [users] SET [group] = ? WHERE [uid] = ?")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Print("Preparing updateGroupPerms statement.")
|
log.Print("Preparing updateGroupPerms statement.")
|
||||||
updateGroupPermsStmt, err = db.Prepare("UPDATE [users_groups] SET [permissions] = ? WHERE [gid] = ?")
|
updateGroupPermsStmt, err = db.Prepare("UPDATE [users_groups] SET [permissions] = ? WHERE [gid] = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -896,13 +876,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing deleteForumPermsByForum statement.")
|
|
||||||
deleteForumPermsByForumStmt, err = db.Prepare("DELETE FROM [forums_permissions] WHERE [fid] = ?")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","DELETE FROM [forums_permissions] WHERE [fid] = ?")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing deleteActivityStreamMatch statement.")
|
log.Print("Preparing deleteActivityStreamMatch statement.")
|
||||||
deleteActivityStreamMatchStmt, err = db.Prepare("DELETE FROM [activity_stream_matches] WHERE [watcher] = ? AND [asid] = ?")
|
deleteActivityStreamMatchStmt, err = db.Prepare("DELETE FROM [activity_stream_matches] WHERE [watcher] = ? AND [asid] = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -938,27 +911,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing addForumPermsToForumAdmins statement.")
|
|
||||||
addForumPermsToForumAdminsStmt, err = db.Prepare("INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 1")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 1")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing addForumPermsToForumStaff statement.")
|
|
||||||
addForumPermsToForumStaffStmt, err = db.Prepare("INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 0 AND [is_mod] = 1")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 0 AND [is_mod] = 1")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing addForumPermsToForumMembers statement.")
|
|
||||||
addForumPermsToForumMembersStmt, err = db.Prepare("INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 0 AND [is_mod] = 0 AND [is_banned] = 0")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 0 AND [is_mod] = 0 AND [is_banned] = 0")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing notifyWatchers statement.")
|
log.Print("Preparing notifyWatchers statement.")
|
||||||
notifyWatchersStmt, err = db.Prepare("INSERT INTO [activity_stream_matches] ([watcher],[asid]) SELECT [activity_subscriptions].[user],[activity_stream].[asid] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
|
notifyWatchersStmt, err = db.Prepare("INSERT INTO [activity_stream_matches] ([watcher],[asid]) SELECT [activity_subscriptions].[user],[activity_stream].[asid] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
58
gen_mysql.go
58
gen_mysql.go
|
@ -6,7 +6,7 @@ package main
|
||||||
|
|
||||||
import "log"
|
import "log"
|
||||||
import "database/sql"
|
import "database/sql"
|
||||||
import "./query_gen/lib"
|
//import "./query_gen/lib"
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
var getUserStmt *sql.Stmt
|
var getUserStmt *sql.Stmt
|
||||||
|
@ -67,13 +67,10 @@ var createForumStmt *sql.Stmt
|
||||||
var addForumPermsToForumStmt *sql.Stmt
|
var addForumPermsToForumStmt *sql.Stmt
|
||||||
var addPluginStmt *sql.Stmt
|
var addPluginStmt *sql.Stmt
|
||||||
var addThemeStmt *sql.Stmt
|
var addThemeStmt *sql.Stmt
|
||||||
var createGroupStmt *sql.Stmt
|
|
||||||
var addModlogEntryStmt *sql.Stmt
|
var addModlogEntryStmt *sql.Stmt
|
||||||
var addAdminlogEntryStmt *sql.Stmt
|
var addAdminlogEntryStmt *sql.Stmt
|
||||||
var addAttachmentStmt *sql.Stmt
|
var addAttachmentStmt *sql.Stmt
|
||||||
var createWordFilterStmt *sql.Stmt
|
var createWordFilterStmt *sql.Stmt
|
||||||
var addForumPermsToGroupStmt *qgen.MySQLUpsertCallback
|
|
||||||
var replaceScheduleGroupStmt *qgen.MySQLUpsertCallback
|
|
||||||
var addRepliesToTopicStmt *sql.Stmt
|
var addRepliesToTopicStmt *sql.Stmt
|
||||||
var removeRepliesFromTopicStmt *sql.Stmt
|
var removeRepliesFromTopicStmt *sql.Stmt
|
||||||
var addTopicsToForumStmt *sql.Stmt
|
var addTopicsToForumStmt *sql.Stmt
|
||||||
|
@ -107,6 +104,7 @@ var updatePluginStmt *sql.Stmt
|
||||||
var updatePluginInstallStmt *sql.Stmt
|
var updatePluginInstallStmt *sql.Stmt
|
||||||
var updateThemeStmt *sql.Stmt
|
var updateThemeStmt *sql.Stmt
|
||||||
var updateUserStmt *sql.Stmt
|
var updateUserStmt *sql.Stmt
|
||||||
|
var updateUserGroupStmt *sql.Stmt
|
||||||
var updateGroupPermsStmt *sql.Stmt
|
var updateGroupPermsStmt *sql.Stmt
|
||||||
var updateGroupRankStmt *sql.Stmt
|
var updateGroupRankStmt *sql.Stmt
|
||||||
var updateGroupStmt *sql.Stmt
|
var updateGroupStmt *sql.Stmt
|
||||||
|
@ -118,15 +116,11 @@ var bumpSyncStmt *sql.Stmt
|
||||||
var deleteUserStmt *sql.Stmt
|
var deleteUserStmt *sql.Stmt
|
||||||
var deleteReplyStmt *sql.Stmt
|
var deleteReplyStmt *sql.Stmt
|
||||||
var deleteProfileReplyStmt *sql.Stmt
|
var deleteProfileReplyStmt *sql.Stmt
|
||||||
var deleteForumPermsByForumStmt *sql.Stmt
|
|
||||||
var deleteActivityStreamMatchStmt *sql.Stmt
|
var deleteActivityStreamMatchStmt *sql.Stmt
|
||||||
var deleteWordFilterStmt *sql.Stmt
|
var deleteWordFilterStmt *sql.Stmt
|
||||||
var reportExistsStmt *sql.Stmt
|
var reportExistsStmt *sql.Stmt
|
||||||
var groupCountStmt *sql.Stmt
|
var groupCountStmt *sql.Stmt
|
||||||
var modlogCountStmt *sql.Stmt
|
var modlogCountStmt *sql.Stmt
|
||||||
var addForumPermsToForumAdminsStmt *sql.Stmt
|
|
||||||
var addForumPermsToForumStaffStmt *sql.Stmt
|
|
||||||
var addForumPermsToForumMembersStmt *sql.Stmt
|
|
||||||
var notifyWatchersStmt *sql.Stmt
|
var notifyWatchersStmt *sql.Stmt
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
|
@ -483,12 +477,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing createGroup statement.")
|
|
||||||
createGroupStmt, err = db.Prepare("INSERT INTO `users_groups`(`name`,`tag`,`is_admin`,`is_mod`,`is_banned`,`permissions`) VALUES (?,?,?,?,?,?)")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing addModlogEntry statement.")
|
log.Print("Preparing addModlogEntry statement.")
|
||||||
addModlogEntryStmt, err = db.Prepare("INSERT INTO `moderation_logs`(`action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt`) VALUES (?,?,?,?,?,UTC_TIMESTAMP())")
|
addModlogEntryStmt, err = db.Prepare("INSERT INTO `moderation_logs`(`action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt`) VALUES (?,?,?,?,?,UTC_TIMESTAMP())")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -513,18 +501,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing addForumPermsToGroup statement.")
|
|
||||||
addForumPermsToGroupStmt, err = qgen.PrepareMySQLUpsertCallback(db, "INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE `gid` = ? AND `fid` = ? AND `preset` = ? AND `permissions` = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing replaceScheduleGroup statement.")
|
|
||||||
replaceScheduleGroupStmt, err = qgen.PrepareMySQLUpsertCallback(db, "INSERT INTO `users_groups_scheduler`(`uid`,`set_group`,`issued_by`,`issued_at`,`revert_at`,`temporary`) VALUES (?,?,?,UTC_TIMESTAMP(),?,?) ON DUPLICATE KEY UPDATE `uid` = ? AND `set_group` = ? AND `issued_by` = ? AND `issued_at` = UTC_TIMESTAMP() AND `revert_at` = ? AND `temporary` = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing addRepliesToTopic statement.")
|
log.Print("Preparing addRepliesToTopic statement.")
|
||||||
addRepliesToTopicStmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyBy` = ?,`lastReplyAt` = UTC_TIMESTAMP() WHERE `tid` = ?")
|
addRepliesToTopicStmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyBy` = ?,`lastReplyAt` = UTC_TIMESTAMP() WHERE `tid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -723,6 +699,12 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Print("Preparing updateUserGroup statement.")
|
||||||
|
updateUserGroupStmt, err = db.Prepare("UPDATE `users` SET `group` = ? WHERE `uid` = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Print("Preparing updateGroupPerms statement.")
|
log.Print("Preparing updateGroupPerms statement.")
|
||||||
updateGroupPermsStmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ?")
|
updateGroupPermsStmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -789,12 +771,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing deleteForumPermsByForum statement.")
|
|
||||||
deleteForumPermsByForumStmt, err = db.Prepare("DELETE FROM `forums_permissions` WHERE `fid` = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing deleteActivityStreamMatch statement.")
|
log.Print("Preparing deleteActivityStreamMatch statement.")
|
||||||
deleteActivityStreamMatchStmt, err = db.Prepare("DELETE FROM `activity_stream_matches` WHERE `watcher` = ? AND `asid` = ?")
|
deleteActivityStreamMatchStmt, err = db.Prepare("DELETE FROM `activity_stream_matches` WHERE `watcher` = ? AND `asid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -825,24 +801,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing addForumPermsToForumAdmins statement.")
|
|
||||||
addForumPermsToForumAdminsStmt, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) SELECT `gid`, ? AS `fid`, ? AS `preset`, ? AS `permissions` FROM `users_groups` WHERE `is_admin` = 1")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing addForumPermsToForumStaff statement.")
|
|
||||||
addForumPermsToForumStaffStmt, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) SELECT `gid`, ? AS `fid`, ? AS `preset`, ? AS `permissions` FROM `users_groups` WHERE `is_admin` = 0 AND `is_mod` = 1")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing addForumPermsToForumMembers statement.")
|
|
||||||
addForumPermsToForumMembersStmt, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) SELECT `gid`, ? AS `fid`, ? AS `preset`, ? AS `permissions` FROM `users_groups` WHERE `is_admin` = 0 AND `is_mod` = 0 AND `is_banned` = 0")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing notifyWatchers statement.")
|
log.Print("Preparing notifyWatchers statement.")
|
||||||
notifyWatchersStmt, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) SELECT `activity_subscriptions`.`user`, `activity_stream`.`asid` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?")
|
notifyWatchersStmt, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) SELECT `activity_subscriptions`.`user`, `activity_stream`.`asid` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -40,6 +40,7 @@ var updatePluginStmt *sql.Stmt
|
||||||
var updatePluginInstallStmt *sql.Stmt
|
var updatePluginInstallStmt *sql.Stmt
|
||||||
var updateThemeStmt *sql.Stmt
|
var updateThemeStmt *sql.Stmt
|
||||||
var updateUserStmt *sql.Stmt
|
var updateUserStmt *sql.Stmt
|
||||||
|
var updateUserGroupStmt *sql.Stmt
|
||||||
var updateGroupPermsStmt *sql.Stmt
|
var updateGroupPermsStmt *sql.Stmt
|
||||||
var updateGroupRankStmt *sql.Stmt
|
var updateGroupRankStmt *sql.Stmt
|
||||||
var updateGroupStmt *sql.Stmt
|
var updateGroupStmt *sql.Stmt
|
||||||
|
@ -253,6 +254,12 @@ func _gen_pgsql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Print("Preparing updateUserGroup statement.")
|
||||||
|
updateUserGroupStmt, err = db.Prepare("UPDATE `users` SET `group` = ? WHERE `uid` = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Print("Preparing updateGroupPerms statement.")
|
log.Print("Preparing updateGroupPerms statement.")
|
||||||
updateGroupPermsStmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ?")
|
updateGroupPermsStmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
var dbTablePrimaryKeys = map[string]string{
|
var dbTablePrimaryKeys = map[string]string{
|
||||||
"forums":"fid",
|
|
||||||
"topics":"tid",
|
|
||||||
"attachments":"attachID",
|
|
||||||
"users_replies":"rid",
|
|
||||||
"word_filters":"wfid",
|
|
||||||
"users":"uid",
|
|
||||||
"users_groups":"gid",
|
"users_groups":"gid",
|
||||||
"users_groups_scheduler":"uid",
|
"users_groups_scheduler":"uid",
|
||||||
"replies":"rid",
|
"topics":"tid",
|
||||||
|
"users_replies":"rid",
|
||||||
"activity_stream":"asid",
|
"activity_stream":"asid",
|
||||||
|
"word_filters":"wfid",
|
||||||
|
"users":"uid",
|
||||||
|
"forums":"fid",
|
||||||
|
"replies":"rid",
|
||||||
|
"attachments":"attachID",
|
||||||
}
|
}
|
||||||
|
|
18
group.go
18
group.go
|
@ -27,6 +27,24 @@ type Group struct {
|
||||||
CanSee []int // The IDs of the forums this group can see
|
CanSee []int // The IDs of the forums this group can see
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Reload the group from the database rather than modifying it via it's pointer
|
||||||
|
func (group *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err error) {
|
||||||
|
_, err = updateGroupRankStmt.Exec(isAdmin, isMod, isBanned, group.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
group.IsAdmin = isAdmin
|
||||||
|
group.IsMod = isMod
|
||||||
|
if isAdmin || isMod {
|
||||||
|
group.IsBanned = false
|
||||||
|
} else {
|
||||||
|
group.IsBanned = isBanned
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ! Ahem, don't listen to the comment below. It's not concurrency safe right now.
|
// ! Ahem, don't listen to the comment below. It's not concurrency safe right now.
|
||||||
// Copy gives you a non-pointer concurrency safe copy of the group
|
// Copy gives you a non-pointer concurrency safe copy of the group
|
||||||
func (group *Group) Copy() Group {
|
func (group *Group) Copy() Group {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"./query_gen/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var groupCreateMutex sync.Mutex
|
var groupCreateMutex sync.Mutex
|
||||||
|
@ -19,7 +21,7 @@ type GroupStore interface {
|
||||||
Get(id int) (*Group, error)
|
Get(id int) (*Group, error)
|
||||||
GetCopy(id int) (Group, error)
|
GetCopy(id int) (Group, error)
|
||||||
Exists(id int) bool
|
Exists(id int) bool
|
||||||
Create(groupName string, tag string, isAdmin bool, isMod bool, isBanned bool) (int, error)
|
Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (int, error)
|
||||||
GetAll() ([]*Group, error)
|
GetAll() ([]*Group, error)
|
||||||
GetRange(lower int, higher int) ([]*Group, error)
|
GetRange(lower int, higher int) ([]*Group, error)
|
||||||
}
|
}
|
||||||
|
@ -71,8 +73,11 @@ func (mgs *MemoryGroupStore) LoadGroups() error {
|
||||||
log.Print(group.Name + ": ")
|
log.Print(group.Name + ": ")
|
||||||
log.Printf("%+v\n", group.PluginPerms)
|
log.Printf("%+v\n", group.PluginPerms)
|
||||||
}
|
}
|
||||||
|
|
||||||
//group.Perms.ExtData = make(map[string]bool)
|
//group.Perms.ExtData = make(map[string]bool)
|
||||||
|
// TODO: Can we optimise the bit where this cascades down to the user now?
|
||||||
|
if group.IsAdmin || group.IsMod {
|
||||||
|
group.IsBanned = false
|
||||||
|
}
|
||||||
mgs.groups = append(mgs.groups, &group)
|
mgs.groups = append(mgs.groups, &group)
|
||||||
}
|
}
|
||||||
err = rows.Err()
|
err = rows.Err()
|
||||||
|
@ -110,15 +115,26 @@ func (mgs *MemoryGroupStore) GetCopy(gid int) (Group, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mgs *MemoryGroupStore) Exists(gid int) bool {
|
func (mgs *MemoryGroupStore) Exists(gid int) bool {
|
||||||
return (gid <= mgs.groupCapCount) && (gid > -1) && mgs.groups[gid].Name != ""
|
return (gid <= mgs.groupCapCount) && (gid >= 0) && mgs.groups[gid].Name != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mgs *MemoryGroupStore) Create(groupName string, tag string, isAdmin bool, isMod bool, isBanned bool) (int, error) {
|
// ? Allow two groups with the same name?
|
||||||
|
func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (int, error) {
|
||||||
groupCreateMutex.Lock()
|
groupCreateMutex.Lock()
|
||||||
defer groupCreateMutex.Unlock()
|
defer groupCreateMutex.Unlock()
|
||||||
|
|
||||||
var permstr = "{}"
|
var permstr = "{}"
|
||||||
res, err := createGroupStmt.Exec(groupName, tag, isAdmin, isMod, isBanned, permstr)
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
insertTx, err := qgen.Builder.SimpleInsertTx(tx, "users_groups", "name, tag, is_admin, is_mod, is_banned, permissions", "?,?,?,?,?,?")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
res, err := insertTx.Exec(name, tag, isAdmin, isMod, isBanned, permstr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -138,14 +154,14 @@ func (mgs *MemoryGroupStore) Create(groupName string, tag string, isAdmin bool,
|
||||||
runVhook("create_group_preappend", &pluginPerms, &pluginPermsBytes)
|
runVhook("create_group_preappend", &pluginPerms, &pluginPermsBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
mgs.groups = append(mgs.groups, &Group{gid, groupName, isMod, isAdmin, isBanned, tag, perms, []byte(permstr), pluginPerms, pluginPermsBytes, blankForums, blankIntList})
|
|
||||||
|
|
||||||
// Generate the forum permissions based on the presets...
|
// Generate the forum permissions based on the presets...
|
||||||
fdata, err := fstore.GetAll()
|
fdata, err := fstore.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var presetSet = make(map[int]string)
|
||||||
|
var permSet = make(map[int]ForumPerms)
|
||||||
permUpdateMutex.Lock()
|
permUpdateMutex.Lock()
|
||||||
defer permUpdateMutex.Unlock()
|
defer permUpdateMutex.Unlock()
|
||||||
for _, forum := range fdata {
|
for _, forum := range fdata {
|
||||||
|
@ -161,23 +177,38 @@ func (mgs *MemoryGroupStore) Create(groupName string, tag string, isAdmin bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
permmap := presetToPermmap(forum.Preset)
|
permmap := presetToPermmap(forum.Preset)
|
||||||
permitem := permmap[thePreset]
|
permItem := permmap[thePreset]
|
||||||
permitem.Overrides = true
|
permItem.Overrides = true
|
||||||
permstr, err := json.Marshal(permitem)
|
|
||||||
if err != nil {
|
|
||||||
return gid, err
|
|
||||||
}
|
|
||||||
perms := string(permstr)
|
|
||||||
_, err = addForumPermsToGroupStmt.Exec(gid, forum.ID, forum.Preset, perms)
|
|
||||||
if err != nil {
|
|
||||||
return gid, err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
permSet[forum.ID] = permItem
|
||||||
|
presetSet[forum.ID] = forum.Preset
|
||||||
|
}
|
||||||
|
|
||||||
|
err = replaceForumPermsForGroupTx(tx, gid, presetSet, permSet)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Can we optimise the bit where this cascades down to the user now?
|
||||||
|
if isAdmin || isMod {
|
||||||
|
isBanned = false
|
||||||
|
}
|
||||||
|
|
||||||
|
mgs.groups = append(mgs.groups, &Group{gid, name, isMod, isAdmin, isBanned, tag, perms, []byte(permstr), pluginPerms, pluginPermsBytes, blankForums, blankIntList})
|
||||||
|
mgs.groupCapCount++
|
||||||
|
|
||||||
|
for _, forum := range fdata {
|
||||||
err = rebuildForumPermissions(forum.ID)
|
err = rebuildForumPermissions(forum.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gid, err
|
return gid, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gid, nil
|
return gid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ func (ins *MysqlInstaller) InitDatabase() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ins *MysqlInstaller) TableDefs() error {
|
func (ins *MysqlInstaller) TableDefs() (err error) {
|
||||||
//fmt.Println("Creating the tables")
|
//fmt.Println("Creating the tables")
|
||||||
files, _ := ioutil.ReadDir("./schema/mysql/")
|
files, _ := ioutil.ReadDir("./schema/mysql/")
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
|
@ -115,6 +115,13 @@ func (ins *MysqlInstaller) TableDefs() error {
|
||||||
}
|
}
|
||||||
table = strings.TrimSuffix(table, ext)
|
table = strings.TrimSuffix(table, ext)
|
||||||
|
|
||||||
|
// ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution
|
||||||
|
_, err = ins.db.Exec("DROP TABLE IF EXISTS `" + table + "`;")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed query:", "DROP TABLE IF EXISTS `"+table+"`;")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("Creating table '" + table + "'")
|
fmt.Println("Creating table '" + table + "'")
|
||||||
data, err := ioutil.ReadFile("./schema/mysql/" + f.Name())
|
data, err := ioutil.ReadFile("./schema/mysql/" + f.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
17
main.go
17
main.go
|
@ -55,6 +55,12 @@ var allowedFileExts = StringList{
|
||||||
var imageFileExts = StringList{
|
var imageFileExts = StringList{
|
||||||
"png", "jpg", "jpeg", "svg", "bmp", "gif", "tif", "webp", "apng",
|
"png", "jpg", "jpeg", "svg", "bmp", "gif", "tif", "webp", "apng",
|
||||||
}
|
}
|
||||||
|
var archiveFileExts = StringList{
|
||||||
|
"bz2", "zip", "gz", "7z", "tar", "cab",
|
||||||
|
}
|
||||||
|
var executableFileExts = StringList{
|
||||||
|
"exe", "jar", "phar", "shar", "iso",
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func (slice StringList) Contains(needle string) bool {
|
func (slice StringList) Contains(needle string) bool {
|
||||||
|
@ -70,6 +76,16 @@ var staticFiles = make(map[string]SFile)
|
||||||
var logWriter = io.MultiWriter(os.Stderr)
|
var logWriter = io.MultiWriter(os.Stderr)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// TODO: Recover from panics
|
||||||
|
/*defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r != nil {
|
||||||
|
log.Print(r)
|
||||||
|
debug.PrintStack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()*/
|
||||||
|
|
||||||
// TODO: Have a file for each run with the time/date the server started as the file name?
|
// TODO: Have a file for each run with the time/date the server started as the file name?
|
||||||
// TODO: Log panics with recover()
|
// TODO: Log panics with recover()
|
||||||
f, err := os.OpenFile("./operations.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
f, err := os.OpenFile("./operations.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||||
|
@ -178,7 +194,6 @@ func main() {
|
||||||
|
|
||||||
// TODO: Automatically lock topics, if they're really old, and the associated setting is enabled.
|
// TODO: Automatically lock topics, if they're really old, and the associated setting is enabled.
|
||||||
// TODO: Publish scheduled posts.
|
// TODO: Publish scheduled posts.
|
||||||
// TODO: Delete the empty users_groups_scheduler entries
|
|
||||||
|
|
||||||
// TODO: Add a plugin hook here
|
// TODO: Add a plugin hook here
|
||||||
}
|
}
|
||||||
|
|
497
misc_test.go
497
misc_test.go
|
@ -1,24 +1,26 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http/httptest"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func recordMustExist(t *testing.T, err error, errmsg string) {
|
func recordMustExist(t *testing.T, err error, errmsg string, args ...interface{}) {
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
t.Error(errmsg)
|
t.Errorf(errmsg, args...)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordMustNotExist(t *testing.T, err error, errmsg string) {
|
func recordMustNotExist(t *testing.T, err error, errmsg string, args ...interface{}) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error(errmsg)
|
t.Errorf(errmsg, args...)
|
||||||
} else if err != ErrNoRows {
|
} else if err != ErrNoRows {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -42,50 +44,42 @@ func TestUserStore(t *testing.T) {
|
||||||
userStoreTest(t, 3)
|
userStoreTest(t, 3)
|
||||||
}
|
}
|
||||||
func userStoreTest(t *testing.T, newUserID int) {
|
func userStoreTest(t *testing.T, newUserID int) {
|
||||||
var user *User
|
|
||||||
var err error
|
|
||||||
|
|
||||||
ucache, hasCache := users.(UserCache)
|
ucache, hasCache := users.(UserCache)
|
||||||
if hasCache && ucache.Length() != 0 {
|
// Go doesn't have short-circuiting, so this'll allow us to do one liner tests
|
||||||
t.Error("Initial ucache length isn't zero")
|
if !hasCache {
|
||||||
|
ucache = &NullUserStore{}
|
||||||
}
|
}
|
||||||
|
expect(t, (!hasCache || ucache.Length() == 0), fmt.Sprintf("The initial ucache length should be zero, not %d", ucache.Length()))
|
||||||
|
|
||||||
_, err = users.Get(-1)
|
_, err := users.Get(-1)
|
||||||
recordMustNotExist(t, err, "UID #-1 shouldn't exist")
|
recordMustNotExist(t, err, "UID #-1 shouldn't exist")
|
||||||
|
expect(t, !hasCache || ucache.Length() == 0, fmt.Sprintf("We found %d items in the user cache and it's supposed to be empty", ucache.Length()))
|
||||||
if hasCache && ucache.Length() != 0 {
|
|
||||||
t.Error("There shouldn't be anything in the user cache")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = users.Get(0)
|
_, err = users.Get(0)
|
||||||
recordMustNotExist(t, err, "UID #0 shouldn't exist")
|
recordMustNotExist(t, err, "UID #0 shouldn't exist")
|
||||||
|
expect(t, !hasCache || ucache.Length() == 0, fmt.Sprintf("We found %d items in the user cache and it's supposed to be empty", ucache.Length()))
|
||||||
|
|
||||||
if hasCache && ucache.Length() != 0 {
|
user, err := users.Get(1)
|
||||||
t.Error("There shouldn't be anything in the user cache")
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err = users.Get(1)
|
|
||||||
recordMustExist(t, err, "Couldn't find UID #1")
|
recordMustExist(t, err, "Couldn't find UID #1")
|
||||||
|
|
||||||
if user.ID != 1 {
|
expect(t, user.ID == 1, fmt.Sprintf("user.ID should be 1. Got '%d' instead.", user.ID))
|
||||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
expect(t, user.Name == "Admin", fmt.Sprintf("user.Name should be 'Admin', not '%s'", user.Name))
|
||||||
}
|
expect(t, user.Group == 1, "Admin should be in group 1")
|
||||||
if user.Name != "Admin" {
|
expect(t, user.IsSuperAdmin, "Admin should be a super admin")
|
||||||
t.Error("user.Name should be 'Admin', not '" + user.Name + "'")
|
expect(t, user.IsAdmin, "Admin should be an admin")
|
||||||
}
|
expect(t, user.IsSuperMod, "Admin should be a super mod")
|
||||||
if user.Group != 1 {
|
expect(t, user.IsMod, "Admin should be a mod")
|
||||||
t.Error("Admin should be in group 1")
|
expect(t, !user.IsBanned, "Admin should not be banned")
|
||||||
}
|
|
||||||
|
|
||||||
user, err = users.Get(newUserID)
|
_, err = users.Get(newUserID)
|
||||||
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
||||||
|
|
||||||
if hasCache {
|
if hasCache {
|
||||||
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
||||||
|
|
||||||
user, err = ucache.CacheGet(-1)
|
_, err = ucache.CacheGet(-1)
|
||||||
recordMustNotExist(t, err, "UID #-1 shouldn't exist, even in the cache")
|
recordMustNotExist(t, err, "UID #-1 shouldn't exist, even in the cache")
|
||||||
user, err = ucache.CacheGet(0)
|
_, err = ucache.CacheGet(0)
|
||||||
recordMustNotExist(t, err, "UID #0 shouldn't exist, even in the cache")
|
recordMustNotExist(t, err, "UID #0 shouldn't exist, even in the cache")
|
||||||
user, err = ucache.CacheGet(1)
|
user, err = ucache.CacheGet(1)
|
||||||
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
||||||
|
@ -97,8 +91,8 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
t.Error("user.Name should be 'Admin', not '" + user.Name + "'")
|
t.Error("user.Name should be 'Admin', not '" + user.Name + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = ucache.CacheGet(newUserID)
|
_, err = ucache.CacheGet(newUserID)
|
||||||
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist, even in the cache", newUserID))
|
recordMustNotExist(t, err, "UID #%d shouldn't exist, even in the cache", newUserID)
|
||||||
|
|
||||||
ucache.Flush()
|
ucache.Flush()
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
|
@ -147,9 +141,8 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
||||||
|
|
||||||
if user.ID != 1 {
|
if user.ID != 1 {
|
||||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
t.Errorf("user.ID does not match the requested UID. Got '%d' instead.", user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
ucache.Flush()
|
ucache.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,120 +151,281 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
expect(t, users.Exists(1), "UID #1 should exist")
|
expect(t, users.Exists(1), "UID #1 should exist")
|
||||||
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
||||||
|
|
||||||
if hasCache {
|
expect(t, !hasCache || ucache.Length() == 0, fmt.Sprintf("User cache length should be 0, not %d", ucache.Length()))
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
|
||||||
}
|
|
||||||
expectIntToBeX(t, users.GlobalCount(), 1, "The number of users should be one, not %d")
|
expectIntToBeX(t, users.GlobalCount(), 1, "The number of users should be one, not %d")
|
||||||
|
|
||||||
var awaitingActivation = 5
|
var awaitingActivation = 5
|
||||||
uid, err := users.Create("Sam", "ReallyBadPassword", "sam@localhost.loc", awaitingActivation, 0)
|
uid, err := users.Create("Sam", "ReallyBadPassword", "sam@localhost.loc", awaitingActivation, false)
|
||||||
if err != nil {
|
expectNilErr(t, err)
|
||||||
t.Error(err)
|
expect(t, uid == newUserID, fmt.Sprintf("The UID of the new user should be %d", newUserID))
|
||||||
}
|
expect(t, users.Exists(newUserID), fmt.Sprintf("UID #%d should exist", newUserID))
|
||||||
if uid != newUserID {
|
|
||||||
t.Errorf("The UID of the new user should be %d", newUserID)
|
|
||||||
}
|
|
||||||
if !users.Exists(newUserID) {
|
|
||||||
t.Errorf("UID #%d should exist", newUserID)
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err = users.Get(newUserID)
|
user, err = users.Get(newUserID)
|
||||||
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
if user.ID != newUserID {
|
if user.ID != newUserID {
|
||||||
t.Errorf("The UID of the user record should be %d", newUserID)
|
t.Errorf("The UID of the user record should be %d", newUserID)
|
||||||
}
|
}
|
||||||
if user.Name != "Sam" {
|
|
||||||
t.Error("The user should be named Sam")
|
expect(t, user.Name == "Sam", "The user should be named Sam")
|
||||||
}
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
expect(t, !user.IsMod, "Sam should not be a mod")
|
||||||
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
expectIntToBeX(t, user.Group, 5, "Sam should be in group 5")
|
expectIntToBeX(t, user.Group, 5, "Sam should be in group 5")
|
||||||
|
|
||||||
if hasCache {
|
if hasCache {
|
||||||
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
||||||
user, err = ucache.CacheGet(newUserID)
|
user, err = ucache.CacheGet(newUserID)
|
||||||
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d in the cache", newUserID))
|
recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID)
|
||||||
if user.ID != newUserID {
|
expect(t, user.ID == newUserID, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = user.Activate()
|
err = user.Activate()
|
||||||
if err != nil {
|
expectNilErr(t, err)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
expectIntToBeX(t, user.Group, 5, "Sam should still be in group 5 in this copy")
|
expectIntToBeX(t, user.Group, 5, "Sam should still be in group 5 in this copy")
|
||||||
|
|
||||||
// ? - What if we change the caching mechanism so it isn't hard purged and reloaded? We'll deal with that when we come to it, but for now, this is a sign of a cache bug
|
// ? - What if we change the caching mechanism so it isn't hard purged and reloaded? We'll deal with that when we come to it, but for now, this is a sign of a cache bug
|
||||||
if hasCache {
|
if hasCache {
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
_, err = ucache.CacheGet(newUserID)
|
_, err = ucache.CacheGet(newUserID)
|
||||||
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't be in the cache", newUserID))
|
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = users.Get(newUserID)
|
user, err = users.Get(newUserID)
|
||||||
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
if user.ID != newUserID {
|
|
||||||
t.Errorf("The UID of the user record should be %d", newUserID)
|
expect(t, user.ID == newUserID, fmt.Sprintf("The UID of the user record should be %d, not %d", newUserID, user.ID))
|
||||||
}
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should be in group "+strconv.Itoa(config.DefaultGroup))
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
expect(t, !user.IsMod, "Sam should not be a mod")
|
||||||
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
|
|
||||||
|
expect(t, user.Group == config.DefaultGroup, fmt.Sprintf("Sam should be in group %d, not %d", config.DefaultGroup, user.Group))
|
||||||
|
|
||||||
// Permanent ban
|
// Permanent ban
|
||||||
duration, _ := time.ParseDuration("0")
|
duration, _ := time.ParseDuration("0")
|
||||||
|
|
||||||
// TODO: Attempt a double ban, double activation, and double unban
|
// TODO: Attempt a double ban, double activation, and double unban
|
||||||
err = user.Ban(duration, 1)
|
err = user.Ban(duration, 1)
|
||||||
if err != nil {
|
expectNilErr(t, err)
|
||||||
t.Error(err)
|
expect(t, user.Group == config.DefaultGroup, fmt.Sprintf("Sam should be in group %d, not %d", config.DefaultGroup, user.Group))
|
||||||
}
|
|
||||||
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should still be in the default group in this copy")
|
|
||||||
|
|
||||||
if hasCache {
|
if hasCache {
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
_, err = ucache.CacheGet(2)
|
_, err = ucache.CacheGet(2)
|
||||||
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't be in the cache", newUserID))
|
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = users.Get(newUserID)
|
user, err = users.Get(newUserID)
|
||||||
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
if user.ID != newUserID {
|
if user.ID != newUserID {
|
||||||
t.Errorf("The UID of the user record should be %d", newUserID)
|
t.Errorf("The UID of the user record should be %d", newUserID)
|
||||||
}
|
}
|
||||||
expectIntToBeX(t, user.Group, banGroup, "Sam should be in group "+strconv.Itoa(banGroup))
|
|
||||||
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
expect(t, !user.IsMod, "Sam should not be a mod")
|
||||||
|
expect(t, user.IsBanned, "Sam should be banned")
|
||||||
|
|
||||||
|
expectIntToBeX(t, user.Group, banGroup, "Sam should be in group %d")
|
||||||
|
|
||||||
// TODO: Do tests against the scheduled updates table and the task system to make sure the ban exists there and gets revoked when it should
|
// TODO: Do tests against the scheduled updates table and the task system to make sure the ban exists there and gets revoked when it should
|
||||||
|
|
||||||
err = user.Unban()
|
err = user.Unban()
|
||||||
if err != nil {
|
expectNilErr(t, err)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
expectIntToBeX(t, user.Group, banGroup, "Sam should still be in the ban group in this copy")
|
expectIntToBeX(t, user.Group, banGroup, "Sam should still be in the ban group in this copy")
|
||||||
|
|
||||||
if hasCache {
|
if hasCache {
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
_, err = ucache.CacheGet(newUserID)
|
_, err = ucache.CacheGet(newUserID)
|
||||||
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't be in the cache", newUserID))
|
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = users.Get(newUserID)
|
user, err = users.Get(newUserID)
|
||||||
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
if user.ID != newUserID {
|
expectIntToBeX(t, user.ID, newUserID, "The UID of the user record should be %d")
|
||||||
t.Errorf("The UID of the user record should be %d", newUserID)
|
|
||||||
}
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should be back in group "+strconv.Itoa(config.DefaultGroup))
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
expect(t, !user.IsMod, "Sam should not be a mod")
|
||||||
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
|
|
||||||
|
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should be back in group %d")
|
||||||
|
|
||||||
|
var reportsForumID = 1
|
||||||
|
var generalForumID = 2
|
||||||
|
dummyResponseRecorder := httptest.NewRecorder()
|
||||||
|
bytesBuffer := bytes.NewBuffer([]byte(""))
|
||||||
|
dummyRequest1 := httptest.NewRequest("", "/forum/1", bytesBuffer)
|
||||||
|
dummyRequest2 := httptest.NewRequest("", "/forum/2", bytesBuffer)
|
||||||
|
|
||||||
|
err = user.ChangeGroup(1)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, user.Group == config.DefaultGroup, "Someone's mutated this pointer elsewhere")
|
||||||
|
|
||||||
|
user, err = users.Get(newUserID)
|
||||||
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
|
expectIntToBeX(t, user.ID, newUserID, "The UID of the user record should be %d")
|
||||||
|
var user2 *User = getDummyUser()
|
||||||
|
*user2 = *user
|
||||||
|
|
||||||
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
expect(t, user.IsAdmin, "Sam should be an admin")
|
||||||
|
expect(t, user.IsSuperMod, "Sam should be a super mod")
|
||||||
|
expect(t, user.IsMod, "Sam should be a mod")
|
||||||
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
|
|
||||||
|
_, success := forumUserCheck(dummyResponseRecorder, dummyRequest1, user, reportsForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, user.Perms.ViewTopic, "Admins should be able to access the reports forum")
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest2, user2, generalForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, user2.Perms.ViewTopic, "Sam should be able to access the general forum")
|
||||||
|
|
||||||
|
err = user.ChangeGroup(2)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, user.Group == 1, "Someone's mutated this pointer elsewhere")
|
||||||
|
|
||||||
|
user, err = users.Get(newUserID)
|
||||||
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
|
expectIntToBeX(t, user.ID, newUserID, "The UID of the user record should be %d")
|
||||||
|
user2 = getDummyUser()
|
||||||
|
*user2 = *user
|
||||||
|
|
||||||
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, user.IsSuperMod, "Sam should be a super mod")
|
||||||
|
expect(t, user.IsMod, "Sam should be a mod")
|
||||||
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
|
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest1, user, reportsForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, user.Perms.ViewTopic, "Mods should be able to access the reports forum")
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest2, user2, generalForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, user2.Perms.ViewTopic, "Sam should be able to access the general forum")
|
||||||
|
|
||||||
|
err = user.ChangeGroup(3)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, user.Group == 2, "Someone's mutated this pointer elsewhere")
|
||||||
|
|
||||||
|
user, err = users.Get(newUserID)
|
||||||
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
|
expectIntToBeX(t, user.ID, newUserID, "The UID of the user record should be %d")
|
||||||
|
user2 = getDummyUser()
|
||||||
|
*user2 = *user
|
||||||
|
|
||||||
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
expect(t, !user.IsMod, "Sam should not be a mod")
|
||||||
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
|
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest1, user, reportsForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, !user.Perms.ViewTopic, "Members shouldn't be able to access the reports forum")
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest2, user2, generalForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, user2.Perms.ViewTopic, "Sam should be able to access the general forum")
|
||||||
|
expect(t, user.Perms.ViewTopic != user2.Perms.ViewTopic, "user.Perms.ViewTopic and user2.Perms.ViewTopic should never match")
|
||||||
|
|
||||||
|
err = user.ChangeGroup(4)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, user.Group == 3, "Someone's mutated this pointer elsewhere")
|
||||||
|
|
||||||
|
user, err = users.Get(newUserID)
|
||||||
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
|
expectIntToBeX(t, user.ID, newUserID, "The UID of the user record should be %d")
|
||||||
|
user2 = getDummyUser()
|
||||||
|
*user2 = *user
|
||||||
|
|
||||||
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
expect(t, !user.IsMod, "Sam should not be a mod")
|
||||||
|
expect(t, user.IsBanned, "Sam should be banned")
|
||||||
|
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest1, user, reportsForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, !user.Perms.ViewTopic, "Members shouldn't be able to access the reports forum")
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest2, user2, generalForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, user2.Perms.ViewTopic, "Sam should be able to access the general forum")
|
||||||
|
|
||||||
|
err = user.ChangeGroup(5)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, user.Group == 4, "Someone's mutated this pointer elsewhere")
|
||||||
|
|
||||||
|
user, err = users.Get(newUserID)
|
||||||
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
|
expectIntToBeX(t, user.ID, newUserID, "The UID of the user record should be %d")
|
||||||
|
user2 = getDummyUser()
|
||||||
|
*user2 = *user
|
||||||
|
|
||||||
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
expect(t, !user.IsMod, "Sam should not be a mod")
|
||||||
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
|
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest1, user, reportsForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, !user.Perms.ViewTopic, "Members shouldn't be able to access the reports forum")
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest2, user2, generalForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, user2.Perms.ViewTopic, "Sam should be able to access the general forum")
|
||||||
|
|
||||||
|
err = user.ChangeGroup(6)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, user.Group == 5, "Someone's mutated this pointer elsewhere")
|
||||||
|
|
||||||
|
user, err = users.Get(newUserID)
|
||||||
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
|
expectIntToBeX(t, user.ID, newUserID, "The UID of the user record should be %d")
|
||||||
|
user2 = getDummyUser()
|
||||||
|
*user2 = *user
|
||||||
|
|
||||||
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
expect(t, !user.IsMod, "Sam should not be a mod")
|
||||||
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
|
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest1, user, reportsForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, !user.Perms.ViewTopic, "Members shouldn't be able to access the reports forum")
|
||||||
|
_, success = forumUserCheck(dummyResponseRecorder, dummyRequest2, user2, generalForumID)
|
||||||
|
expect(t, success, "There shouldn't be any errors in forumUserCheck")
|
||||||
|
expect(t, user2.Perms.ViewTopic, "Sam should be able to access the general forum")
|
||||||
|
|
||||||
|
err = user.ChangeGroup(config.DefaultGroup)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, user.Group == 6, "Someone's mutated this pointer elsewhere")
|
||||||
|
|
||||||
err = user.Delete()
|
err = user.Delete()
|
||||||
if err != nil {
|
expectNilErr(t, err)
|
||||||
t.Error(err)
|
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d should no longer exist", newUserID))
|
||||||
}
|
|
||||||
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d should not longer exist", newUserID))
|
|
||||||
|
|
||||||
if hasCache {
|
if hasCache {
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
_, err = ucache.CacheGet(newUserID)
|
_, err = ucache.CacheGet(newUserID)
|
||||||
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't be in the cache", newUserID))
|
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Works for now but might cause a data race with the task system
|
_, err = users.Get(newUserID)
|
||||||
//ResetTables()
|
recordMustNotExist(t, err, "UID #%d shouldn't exist", newUserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add an error message to this?
|
||||||
|
func expectNilErr(t *testing.T, item error) {
|
||||||
|
if item != nil {
|
||||||
|
debug.PrintStack()
|
||||||
|
t.Fatal(item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectIntToBeX(t *testing.T, item int, expect int, errmsg string) {
|
func expectIntToBeX(t *testing.T, item int, expect int, errmsg string) {
|
||||||
|
@ -284,7 +438,7 @@ func expectIntToBeX(t *testing.T, item int, expect int, errmsg string) {
|
||||||
func expect(t *testing.T, item bool, errmsg string) {
|
func expect(t *testing.T, item bool, errmsg string) {
|
||||||
if !item {
|
if !item {
|
||||||
debug.PrintStack()
|
debug.PrintStack()
|
||||||
t.Fatalf(errmsg)
|
t.Fatal(errmsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,18 +463,10 @@ func topicStoreTest(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
_, err = topics.Get(-1)
|
_, err = topics.Get(-1)
|
||||||
if err == nil {
|
recordMustNotExist(t, err, "TID #-1 shouldn't exist")
|
||||||
t.Error("TID #-1 shouldn't exist")
|
|
||||||
} else if err != ErrNoRows {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = topics.Get(0)
|
_, err = topics.Get(0)
|
||||||
if err == nil {
|
recordMustNotExist(t, err, "TID #0 shouldn't exist")
|
||||||
t.Error("TID #0 shouldn't exist")
|
|
||||||
} else if err != ErrNoRows {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
topic, err = topics.Get(1)
|
topic, err = topics.Get(1)
|
||||||
recordMustExist(t, err, "Couldn't find TID #1")
|
recordMustExist(t, err, "Couldn't find TID #1")
|
||||||
|
@ -361,24 +507,13 @@ func TestForumStore(t *testing.T) {
|
||||||
initPlugins()
|
initPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
var forum *Forum
|
_, err := fstore.Get(-1)
|
||||||
var err error
|
recordMustNotExist(t, err, "FID #-1 shouldn't exist")
|
||||||
|
|
||||||
_, err = fstore.Get(-1)
|
_, err = fstore.Get(0)
|
||||||
if err == nil {
|
recordMustNotExist(t, err, "FID #0 shouldn't exist")
|
||||||
t.Error("FID #-1 shouldn't exist")
|
|
||||||
} else if err != ErrNoRows {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
forum, err = fstore.Get(0)
|
forum, err := fstore.Get(1)
|
||||||
if err == nil {
|
|
||||||
t.Error("FID #0 shouldn't exist")
|
|
||||||
} else if err != ErrNoRows {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
forum, err = fstore.Get(1)
|
|
||||||
recordMustExist(t, err, "Couldn't find FID #1")
|
recordMustExist(t, err, "Couldn't find FID #1")
|
||||||
|
|
||||||
if forum.ID != 1 {
|
if forum.ID != 1 {
|
||||||
|
@ -417,50 +552,95 @@ func TestGroupStore(t *testing.T) {
|
||||||
initPlugins()
|
initPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
var group *Group
|
_, err := gstore.Get(-1)
|
||||||
var err error
|
recordMustNotExist(t, err, "GID #-1 shouldn't exist")
|
||||||
|
|
||||||
_, err = gstore.Get(-1)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("GID #-1 shouldn't exist")
|
|
||||||
} else if err != ErrNoRows {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Refactor the group store to remove GID #0
|
// TODO: Refactor the group store to remove GID #0
|
||||||
group, err = gstore.Get(0)
|
group, err := gstore.Get(0)
|
||||||
recordMustExist(t, err, "Couldn't find GID #0")
|
recordMustExist(t, err, "Couldn't find GID #0")
|
||||||
|
|
||||||
if group.ID != 0 {
|
if group.ID != 0 {
|
||||||
t.Error("group.ID doesn't not match the requested GID. Got '" + strconv.Itoa(group.ID) + "' instead.")
|
t.Errorf("group.ID doesn't not match the requested GID. Got '%d' instead.", group.ID)
|
||||||
}
|
|
||||||
if group.Name != "Unknown" {
|
|
||||||
t.Error("GID #0 is named '" + group.Name + "' and not 'Unknown'")
|
|
||||||
}
|
}
|
||||||
|
expect(t, group.Name == "Unknown", fmt.Sprintf("GID #0 is named '%s' and not 'Unknown'", group.Name))
|
||||||
|
|
||||||
group, err = gstore.Get(1)
|
group, err = gstore.Get(1)
|
||||||
recordMustExist(t, err, "Couldn't find GID #1")
|
recordMustExist(t, err, "Couldn't find GID #1")
|
||||||
|
|
||||||
if group.ID != 1 {
|
if group.ID != 1 {
|
||||||
t.Error("group.ID doesn't not match the requested GID. Got '" + strconv.Itoa(group.ID) + "' instead.'")
|
t.Errorf("group.ID doesn't not match the requested GID. Got '%d' instead.'", group.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = group
|
|
||||||
|
|
||||||
ok := gstore.Exists(-1)
|
ok := gstore.Exists(-1)
|
||||||
if ok {
|
expect(t, !ok, "GID #-1 shouldn't exist")
|
||||||
t.Error("GID #-1 shouldn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 0 aka Unknown, for system posts and other oddities
|
||||||
ok = gstore.Exists(0)
|
ok = gstore.Exists(0)
|
||||||
if !ok {
|
expect(t, ok, "GID #0 should exist")
|
||||||
t.Error("GID #0 should exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = gstore.Exists(1)
|
ok = gstore.Exists(1)
|
||||||
if !ok {
|
expect(t, ok, "GID #1 should exist")
|
||||||
t.Error("GID #1 should exist")
|
|
||||||
}
|
var isAdmin = true
|
||||||
|
var isMod = true
|
||||||
|
var isBanned = false
|
||||||
|
gid, err := gstore.Create("Testing", "Test", isAdmin, isMod, isBanned)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, gstore.Exists(gid), "The group we just made doesn't exist")
|
||||||
|
|
||||||
|
group, err = gstore.Get(gid)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
|
expect(t, group.IsAdmin, "This should be an admin group")
|
||||||
|
expect(t, group.IsMod, "This should be a mod group")
|
||||||
|
expect(t, !group.IsBanned, "This shouldn't be a ban group")
|
||||||
|
|
||||||
|
isAdmin = false
|
||||||
|
isMod = true
|
||||||
|
isBanned = true
|
||||||
|
gid, err = gstore.Create("Testing 2", "Test", isAdmin, isMod, isBanned)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, gstore.Exists(gid), "The group we just made doesn't exist")
|
||||||
|
|
||||||
|
group, err = gstore.Get(gid)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
|
expect(t, !group.IsAdmin, "This should not be an admin group")
|
||||||
|
expect(t, group.IsMod, "This should be a mod group")
|
||||||
|
expect(t, !group.IsBanned, "This shouldn't be a ban group")
|
||||||
|
|
||||||
|
// TODO: Make sure this pointer doesn't change once we refactor the group store to stop updating the pointer
|
||||||
|
err = group.ChangeRank(false, false, true)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
|
||||||
|
group, err = gstore.Get(gid)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
|
expect(t, !group.IsAdmin, "This shouldn't be an admin group")
|
||||||
|
expect(t, !group.IsMod, "This shouldn't be a mod group")
|
||||||
|
expect(t, group.IsBanned, "This should be a ban group")
|
||||||
|
|
||||||
|
err = group.ChangeRank(true, true, true)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
|
||||||
|
group, err = gstore.Get(gid)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
|
expect(t, group.IsAdmin, "This should be an admin group")
|
||||||
|
expect(t, group.IsMod, "This should be a mod group")
|
||||||
|
expect(t, !group.IsBanned, "This shouldn't be a ban group")
|
||||||
|
|
||||||
|
err = group.ChangeRank(false, true, true)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
|
||||||
|
group, err = gstore.Get(gid)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
|
expect(t, !group.IsAdmin, "This shouldn't be an admin group")
|
||||||
|
expect(t, group.IsMod, "This should be a mod group")
|
||||||
|
expect(t, !group.IsBanned, "This shouldn't be a ban group")
|
||||||
|
|
||||||
|
// TODO: Test group deletion
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplyStore(t *testing.T) {
|
func TestReplyStore(t *testing.T) {
|
||||||
|
@ -471,28 +651,23 @@ func TestReplyStore(t *testing.T) {
|
||||||
initPlugins()
|
initPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
reply, err := rstore.Get(-1)
|
_, err := rstore.Get(-1)
|
||||||
if err == nil {
|
recordMustNotExist(t, err, "RID #-1 shouldn't exist")
|
||||||
t.Error("RID #-1 shouldn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
reply, err = rstore.Get(0)
|
_, err = rstore.Get(0)
|
||||||
if err == nil {
|
recordMustNotExist(t, err, "RID #0 shouldn't exist")
|
||||||
t.Error("RID #0 shouldn't exist")
|
|
||||||
}
|
reply, err := rstore.Get(1)
|
||||||
|
expectNilErr(t, err)
|
||||||
|
|
||||||
reply, err = rstore.Get(1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if reply.ID != 1 {
|
if reply.ID != 1 {
|
||||||
t.Error("RID #1 has the wrong ID. It should be 1 not " + strconv.Itoa(reply.ID))
|
t.Errorf("RID #1 has the wrong ID. It should be 1 not %d", reply.ID)
|
||||||
}
|
}
|
||||||
if reply.ParentID != 1 {
|
if reply.ParentID != 1 {
|
||||||
t.Error("The parent topic of RID #1 should be 1 not " + strconv.Itoa(reply.ParentID))
|
t.Errorf("The parent topic of RID #1 should be 1 not %d", reply.ParentID)
|
||||||
}
|
}
|
||||||
if reply.CreatedBy != 1 {
|
if reply.CreatedBy != 1 {
|
||||||
t.Error("The creator of RID #1 should be 1 not " + strconv.Itoa(reply.CreatedBy))
|
t.Errorf("The creator of RID #1 should be 1 not %d", reply.CreatedBy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,14 +680,10 @@ func TestProfileReplyStore(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := prstore.Get(-1)
|
_, err := prstore.Get(-1)
|
||||||
if err == nil {
|
recordMustNotExist(t, err, "RID #-1 shouldn't exist")
|
||||||
t.Error("RID #-1 shouldn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = prstore.Get(0)
|
_, err = prstore.Get(0)
|
||||||
if err == nil {
|
recordMustNotExist(t, err, "RID #0 shouldn't exist")
|
||||||
t.Error("RID #0 shouldn't exist")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSlugs(t *testing.T) {
|
func TestSlugs(t *testing.T) {
|
||||||
|
@ -558,7 +729,7 @@ func TestAuth(t *testing.T) {
|
||||||
|
|
||||||
/* No extra salt tests, we might not need this extra salt, as bcrypt has it's own? */
|
/* No extra salt tests, we might not need this extra salt, as bcrypt has it's own? */
|
||||||
realPassword = "Madame Cassandra's Mystic Orb"
|
realPassword = "Madame Cassandra's Mystic Orb"
|
||||||
t.Log("Set real_password to '" + realPassword + "'")
|
t.Log("Set realPassword to '" + realPassword + "'")
|
||||||
t.Log("Hashing the real password")
|
t.Log("Hashing the real password")
|
||||||
hashedPassword, err = BcryptGeneratePasswordNoSalt(realPassword)
|
hashedPassword, err = BcryptGeneratePasswordNoSalt(realPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -819,7 +819,10 @@ func routeUnban(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = targetUser.Unban()
|
err = targetUser.Unban()
|
||||||
if err == ErrNoRows {
|
if err == ErrNoTempGroup {
|
||||||
|
LocalError("The user you're trying to unban is not banned", w, r, user)
|
||||||
|
return
|
||||||
|
} else if err == ErrNoRows {
|
||||||
LocalError("The user you're trying to unban no longer exists.", w, r, user)
|
LocalError("The user you're trying to unban no longer exists.", w, r, user)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
|
58
pages.go
58
pages.go
|
@ -456,6 +456,7 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
msg = strings.Replace(msg, ":O", "😲", -1)
|
msg = strings.Replace(msg, ":O", "😲", -1)
|
||||||
msg = strings.Replace(msg, ":p", "😛", -1)
|
msg = strings.Replace(msg, ":p", "😛", -1)
|
||||||
msg = strings.Replace(msg, ":o", "😲", -1)
|
msg = strings.Replace(msg, ":o", "😲", -1)
|
||||||
|
msg = strings.Replace(msg, ";)", "😉", -1)
|
||||||
//msg = url_reg.ReplaceAllString(msg,"<a href=\"$2$3//$4\" rel=\"nofollow\">$2$3//$4</a>")
|
//msg = url_reg.ReplaceAllString(msg,"<a href=\"$2$3//$4\" rel=\"nofollow\">$2$3//$4</a>")
|
||||||
|
|
||||||
// Word filter list. E.g. Swear words and other things the admins don't like
|
// Word filter list. E.g. Swear words and other things the admins don't like
|
||||||
|
@ -473,13 +474,12 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
var lastItem = 0
|
var lastItem = 0
|
||||||
var i = 0
|
var i = 0
|
||||||
for ; len(msgbytes) > (i + 1); i++ {
|
for ; len(msgbytes) > (i + 1); i++ {
|
||||||
//log.Print("Index:",i)
|
//log.Print("Index: ",i)
|
||||||
//log.Print("Index Item: ",msgbytes[i])
|
//log.Print("Index Item: ",msgbytes[i])
|
||||||
//log.Print("string(msgbytes[i]): ",string(msgbytes[i]))
|
//log.Print("string(msgbytes[i]): ",string(msgbytes[i]))
|
||||||
//log.Print("End Index")
|
//log.Print("End Index")
|
||||||
if (i == 0 && (msgbytes[0] > 32)) || ((msgbytes[i] < 33) && (msgbytes[i+1] > 32)) {
|
if (i == 0 && (msgbytes[0] > 32)) || ((msgbytes[i] < 33) && (msgbytes[i+1] > 32)) {
|
||||||
//log.Print("IN")
|
//log.Print("IN ",msgbytes[i])
|
||||||
//log.Print(msgbytes[i])
|
|
||||||
if (i != 0) || msgbytes[i] < 33 {
|
if (i != 0) || msgbytes[i] < 33 {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@ -509,12 +509,12 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
outbytes = append(outbytes, urlClose...)
|
outbytes = append(outbytes, urlClose...)
|
||||||
lastItem = i
|
lastItem = i
|
||||||
|
|
||||||
//log.Print("string(msgbytes) ",string(msgbytes))
|
//log.Print("string(msgbytes): ",string(msgbytes))
|
||||||
//log.Print("msgbytes ",msgbytes)
|
//log.Print("msgbytes: ",msgbytes)
|
||||||
//log.Print(msgbytes[lastItem - 1])
|
//log.Print("msgbytes[lastItem - 1]: ",msgbytes[lastItem - 1])
|
||||||
//log.Print(lastItem - 1)
|
//log.Print("lastItem - 1: ",lastItem - 1)
|
||||||
//log.Print(msgbytes[lastItem])
|
//log.Print("msgbytes[lastItem]: ",msgbytes[lastItem])
|
||||||
//log.Print("lastItem ",lastItem)
|
//log.Print("lastItem: ",lastItem)
|
||||||
} else if bytes.Equal(msgbytes[i+1:i+5], []byte("rid-")) {
|
} else if bytes.Equal(msgbytes[i+1:i+5], []byte("rid-")) {
|
||||||
outbytes = append(outbytes, msgbytes[lastItem:i]...)
|
outbytes = append(outbytes, msgbytes[lastItem:i]...)
|
||||||
i += 5
|
i += 5
|
||||||
|
@ -589,10 +589,10 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
|
|
||||||
//log.Print(string(msgbytes))
|
//log.Print(string(msgbytes))
|
||||||
//log.Print(msgbytes)
|
//log.Print(msgbytes)
|
||||||
//log.Print(msgbytes[lastItem - 1])
|
//log.Print("msgbytes[lastItem - 1]: ", msgbytes[lastItem - 1])
|
||||||
//log.Print("lastItem - 1",lastItem - 1)
|
//log.Print("lastItem - 1: ", lastItem - 1)
|
||||||
//log.Print("msgbytes[lastItem]",msgbytes[lastItem])
|
//log.Print("msgbytes[lastItem]: ", msgbytes[lastItem])
|
||||||
//log.Print("lastItem",lastItem)
|
//log.Print("lastItem: ", lastItem)
|
||||||
} else if msgbytes[i] == 'h' || msgbytes[i] == 'f' || msgbytes[i] == 'g' {
|
} else if msgbytes[i] == 'h' || msgbytes[i] == 'f' || msgbytes[i] == 'g' {
|
||||||
//log.Print("IN hfg")
|
//log.Print("IN hfg")
|
||||||
if msgbytes[i+1] == 't' && msgbytes[i+2] == 't' && msgbytes[i+3] == 'p' {
|
if msgbytes[i+1] == 't' && msgbytes[i+2] == 't' && msgbytes[i+3] == 'p' {
|
||||||
|
@ -616,10 +616,10 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
urlLen := partialURLBytesLen(msgbytes[i:])
|
urlLen := partialURLBytesLen(msgbytes[i:])
|
||||||
if msgbytes[i+urlLen] > 32 { // space and invisibles
|
if msgbytes[i+urlLen] > 32 { // space and invisibles
|
||||||
//log.Print("INVALID URL")
|
//log.Print("INVALID URL")
|
||||||
//log.Print("msgbytes[i+urlLen]", msgbytes[i+urlLen])
|
//log.Print("msgbytes[i+urlLen]: ", msgbytes[i+urlLen])
|
||||||
//log.Print("string(msgbytes[i+urlLen])", string(msgbytes[i+urlLen]))
|
//log.Print("string(msgbytes[i+urlLen]): ", string(msgbytes[i+urlLen]))
|
||||||
//log.Print("msgbytes[i:i+urlLen]", msgbytes[i:i+urlLen])
|
//log.Print("msgbytes[i:i+urlLen]: ", msgbytes[i:i+urlLen])
|
||||||
//log.Print("string(msgbytes[i:i+urlLen])", string(msgbytes[i:i+urlLen]))
|
//log.Print("string(msgbytes[i:i+urlLen]): ", string(msgbytes[i:i+urlLen]))
|
||||||
outbytes = append(outbytes, invalidURL...)
|
outbytes = append(outbytes, invalidURL...)
|
||||||
i += urlLen
|
i += urlLen
|
||||||
continue
|
continue
|
||||||
|
@ -673,18 +673,18 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
urlLen := partialURLBytesLen(msgbytes[i:])
|
urlLen := partialURLBytesLen(msgbytes[i:])
|
||||||
if msgbytes[i+urlLen] > 32 { // space and invisibles
|
if msgbytes[i+urlLen] > 32 { // space and invisibles
|
||||||
//log.Print("INVALID URL")
|
//log.Print("INVALID URL")
|
||||||
//log.Print("msgbytes[i+urlLen]", msgbytes[i+urlLen])
|
//log.Print("msgbytes[i+urlLen]: ", msgbytes[i+urlLen])
|
||||||
//log.Print("string(msgbytes[i+urlLen])", string(msgbytes[i+urlLen]))
|
//log.Print("string(msgbytes[i+urlLen]): ", string(msgbytes[i+urlLen]))
|
||||||
//log.Print("msgbytes[i:i+urlLen]", msgbytes[i:i+urlLen])
|
//log.Print("msgbytes[i:i+urlLen]: ", msgbytes[i:i+urlLen])
|
||||||
//log.Print("string(msgbytes[i:i+urlLen])", string(msgbytes[i:i+urlLen]))
|
//log.Print("string(msgbytes[i:i+urlLen]): ", string(msgbytes[i:i+urlLen]))
|
||||||
outbytes = append(outbytes, invalidURL...)
|
outbytes = append(outbytes, invalidURL...)
|
||||||
i += urlLen
|
i += urlLen
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("VALID URL")
|
//log.Print("VALID URL")
|
||||||
//log.Print("msgbytes[i:i+urlLen]", msgbytes[i:i+urlLen])
|
//log.Print("msgbytes[i:i+urlLen]: ", msgbytes[i:i+urlLen])
|
||||||
//log.Print("string(msgbytes[i:i+urlLen])", string(msgbytes[i:i+urlLen]))
|
//log.Print("string(msgbytes[i:i+urlLen]): ", string(msgbytes[i:i+urlLen]))
|
||||||
media, ok := parseMediaBytes(msgbytes[i : i+urlLen])
|
media, ok := parseMediaBytes(msgbytes[i : i+urlLen])
|
||||||
if !ok {
|
if !ok {
|
||||||
outbytes = append(outbytes, invalidURL...)
|
outbytes = append(outbytes, invalidURL...)
|
||||||
|
@ -733,10 +733,10 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lastItem != i && len(outbytes) != 0 {
|
if lastItem != i && len(outbytes) != 0 {
|
||||||
//log.Print("lastItem: ",msgbytes[lastItem])
|
//log.Print("lastItem: ", msgbytes[lastItem])
|
||||||
//log.Print("lastItem index: ",lastItem)
|
//log.Print("lastItem index: ", lastItem)
|
||||||
//log.Print("i: ",i)
|
//log.Print("i: ", i)
|
||||||
//log.Print("lastItem to end: ",msgbytes[lastItem:])
|
//log.Print("lastItem to end: ", msgbytes[lastItem:])
|
||||||
//log.Print("-----")
|
//log.Print("-----")
|
||||||
calclen := len(msgbytes) - 10
|
calclen := len(msgbytes) - 10
|
||||||
if calclen <= lastItem {
|
if calclen <= lastItem {
|
||||||
|
@ -756,7 +756,7 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func regexParseMessage(msg string) string {
|
/*func regexParseMessage(msg string) string {
|
||||||
msg = strings.Replace(msg, ":)", "😀", -1)
|
msg = strings.Replace(msg, ":)", "😀", -1)
|
||||||
msg = strings.Replace(msg, ":D", "😃", -1)
|
msg = strings.Replace(msg, ":D", "😃", -1)
|
||||||
msg = strings.Replace(msg, ":P", "😛", -1)
|
msg = strings.Replace(msg, ":P", "😛", -1)
|
||||||
|
@ -766,7 +766,7 @@ func regexParseMessage(msg string) string {
|
||||||
msg = runSshook("parse_assign", msg)
|
msg = runSshook("parse_assign", msg)
|
||||||
}
|
}
|
||||||
return msg
|
return msg
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// 6, 7, 8, 6, 2, 7
|
// 6, 7, 8, 6, 2, 7
|
||||||
// ftp://, http://, https:// git://, //, mailto: (not a URL, just here for length comparison purposes)
|
// ftp://, http://, https:// git://, //, mailto: (not a URL, just here for length comparison purposes)
|
||||||
|
|
|
@ -451,7 +451,6 @@ func routePanelForumsEditSubmit(w http.ResponseWriter, r *http.Request, user Use
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! This probably misses the forumView cache
|
|
||||||
func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, user User, sfid string) {
|
func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, user User, sfid string) {
|
||||||
_, ok := SimplePanelUserCheck(w, r, &user)
|
_, ok := SimplePanelUserCheck(w, r, &user)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -509,24 +508,24 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
|
||||||
}
|
}
|
||||||
group.Forums[fid] = fperms
|
group.Forums[fid] = fperms
|
||||||
|
|
||||||
perms, err := json.Marshal(fperms)
|
err = replaceForumPermsForGroup(gid, map[int]string{fid: permPreset}, map[int]ForumPerms{fid: fperms})
|
||||||
if err != nil {
|
|
||||||
InternalErrorJSQ(err, w, r, isJs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = addForumPermsToGroupStmt.Exec(gid, fid, permPreset, perms)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalErrorJSQ(err, w, r, isJs)
|
InternalErrorJSQ(err, w, r, isJs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add this and replaceForumPermsForGroup into a transaction?
|
||||||
_, err = updateForumStmt.Exec(forum.Name, forum.Desc, forum.Active, "", fid)
|
_, err = updateForumStmt.Exec(forum.Name, forum.Desc, forum.Active, "", fid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalErrorJSQ(err, w, r, isJs)
|
InternalErrorJSQ(err, w, r, isJs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
forum.Preset = ""
|
err = fstore.Reload(fid)
|
||||||
|
if err != nil {
|
||||||
|
// Log this? -- Another admin might have deleted it
|
||||||
|
LocalErrorJSQ("Unable to reload forum", w, r, user, isJs)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isJs {
|
if !isJs {
|
||||||
|
@ -1347,7 +1346,7 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
perPage := 9
|
perPage := 9
|
||||||
offset, page, lastPage := pageOffset(stats.Groups, page, perPage)
|
offset, page, lastPage := pageOffset(stats.Groups, page, perPage)
|
||||||
|
|
||||||
// Skip the System group
|
// Skip the 'Unknown' group
|
||||||
offset++
|
offset++
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
|
@ -1436,15 +1435,16 @@ func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user User, sgi
|
||||||
}
|
}
|
||||||
|
|
||||||
var rank string
|
var rank string
|
||||||
if group.IsAdmin {
|
switch {
|
||||||
|
case group.IsAdmin:
|
||||||
rank = "Admin"
|
rank = "Admin"
|
||||||
} else if group.IsMod {
|
case group.IsMod:
|
||||||
rank = "Mod"
|
rank = "Mod"
|
||||||
} else if group.IsBanned {
|
case group.IsBanned:
|
||||||
rank = "Banned"
|
rank = "Banned"
|
||||||
} else if group.ID == 6 {
|
case group.ID == 6:
|
||||||
rank = "Guest"
|
rank = "Guest"
|
||||||
} else {
|
default:
|
||||||
rank = "Member"
|
rank = "Member"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1619,54 +1619,28 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user Use
|
||||||
LocalError("You need the EditGroupAdmin permission to designate this group as an admin group.", w, r, user)
|
LocalError("You need the EditGroupAdmin permission to designate this group as an admin group.", w, r, user)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = group.ChangeRank(true, true, false)
|
||||||
_, err = updateGroupRankStmt.Exec(1, 1, 0, gid)
|
|
||||||
if err != nil {
|
|
||||||
InternalError(err, w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
group.IsAdmin = true
|
|
||||||
group.IsMod = true
|
|
||||||
group.IsBanned = false
|
|
||||||
case "Mod":
|
case "Mod":
|
||||||
if !user.Perms.EditGroupSuperMod {
|
if !user.Perms.EditGroupSuperMod {
|
||||||
LocalError("You need the EditGroupSuperMod permission to designate this group as a super-mod group.", w, r, user)
|
LocalError("You need the EditGroupSuperMod permission to designate this group as a super-mod group.", w, r, user)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = group.ChangeRank(false, true, false)
|
||||||
_, err = updateGroupRankStmt.Exec(0, 1, 0, gid)
|
|
||||||
if err != nil {
|
|
||||||
InternalError(err, w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
group.IsAdmin = false
|
|
||||||
group.IsMod = true
|
|
||||||
group.IsBanned = false
|
|
||||||
case "Banned":
|
case "Banned":
|
||||||
_, err = updateGroupRankStmt.Exec(0, 0, 1, gid)
|
err = group.ChangeRank(false, false, true)
|
||||||
if err != nil {
|
|
||||||
InternalError(err, w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
group.IsAdmin = false
|
|
||||||
group.IsMod = false
|
|
||||||
group.IsBanned = true
|
|
||||||
case "Guest":
|
case "Guest":
|
||||||
LocalError("You can't designate a group as a guest group.", w, r, user)
|
LocalError("You can't designate a group as a guest group.", w, r, user)
|
||||||
return
|
return
|
||||||
case "Member":
|
case "Member":
|
||||||
_, err = updateGroupRankStmt.Exec(0, 0, 0, gid)
|
err = group.ChangeRank(false, false, false)
|
||||||
if err != nil {
|
|
||||||
InternalError(err, w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
group.IsAdmin = false
|
|
||||||
group.IsMod = false
|
|
||||||
group.IsBanned = false
|
|
||||||
default:
|
default:
|
||||||
LocalError("Invalid group type.", w, r, user)
|
LocalError("Invalid group type.", w, r, user)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
InternalError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = updateGroupStmt.Exec(gname, gtag, gid)
|
_, err = updateGroupStmt.Exec(gname, gtag, gid)
|
||||||
|
|
116
permissions.go
116
permissions.go
|
@ -1,9 +1,16 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "log"
|
import (
|
||||||
import "sync"
|
"database/sql"
|
||||||
import "strconv"
|
"encoding/json"
|
||||||
import "encoding/json"
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"./query_gen/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Refactor the perms system
|
||||||
|
|
||||||
var permUpdateMutex sync.Mutex
|
var permUpdateMutex sync.Mutex
|
||||||
var BlankPerms Perms
|
var BlankPerms Perms
|
||||||
|
@ -261,10 +268,18 @@ func presetToPermmap(preset string) (out map[string]ForumPerms) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
||||||
permUpdateMutex.Lock()
|
tx, err := db.Begin()
|
||||||
defer permUpdateMutex.Unlock()
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
_, err := deleteForumPermsByForumStmt.Exec(fid)
|
deleteForumPermsByForumTx, err := qgen.Builder.SimpleDeleteTx(tx, "forums_permissions", "fid = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = deleteForumPermsByForumTx.Exec(fid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -273,7 +288,16 @@ func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = addForumPermsToForumAdminsStmt.Exec(fid, "", perms)
|
|
||||||
|
addForumPermsToForumAdminsTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
|
||||||
|
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
||||||
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = addForumPermsToForumAdminsTx.Exec(fid, "", perms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -282,7 +306,15 @@ func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = addForumPermsToForumStaffStmt.Exec(fid, "", perms)
|
|
||||||
|
addForumPermsToForumStaffTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
|
||||||
|
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
||||||
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = addForumPermsToForumStaffTx.Exec(fid, "", perms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -291,23 +323,77 @@ func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = addForumPermsToForumMembersStmt.Exec(fid, "", perms)
|
|
||||||
|
addForumPermsToForumMembersTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
|
||||||
|
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
||||||
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = addForumPermsToForumMembersTx.Exec(fid, "", perms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
perms, err = json.Marshal(permmap["guests"])
|
// 6 is the ID of the Not Loggedin Group
|
||||||
if err != nil {
|
// TODO: Use a shared variable rather than a literal for the group ID
|
||||||
return err
|
err = replaceForumPermsForGroupTx(tx, 6, map[int]string{fid: ""}, map[int]ForumPerms{fid: permmap["guests"]})
|
||||||
}
|
|
||||||
_, err = addForumPermsToGroupStmt.Exec(6, fid, "", perms)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
permUpdateMutex.Lock()
|
||||||
|
defer permUpdateMutex.Unlock()
|
||||||
return rebuildForumPermissions(fid)
|
return rebuildForumPermissions(fid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func replaceForumPermsForGroup(gid int, presetSet map[int]string, permSets map[int]ForumPerms) error {
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
err = replaceForumPermsForGroupTx(tx, gid, presetSet, permSets)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceForumPermsForGroupTx(tx *sql.Tx, gid int, presetSets map[int]string, permSets map[int]ForumPerms) error {
|
||||||
|
deleteForumPermsForGroupTx, err := qgen.Builder.SimpleDeleteTx(tx, "forums_permissions", "gid = ? AND fid = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
addForumPermsToGroupTx, err := qgen.Builder.SimpleInsertTx(tx, "forums_permissions", "gid, fid, preset, permissions", "?,?,?,?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for fid, permSet := range permSets {
|
||||||
|
permstr, err := json.Marshal(permSet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = deleteForumPermsForGroupTx.Exec(gid, fid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = addForumPermsToGroupTx.Exec(gid, fid, presetSets[fid], string(permstr))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Need a more thread-safe way of doing this. Possibly with sync.Map?
|
// TODO: Need a more thread-safe way of doing this. Possibly with sync.Map?
|
||||||
func rebuildForumPermissions(fid int) error {
|
func rebuildForumPermissions(fid int) error {
|
||||||
if dev.DebugMode {
|
if dev.DebugMode {
|
||||||
|
|
|
@ -2,30 +2,27 @@ package main
|
||||||
|
|
||||||
//import "fmt"
|
//import "fmt"
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings
|
var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings
|
||||||
var markdownUnclosedElement []byte
|
var markdownUnclosedElement []byte
|
||||||
|
|
||||||
var markdownBoldTagOpen, markdownBoldTagClose []byte
|
var markdownBoldTagOpen []byte
|
||||||
var markdownItalicTagOpen, markdownItalicTagClose []byte
|
var markdownBoldTagClose []byte
|
||||||
var markdownUnderlineTagOpen, markdownUnderlineTagClose []byte
|
var markdownItalicTagOpen []byte
|
||||||
var markdownStrikeTagOpen, markdownStrikeTagClose []byte
|
var markdownItalicTagClose []byte
|
||||||
|
var markdownUnderlineTagOpen []byte
|
||||||
var markdownBoldItalic *regexp.Regexp
|
var markdownUnderlineTagClose []byte
|
||||||
var markdownBold *regexp.Regexp
|
var markdownStrikeTagOpen []byte
|
||||||
var markdownItalic *regexp.Regexp
|
var markdownStrikeTagClose []byte
|
||||||
var markdownStrike *regexp.Regexp
|
|
||||||
var markdownUnderline *regexp.Regexp
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
plugins["markdown"] = NewPlugin("markdown", "Markdown", "Azareal", "http://github.com/Azareal", "", "", "", initMarkdown, nil, deactivateMarkdown, nil, nil)
|
plugins["markdown"] = NewPlugin("markdown", "Markdown", "Azareal", "http://github.com/Azareal", "", "", "", initMarkdown, nil, deactivateMarkdown, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initMarkdown() error {
|
func initMarkdown() error {
|
||||||
//plugins["markdown"].AddHook("parse_assign", markdownRegexParse)
|
|
||||||
plugins["markdown"].AddHook("parse_assign", markdownParse)
|
plugins["markdown"].AddHook("parse_assign", markdownParse)
|
||||||
|
|
||||||
markdownUnclosedElement = []byte("<span style='color: red;'>[Unclosed Element]</span>")
|
markdownUnclosedElement = []byte("<span style='color: red;'>[Unclosed Element]</span>")
|
||||||
|
@ -38,35 +35,19 @@ func initMarkdown() error {
|
||||||
markdownUnderlineTagClose = []byte("</u>")
|
markdownUnderlineTagClose = []byte("</u>")
|
||||||
markdownStrikeTagOpen = []byte("<s>")
|
markdownStrikeTagOpen = []byte("<s>")
|
||||||
markdownStrikeTagClose = []byte("</s>")
|
markdownStrikeTagClose = []byte("</s>")
|
||||||
|
|
||||||
markdownBoldItalic = regexp.MustCompile(`\*\*\*(.*)\*\*\*`)
|
|
||||||
markdownBold = regexp.MustCompile(`\*\*(.*)\*\*`)
|
|
||||||
markdownItalic = regexp.MustCompile(`\*(.*)\*`)
|
|
||||||
//markdownStrike = regexp.MustCompile(`\~\~(.*)\~\~`)
|
|
||||||
markdownStrike = regexp.MustCompile(`\~(.*)\~`)
|
|
||||||
//markdown_underline = regexp.MustCompile(`\_\_(.*)\_\_`)
|
|
||||||
markdownUnderline = regexp.MustCompile(`\_(.*)\_`)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deactivateMarkdown() {
|
func deactivateMarkdown() {
|
||||||
//plugins["markdown"].RemoveHook("parse_assign", markdownRegexParse)
|
|
||||||
plugins["markdown"].RemoveHook("parse_assign", markdownParse)
|
plugins["markdown"].RemoveHook("parse_assign", markdownParse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func markdownRegexParse(msg string) string {
|
|
||||||
msg = markdownBoldItalic.ReplaceAllString(msg, "<i><b>$1</b></i>")
|
|
||||||
msg = markdownBold.ReplaceAllString(msg, "<b>$1</b>")
|
|
||||||
msg = markdownItalic.ReplaceAllString(msg, "<i>$1</i>")
|
|
||||||
msg = markdownStrike.ReplaceAllString(msg, "<s>$1</s>")
|
|
||||||
msg = markdownUnderline.ReplaceAllString(msg, "<u>$1</u>")
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
// An adapter for the parser, so that the parser can call itself recursively.
|
// An adapter for the parser, so that the parser can call itself recursively.
|
||||||
// This is less for the simple Markdown elements like bold and italics and more for the really complicated ones I plan on adding at some point.
|
// This is less for the simple Markdown elements like bold and italics and more for the really complicated ones I plan on adding at some point.
|
||||||
func markdownParse(msg string) string {
|
func markdownParse(msg string) string {
|
||||||
return strings.TrimSpace(_markdownParse(msg+" ", 0))
|
msg = strings.TrimSpace(_markdownParse(msg+" ", 0))
|
||||||
|
log.Print("final msg: ", msg)
|
||||||
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// Under Construction!
|
// Under Construction!
|
||||||
|
@ -77,16 +58,14 @@ func _markdownParse(msg string, n int) string {
|
||||||
|
|
||||||
var outbytes []byte
|
var outbytes []byte
|
||||||
var lastElement int
|
var lastElement int
|
||||||
//log.Print("enter message loop")
|
log.Printf("Initial Message: %+v\n", strings.Replace(msg, "\r", "\\r", -1))
|
||||||
//log.Print("Message: %v\n",strings.Replace(msg,"\r","\\r",-1))
|
|
||||||
|
|
||||||
for index := 0; index < len(msg); index++ {
|
for index := 0; index < len(msg); index++ {
|
||||||
/*//log.Print("--OUTER MARKDOWN LOOP START--")
|
//log.Print("--OUTER MARKDOWN LOOP START--")
|
||||||
//log.Print("index",index)
|
//log.Print("index: ", index)
|
||||||
//log.Print("msg[index]",msg[index])
|
//log.Print("msg[index]: ", msg[index])
|
||||||
//log.Print("string(msg[index])",string(msg[index]))
|
//log.Print("string(msg[index]): ", string(msg[index]))
|
||||||
//log.Print("--OUTER MARKDOWN LOOP END--")
|
//log.Printf("--OUTER MARKDOWN LOOP END--\n\n")
|
||||||
//log.Print(" ")*/
|
|
||||||
|
|
||||||
switch msg[index] {
|
switch msg[index] {
|
||||||
// TODO: Do something slightly less hacky for skipping URLs
|
// TODO: Do something slightly less hacky for skipping URLs
|
||||||
|
@ -106,7 +85,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
|
|
||||||
index++
|
index++
|
||||||
index = markdownSkipUntilChar(msg, index, '_')
|
index = markdownSkipUntilChar(msg, index, '_')
|
||||||
if (index-(startIndex+1)) < 2 || index >= len(msg) {
|
if (index-(startIndex+1)) < 1 || index >= len(msg) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +95,8 @@ func _markdownParse(msg string, n int) string {
|
||||||
|
|
||||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||||
outbytes = append(outbytes, markdownUnderlineTagOpen...)
|
outbytes = append(outbytes, markdownUnderlineTagOpen...)
|
||||||
outbytes = append(outbytes, msg[sIndex:lIndex]...)
|
// TODO: Implement this without as many type conversions
|
||||||
|
outbytes = append(outbytes, []byte(_markdownParse(msg[sIndex:lIndex], n+1))...)
|
||||||
outbytes = append(outbytes, markdownUnderlineTagClose...)
|
outbytes = append(outbytes, markdownUnderlineTagClose...)
|
||||||
|
|
||||||
lastElement = index
|
lastElement = index
|
||||||
|
@ -129,7 +109,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
|
|
||||||
index++
|
index++
|
||||||
index = markdownSkipUntilChar(msg, index, '~')
|
index = markdownSkipUntilChar(msg, index, '~')
|
||||||
if (index-(startIndex+1)) < 2 || index >= len(msg) {
|
if (index-(startIndex+1)) < 1 || index >= len(msg) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,28 +119,28 @@ func _markdownParse(msg string, n int) string {
|
||||||
|
|
||||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||||
outbytes = append(outbytes, markdownStrikeTagOpen...)
|
outbytes = append(outbytes, markdownStrikeTagOpen...)
|
||||||
outbytes = append(outbytes, msg[sIndex:lIndex]...)
|
// TODO: Implement this without as many type conversions
|
||||||
|
outbytes = append(outbytes, []byte(_markdownParse(msg[sIndex:lIndex], n+1))...)
|
||||||
outbytes = append(outbytes, markdownStrikeTagClose...)
|
outbytes = append(outbytes, markdownStrikeTagClose...)
|
||||||
|
|
||||||
lastElement = index
|
lastElement = index
|
||||||
index--
|
index--
|
||||||
case '*':
|
case '*':
|
||||||
//log.Print("------")
|
//log.Print("------")
|
||||||
//log.Print("[]byte(msg):",[]byte(msg))
|
//log.Print("[]byte(msg): ", []byte(msg))
|
||||||
//log.Print("len(msg)",len(msg))
|
//log.Print("len(msg): ", len(msg))
|
||||||
//log.Print("start index",index)
|
//log.Print("start index: ", index)
|
||||||
//log.Print("start msg[index]",msg[index])
|
//log.Print("start msg[index]: ", msg[index])
|
||||||
//log.Print("start string(msg[index])",string(msg[index]))
|
//log.Print("start string(msg[index]): ", string(msg[index]))
|
||||||
//log.Print("start []byte(msg[:index])",[]byte(msg[:index]))
|
//log.Print("start []byte(msg[:index]): ", []byte(msg[:index]))
|
||||||
|
|
||||||
var startIndex = index
|
var startIndex = index
|
||||||
var italic = true
|
var italic = true
|
||||||
var bold bool
|
var bold = false
|
||||||
if (index + 2) < len(msg) {
|
if (index + 2) < len(msg) {
|
||||||
//log.Print("start index + 1",index + 1)
|
//log.Print("start index + 1: ", index + 1)
|
||||||
//log.Print("start msg[index]",msg[index + 1])
|
//log.Print("start msg[index]: ", msg[index + 1])
|
||||||
//log.Print("start string(msg[index])",string(msg[index + 1]))
|
//log.Print("start string(msg[index]): ", string(msg[index + 1]))
|
||||||
|
|
||||||
if msg[index+1] == '*' {
|
if msg[index+1] == '*' {
|
||||||
//log.Print("two asterisks")
|
//log.Print("two asterisks")
|
||||||
bold = true
|
bold = true
|
||||||
|
@ -174,16 +154,16 @@ func _markdownParse(msg string, n int) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("lastElement",lastElement)
|
//log.Print("lastElement: ", lastElement)
|
||||||
//log.Print("startIndex:",startIndex)
|
//log.Print("startIndex: ", startIndex)
|
||||||
//log.Print("msg[startIndex]",msg[startIndex])
|
//log.Print("msg[startIndex]: ", msg[startIndex])
|
||||||
//log.Print("string(msg[startIndex])",string(msg[startIndex]))
|
//log.Print("string(msg[startIndex]): ", string(msg[startIndex]))
|
||||||
|
|
||||||
//log.Print("preabrupt index",index)
|
//log.Print("preabrupt index: ", index)
|
||||||
//log.Print("preabrupt msg[index]",msg[index])
|
//log.Print("preabrupt msg[index]: ", msg[index])
|
||||||
//log.Print("preabrupt string(msg[index])",string(msg[index]))
|
//log.Print("preabrupt string(msg[index]): ", string(msg[index]))
|
||||||
//log.Print("preabrupt []byte(msg[:index])",[]byte(msg[:index]))
|
//log.Print("preabrupt []byte(msg[:index]): ", []byte(msg[:index]))
|
||||||
//log.Print("preabrupt msg[:index]",msg[:index])
|
//log.Print("preabrupt msg[:index]: ", msg[:index])
|
||||||
|
|
||||||
// Does the string terminate abruptly?
|
// Does the string terminate abruptly?
|
||||||
if (index + 1) >= len(msg) {
|
if (index + 1) >= len(msg) {
|
||||||
|
@ -195,16 +175,15 @@ func _markdownParse(msg string, n int) string {
|
||||||
//log.Print("preskip index",index)
|
//log.Print("preskip index",index)
|
||||||
//log.Print("preskip msg[index]",msg[index])
|
//log.Print("preskip msg[index]",msg[index])
|
||||||
//log.Print("preskip string(msg[index])",string(msg[index]))
|
//log.Print("preskip string(msg[index])",string(msg[index]))
|
||||||
|
|
||||||
index = markdownSkipUntilAsterisk(msg, index)
|
index = markdownSkipUntilAsterisk(msg, index)
|
||||||
|
|
||||||
if index >= len(msg) {
|
if index >= len(msg) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("index",index)
|
//log.Print("index: ", index)
|
||||||
//log.Print("[]byte(msg[:index])",[]byte(msg[:index]))
|
//log.Print("[]byte(msg[:index]): ", []byte(msg[:index]))
|
||||||
//log.Print("msg[index]",msg[index])
|
//log.Print("msg[index]: ", msg[index])
|
||||||
|
|
||||||
sIndex := startIndex
|
sIndex := startIndex
|
||||||
lIndex := index
|
lIndex := index
|
||||||
|
@ -213,7 +192,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
if (index + 3) >= len(msg) {
|
if (index + 3) >= len(msg) {
|
||||||
//log.Print("unclosed markdown element @ exit element")
|
//log.Print("unclosed markdown element @ exit element")
|
||||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||||
lastElement = startIndex
|
lastElement = startIndex
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -224,7 +203,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
if (index + 2) >= len(msg) {
|
if (index + 2) >= len(msg) {
|
||||||
//log.Print("true unclosed markdown element @ exit element")
|
//log.Print("true unclosed markdown element @ exit element")
|
||||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||||
lastElement = startIndex
|
lastElement = startIndex
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -235,7 +214,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
if (index + 1) >= len(msg) {
|
if (index + 1) >= len(msg) {
|
||||||
//log.Print("true unclosed markdown element @ exit element")
|
//log.Print("true unclosed markdown element @ exit element")
|
||||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||||
lastElement = startIndex
|
lastElement = startIndex
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -249,7 +228,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
if lIndex <= sIndex {
|
if lIndex <= sIndex {
|
||||||
//log.Print("unclosed markdown element @ lIndex <= sIndex")
|
//log.Print("unclosed markdown element @ lIndex <= sIndex")
|
||||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||||
lastElement = startIndex
|
lastElement = startIndex
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -257,7 +236,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
if sIndex < 0 || lIndex < 0 {
|
if sIndex < 0 || lIndex < 0 {
|
||||||
//log.Print("unclosed markdown element @ sIndex < 0 || lIndex < 0")
|
//log.Print("unclosed markdown element @ sIndex < 0 || lIndex < 0")
|
||||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||||
lastElement = startIndex
|
lastElement = startIndex
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -285,7 +264,8 @@ func _markdownParse(msg string, n int) string {
|
||||||
outbytes = append(outbytes, markdownItalicTagOpen...)
|
outbytes = append(outbytes, markdownItalicTagOpen...)
|
||||||
}
|
}
|
||||||
|
|
||||||
outbytes = append(outbytes, msg[sIndex:lIndex]...)
|
// TODO: Implement this without as many type conversions
|
||||||
|
outbytes = append(outbytes, []byte(_markdownParse(msg[sIndex:lIndex], n+1))...)
|
||||||
|
|
||||||
if italic {
|
if italic {
|
||||||
outbytes = append(outbytes, markdownItalicTagClose...)
|
outbytes = append(outbytes, markdownItalicTagClose...)
|
||||||
|
@ -296,13 +276,18 @@ func _markdownParse(msg string, n int) string {
|
||||||
|
|
||||||
lastElement = index
|
lastElement = index
|
||||||
index--
|
index--
|
||||||
|
case '\\':
|
||||||
|
if (index + 1) < len(msg) {
|
||||||
|
outbytes = append(outbytes, msg[lastElement:index]...)
|
||||||
|
index++
|
||||||
|
lastElement = index
|
||||||
|
}
|
||||||
//case '`':
|
//case '`':
|
||||||
//case '_':
|
//case '_':
|
||||||
//case '~':
|
//case '~':
|
||||||
//case 10: // newline
|
//case 10: // newline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("exit message loop")
|
//log.Print("exit message loop")
|
||||||
|
|
||||||
if len(outbytes) == 0 {
|
if len(outbytes) == 0 {
|
||||||
|
|
|
@ -33,14 +33,14 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
msgList = addMEPair(msgList, "[s]hi[/s]", "<s>hi</s>")
|
msgList = addMEPair(msgList, "[s]hi[/s]", "<s>hi</s>")
|
||||||
msgList = addMEPair(msgList, "[c]hi[/c]", "[c]hi[/c]")
|
msgList = addMEPair(msgList, "[c]hi[/c]", "[c]hi[/c]")
|
||||||
if !testing.Short() {
|
if !testing.Short() {
|
||||||
msgList = addMEPair(msgList, "[b]hi[/i]", "[b]hi[/i]")
|
//msgList = addMEPair(msgList, "[b]hi[/i]", "[b]hi[/i]")
|
||||||
msgList = addMEPair(msgList, "[/b]hi[b]", "[/b]hi[b]")
|
//msgList = addMEPair(msgList, "[/b]hi[b]", "[/b]hi[b]")
|
||||||
msgList = addMEPair(msgList, "[/b]hi[/b]", "[/b]hi[/b]")
|
//msgList = addMEPair(msgList, "[/b]hi[/b]", "[/b]hi[/b]")
|
||||||
msgList = addMEPair(msgList, "[b][b]hi[/b]", "<b>hi</b>")
|
//msgList = addMEPair(msgList, "[b][b]hi[/b]", "<b>hi</b>")
|
||||||
|
//msgList = addMEPair(msgList, "[b][b]hi", "[b][b]hi")
|
||||||
|
//msgList = addMEPair(msgList, "[b][b][b]hi", "[b][b][b]hi")
|
||||||
|
//msgList = addMEPair(msgList, "[/b]hi", "[/b]hi")
|
||||||
}
|
}
|
||||||
msgList = addMEPair(msgList, "[b][b]hi", "[b][b]hi")
|
|
||||||
msgList = addMEPair(msgList, "[b][b][b]hi", "[b][b][b]hi")
|
|
||||||
msgList = addMEPair(msgList, "[/b]hi", "[/b]hi")
|
|
||||||
msgList = addMEPair(msgList, "[code]hi[/code]", "<span class='codequotes'>hi</span>")
|
msgList = addMEPair(msgList, "[code]hi[/code]", "<span class='codequotes'>hi</span>")
|
||||||
msgList = addMEPair(msgList, "[code][b]hi[/b][/code]", "<span class='codequotes'>[b]hi[/b]</span>")
|
msgList = addMEPair(msgList, "[code][b]hi[/b][/code]", "<span class='codequotes'>[b]hi[/b]</span>")
|
||||||
msgList = addMEPair(msgList, "[code][b]hi[/code][/b]", "<span class='codequotes'>[b]hi</span>[/b]")
|
msgList = addMEPair(msgList, "[code][b]hi[/code][/b]", "<span class='codequotes'>[b]hi</span>[/b]")
|
||||||
|
@ -51,9 +51,9 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
|
|
||||||
t.Log("Testing bbcodeFullParse")
|
t.Log("Testing bbcodeFullParse")
|
||||||
for _, item := range msgList {
|
for _, item := range msgList {
|
||||||
t.Log("Testing string '" + item.Msg + "'")
|
|
||||||
res = bbcodeFullParse(item.Msg)
|
res = bbcodeFullParse(item.Msg)
|
||||||
if res != item.Expects {
|
if res != item.Expects {
|
||||||
|
t.Error("Testing string '" + item.Msg + "'")
|
||||||
t.Error("Bad output:", "'"+res+"'")
|
t.Error("Bad output:", "'"+res+"'")
|
||||||
t.Error("Expected:", item.Expects)
|
t.Error("Expected:", item.Expects)
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
conv, err = strconv.Atoi(res)
|
conv, err = strconv.Atoi(res)
|
||||||
if err != nil || ( /*conv > 18446744073709551615 || */ conv < 0) {
|
if err != nil && res != "<span style='color: red;'>[Invalid Number]</span>[rand]18446744073709551615[/rand]" {
|
||||||
t.Error("Bad output:", "'"+res+"'")
|
t.Error("Bad output:", "'"+res+"'")
|
||||||
t.Error("Expected a number between 0 and 18446744073709551615")
|
t.Error("Expected a number between 0 and 18446744073709551615")
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
conv, err = strconv.Atoi(res)
|
conv, err = strconv.Atoi(res)
|
||||||
if err != nil || ( /*conv > 170141183460469231731687303715884105727 || */ conv < 0) {
|
if err != nil && res != "<span style='color: red;'>[Invalid Number]</span>[rand]170141183460469231731687303715884105727[/rand]" {
|
||||||
t.Error("Bad output:", "'"+res+"'")
|
t.Error("Bad output:", "'"+res+"'")
|
||||||
t.Error("Expected a number between 0 and 170141183460469231731687303715884105727")
|
t.Error("Expected a number between 0 and 170141183460469231731687303715884105727")
|
||||||
}
|
}
|
||||||
|
@ -193,19 +193,42 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
|
|
||||||
func TestMarkdownRender(t *testing.T) {
|
func TestMarkdownRender(t *testing.T) {
|
||||||
//t.Skip()
|
//t.Skip()
|
||||||
|
err := initMarkdown()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
var res string
|
var res string
|
||||||
var msgList []MEPair
|
var msgList []MEPair
|
||||||
msgList = addMEPair(msgList, "hi", "hi")
|
msgList = addMEPair(msgList, "hi", "hi")
|
||||||
|
msgList = addMEPair(msgList, "**h**", "<b>h</b>")
|
||||||
msgList = addMEPair(msgList, "**hi**", "<b>hi</b>")
|
msgList = addMEPair(msgList, "**hi**", "<b>hi</b>")
|
||||||
|
msgList = addMEPair(msgList, "_h_", "<u>h</u>")
|
||||||
msgList = addMEPair(msgList, "_hi_", "<u>hi</u>")
|
msgList = addMEPair(msgList, "_hi_", "<u>hi</u>")
|
||||||
|
msgList = addMEPair(msgList, "*h*", "<i>h</i>")
|
||||||
msgList = addMEPair(msgList, "*hi*", "<i>hi</i>")
|
msgList = addMEPair(msgList, "*hi*", "<i>hi</i>")
|
||||||
|
msgList = addMEPair(msgList, "~h~", "<s>h</s>")
|
||||||
msgList = addMEPair(msgList, "~hi~", "<s>hi</s>")
|
msgList = addMEPair(msgList, "~hi~", "<s>hi</s>")
|
||||||
msgList = addMEPair(msgList, "*hi**", "<i>hi</i>*")
|
msgList = addMEPair(msgList, "*hi**", "<i>hi</i>*")
|
||||||
msgList = addMEPair(msgList, "**hi***", "<b>hi</b>*")
|
msgList = addMEPair(msgList, "**hi***", "<b>hi</b>*")
|
||||||
msgList = addMEPair(msgList, "**hi*", "*<i>hi</i>")
|
msgList = addMEPair(msgList, "**hi*", "*<i>hi</i>")
|
||||||
msgList = addMEPair(msgList, "***hi***", "*<b><i>hi</i></b>")
|
msgList = addMEPair(msgList, "***hi***", "<b><i>hi</i></b>")
|
||||||
|
msgList = addMEPair(msgList, "***h***", "<b><i>h</i></b>")
|
||||||
msgList = addMEPair(msgList, "\\*hi\\*", "*hi*")
|
msgList = addMEPair(msgList, "\\*hi\\*", "*hi*")
|
||||||
|
msgList = addMEPair(msgList, "d\\*hi\\*", "d*hi*")
|
||||||
|
msgList = addMEPair(msgList, "\\*hi\\*d", "*hi*d")
|
||||||
|
msgList = addMEPair(msgList, "d\\*hi\\*d", "d*hi*d")
|
||||||
|
msgList = addMEPair(msgList, "\\", "\\")
|
||||||
|
msgList = addMEPair(msgList, "\\\\", "\\\\")
|
||||||
|
msgList = addMEPair(msgList, "\\d", "\\d")
|
||||||
|
msgList = addMEPair(msgList, "\\\\d", "\\\\d")
|
||||||
|
msgList = addMEPair(msgList, "\\\\\\d", "\\\\\\d")
|
||||||
|
msgList = addMEPair(msgList, "d\\", "d\\")
|
||||||
|
msgList = addMEPair(msgList, "\\d\\", "\\d\\")
|
||||||
msgList = addMEPair(msgList, "*~hi~*", "<i><s>hi</s></i>")
|
msgList = addMEPair(msgList, "*~hi~*", "<i><s>hi</s></i>")
|
||||||
|
msgList = addMEPair(msgList, "~*hi*~", "<s><i>hi</i></s>")
|
||||||
|
msgList = addMEPair(msgList, "_~hi~_", "<u><s>hi</s></u>")
|
||||||
|
msgList = addMEPair(msgList, "***~hi~***", "<b><i><s>hi</s></i></b>")
|
||||||
msgList = addMEPair(msgList, "**", "**")
|
msgList = addMEPair(msgList, "**", "**")
|
||||||
msgList = addMEPair(msgList, "***", "***")
|
msgList = addMEPair(msgList, "***", "***")
|
||||||
msgList = addMEPair(msgList, "****", "****")
|
msgList = addMEPair(msgList, "****", "****")
|
||||||
|
@ -224,10 +247,11 @@ func TestMarkdownRender(t *testing.T) {
|
||||||
msgList = addMEPair(msgList, "*** ***", "<b><i> </i></b>")
|
msgList = addMEPair(msgList, "*** ***", "<b><i> </i></b>")
|
||||||
|
|
||||||
for _, item := range msgList {
|
for _, item := range msgList {
|
||||||
t.Log("Testing string '" + item.Msg + "'")
|
|
||||||
res = markdownParse(item.Msg)
|
res = markdownParse(item.Msg)
|
||||||
if res != item.Expects {
|
if res != item.Expects {
|
||||||
|
t.Error("Testing string '" + item.Msg + "'")
|
||||||
t.Error("Bad output:", "'"+res+"'")
|
t.Error("Bad output:", "'"+res+"'")
|
||||||
|
//t.Error("Ouput in bytes:", []byte(res))
|
||||||
t.Error("Expected:", item.Expects)
|
t.Error("Expected:", item.Expects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ function load_alerts(menu_alerts)
|
||||||
}
|
}
|
||||||
|
|
||||||
var alist = "";
|
var alist = "";
|
||||||
var anyAvatar = false
|
|
||||||
for(var i in data.msgs) {
|
for(var i in data.msgs) {
|
||||||
var msg = data.msgs[i];
|
var msg = data.msgs[i];
|
||||||
var mmsg = msg.msg;
|
var mmsg = msg.msg;
|
||||||
|
@ -44,15 +43,13 @@ function load_alerts(menu_alerts)
|
||||||
if("sub" in msg) {
|
if("sub" in msg) {
|
||||||
for(var i = 0; i < msg.sub.length; i++) {
|
for(var i = 0; i < msg.sub.length; i++) {
|
||||||
mmsg = mmsg.replace("\{"+i+"\}", msg.sub[i]);
|
mmsg = mmsg.replace("\{"+i+"\}", msg.sub[i]);
|
||||||
//console.log("Sub #" + i);
|
//console.log("Sub #" + i + ":",msg.sub[i]);
|
||||||
//console.log(msg.sub[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if("avatar" in msg) {
|
if("avatar" in msg) {
|
||||||
alist += "<div class='alertItem withAvatar' style='background-image:url(\""+msg.avatar+"\");'><a class='text' data-asid='"+msg.asid+"' href=\""+msg.path+"\">"+mmsg+"</a></div>";
|
alist += "<div class='alertItem withAvatar' style='background-image:url(\""+msg.avatar+"\");'><a class='text' data-asid='"+msg.asid+"' href=\""+msg.path+"\">"+mmsg+"</a></div>";
|
||||||
alertList.push("<div class='alertItem withAvatar' style='background-image:url(\""+msg.avatar+"\");'><a class='text' data-asid='"+msg.asid+"' href=\""+msg.path+"\">"+mmsg+"</a></div>");
|
alertList.push("<div class='alertItem withAvatar' style='background-image:url(\""+msg.avatar+"\");'><a class='text' data-asid='"+msg.asid+"' href=\""+msg.path+"\">"+mmsg+"</a></div>");
|
||||||
anyAvatar = true
|
|
||||||
} else {
|
} else {
|
||||||
alist += "<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>";
|
alist += "<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>";
|
||||||
alertList.push("<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>");
|
alertList.push("<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>");
|
||||||
|
@ -62,11 +59,8 @@ function load_alerts(menu_alerts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(alist == "") alist = "<div class='alertItem'>You don't have any alerts</div>";
|
if(alist == "") alist = "<div class='alertItem'>You don't have any alerts</div>";
|
||||||
else {
|
|
||||||
//menu_alerts.removeClass("hasAvatars");
|
|
||||||
//if(anyAvatar) menu_alerts.addClass("hasAvatars");
|
|
||||||
}
|
|
||||||
alertListNode.innerHTML = alist;
|
alertListNode.innerHTML = alist;
|
||||||
|
|
||||||
if(data.msgCount != 0 && data.msgCount != undefined) {
|
if(data.msgCount != 0 && data.msgCount != undefined) {
|
||||||
alertCounterNode.textContent = data.msgCount;
|
alertCounterNode.textContent = data.msgCount;
|
||||||
menu_alerts.classList.add("has_alerts");
|
menu_alerts.classList.add("has_alerts");
|
||||||
|
@ -88,6 +82,7 @@ function load_alerts(menu_alerts)
|
||||||
console.log(magic.responseText);
|
console.log(magic.responseText);
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
console.log("error: ",error);
|
||||||
alertListNode.innerHTML = "<div class='alertItem'>"+errtxt+"</div>";
|
alertListNode.innerHTML = "<div class='alertItem'>"+errtxt+"</div>";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -176,8 +171,7 @@ $(document).ready(function(){
|
||||||
|
|
||||||
var messages = event.data.split('\r');
|
var messages = event.data.split('\r');
|
||||||
for(var i = 0; i < messages.length; i++) {
|
for(var i = 0; i < messages.length; i++) {
|
||||||
//console.log("Message:");
|
//console.log("Message: ",messages[i]);
|
||||||
//console.log(messages[i]);
|
|
||||||
if(messages[i].startsWith("set ")) {
|
if(messages[i].startsWith("set ")) {
|
||||||
//msgblocks = messages[i].split(' ',3);
|
//msgblocks = messages[i].split(' ',3);
|
||||||
let msgblocks = SplitN(messages[i]," ",3);
|
let msgblocks = SplitN(messages[i]," ",3);
|
||||||
|
@ -214,10 +208,10 @@ $(document).ready(function(){
|
||||||
let topicStatusInput = $('.topic_status_input').val();
|
let topicStatusInput = $('.topic_status_input').val();
|
||||||
let topicContentInput = $('.topic_content_input').val();
|
let topicContentInput = $('.topic_content_input').val();
|
||||||
let formAction = this.form.getAttribute("action");
|
let formAction = this.form.getAttribute("action");
|
||||||
//console.log("New Topic Name: " + topicNameInput);
|
//console.log("New Topic Name: ", topicNameInput);
|
||||||
//console.log("New Topic Status: " + topicStatusInput);
|
//console.log("New Topic Status: ", topicStatusInput);
|
||||||
//console.log("New Topic Content: " + topicContentInput);
|
//console.log("New Topic Content: ", topicContentInput);
|
||||||
//console.log("Form Action: " + formAction);
|
//console.log("Form Action: ", formAction);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: formAction,
|
url: formAction,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
|
@ -253,7 +247,7 @@ $(document).ready(function(){
|
||||||
block.html(newContent);
|
block.html(newContent);
|
||||||
|
|
||||||
var formAction = $(this).closest('a').attr("href");
|
var formAction = $(this).closest('a').attr("href");
|
||||||
//console.log("Form Action: " + form_action);
|
//console.log("Form Action:",formAction);
|
||||||
$.ajax({ url: formAction, type: "POST", dataType: "json", data: { isJs: "1", edit_item: newContent }
|
$.ajax({ url: formAction, type: "POST", dataType: "json", data: { isJs: "1", edit_item: newContent }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -266,8 +260,7 @@ $(document).ready(function(){
|
||||||
let block = blockParent.find('.editable_block').eq(0);
|
let block = blockParent.find('.editable_block').eq(0);
|
||||||
block.html("<input name='edit_field' value='" + block.text() + "' type='text'/><a href='" + $(this).closest('a').attr("href") + "'><button class='submit_edit' type='submit'>Update</button></a>");
|
block.html("<input name='edit_field' value='" + block.text() + "' type='text'/><a href='" + $(this).closest('a').attr("href") + "'><button class='submit_edit' type='submit'>Update</button></a>");
|
||||||
|
|
||||||
$(".submit_edit").click(function(event)
|
$(".submit_edit").click(function(event) {
|
||||||
{
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let blockParent = $(this).closest('.editable_parent');
|
let blockParent = $(this).closest('.editable_parent');
|
||||||
let block = blockParent.find('.editable_block').eq(0);
|
let block = blockParent.find('.editable_block').eq(0);
|
||||||
|
@ -275,12 +268,12 @@ $(document).ready(function(){
|
||||||
block.html(newContent);
|
block.html(newContent);
|
||||||
|
|
||||||
let formAction = $(this).closest('a').attr("href");
|
let formAction = $(this).closest('a').attr("href");
|
||||||
//console.log("Form Action: " + formAction);
|
//console.log("Form Action:", formAction);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: formAction + "?session=" + session,
|
url: formAction + "?session=" + session,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
data: {isJs: "1",edit_item: newContent}
|
data: { isJs: "1", edit_item: newContent }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -305,9 +298,9 @@ $(document).ready(function(){
|
||||||
else var it = ['No','Yes'];
|
else var it = ['No','Yes'];
|
||||||
var itLen = it.length;
|
var itLen = it.length;
|
||||||
var out = "";
|
var out = "";
|
||||||
//console.log("Field Name '" + field_name + "'")
|
//console.log("Field Name:",field_name);
|
||||||
//console.log("Field Type",field_type)
|
//console.log("Field Type:",field_type);
|
||||||
//console.log("Field Value '" + field_value + "'")
|
//console.log("Field Value:",field_value);
|
||||||
for (var i = 0; i < itLen; i++) {
|
for (var i = 0; i < itLen; i++) {
|
||||||
var sel = "";
|
var sel = "";
|
||||||
if(field_value == i || field_value == it[i]) {
|
if(field_value == i || field_value == it[i]) {
|
||||||
|
@ -332,7 +325,7 @@ $(document).ready(function(){
|
||||||
//console.log("running .submit_edit event");
|
//console.log("running .submit_edit event");
|
||||||
var out_data = {isJs: "1"}
|
var out_data = {isJs: "1"}
|
||||||
var block_parent = $(this).closest('.editable_parent');
|
var block_parent = $(this).closest('.editable_parent');
|
||||||
block_parent.find('.editable_block').each(function(){
|
block_parent.find('.editable_block').each(function() {
|
||||||
var field_name = this.getAttribute("data-field");
|
var field_name = this.getAttribute("data-field");
|
||||||
var field_type = this.getAttribute("data-type");
|
var field_type = this.getAttribute("data-type");
|
||||||
if(field_type=="list") {
|
if(field_type=="list") {
|
||||||
|
@ -350,7 +343,7 @@ $(document).ready(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
var form_action = $(this).closest('a').attr("href");
|
var form_action = $(this).closest('a').attr("href");
|
||||||
//console.log("Form Action: " + form_action);
|
//console.log("Form Action:", form_action);
|
||||||
//console.log(out_data);
|
//console.log(out_data);
|
||||||
$.ajax({ url: form_action + "?session=" + session, type:"POST", dataType:"json", data: out_data });
|
$.ajax({ url: form_action + "?session=" + session, type:"POST", dataType:"json", data: out_data });
|
||||||
block_parent.find('.hide_on_edit').show();
|
block_parent.find('.hide_on_edit').show();
|
||||||
|
|
|
@ -132,3 +132,101 @@ func (build *builder) Purge(table string) (stmt *sql.Stmt, err error) {
|
||||||
}
|
}
|
||||||
return build.conn.Prepare(res)
|
return build.conn.Prepare(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These ones support transactions
|
||||||
|
func (build *builder) SimpleSelectTx(tx *sql.Tx, table string, columns string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleSelect("_builder", table, columns, where, orderby, limit)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleCountTx(tx *sql.Tx, table string, where string, limit string) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleCount("_builder", table, where, limit)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleLeftJoinTx(tx *sql.Tx, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleLeftJoin("_builder", table1, table2, columns, joiners, where, orderby, limit)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleInnerJoinTx(tx *sql.Tx, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleInnerJoin("_builder", table1, table2, columns, joiners, where, orderby, limit)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) CreateTableTx(tx *sql.Tx, table string, charset string, collation string, columns []DB_Table_Column, keys []DB_Table_Key) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.CreateTable("_builder", table, charset, collation, columns, keys)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleInsertTx(tx *sql.Tx, table string, columns string, fields string) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleInsert("_builder", table, columns, fields)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleInsertSelectTx(tx *sql.Tx, ins DB_Insert, sel DB_Select) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleInsertSelect("_builder", ins, sel)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleInsertLeftJoinTx(tx *sql.Tx, ins DB_Insert, sel DB_Join) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleInsertLeftJoin("_builder", ins, sel)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleInsertInnerJoinTx(tx *sql.Tx, ins DB_Insert, sel DB_Join) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleInsertInnerJoin("_builder", ins, sel)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleUpdateTx(tx *sql.Tx, table string, set string, where string) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleUpdate("_builder", table, set, where)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (build *builder) SimpleDeleteTx(tx *sql.Tx, table string, where string) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.SimpleDelete("_builder", table, where)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// I don't know why you need this, but here it is x.x
|
||||||
|
func (build *builder) PurgeTx(tx *sql.Tx, table string) (stmt *sql.Stmt, err error) {
|
||||||
|
res, err := build.adapter.Purge("_builder", table)
|
||||||
|
if err != nil {
|
||||||
|
return stmt, err
|
||||||
|
}
|
||||||
|
return tx.Prepare(res)
|
||||||
|
}
|
||||||
|
|
|
@ -451,8 +451,9 @@ func (adapter *Mssql_Adapter) SimpleSelect(name string, table string, columns st
|
||||||
querystr += " ?" + strconv.Itoa(substituteCount)
|
querystr += " ?" + strconv.Itoa(substituteCount)
|
||||||
case "function", "operator", "number":
|
case "function", "operator", "number":
|
||||||
// TODO: Split the function case off to speed things up
|
// TODO: Split the function case off to speed things up
|
||||||
|
// MSSQL seems to convert the formats? so we'll compare it with a regular date. Do this with the other methods too?
|
||||||
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
|
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
|
||||||
token.Contents = "GETUTCDATE()"
|
token.Contents = "GETDATE()"
|
||||||
}
|
}
|
||||||
querystr += " " + token.Contents
|
querystr += " " + token.Contents
|
||||||
case "column":
|
case "column":
|
||||||
|
@ -800,12 +801,21 @@ func (adapter *Mssql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel
|
||||||
/* Select */
|
/* Select */
|
||||||
var substituteCount = 0
|
var substituteCount = 0
|
||||||
|
|
||||||
// Escape the column names, just in case we've used a reserved keyword
|
for _, column := range processColumns(sel.Columns) {
|
||||||
var colslice = strings.Split(strings.TrimSpace(sel.Columns), ",")
|
var source, alias string
|
||||||
for _, column := range colslice {
|
|
||||||
querystr += "[" + strings.TrimSpace(column) + "],"
|
// Escape the column names, just in case we've used a reserved keyword
|
||||||
|
if column.Type == "function" || column.Type == "substitute" {
|
||||||
|
source = column.Left
|
||||||
|
} else {
|
||||||
|
source = "[" + column.Left + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if column.Alias != "" {
|
||||||
|
alias = " AS [" + column.Alias + "]"
|
||||||
|
}
|
||||||
|
querystr += " " + source + alias + ","
|
||||||
}
|
}
|
||||||
// Remove the trailing comma
|
|
||||||
querystr = querystr[0 : len(querystr)-1]
|
querystr = querystr[0 : len(querystr)-1]
|
||||||
querystr += " FROM [" + sel.Table + "] "
|
querystr += " FROM [" + sel.Table + "] "
|
||||||
|
|
||||||
|
|
|
@ -903,7 +903,7 @@ package main
|
||||||
|
|
||||||
import "log"
|
import "log"
|
||||||
import "database/sql"
|
import "database/sql"
|
||||||
import "./query_gen/lib"
|
//import "./query_gen/lib"
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
` + stmts + `
|
` + stmts + `
|
||||||
|
|
|
@ -102,10 +102,9 @@ type DB_Adapter interface {
|
||||||
SimpleInsert(name string, table string, columns string, fields string) (string, error)
|
SimpleInsert(name string, table string, columns string, fields string) (string, error)
|
||||||
|
|
||||||
// ! DEPRECATED
|
// ! DEPRECATED
|
||||||
SimpleReplace(name string, table string, columns string, fields string) (string, error)
|
//SimpleReplace(name string, table string, columns string, fields string) (string, error)
|
||||||
|
// ! NOTE: MySQL doesn't support upserts properly, so I'm removing this from the interface until we find a way to patch it in
|
||||||
// ! NOTE: MySQL doesn't support upserts properly, asides from for keys, so this is just a less destructive replace atm
|
//SimpleUpsert(name string, table string, columns string, fields string, where string) (string, error)
|
||||||
SimpleUpsert(name string, table string, columns string, fields string, where string) (string, error)
|
|
||||||
SimpleUpdate(name string, table string, set string, where string) (string, error)
|
SimpleUpdate(name string, table string, set string, where string) (string, error)
|
||||||
SimpleDelete(name string, table string, where string) (string, error)
|
SimpleDelete(name string, table string, where string) (string, error)
|
||||||
Purge(name string, table string) (string, error)
|
Purge(name string, table string) (string, error)
|
||||||
|
|
|
@ -75,7 +75,7 @@ func writeStatements(adapter qgen.DB_Adapter) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writeReplaces(adapter)
|
/*err = writeReplaces(adapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func writeStatements(adapter qgen.DB_Adapter) error {
|
||||||
err = writeUpserts(adapter)
|
err = writeUpserts(adapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}*/
|
||||||
|
|
||||||
err = writeUpdates(adapter)
|
err = writeUpdates(adapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -356,8 +356,6 @@ func writeInserts(adapter qgen.DB_Adapter) error {
|
||||||
|
|
||||||
adapter.SimpleInsert("addTheme", "themes", "uname, default", "?,?")
|
adapter.SimpleInsert("addTheme", "themes", "uname, default", "?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("createGroup", "users_groups", "name, tag, is_admin, is_mod, is_banned, permissions", "?,?,?,?,?,?")
|
|
||||||
|
|
||||||
adapter.SimpleInsert("addModlogEntry", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "?,?,?,?,?,UTC_TIMESTAMP()")
|
adapter.SimpleInsert("addModlogEntry", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "?,?,?,?,?,UTC_TIMESTAMP()")
|
||||||
|
|
||||||
adapter.SimpleInsert("addAdminlogEntry", "administration_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "?,?,?,?,?,UTC_TIMESTAMP()")
|
adapter.SimpleInsert("addAdminlogEntry", "administration_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "?,?,?,?,?,UTC_TIMESTAMP()")
|
||||||
|
@ -373,7 +371,8 @@ func writeReplaces(adapter qgen.DB_Adapter) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeUpserts(adapter qgen.DB_Adapter) (err error) {
|
// ! Upserts are broken atm
|
||||||
|
/*func writeUpserts(adapter qgen.DB_Adapter) (err error) {
|
||||||
_, err = adapter.SimpleUpsert("addForumPermsToGroup", "forums_permissions", "gid, fid, preset, permissions", "?,?,?,?", "gid = ? AND fid = ?")
|
_, err = adapter.SimpleUpsert("addForumPermsToGroup", "forums_permissions", "gid, fid, preset, permissions", "?,?,?,?", "gid = ? AND fid = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -385,7 +384,7 @@ func writeUpserts(adapter qgen.DB_Adapter) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}*/
|
||||||
|
|
||||||
func writeUpdates(adapter qgen.DB_Adapter) error {
|
func writeUpdates(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleUpdate("addRepliesToTopic", "topics", "postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()", "tid = ?")
|
adapter.SimpleUpdate("addRepliesToTopic", "topics", "postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()", "tid = ?")
|
||||||
|
@ -454,6 +453,8 @@ func writeUpdates(adapter qgen.DB_Adapter) error {
|
||||||
|
|
||||||
adapter.SimpleUpdate("updateUser", "users", "name = ?, email = ?, group = ?", "uid = ?")
|
adapter.SimpleUpdate("updateUser", "users", "name = ?, email = ?, group = ?", "uid = ?")
|
||||||
|
|
||||||
|
adapter.SimpleUpdate("updateUserGroup", "users", "group = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("updateGroupPerms", "users_groups", "permissions = ?", "gid = ?")
|
adapter.SimpleUpdate("updateGroupPerms", "users_groups", "permissions = ?", "gid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("updateGroupRank", "users_groups", "is_admin = ?, is_mod = ?, is_banned = ?", "gid = ?")
|
adapter.SimpleUpdate("updateGroupRank", "users_groups", "is_admin = ?, is_mod = ?, is_banned = ?", "gid = ?")
|
||||||
|
@ -480,10 +481,10 @@ func writeDeletes(adapter qgen.DB_Adapter) error {
|
||||||
|
|
||||||
adapter.SimpleDelete("deleteProfileReply", "users_replies", "rid = ?")
|
adapter.SimpleDelete("deleteProfileReply", "users_replies", "rid = ?")
|
||||||
|
|
||||||
adapter.SimpleDelete("deleteForumPermsByForum", "forums_permissions", "fid = ?")
|
//adapter.SimpleDelete("deleteForumPermsByForum", "forums_permissions", "fid = ?")
|
||||||
|
|
||||||
adapter.SimpleDelete("deleteActivityStreamMatch", "activity_stream_matches", "watcher = ? AND asid = ?")
|
adapter.SimpleDelete("deleteActivityStreamMatch", "activity_stream_matches", "watcher = ? AND asid = ?")
|
||||||
//adapter.SimpleDelete("delete_activity_stream_matches_by_watcher","activity_stream_matches","watcher = ?")
|
//adapter.SimpleDelete("deleteActivityStreamMatchesByWatcher","activity_stream_matches","watcher = ?")
|
||||||
|
|
||||||
adapter.SimpleDelete("deleteWordFilter", "word_filters", "wfid = ?")
|
adapter.SimpleDelete("deleteWordFilter", "word_filters", "wfid = ?")
|
||||||
|
|
||||||
|
@ -501,20 +502,20 @@ func writeSimpleCounts(adapter qgen.DB_Adapter) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeInsertSelects(adapter qgen.DB_Adapter) error {
|
func writeInsertSelects(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleInsertSelect("addForumPermsToForumAdmins",
|
/*adapter.SimpleInsertSelect("addForumPermsToForumAdmins",
|
||||||
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
||||||
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""},
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""},
|
||||||
)
|
)*/
|
||||||
|
|
||||||
adapter.SimpleInsertSelect("addForumPermsToForumStaff",
|
/*adapter.SimpleInsertSelect("addForumPermsToForumStaff",
|
||||||
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
||||||
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""},
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""},
|
||||||
)
|
)*/
|
||||||
|
|
||||||
adapter.SimpleInsertSelect("addForumPermsToForumMembers",
|
/*adapter.SimpleInsertSelect("addForumPermsToForumMembers",
|
||||||
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
|
||||||
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""},
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""},
|
||||||
)
|
)*/
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
//+build experiment
|
||||||
|
|
||||||
|
// ! EXPERIMENTAL
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tagFinder *regexp.Regexp
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tagFinder = regexp.MustCompile(`(?s)\{\{(.*)\}\}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func icecreamSoup(tmpl string) error {
|
||||||
|
tagIndices := tagFinder.FindAllStringIndex(tmpl, -1)
|
||||||
|
if tagIndices != nil && len(tagIndices) > 0 {
|
||||||
|
|
||||||
|
if tagIndices[0][0] == 0 {
|
||||||
|
return errors.New("We don't support tags in the outermost layer yet")
|
||||||
|
}
|
||||||
|
for _, tagIndex := range tagIndices {
|
||||||
|
var nestingLayer = 0
|
||||||
|
for i := tagIndex[0]; i > 0; i-- {
|
||||||
|
switch tmpl[i] {
|
||||||
|
case '>':
|
||||||
|
i, closeTag, err := tasteTagToLeft(tmpl, i)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if closeTag {
|
||||||
|
nestingLayer++
|
||||||
|
}
|
||||||
|
case '<':
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tasteTagToLeft(tmpl string, index int) (indexOut int, closeTag bool, err error) {
|
||||||
|
var foundLeftBrace = false
|
||||||
|
for ; index > 0; index-- {
|
||||||
|
// What if the / isn't adjacent to the < but has a space instead? Is that even valid?
|
||||||
|
if index >= 1 && tmpl[index] == '/' && tmpl[index-1] == '<' {
|
||||||
|
closeTag = true
|
||||||
|
break
|
||||||
|
} else if tmpl[index] == '<' {
|
||||||
|
foundLeftBrace = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundLeftBrace {
|
||||||
|
return errors.New("The left portion of the tag is missing")
|
||||||
|
}
|
||||||
|
return index, closeTag, nil
|
||||||
|
}
|
|
@ -332,6 +332,8 @@ func routeForum(w http.ResponseWriter, r *http.Request, user User, sfid string)
|
||||||
} else {
|
} else {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move this to *Forum
|
||||||
rows, err := getForumTopicsOffsetStmt.Query(fid, offset, config.ItemsPerPage)
|
rows, err := getForumTopicsOffsetStmt.Query(fid, offset, config.ItemsPerPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
|
@ -904,10 +906,11 @@ func routeRegisterSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var active, group int
|
var active bool
|
||||||
|
var group int
|
||||||
switch headerLite.Settings["activation_type"] {
|
switch headerLite.Settings["activation_type"] {
|
||||||
case 1: // Activate All
|
case 1: // Activate All
|
||||||
active = 1
|
active = true
|
||||||
group = config.DefaultGroup
|
group = config.DefaultGroup
|
||||||
default: // Anything else. E.g. Admin Activation or Email Activation.
|
default: // Anything else. E.g. Admin Activation or Email Activation.
|
||||||
group = config.ActivationGroup
|
group = config.ActivationGroup
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
@echo off
|
||||||
|
echo Generating the dynamic code
|
||||||
|
go generate
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Building the router generator
|
||||||
|
go build ./router_gen
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
echo Running the router generator
|
||||||
|
router_gen.exe
|
||||||
|
|
||||||
|
echo Building the query generator
|
||||||
|
go build ./query_gen
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
echo Running the query generator
|
||||||
|
query_gen.exe
|
||||||
|
|
||||||
|
echo Building the executable
|
||||||
|
go test
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
pause
|
|
@ -0,0 +1,33 @@
|
||||||
|
@echo off
|
||||||
|
echo Generating the dynamic code
|
||||||
|
go generate
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Building the router generator
|
||||||
|
go build ./router_gen
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
echo Running the router generator
|
||||||
|
router_gen.exe
|
||||||
|
|
||||||
|
echo Building the query generator
|
||||||
|
go build ./query_gen
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
echo Running the query generator
|
||||||
|
query_gen.exe
|
||||||
|
|
||||||
|
echo Building the executable
|
||||||
|
go test -tags mssql
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
pause
|
16
tasks.go
16
tasks.go
|
@ -25,25 +25,19 @@ func handleExpiredScheduledGroups() error {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var uid int
|
var uid int
|
||||||
ucache, ok := users.(UserCache)
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err := rows.Scan(&uid)
|
err := rows.Scan(&uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = replaceScheduleGroupStmt.Exec(uid, 0, 0, time.Now(), false, uid)
|
|
||||||
|
// Sneaky way of initialising a *User, please use the methods on the UserStore instead
|
||||||
|
user := getDummyUser()
|
||||||
|
user.ID = uid
|
||||||
|
err = user.RevertGroupUpdate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("Unable to replace the scheduled group")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = setTempGroupStmt.Exec(0, uid)
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Unable to reset the tempgroup")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
ucache.CacheRemove(uid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,87 +105,90 @@ if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||||
w.Write(forum_12)
|
w.Write(forum_12)
|
||||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||||
w.Write(forum_13)
|
w.Write(forum_13)
|
||||||
} else {
|
|
||||||
w.Write(forum_14)
|
w.Write(forum_14)
|
||||||
}
|
} else {
|
||||||
w.Write(forum_15)
|
w.Write(forum_15)
|
||||||
}
|
}
|
||||||
w.Write(forum_16)
|
w.Write(forum_16)
|
||||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
|
||||||
w.Write(forum_17)
|
|
||||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
|
||||||
w.Write(forum_18)
|
|
||||||
if tmpl_forum_vars.CurrentUser.Perms.UploadFiles {
|
|
||||||
w.Write(forum_19)
|
|
||||||
}
|
}
|
||||||
|
w.Write(forum_17)
|
||||||
|
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||||
|
w.Write(forum_18)
|
||||||
|
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||||
|
w.Write(forum_19)
|
||||||
|
if tmpl_forum_vars.CurrentUser.Perms.UploadFiles {
|
||||||
w.Write(forum_20)
|
w.Write(forum_20)
|
||||||
}
|
}
|
||||||
w.Write(forum_21)
|
w.Write(forum_21)
|
||||||
|
}
|
||||||
|
w.Write(forum_22)
|
||||||
if len(tmpl_forum_vars.ItemList) != 0 {
|
if len(tmpl_forum_vars.ItemList) != 0 {
|
||||||
for _, item := range tmpl_forum_vars.ItemList {
|
for _, item := range tmpl_forum_vars.ItemList {
|
||||||
w.Write(forum_22)
|
|
||||||
if item.Sticky {
|
|
||||||
w.Write(forum_23)
|
w.Write(forum_23)
|
||||||
|
if item.Sticky {
|
||||||
|
w.Write(forum_24)
|
||||||
} else {
|
} else {
|
||||||
if item.IsClosed {
|
if item.IsClosed {
|
||||||
w.Write(forum_24)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(forum_25)
|
w.Write(forum_25)
|
||||||
if item.Creator.Avatar != "" {
|
}
|
||||||
|
}
|
||||||
w.Write(forum_26)
|
w.Write(forum_26)
|
||||||
w.Write([]byte(item.Creator.Avatar))
|
if item.Creator.Avatar != "" {
|
||||||
w.Write(forum_27)
|
w.Write(forum_27)
|
||||||
}
|
w.Write([]byte(item.Creator.Avatar))
|
||||||
w.Write(forum_28)
|
w.Write(forum_28)
|
||||||
w.Write([]byte(item.Link))
|
|
||||||
w.Write(forum_29)
|
|
||||||
w.Write([]byte(item.Title))
|
|
||||||
w.Write(forum_30)
|
|
||||||
w.Write([]byte(item.Creator.Link))
|
|
||||||
w.Write(forum_31)
|
|
||||||
w.Write([]byte(item.Creator.Name))
|
|
||||||
w.Write(forum_32)
|
|
||||||
if item.IsClosed {
|
|
||||||
w.Write(forum_33)
|
|
||||||
}
|
}
|
||||||
if item.Sticky {
|
w.Write(forum_29)
|
||||||
|
w.Write([]byte(item.Link))
|
||||||
|
w.Write(forum_30)
|
||||||
|
w.Write([]byte(item.Title))
|
||||||
|
w.Write(forum_31)
|
||||||
|
w.Write([]byte(item.Creator.Link))
|
||||||
|
w.Write(forum_32)
|
||||||
|
w.Write([]byte(item.Creator.Name))
|
||||||
|
w.Write(forum_33)
|
||||||
|
if item.IsClosed {
|
||||||
w.Write(forum_34)
|
w.Write(forum_34)
|
||||||
}
|
}
|
||||||
w.Write(forum_35)
|
|
||||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
|
||||||
w.Write(forum_36)
|
|
||||||
if item.Sticky {
|
if item.Sticky {
|
||||||
|
w.Write(forum_35)
|
||||||
|
}
|
||||||
|
w.Write(forum_36)
|
||||||
|
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||||
w.Write(forum_37)
|
w.Write(forum_37)
|
||||||
|
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||||
|
w.Write(forum_38)
|
||||||
|
if item.Sticky {
|
||||||
|
w.Write(forum_39)
|
||||||
} else {
|
} else {
|
||||||
if item.IsClosed {
|
if item.IsClosed {
|
||||||
w.Write(forum_38)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(forum_39)
|
|
||||||
if item.LastUser.Avatar != "" {
|
|
||||||
w.Write(forum_40)
|
w.Write(forum_40)
|
||||||
w.Write([]byte(item.LastUser.Avatar))
|
|
||||||
w.Write(forum_41)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
w.Write(forum_41)
|
||||||
|
if item.LastUser.Avatar != "" {
|
||||||
w.Write(forum_42)
|
w.Write(forum_42)
|
||||||
w.Write([]byte(item.LastUser.Link))
|
w.Write([]byte(item.LastUser.Avatar))
|
||||||
w.Write(forum_43)
|
w.Write(forum_43)
|
||||||
w.Write([]byte(item.LastUser.Name))
|
}
|
||||||
w.Write(forum_44)
|
w.Write(forum_44)
|
||||||
w.Write([]byte(item.RelativeLastReplyAt))
|
w.Write([]byte(item.LastUser.Link))
|
||||||
w.Write(forum_45)
|
w.Write(forum_45)
|
||||||
|
w.Write([]byte(item.LastUser.Name))
|
||||||
|
w.Write(forum_46)
|
||||||
|
w.Write([]byte(item.RelativeLastReplyAt))
|
||||||
|
w.Write(forum_47)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
w.Write(forum_46)
|
|
||||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
|
||||||
w.Write(forum_47)
|
|
||||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
|
||||||
w.Write(forum_48)
|
w.Write(forum_48)
|
||||||
}
|
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||||
w.Write(forum_49)
|
w.Write(forum_49)
|
||||||
}
|
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||||
w.Write(forum_50)
|
w.Write(forum_50)
|
||||||
|
}
|
||||||
|
w.Write(forum_51)
|
||||||
|
}
|
||||||
|
w.Write(forum_52)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if len(tmpl_forum_vars.Header.Themes) != 0 {
|
if len(tmpl_forum_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_forum_vars.Header.Themes {
|
for _, item := range tmpl_forum_vars.Header.Themes {
|
||||||
|
|
198
template_list.go
198
template_list.go
|
@ -678,33 +678,38 @@ var forums_19 = []byte(`
|
||||||
var topics_0 = []byte(`
|
var topics_0 = []byte(`
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
<div class="rowblock rowhead">
|
<div class="rowblock rowhead topic_list_title_block">
|
||||||
<div class="rowitem topic_list_title`)
|
<div class="rowitem topic_list_title`)
|
||||||
var topics_1 = []byte(` has_opt`)
|
var topics_1 = []byte(` has_opt`)
|
||||||
var topics_2 = []byte(`"><h1>All Topics</h1></div>
|
var topics_2 = []byte(`"><h1>All Topics</h1></div>
|
||||||
`)
|
`)
|
||||||
var topics_3 = []byte(`
|
var topics_3 = []byte(`
|
||||||
<div class="opt create_topic_opt" title="Create Topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
||||||
`)
|
`)
|
||||||
var topics_4 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic"><a></a></div>`)
|
var topics_4 = []byte(`
|
||||||
var topics_5 = []byte(`
|
<div class="opt mod_opt" title="Moderate">
|
||||||
|
<a class="moderate_link" href="#"></a>
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
var topics_5 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic anywhere"><a></a></div>`)
|
||||||
|
var topics_6 = []byte(`
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
`)
|
`)
|
||||||
var topics_6 = []byte(`
|
var topics_7 = []byte(`
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var topics_7 = []byte(`
|
var topics_8 = []byte(`
|
||||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
||||||
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||||
<div class="formrow topic_board_row real_first_child">
|
<div class="formrow topic_board_row real_first_child">
|
||||||
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">
|
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">
|
||||||
`)
|
`)
|
||||||
var topics_8 = []byte(`<option `)
|
var topics_9 = []byte(`<option `)
|
||||||
var topics_9 = []byte(`selected`)
|
var topics_10 = []byte(`selected`)
|
||||||
var topics_10 = []byte(` value="`)
|
var topics_11 = []byte(` value="`)
|
||||||
var topics_11 = []byte(`">`)
|
var topics_12 = []byte(`">`)
|
||||||
var topics_12 = []byte(`</option>`)
|
var topics_13 = []byte(`</option>`)
|
||||||
var topics_13 = []byte(`
|
var topics_14 = []byte(`
|
||||||
</select></div>
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow topic_name_row">
|
<div class="formrow topic_name_row">
|
||||||
|
@ -721,71 +726,73 @@ var topics_13 = []byte(`
|
||||||
<div class="formitem">
|
<div class="formitem">
|
||||||
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
|
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
|
||||||
`)
|
`)
|
||||||
var topics_14 = []byte(`
|
var topics_15 = []byte(`
|
||||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||||
<div id="upload_file_dock"></div>`)
|
<div id="upload_file_dock"></div>`)
|
||||||
var topics_15 = []byte(`
|
var topics_16 = []byte(`
|
||||||
<button class="formbutton close_form">Cancel</button>
|
<button class="formbutton close_form">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var topics_16 = []byte(`
|
var topics_17 = []byte(`
|
||||||
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
|
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
|
||||||
`)
|
`)
|
||||||
var topics_17 = []byte(`<div class="topic_row">
|
var topics_18 = []byte(`<div class="topic_row">
|
||||||
<div class="rowitem topic_left passive datarow `)
|
<div class="rowitem topic_left passive datarow `)
|
||||||
var topics_18 = []byte(`topic_sticky`)
|
var topics_19 = []byte(`topic_sticky`)
|
||||||
var topics_19 = []byte(`topic_closed`)
|
var topics_20 = []byte(`topic_closed`)
|
||||||
var topics_20 = []byte(`">
|
var topics_21 = []byte(`">
|
||||||
|
<span class="selector"></span>
|
||||||
`)
|
`)
|
||||||
var topics_21 = []byte(`<img src="`)
|
var topics_22 = []byte(`<img src="`)
|
||||||
var topics_22 = []byte(`" height="64" />`)
|
var topics_23 = []byte(`" height="64" />`)
|
||||||
var topics_23 = []byte(`
|
var topics_24 = []byte(`
|
||||||
<span class="topic_inner_left">
|
<span class="topic_inner_left">
|
||||||
<a class="rowtopic" href="`)
|
<a class="rowtopic" href="`)
|
||||||
var topics_24 = []byte(`">`)
|
var topics_25 = []byte(`"><span>`)
|
||||||
var topics_25 = []byte(`</a> `)
|
var topics_26 = []byte(`</span></a> `)
|
||||||
var topics_26 = []byte(`<a class="rowsmall parent_forum" href="`)
|
var topics_27 = []byte(`<a class="rowsmall parent_forum" href="`)
|
||||||
var topics_27 = []byte(`">`)
|
var topics_28 = []byte(`">`)
|
||||||
var topics_28 = []byte(`</a>`)
|
var topics_29 = []byte(`</a>`)
|
||||||
var topics_29 = []byte(`
|
var topics_30 = []byte(`
|
||||||
<br /><a class="rowsmall starter" href="`)
|
<br /><a class="rowsmall starter" href="`)
|
||||||
var topics_30 = []byte(`">`)
|
var topics_31 = []byte(`">`)
|
||||||
var topics_31 = []byte(`</a>
|
var topics_32 = []byte(`</a>
|
||||||
`)
|
`)
|
||||||
var topics_32 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
var topics_33 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||||
var topics_33 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
var topics_34 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||||
var topics_34 = []byte(`
|
var topics_35 = []byte(`
|
||||||
</span>
|
</span>
|
||||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">`)
|
<span class="replyCount">`)
|
||||||
var topics_35 = []byte(` replies</span><br />
|
var topics_36 = []byte(`</span><br />
|
||||||
<span class="topicCount">x topics</span>
|
<span class="likeCount">`)
|
||||||
|
var topics_37 = []byte(`</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowitem topic_right passive datarow `)
|
<div class="rowitem topic_right passive datarow `)
|
||||||
var topics_36 = []byte(`topic_sticky`)
|
var topics_38 = []byte(`topic_sticky`)
|
||||||
var topics_37 = []byte(`topic_closed`)
|
var topics_39 = []byte(`topic_closed`)
|
||||||
var topics_38 = []byte(`">
|
var topics_40 = []byte(`">
|
||||||
`)
|
`)
|
||||||
var topics_39 = []byte(`<img src="`)
|
var topics_41 = []byte(`<img src="`)
|
||||||
var topics_40 = []byte(`" height="64" />`)
|
var topics_42 = []byte(`" height="64" />`)
|
||||||
var topics_41 = []byte(`
|
var topics_43 = []byte(`
|
||||||
<span>
|
<span>
|
||||||
<a href="`)
|
<a href="`)
|
||||||
var topics_42 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
var topics_44 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||||
var topics_43 = []byte(`</a><br>
|
var topics_45 = []byte(`</a><br>
|
||||||
<span class="rowsmall lastReplyAt">Last: `)
|
<span class="rowsmall lastReplyAt">`)
|
||||||
var topics_44 = []byte(`</span>
|
var topics_46 = []byte(`</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>`)
|
</div>`)
|
||||||
var topics_45 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
var topics_47 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
||||||
var topics_46 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
var topics_48 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||||
var topics_47 = []byte(`</div>`)
|
var topics_49 = []byte(`</div>`)
|
||||||
var topics_48 = []byte(`
|
var topics_50 = []byte(`
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
@ -803,7 +810,7 @@ var forum_8 = []byte(`
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
<div id="forum_head_block" class="rowblock rowhead">
|
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||||
<div class="rowitem forum_title`)
|
<div class="rowitem forum_title`)
|
||||||
var forum_9 = []byte(` has_opt`)
|
var forum_9 = []byte(` has_opt`)
|
||||||
var forum_10 = []byte(`"><h1>`)
|
var forum_10 = []byte(`"><h1>`)
|
||||||
|
@ -811,21 +818,26 @@ var forum_11 = []byte(`</h1>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var forum_12 = []byte(`
|
var forum_12 = []byte(`
|
||||||
<div class="opt create_topic_opt" title="Create Topic"><a class="create_topic_link" href="/topics/create/`)
|
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/`)
|
||||||
var forum_13 = []byte(`"></a></div>
|
var forum_13 = []byte(`"></a></div>
|
||||||
`)
|
`)
|
||||||
var forum_14 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic"><a></a></div>`)
|
var forum_14 = []byte(`
|
||||||
var forum_15 = []byte(`
|
<div class="opt mod_opt" title="Moderate">
|
||||||
|
<a class="moderate_link" href="#"></a>
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
var forum_15 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic in this forum"><a></a></div>`)
|
||||||
|
var forum_16 = []byte(`
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
`)
|
`)
|
||||||
var forum_16 = []byte(`
|
var forum_17 = []byte(`
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var forum_17 = []byte(`
|
var forum_18 = []byte(`
|
||||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
||||||
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||||
<input form="topic_create_form_form" id="topic_board_input" name="topic-board" value="`)
|
<input form="topic_create_form_form" id="topic_board_input" name="topic-board" value="`)
|
||||||
var forum_18 = []byte(`" type="hidden">
|
var forum_19 = []byte(`" type="hidden">
|
||||||
<div class="formrow topic_name_row real_first_child">
|
<div class="formrow topic_name_row real_first_child">
|
||||||
<div class="formitem">
|
<div class="formitem">
|
||||||
<input form="topic_create_form_form" name="topic-name" placeholder="Topic title" required>
|
<input form="topic_create_form_form" name="topic-name" placeholder="Topic title" required>
|
||||||
|
@ -840,68 +852,70 @@ var forum_18 = []byte(`" type="hidden">
|
||||||
<div class="formitem">
|
<div class="formitem">
|
||||||
<button form="topic_create_form_form" name="topic-button" class="formbutton">Create Topic</button>
|
<button form="topic_create_form_form" name="topic-button" class="formbutton">Create Topic</button>
|
||||||
`)
|
`)
|
||||||
var forum_19 = []byte(`
|
var forum_20 = []byte(`
|
||||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||||
<div id="upload_file_dock"></div>`)
|
<div id="upload_file_dock"></div>`)
|
||||||
var forum_20 = []byte(`
|
var forum_21 = []byte(`
|
||||||
<button class="formbutton close_form">Cancel</button>
|
<button class="formbutton close_form">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var forum_21 = []byte(`
|
var forum_22 = []byte(`
|
||||||
<div id="forum_topic_list" class="rowblock topic_list">
|
<div id="forum_topic_list" class="rowblock topic_list">
|
||||||
`)
|
`)
|
||||||
var forum_22 = []byte(`<div class="topic_row">
|
var forum_23 = []byte(`<div class="topic_row">
|
||||||
<div class="rowitem topic_left passive datarow `)
|
<div class="rowitem topic_left passive datarow `)
|
||||||
var forum_23 = []byte(`topic_sticky`)
|
var forum_24 = []byte(`topic_sticky`)
|
||||||
var forum_24 = []byte(`topic_closed`)
|
var forum_25 = []byte(`topic_closed`)
|
||||||
var forum_25 = []byte(`">
|
var forum_26 = []byte(`">
|
||||||
|
<span class="selector"></span>
|
||||||
`)
|
`)
|
||||||
var forum_26 = []byte(`<img src="`)
|
var forum_27 = []byte(`<img src="`)
|
||||||
var forum_27 = []byte(`" height="64" />`)
|
var forum_28 = []byte(`" height="64" />`)
|
||||||
var forum_28 = []byte(`
|
var forum_29 = []byte(`
|
||||||
<span class="topic_inner_left">
|
<span class="topic_inner_left">
|
||||||
<a class="rowtopic" href="`)
|
<a class="rowtopic" href="`)
|
||||||
var forum_29 = []byte(`">`)
|
var forum_30 = []byte(`"><span>`)
|
||||||
var forum_30 = []byte(`</a>
|
var forum_31 = []byte(`</span></a>
|
||||||
<br /><a class="rowsmall starter" href="`)
|
<br /><a class="rowsmall starter" href="`)
|
||||||
var forum_31 = []byte(`">`)
|
var forum_32 = []byte(`">`)
|
||||||
var forum_32 = []byte(`</a>
|
var forum_33 = []byte(`</a>
|
||||||
`)
|
`)
|
||||||
var forum_33 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
var forum_34 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||||
var forum_34 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
var forum_35 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||||
var forum_35 = []byte(`
|
var forum_36 = []byte(`
|
||||||
</span>
|
</span>
|
||||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">`)
|
<span class="replyCount">`)
|
||||||
var forum_36 = []byte(` replies</span><br />
|
var forum_37 = []byte(`</span><br />
|
||||||
<span class="topicCount">x topics</span>
|
<span class="likeCount">`)
|
||||||
|
var forum_38 = []byte(`</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowitem topic_right passive datarow `)
|
<div class="rowitem topic_right passive datarow `)
|
||||||
var forum_37 = []byte(`topic_sticky`)
|
var forum_39 = []byte(`topic_sticky`)
|
||||||
var forum_38 = []byte(`topic_closed`)
|
var forum_40 = []byte(`topic_closed`)
|
||||||
var forum_39 = []byte(`">
|
var forum_41 = []byte(`">
|
||||||
`)
|
`)
|
||||||
var forum_40 = []byte(`<img src="`)
|
var forum_42 = []byte(`<img src="`)
|
||||||
var forum_41 = []byte(`" height="64" />`)
|
var forum_43 = []byte(`" height="64" />`)
|
||||||
var forum_42 = []byte(`
|
var forum_44 = []byte(`
|
||||||
<span>
|
<span>
|
||||||
<a href="`)
|
<a href="`)
|
||||||
var forum_43 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
var forum_45 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||||
var forum_44 = []byte(`</a><br>
|
var forum_46 = []byte(`</a><br>
|
||||||
<span class="rowsmall lastReplyAt">Last: `)
|
<span class="rowsmall lastReplyAt">`)
|
||||||
var forum_45 = []byte(`</span>
|
var forum_47 = []byte(`</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>`)
|
</div>`)
|
||||||
var forum_46 = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
var forum_48 = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
||||||
var forum_47 = []byte(` <a href="/topics/create/`)
|
var forum_49 = []byte(` <a href="/topics/create/`)
|
||||||
var forum_48 = []byte(`">Start one?</a>`)
|
var forum_50 = []byte(`">Start one?</a>`)
|
||||||
var forum_49 = []byte(`</div>`)
|
var forum_51 = []byte(`</div>`)
|
||||||
var forum_50 = []byte(`
|
var forum_52 = []byte(`
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -83,107 +83,110 @@ w.Write(topics_2)
|
||||||
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
||||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||||
w.Write(topics_3)
|
w.Write(topics_3)
|
||||||
} else {
|
|
||||||
w.Write(topics_4)
|
w.Write(topics_4)
|
||||||
}
|
} else {
|
||||||
w.Write(topics_5)
|
w.Write(topics_5)
|
||||||
}
|
}
|
||||||
w.Write(topics_6)
|
w.Write(topics_6)
|
||||||
|
}
|
||||||
|
w.Write(topics_7)
|
||||||
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
||||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||||
w.Write(topics_7)
|
w.Write(topics_8)
|
||||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||||
for _, item := range tmpl_topics_vars.ForumList {
|
for _, item := range tmpl_topics_vars.ForumList {
|
||||||
w.Write(topics_8)
|
|
||||||
if item.ID == tmpl_topics_vars.DefaultForum {
|
|
||||||
w.Write(topics_9)
|
w.Write(topics_9)
|
||||||
}
|
if item.ID == tmpl_topics_vars.DefaultForum {
|
||||||
w.Write(topics_10)
|
w.Write(topics_10)
|
||||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
}
|
||||||
w.Write(topics_11)
|
w.Write(topics_11)
|
||||||
w.Write([]byte(item.Name))
|
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||||
w.Write(topics_12)
|
w.Write(topics_12)
|
||||||
}
|
w.Write([]byte(item.Name))
|
||||||
}
|
|
||||||
w.Write(topics_13)
|
w.Write(topics_13)
|
||||||
if tmpl_topics_vars.CurrentUser.Perms.UploadFiles {
|
|
||||||
w.Write(topics_14)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
w.Write(topics_14)
|
||||||
|
if tmpl_topics_vars.CurrentUser.Perms.UploadFiles {
|
||||||
w.Write(topics_15)
|
w.Write(topics_15)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
w.Write(topics_16)
|
w.Write(topics_16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Write(topics_17)
|
||||||
if len(tmpl_topics_vars.TopicList) != 0 {
|
if len(tmpl_topics_vars.TopicList) != 0 {
|
||||||
for _, item := range tmpl_topics_vars.TopicList {
|
for _, item := range tmpl_topics_vars.TopicList {
|
||||||
w.Write(topics_17)
|
|
||||||
if item.Sticky {
|
|
||||||
w.Write(topics_18)
|
w.Write(topics_18)
|
||||||
|
if item.Sticky {
|
||||||
|
w.Write(topics_19)
|
||||||
} else {
|
} else {
|
||||||
if item.IsClosed {
|
if item.IsClosed {
|
||||||
w.Write(topics_19)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(topics_20)
|
w.Write(topics_20)
|
||||||
if item.Creator.Avatar != "" {
|
}
|
||||||
|
}
|
||||||
w.Write(topics_21)
|
w.Write(topics_21)
|
||||||
w.Write([]byte(item.Creator.Avatar))
|
if item.Creator.Avatar != "" {
|
||||||
w.Write(topics_22)
|
w.Write(topics_22)
|
||||||
}
|
w.Write([]byte(item.Creator.Avatar))
|
||||||
w.Write(topics_23)
|
w.Write(topics_23)
|
||||||
w.Write([]byte(item.Link))
|
}
|
||||||
w.Write(topics_24)
|
w.Write(topics_24)
|
||||||
w.Write([]byte(item.Title))
|
w.Write([]byte(item.Link))
|
||||||
w.Write(topics_25)
|
w.Write(topics_25)
|
||||||
if item.ForumName != "" {
|
w.Write([]byte(item.Title))
|
||||||
w.Write(topics_26)
|
w.Write(topics_26)
|
||||||
w.Write([]byte(item.ForumLink))
|
if item.ForumName != "" {
|
||||||
w.Write(topics_27)
|
w.Write(topics_27)
|
||||||
w.Write([]byte(item.ForumName))
|
w.Write([]byte(item.ForumLink))
|
||||||
w.Write(topics_28)
|
w.Write(topics_28)
|
||||||
}
|
w.Write([]byte(item.ForumName))
|
||||||
w.Write(topics_29)
|
w.Write(topics_29)
|
||||||
w.Write([]byte(item.Creator.Link))
|
|
||||||
w.Write(topics_30)
|
|
||||||
w.Write([]byte(item.Creator.Name))
|
|
||||||
w.Write(topics_31)
|
|
||||||
if item.IsClosed {
|
|
||||||
w.Write(topics_32)
|
|
||||||
}
|
}
|
||||||
if item.Sticky {
|
w.Write(topics_30)
|
||||||
|
w.Write([]byte(item.Creator.Link))
|
||||||
|
w.Write(topics_31)
|
||||||
|
w.Write([]byte(item.Creator.Name))
|
||||||
|
w.Write(topics_32)
|
||||||
|
if item.IsClosed {
|
||||||
w.Write(topics_33)
|
w.Write(topics_33)
|
||||||
}
|
}
|
||||||
w.Write(topics_34)
|
|
||||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
|
||||||
w.Write(topics_35)
|
|
||||||
if item.Sticky {
|
if item.Sticky {
|
||||||
|
w.Write(topics_34)
|
||||||
|
}
|
||||||
|
w.Write(topics_35)
|
||||||
|
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||||
w.Write(topics_36)
|
w.Write(topics_36)
|
||||||
|
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||||
|
w.Write(topics_37)
|
||||||
|
if item.Sticky {
|
||||||
|
w.Write(topics_38)
|
||||||
} else {
|
} else {
|
||||||
if item.IsClosed {
|
if item.IsClosed {
|
||||||
w.Write(topics_37)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(topics_38)
|
|
||||||
if item.LastUser.Avatar != "" {
|
|
||||||
w.Write(topics_39)
|
w.Write(topics_39)
|
||||||
w.Write([]byte(item.LastUser.Avatar))
|
}
|
||||||
|
}
|
||||||
w.Write(topics_40)
|
w.Write(topics_40)
|
||||||
}
|
if item.LastUser.Avatar != "" {
|
||||||
w.Write(topics_41)
|
w.Write(topics_41)
|
||||||
w.Write([]byte(item.LastUser.Link))
|
w.Write([]byte(item.LastUser.Avatar))
|
||||||
w.Write(topics_42)
|
w.Write(topics_42)
|
||||||
w.Write([]byte(item.LastUser.Name))
|
|
||||||
w.Write(topics_43)
|
|
||||||
w.Write([]byte(item.RelativeLastReplyAt))
|
|
||||||
w.Write(topics_44)
|
|
||||||
}
|
}
|
||||||
} else {
|
w.Write(topics_43)
|
||||||
|
w.Write([]byte(item.LastUser.Link))
|
||||||
|
w.Write(topics_44)
|
||||||
|
w.Write([]byte(item.LastUser.Name))
|
||||||
w.Write(topics_45)
|
w.Write(topics_45)
|
||||||
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
w.Write([]byte(item.RelativeLastReplyAt))
|
||||||
w.Write(topics_46)
|
w.Write(topics_46)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
w.Write(topics_47)
|
w.Write(topics_47)
|
||||||
}
|
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
||||||
w.Write(topics_48)
|
w.Write(topics_48)
|
||||||
|
}
|
||||||
|
w.Write(topics_49)
|
||||||
|
}
|
||||||
|
w.Write(topics_50)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if len(tmpl_topics_vars.Header.Themes) != 0 {
|
if len(tmpl_topics_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_topics_vars.Header.Themes {
|
for _, item := range tmpl_topics_vars.Header.Themes {
|
||||||
|
|
|
@ -6,18 +6,22 @@
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
<div id="forum_head_block" class="rowblock rowhead">
|
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||||
<div class="rowitem forum_title{{if ne .CurrentUser.ID 0}} has_opt{{end}}"><h1>{{.Title}}</h1>
|
<div class="rowitem forum_title{{if ne .CurrentUser.ID 0}} has_opt{{end}}"><h1>{{.Title}}</h1>
|
||||||
</div>
|
</div>
|
||||||
{{if ne .CurrentUser.ID 0}}
|
{{if ne .CurrentUser.ID 0}}
|
||||||
{{if .CurrentUser.Perms.CreateTopic}}
|
{{if .CurrentUser.Perms.CreateTopic}}
|
||||||
<div class="opt create_topic_opt" title="Create Topic"><a class="create_topic_link" href="/topics/create/{{.Forum.ID}}"></a></div>
|
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/{{.Forum.ID}}"></a></div>
|
||||||
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic"><a></a></div>{{end}}
|
{{/** TODO: Add a permissions check for this **/}}
|
||||||
|
<div class="opt mod_opt" title="Moderate">
|
||||||
|
<a class="moderate_link" href="#"></a>
|
||||||
|
</div>
|
||||||
|
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic in this forum"><a></a></div>{{end}}
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{if .CurrentUser.Perms.CreateTopic}}
|
{{if .CurrentUser.Perms.CreateTopic}}
|
||||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
||||||
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||||
<input form="topic_create_form_form" id="topic_board_input" name="topic-board" value="{{.Forum.ID}}" type="hidden">
|
<input form="topic_create_form_form" id="topic_board_input" name="topic-board" value="{{.Forum.ID}}" type="hidden">
|
||||||
<div class="formrow topic_name_row real_first_child">
|
<div class="formrow topic_name_row real_first_child">
|
||||||
|
@ -45,24 +49,25 @@
|
||||||
<div id="forum_topic_list" class="rowblock topic_list">
|
<div id="forum_topic_list" class="rowblock topic_list">
|
||||||
{{range .ItemList}}<div class="topic_row">
|
{{range .ItemList}}<div class="topic_row">
|
||||||
<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||||
|
<span class="selector"></span>
|
||||||
{{if .Creator.Avatar}}<img src="{{.Creator.Avatar}}" height="64" />{{end}}
|
{{if .Creator.Avatar}}<img src="{{.Creator.Avatar}}" height="64" />{{end}}
|
||||||
<span class="topic_inner_left">
|
<span class="topic_inner_left">
|
||||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
<a class="rowtopic" href="{{.Link}}"><span>{{.Title}}</span></a>
|
||||||
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a>
|
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a>
|
||||||
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
||||||
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
||||||
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||||
</span>
|
</span>
|
||||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
<span class="replyCount">{{.PostCount}}</span><br />
|
||||||
<span class="topicCount">x topics</span>
|
<span class="likeCount">{{.LikeCount}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||||
{{if .LastUser.Avatar}}<img src="{{.LastUser.Avatar}}" height="64" />{{end}}
|
{{if .LastUser.Avatar}}<img src="{{.LastUser.Avatar}}" height="64" />{{end}}
|
||||||
<span>
|
<span>
|
||||||
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
||||||
<span class="rowsmall lastReplyAt">Last: {{.RelativeLastReplyAt}}</span>
|
<span class="rowsmall lastReplyAt">{{.RelativeLastReplyAt}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>{{else}}<div class="rowitem passive">There aren't any topics in this forum yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
</div>{{else}}<div class="rowitem passive">There aren't any topics in this forum yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
<div class="rowblock rowhead">
|
<div class="rowblock rowhead topic_list_title_block">
|
||||||
<div class="rowitem topic_list_title{{if ne .CurrentUser.ID 0}} has_opt{{end}}"><h1>All Topics</h1></div>
|
<div class="rowitem topic_list_title{{if ne .CurrentUser.ID 0}} has_opt{{end}}"><h1>All Topics</h1></div>
|
||||||
{{if ne .CurrentUser.ID 0}}
|
{{if ne .CurrentUser.ID 0}}
|
||||||
{{if .ForumList}}
|
{{if .ForumList}}
|
||||||
<div class="opt create_topic_opt" title="Create Topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
||||||
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic"><a></a></div>{{end}}
|
{{/** TODO: Add a permissions check for this **/}}
|
||||||
|
<div class="opt mod_opt" title="Moderate">
|
||||||
|
<a class="moderate_link" href="#"></a>
|
||||||
|
</div>
|
||||||
|
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic anywhere"><a></a></div>{{end}}
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{if ne .CurrentUser.ID 0}}
|
{{if ne .CurrentUser.ID 0}}
|
||||||
{{if .ForumList}}
|
{{if .ForumList}}
|
||||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
||||||
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||||
<div class="formrow topic_board_row real_first_child">
|
<div class="formrow topic_board_row real_first_child">
|
||||||
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">
|
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">
|
||||||
|
@ -45,24 +49,25 @@
|
||||||
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
|
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
|
||||||
{{range .TopicList}}<div class="topic_row">
|
{{range .TopicList}}<div class="topic_row">
|
||||||
<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||||
|
<span class="selector"></span>
|
||||||
{{if .Creator.Avatar}}<img src="{{.Creator.Avatar}}" height="64" />{{end}}
|
{{if .Creator.Avatar}}<img src="{{.Creator.Avatar}}" height="64" />{{end}}
|
||||||
<span class="topic_inner_left">
|
<span class="topic_inner_left">
|
||||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a> {{if .ForumName}}<a class="rowsmall parent_forum" href="{{.ForumLink}}">{{.ForumName}}</a>{{end}}
|
<a class="rowtopic" href="{{.Link}}"><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>
|
<br /><a class="rowsmall starter" href="{{.Creator.Link}}">{{.Creator.Name}}</a>
|
||||||
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
||||||
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
||||||
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||||
</span>
|
</span>
|
||||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
<span class="replyCount">{{.PostCount}}</span><br />
|
||||||
<span class="topicCount">x topics</span>
|
<span class="likeCount">{{.LikeCount}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||||
{{if .LastUser.Avatar}}<img src="{{.LastUser.Avatar}}" height="64" />{{end}}
|
{{if .LastUser.Avatar}}<img src="{{.LastUser.Avatar}}" height="64" />{{end}}
|
||||||
<span>
|
<span>
|
||||||
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
||||||
<span class="rowsmall lastReplyAt">Last: {{.RelativeLastReplyAt}}</span>
|
<span class="rowsmall lastReplyAt">{{.RelativeLastReplyAt}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>{{else}}<div class="rowitem passive">There aren't any topics yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/">Start one?</a>{{end}}</div>{{end}}
|
</div>{{else}}<div class="rowitem passive">There aren't any topics yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/">Start one?</a>{{end}}</div>{{end}}
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
--header-border-color: hsl(0,0%,85%);
|
--header-border-color: hsl(0,0%,85%);
|
||||||
--element-border-color: hsl(0,0%,90%);
|
--element-border-color: hsl(0,0%,90%);
|
||||||
--element-background-color: white;
|
--element-background-color: white;
|
||||||
|
--replies-lang-string: " replies";
|
||||||
|
--topics-lang-string: " topics";
|
||||||
|
--likes-lang-string: " likes";
|
||||||
|
--primary-link-color: hsl(0,0%,40%);
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
|
@ -10,6 +14,14 @@
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'FontAwesome';
|
||||||
|
src: url('../font-awesome-4.7.0/fonts/fontawesome-webfont.eot?v=4.7.0');
|
||||||
|
src: url('../font-awesome-4.7.0/fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../font-awesome-4.7.0/fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../font-awesome-4.7.0/fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../font-awesome-4.7.0/fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../font-awesome-4.7.0/fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-family: arial;
|
font-family: arial;
|
||||||
|
@ -18,7 +30,7 @@ body {
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: hsl(0,0%,40%);
|
color: var(--primary-link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#back {
|
#back {
|
||||||
|
@ -53,10 +65,11 @@ li {
|
||||||
.menu_overview {
|
.menu_overview {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
.menu_overview a:after {
|
.menu_overview a:after {
|
||||||
margin-left: 14px;
|
margin-left: 13px;
|
||||||
margin-right: 4px;
|
margin-right: 5px;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -3px;
|
top: -3px;
|
||||||
}
|
}
|
||||||
|
@ -74,8 +87,37 @@ li {
|
||||||
top: -2px;
|
top: -2px;
|
||||||
}
|
}
|
||||||
.menu_alerts {
|
.menu_alerts {
|
||||||
|
color: hsl(0,0%,40%);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.alert_bell:before {
|
||||||
|
content: "\f01c";
|
||||||
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
}
|
||||||
|
.menu_alerts:not(.has_alerts) .alert_counter {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.alert_counter {
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: red;
|
||||||
|
opacity: 0.7;
|
||||||
|
border-radius: 30px;
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
left: -1px;
|
||||||
|
}
|
||||||
|
.alert_aftercounter:before {
|
||||||
|
content: "Alerts";
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
.alert_aftercounter:after {
|
||||||
|
content: "|";
|
||||||
|
margin-left: 12px;
|
||||||
|
color: var(--header-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
.menu_account a:before {
|
.menu_account a:before {
|
||||||
content: "\f2c3";
|
content: "\f2c3";
|
||||||
font: normal normal normal 14px/1 FontAwesome;
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
@ -117,8 +159,8 @@ ul {
|
||||||
border: 1px solid var(--header-border-color);
|
border: 1px solid var(--header-border-color);
|
||||||
border-bottom: 2px solid var(--header-border-color);
|
border-bottom: 2px solid var(--header-border-color);
|
||||||
background-color: var(--element-background-color);
|
background-color: var(--element-background-color);
|
||||||
margin-left: 4px;
|
margin-left: 12px;
|
||||||
margin-right: 4px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rowhead {
|
.rowhead {
|
||||||
|
@ -135,6 +177,23 @@ ul {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic_list_title_block {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.mod_opt {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
.mod_opt .moderate_link {
|
||||||
|
border-left: 1px solid var(--element-border-color);
|
||||||
|
padding-left: 12px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
.mod_opt .moderate_link:before {
|
||||||
|
content: "\f0e3";
|
||||||
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.topic_list {
|
.topic_list {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
@ -152,26 +211,37 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_list .rowtopic {
|
.topic_list .rowtopic {
|
||||||
font-size: 18px;
|
font-size: 17px;
|
||||||
color: hsl(0,0%,30%);
|
color: hsl(0,0%,30%);
|
||||||
margin-right: 2px;
|
margin-right: 1px;
|
||||||
max-width: 230px;
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.topic_list .rowtopic span {
|
||||||
|
max-width: 112px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
}
|
||||||
|
|
||||||
|
.topic_list .rowsmall {
|
||||||
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_list .rowsmall.starter:before {
|
.topic_list .rowsmall.starter:before {
|
||||||
content: "\f007";
|
content: "\f007";
|
||||||
font: normal normal normal 14px/1 FontAwesome;
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
font-size: 16px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_list .rowsmall.starter:before {
|
.topic_list .rowsmall.starter:before {
|
||||||
content: "\f007";
|
content: "\f007";
|
||||||
font: normal normal normal 14px/1 FontAwesome;
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
font-size: 16px;
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic_list .lastReplyAt {
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_list .topic_status_e {
|
.topic_list .topic_status_e {
|
||||||
|
@ -179,19 +249,50 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_left {
|
.topic_left {
|
||||||
flex: 1 1 calc(100% - 400px);
|
flex: 1 1 calc(100% - 380px);
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_inner_right {
|
.topic_inner_right {
|
||||||
margin-left: auto;
|
margin-left: 15%;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic_inner_right.rowsmall {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Experimenting here */
|
||||||
|
.topic_inner_right {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic_inner_right span {
|
||||||
|
/*font-size: 15px;*/
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic_inner_right span:after {
|
||||||
|
font-size: 13.5px;
|
||||||
|
}
|
||||||
|
/* End Experiment */
|
||||||
|
|
||||||
|
.topic_inner_right .replyCount:after {
|
||||||
|
content: var(--replies-lang-string);
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic_inner_right .topicCount:after {
|
||||||
|
content: var(--topics-lang-string);
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic_inner_right .likeCount:after {
|
||||||
|
content: var(--likes-lang-string);
|
||||||
|
}
|
||||||
|
|
||||||
.topic_right {
|
.topic_right {
|
||||||
flex: 1 1 150px;
|
flex: 1 1 0px; /*150px*/
|
||||||
border-left: none;
|
border-left: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,15 +306,16 @@ ul {
|
||||||
|
|
||||||
.topic_right img {
|
.topic_right img {
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
height: 48px;
|
height: 42px;
|
||||||
width: 48px;
|
width: 42px;
|
||||||
margin-top: 8px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_left .topic_inner_left {
|
.topic_left .topic_inner_left {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
|
width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_right > span {
|
.topic_right > span {
|
||||||
|
@ -222,11 +324,22 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_sticky {
|
.topic_sticky {
|
||||||
border-bottom: 2px solid hsl(51, 60%, 50%);
|
border-bottom: 2px solid hsl(51, 60%, 70%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@element .topic_left .rowtopic and (min-characters: 10) {
|
@element .topic_left .rowtopic and (min-width: 110px) {
|
||||||
}*/
|
$this, $this span, $this + .parent_forum {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
$this + .parent_forum {
|
||||||
|
margin: 2px;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
$this:after {
|
||||||
|
content: "...";
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media(max-width: 670px) {
|
@media(max-width: 670px) {
|
||||||
.topic_inner_right {
|
.topic_inner_right {
|
||||||
|
|
|
@ -7,10 +7,6 @@
|
||||||
"Tag": "WIP",
|
"Tag": "WIP",
|
||||||
"Sidebars":"right",
|
"Sidebars":"right",
|
||||||
"Resources": [
|
"Resources": [
|
||||||
{
|
|
||||||
"Name":"font-awesome-4.7.0/css/font-awesome.css",
|
|
||||||
"Location":"global"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Name":"EQCSS.min.js",
|
"Name":"EQCSS.min.js",
|
||||||
"Location":"global"
|
"Location":"global"
|
||||||
|
|
|
@ -599,6 +599,9 @@ input, select, textarea {
|
||||||
.topic_list .lastReplyAt {
|
.topic_list .lastReplyAt {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.topic_list .lastReplyAt:before {
|
||||||
|
content: "Last: ";
|
||||||
|
}
|
||||||
.topic_list .starter:before {
|
.topic_list .starter:before {
|
||||||
content: "Starter: ";
|
content: "Starter: ";
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,6 +405,9 @@ li a {
|
||||||
.topic_list .lastReplyAt {
|
.topic_list .lastReplyAt {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.topic_list .lastReplyAt:before {
|
||||||
|
content: "Last: ";
|
||||||
|
}
|
||||||
.topic_list .starter:before {
|
.topic_list .starter:before {
|
||||||
content: "Starter: ";
|
content: "Starter: ";
|
||||||
}
|
}
|
||||||
|
|
|
@ -408,6 +408,9 @@ li a {
|
||||||
.topic_list .lastReplyAt {
|
.topic_list .lastReplyAt {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.topic_list .lastReplyAt:before {
|
||||||
|
content: "Last: ";
|
||||||
|
}
|
||||||
.topic_list .starter:before {
|
.topic_list .starter:before {
|
||||||
content: "Starter: ";
|
content: "Starter: ";
|
||||||
}
|
}
|
||||||
|
|
79
user.go
79
user.go
|
@ -9,9 +9,12 @@ package main
|
||||||
import (
|
import (
|
||||||
//"log"
|
//"log"
|
||||||
//"fmt"
|
//"fmt"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"./query_gen/lib"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,6 +28,7 @@ var CheckPassword = BcryptCheckPassword
|
||||||
|
|
||||||
//func(password string) (hashed_password string, salt string, err error)
|
//func(password string) (hashed_password string, salt string, err error)
|
||||||
var GeneratePassword = BcryptGeneratePassword
|
var GeneratePassword = BcryptGeneratePassword
|
||||||
|
var ErrNoTempGroup = errors.New("We couldn't find a temporary group for this user")
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int
|
ID int
|
||||||
|
@ -66,28 +70,61 @@ func (user *User) Ban(duration time.Duration, issuedBy int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) Unban() error {
|
func (user *User) Unban() error {
|
||||||
err := user.RevertGroupUpdate()
|
return user.RevertGroupUpdate()
|
||||||
ucache, ok := users.(UserCache)
|
}
|
||||||
if ok {
|
|
||||||
ucache.CacheRemove(user.ID)
|
func (user *User) deleteScheduleGroupTx(tx *sql.Tx) error {
|
||||||
|
deleteScheduleGroupStmt, err := qgen.Builder.SimpleDeleteTx(tx, "users_groups_scheduler", "uid = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
_, err = deleteScheduleGroupStmt.Exec(user.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) setTempGroupTx(tx *sql.Tx, tempGroup int) error {
|
||||||
|
setTempGroupStmt, err := qgen.Builder.SimpleUpdateTx(tx, "users", "temp_group = ?", "uid = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = setTempGroupStmt.Exec(tempGroup, user.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use a transaction to avoid race conditions
|
|
||||||
// Make this more stateless?
|
// Make this more stateless?
|
||||||
func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Duration) error {
|
func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Duration) error {
|
||||||
var temporary bool
|
var temporary bool
|
||||||
if duration.Nanoseconds() != 0 {
|
if duration.Nanoseconds() != 0 {
|
||||||
temporary = true
|
temporary = true
|
||||||
}
|
}
|
||||||
|
|
||||||
revertAt := time.Now().Add(duration)
|
revertAt := time.Now().Add(duration)
|
||||||
_, err := replaceScheduleGroupStmt.Exec(user.ID, gid, issuedBy, revertAt, temporary, user.ID)
|
|
||||||
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = setTempGroupStmt.Exec(gid, user.ID)
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
err = user.deleteScheduleGroupTx(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
createScheduleGroupTx, err := qgen.Builder.SimpleInsertTx(tx, "users_groups_scheduler", "uid, set_group, issued_by, issued_at, revert_at, temporary", "?,?,?,UTC_TIMESTAMP(),?,?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = createScheduleGroupTx.Exec(user.ID, gid, issuedBy, revertAt, temporary)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = user.setTempGroupTx(tx, gid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
|
||||||
ucache, ok := users.(UserCache)
|
ucache, ok := users.(UserCache)
|
||||||
if ok {
|
if ok {
|
||||||
ucache.CacheRemove(user.ID)
|
ucache.CacheRemove(user.ID)
|
||||||
|
@ -95,13 +132,24 @@ func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Durat
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use a transaction to avoid race conditions
|
|
||||||
func (user *User) RevertGroupUpdate() error {
|
func (user *User) RevertGroupUpdate() error {
|
||||||
_, err := replaceScheduleGroupStmt.Exec(user.ID, 0, 0, time.Now(), false, user.ID)
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = setTempGroupStmt.Exec(0, user.ID)
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
err = user.deleteScheduleGroupTx(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = user.setTempGroupTx(tx, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
|
||||||
ucache, ok := users.(UserCache)
|
ucache, ok := users.(UserCache)
|
||||||
if ok {
|
if ok {
|
||||||
ucache.CacheRemove(user.ID)
|
ucache.CacheRemove(user.ID)
|
||||||
|
@ -157,6 +205,15 @@ func (user *User) ChangeAvatar(avatar string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) ChangeGroup(group int) (err error) {
|
||||||
|
_, err = updateUserGroupStmt.Exec(group, user.ID)
|
||||||
|
ucache, ok := users.(UserCache)
|
||||||
|
if ok {
|
||||||
|
ucache.CacheRemove(user.ID)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (user *User) increasePostStats(wcount int, topic bool) error {
|
func (user *User) increasePostStats(wcount int, topic bool) error {
|
||||||
var mod int
|
var mod int
|
||||||
baseScore := 1
|
baseScore := 1
|
||||||
|
|
|
@ -24,7 +24,7 @@ type UserStore interface {
|
||||||
//BulkGet(ids []int) ([]*User, error)
|
//BulkGet(ids []int) ([]*User, error)
|
||||||
BulkGetMap(ids []int) (map[int]*User, error)
|
BulkGetMap(ids []int) (map[int]*User, error)
|
||||||
BypassGet(id int) (*User, error)
|
BypassGet(id int) (*User, error)
|
||||||
Create(username string, password string, email string, group int, active int) (int, error)
|
Create(username string, password string, email string, group int, active bool) (int, error)
|
||||||
GlobalCount() int
|
GlobalCount() int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +124,7 @@ func (mus *MemoryUserStore) Get(id int) (*User, error) {
|
||||||
user = &User{ID: id, Loggedin: true}
|
user = &User{ID: id, Loggedin: true}
|
||||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.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.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||||
|
|
||||||
|
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||||
if user.Avatar != "" {
|
if user.Avatar != "" {
|
||||||
if user.Avatar[0] == '.' {
|
if user.Avatar[0] == '.' {
|
||||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||||
|
@ -202,6 +203,7 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||||
// Initialise the user
|
// Initialise the user
|
||||||
if user.Avatar != "" {
|
if user.Avatar != "" {
|
||||||
if user.Avatar[0] == '.' {
|
if user.Avatar[0] == '.' {
|
||||||
|
@ -253,6 +255,7 @@ func (mus *MemoryUserStore) BypassGet(id int) (*User, error) {
|
||||||
user := &User{ID: id, Loggedin: true}
|
user := &User{ID: id, Loggedin: true}
|
||||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.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.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||||
|
|
||||||
|
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||||
if user.Avatar != "" {
|
if user.Avatar != "" {
|
||||||
if user.Avatar[0] == '.' {
|
if user.Avatar[0] == '.' {
|
||||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||||
|
@ -274,6 +277,7 @@ func (mus *MemoryUserStore) Reload(id int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||||
if user.Avatar != "" {
|
if user.Avatar != "" {
|
||||||
if user.Avatar[0] == '.' {
|
if user.Avatar[0] == '.' {
|
||||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||||
|
@ -357,7 +361,7 @@ func (mus *MemoryUserStore) CacheRemoveUnsafe(id int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Change active to a bool?
|
// TODO: Change active to a bool?
|
||||||
func (mus *MemoryUserStore) Create(username string, password string, email string, group int, active int) (int, error) {
|
func (mus *MemoryUserStore) Create(username string, password string, email string, group int, active bool) (int, error) {
|
||||||
// Is this username already taken..?
|
// Is this username already taken..?
|
||||||
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
||||||
if err != ErrNoRows {
|
if err != ErrNoRows {
|
||||||
|
@ -405,8 +409,7 @@ func (mus *MemoryUserStore) GetCapacity() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalCount returns the total number of users registered on the forums
|
// GlobalCount returns the total number of users registered on the forums
|
||||||
func (mus *MemoryUserStore) GlobalCount() int {
|
func (mus *MemoryUserStore) GlobalCount() (ucount int) {
|
||||||
var ucount int
|
|
||||||
err := mus.userCount.QueryRow().Scan(&ucount)
|
err := mus.userCount.QueryRow().Scan(&ucount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
|
@ -548,7 +551,7 @@ func (mus *SQLUserStore) Exists(id int) bool {
|
||||||
return err != ErrNoRows
|
return err != ErrNoRows
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *SQLUserStore) Create(username string, password string, email string, group int, active int) (int, error) {
|
func (mus *SQLUserStore) Create(username string, password string, email string, group int, active bool) (int, error) {
|
||||||
// Is this username already taken..?
|
// Is this username already taken..?
|
||||||
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
||||||
if err != ErrNoRows {
|
if err != ErrNoRows {
|
||||||
|
@ -575,11 +578,62 @@ func (mus *SQLUserStore) Create(username string, password string, email string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalCount returns the total number of users registered on the forums
|
// GlobalCount returns the total number of users registered on the forums
|
||||||
func (mus *SQLUserStore) GlobalCount() int {
|
func (mus *SQLUserStore) GlobalCount() (ucount int) {
|
||||||
var ucount int
|
|
||||||
err := mus.userCount.QueryRow().Scan(&ucount)
|
err := mus.userCount.QueryRow().Scan(&ucount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return ucount
|
return ucount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: MockUserStore
|
||||||
|
|
||||||
|
// NullUserStore is here for tests because Go doesn't have short-circuiting
|
||||||
|
type NullUserStore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) CacheGet(_ int) (*User, error) {
|
||||||
|
return nil, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) CacheGetUnsafe(_ int) (*User, error) {
|
||||||
|
return nil, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) CacheSet(_ *User) error {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) CacheAdd(_ *User) error {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) CacheAddUnsafe(_ *User) error {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) CacheRemove(_ int) error {
|
||||||
|
return ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) CacheRemoveUnsafe(_ int) error {
|
||||||
|
return ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) Flush() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) Reload(_ int) error {
|
||||||
|
return ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) Length() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) SetCapacity(_ int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nus *NullUserStore) GetCapacity() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue