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
|
||||
var ErrMismatchedHashAndPassword = bcrypt.ErrMismatchedHashAndPassword
|
||||
|
||||
// nolint
|
||||
// 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 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.
|
||||
type Auth interface {
|
||||
|
@ -61,24 +65,24 @@ func (auth *DefaultAuth) Authenticate(username string, password string) (uid int
|
|||
var realPassword, salt string
|
||||
err = auth.login.QueryRow(username).Scan(&uid, &realPassword, &salt)
|
||||
if err == ErrNoRows {
|
||||
return 0, errors.New("We couldn't find an account with that username.") // nolint
|
||||
return 0, ErrNoUserByName
|
||||
} else if err != nil {
|
||||
LogError(err)
|
||||
return 0, errors.New("There was a glitch in the system. Please contact your local administrator.") // nolint
|
||||
return 0, ErrSecretError
|
||||
}
|
||||
|
||||
if salt == "" {
|
||||
// Send an email to admin for this?
|
||||
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)
|
||||
if err == ErrMismatchedHashAndPassword {
|
||||
return 0, errors.New("That's not the correct password.")
|
||||
return 0, ErrWrongPassword
|
||||
} else if err != nil {
|
||||
LogError(err)
|
||||
return 0, errors.New("There was a glitch in the system. Please contact your local administrator.")
|
||||
return 0, ErrSecretError
|
||||
}
|
||||
|
||||
return uid, nil
|
||||
|
@ -89,7 +93,7 @@ func (auth *DefaultAuth) ForceLogout(uid int) error {
|
|||
_, err := auth.logout.Exec(uid)
|
||||
if err != nil {
|
||||
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
|
||||
|
@ -110,6 +114,7 @@ func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: year}
|
||||
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.
|
||||
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.
|
||||
var ErrStoreCapacityOverflow = errors.New("This datastore has reached it's maximum capacity.")
|
||||
// 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.") // nolint
|
||||
|
||||
// nolint
|
||||
type DataStore interface {
|
||||
|
|
|
@ -14,14 +14,18 @@ var errorBuffer []error
|
|||
//var notfoundCountPerSecond 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) {
|
||||
LogWarning(err)
|
||||
log.Fatal("")
|
||||
}
|
||||
|
||||
func LogWarning(err error) {
|
||||
log.Print(err)
|
||||
debug.PrintStack()
|
||||
errorBufferMutex.Lock()
|
||||
defer errorBufferMutex.Unlock()
|
||||
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
|
||||
|
|
|
@ -405,3 +405,5 @@ func (mfs *MemoryForumStore) GlobalCount() (fcount int) {
|
|||
}
|
||||
|
||||
// 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 addPluginStmt *sql.Stmt
|
||||
var addThemeStmt *sql.Stmt
|
||||
var createGroupStmt *sql.Stmt
|
||||
var addModlogEntryStmt *sql.Stmt
|
||||
var addAdminlogEntryStmt *sql.Stmt
|
||||
var addAttachmentStmt *sql.Stmt
|
||||
var createWordFilterStmt *sql.Stmt
|
||||
var addForumPermsToGroupStmt *sql.Stmt
|
||||
var replaceScheduleGroupStmt *sql.Stmt
|
||||
var addRepliesToTopicStmt *sql.Stmt
|
||||
var removeRepliesFromTopicStmt *sql.Stmt
|
||||
var addTopicsToForumStmt *sql.Stmt
|
||||
|
@ -105,6 +102,7 @@ var updatePluginStmt *sql.Stmt
|
|||
var updatePluginInstallStmt *sql.Stmt
|
||||
var updateThemeStmt *sql.Stmt
|
||||
var updateUserStmt *sql.Stmt
|
||||
var updateUserGroupStmt *sql.Stmt
|
||||
var updateGroupPermsStmt *sql.Stmt
|
||||
var updateGroupRankStmt *sql.Stmt
|
||||
var updateGroupStmt *sql.Stmt
|
||||
|
@ -116,15 +114,11 @@ var bumpSyncStmt *sql.Stmt
|
|||
var deleteUserStmt *sql.Stmt
|
||||
var deleteReplyStmt *sql.Stmt
|
||||
var deleteProfileReplyStmt *sql.Stmt
|
||||
var deleteForumPermsByForumStmt *sql.Stmt
|
||||
var deleteActivityStreamMatchStmt *sql.Stmt
|
||||
var deleteWordFilterStmt *sql.Stmt
|
||||
var reportExistsStmt *sql.Stmt
|
||||
var groupCountStmt *sql.Stmt
|
||||
var modlogCountStmt *sql.Stmt
|
||||
var addForumPermsToForumAdminsStmt *sql.Stmt
|
||||
var addForumPermsToForumStaffStmt *sql.Stmt
|
||||
var addForumPermsToForumMembersStmt *sql.Stmt
|
||||
var notifyWatchersStmt *sql.Stmt
|
||||
|
||||
// nolint
|
||||
|
@ -365,9 +359,9 @@ func _gen_mssql() (err error) {
|
|||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -539,13 +533,6 @@ func _gen_mssql() (err error) {
|
|||
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.")
|
||||
addModlogEntryStmt, err = db.Prepare("INSERT INTO [moderation_logs] ([action],[elementID],[elementType],[ipaddress],[actorID],[doneAt]) VALUES (?,?,?,?,?,GETUTCDATE())")
|
||||
if err != nil {
|
||||
|
@ -574,20 +561,6 @@ func _gen_mssql() (err error) {
|
|||
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.")
|
||||
addRepliesToTopicStmt, err = db.Prepare("UPDATE [topics] SET [postCount] = [postCount] + ?,[lastReplyBy] = ?,[lastReplyAt] = GETUTCDATE() WHERE [tid] = ?")
|
||||
if err != nil {
|
||||
|
@ -819,6 +792,13 @@ func _gen_mssql() (err error) {
|
|||
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.")
|
||||
updateGroupPermsStmt, err = db.Prepare("UPDATE [users_groups] SET [permissions] = ? WHERE [gid] = ?")
|
||||
if err != nil {
|
||||
|
@ -896,13 +876,6 @@ func _gen_mssql() (err error) {
|
|||
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.")
|
||||
deleteActivityStreamMatchStmt, err = db.Prepare("DELETE FROM [activity_stream_matches] WHERE [watcher] = ? AND [asid] = ?")
|
||||
if err != nil {
|
||||
|
@ -938,27 +911,6 @@ func _gen_mssql() (err error) {
|
|||
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.")
|
||||
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 {
|
||||
|
|
58
gen_mysql.go
58
gen_mysql.go
|
@ -6,7 +6,7 @@ package main
|
|||
|
||||
import "log"
|
||||
import "database/sql"
|
||||
import "./query_gen/lib"
|
||||
//import "./query_gen/lib"
|
||||
|
||||
// nolint
|
||||
var getUserStmt *sql.Stmt
|
||||
|
@ -67,13 +67,10 @@ var createForumStmt *sql.Stmt
|
|||
var addForumPermsToForumStmt *sql.Stmt
|
||||
var addPluginStmt *sql.Stmt
|
||||
var addThemeStmt *sql.Stmt
|
||||
var createGroupStmt *sql.Stmt
|
||||
var addModlogEntryStmt *sql.Stmt
|
||||
var addAdminlogEntryStmt *sql.Stmt
|
||||
var addAttachmentStmt *sql.Stmt
|
||||
var createWordFilterStmt *sql.Stmt
|
||||
var addForumPermsToGroupStmt *qgen.MySQLUpsertCallback
|
||||
var replaceScheduleGroupStmt *qgen.MySQLUpsertCallback
|
||||
var addRepliesToTopicStmt *sql.Stmt
|
||||
var removeRepliesFromTopicStmt *sql.Stmt
|
||||
var addTopicsToForumStmt *sql.Stmt
|
||||
|
@ -107,6 +104,7 @@ var updatePluginStmt *sql.Stmt
|
|||
var updatePluginInstallStmt *sql.Stmt
|
||||
var updateThemeStmt *sql.Stmt
|
||||
var updateUserStmt *sql.Stmt
|
||||
var updateUserGroupStmt *sql.Stmt
|
||||
var updateGroupPermsStmt *sql.Stmt
|
||||
var updateGroupRankStmt *sql.Stmt
|
||||
var updateGroupStmt *sql.Stmt
|
||||
|
@ -118,15 +116,11 @@ var bumpSyncStmt *sql.Stmt
|
|||
var deleteUserStmt *sql.Stmt
|
||||
var deleteReplyStmt *sql.Stmt
|
||||
var deleteProfileReplyStmt *sql.Stmt
|
||||
var deleteForumPermsByForumStmt *sql.Stmt
|
||||
var deleteActivityStreamMatchStmt *sql.Stmt
|
||||
var deleteWordFilterStmt *sql.Stmt
|
||||
var reportExistsStmt *sql.Stmt
|
||||
var groupCountStmt *sql.Stmt
|
||||
var modlogCountStmt *sql.Stmt
|
||||
var addForumPermsToForumAdminsStmt *sql.Stmt
|
||||
var addForumPermsToForumStaffStmt *sql.Stmt
|
||||
var addForumPermsToForumMembersStmt *sql.Stmt
|
||||
var notifyWatchersStmt *sql.Stmt
|
||||
|
||||
// nolint
|
||||
|
@ -483,12 +477,6 @@ func _gen_mysql() (err error) {
|
|||
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.")
|
||||
addModlogEntryStmt, err = db.Prepare("INSERT INTO `moderation_logs`(`action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt`) VALUES (?,?,?,?,?,UTC_TIMESTAMP())")
|
||||
if err != nil {
|
||||
|
@ -513,18 +501,6 @@ func _gen_mysql() (err error) {
|
|||
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.")
|
||||
addRepliesToTopicStmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyBy` = ?,`lastReplyAt` = UTC_TIMESTAMP() WHERE `tid` = ?")
|
||||
if err != nil {
|
||||
|
@ -723,6 +699,12 @@ func _gen_mysql() (err error) {
|
|||
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.")
|
||||
updateGroupPermsStmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ?")
|
||||
if err != nil {
|
||||
|
@ -789,12 +771,6 @@ func _gen_mysql() (err error) {
|
|||
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.")
|
||||
deleteActivityStreamMatchStmt, err = db.Prepare("DELETE FROM `activity_stream_matches` WHERE `watcher` = ? AND `asid` = ?")
|
||||
if err != nil {
|
||||
|
@ -825,24 +801,6 @@ func _gen_mysql() (err error) {
|
|||
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.")
|
||||
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 {
|
||||
|
|
|
@ -40,6 +40,7 @@ var updatePluginStmt *sql.Stmt
|
|||
var updatePluginInstallStmt *sql.Stmt
|
||||
var updateThemeStmt *sql.Stmt
|
||||
var updateUserStmt *sql.Stmt
|
||||
var updateUserGroupStmt *sql.Stmt
|
||||
var updateGroupPermsStmt *sql.Stmt
|
||||
var updateGroupRankStmt *sql.Stmt
|
||||
var updateGroupStmt *sql.Stmt
|
||||
|
@ -253,6 +254,12 @@ func _gen_pgsql() (err error) {
|
|||
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.")
|
||||
updateGroupPermsStmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ?")
|
||||
if err != nil {
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
package main
|
||||
|
||||
var dbTablePrimaryKeys = map[string]string{
|
||||
"forums":"fid",
|
||||
"topics":"tid",
|
||||
"attachments":"attachID",
|
||||
"users_replies":"rid",
|
||||
"word_filters":"wfid",
|
||||
"users":"uid",
|
||||
"users_groups":"gid",
|
||||
"users_groups_scheduler":"uid",
|
||||
"replies":"rid",
|
||||
"topics":"tid",
|
||||
"users_replies":"rid",
|
||||
"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
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Copy gives you a non-pointer concurrency safe copy of the group
|
||||
func (group *Group) Copy() Group {
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"errors"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
var groupCreateMutex sync.Mutex
|
||||
|
@ -19,7 +21,7 @@ type GroupStore interface {
|
|||
Get(id int) (*Group, error)
|
||||
GetCopy(id int) (Group, error)
|
||||
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)
|
||||
GetRange(lower int, higher int) ([]*Group, error)
|
||||
}
|
||||
|
@ -71,8 +73,11 @@ func (mgs *MemoryGroupStore) LoadGroups() error {
|
|||
log.Print(group.Name + ": ")
|
||||
log.Printf("%+v\n", group.PluginPerms)
|
||||
}
|
||||
|
||||
//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)
|
||||
}
|
||||
err = rows.Err()
|
||||
|
@ -110,15 +115,26 @@ func (mgs *MemoryGroupStore) GetCopy(gid int) (Group, error) {
|
|||
}
|
||||
|
||||
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()
|
||||
defer groupCreateMutex.Unlock()
|
||||
|
||||
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 {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -138,14 +154,14 @@ func (mgs *MemoryGroupStore) Create(groupName string, tag string, isAdmin bool,
|
|||
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...
|
||||
fdata, err := fstore.GetAll()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var presetSet = make(map[int]string)
|
||||
var permSet = make(map[int]ForumPerms)
|
||||
permUpdateMutex.Lock()
|
||||
defer permUpdateMutex.Unlock()
|
||||
for _, forum := range fdata {
|
||||
|
@ -161,23 +177,38 @@ func (mgs *MemoryGroupStore) Create(groupName string, tag string, isAdmin bool,
|
|||
}
|
||||
|
||||
permmap := presetToPermmap(forum.Preset)
|
||||
permitem := permmap[thePreset]
|
||||
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
|
||||
permItem := permmap[thePreset]
|
||||
permItem.Overrides = true
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return gid, err
|
||||
}
|
||||
}
|
||||
|
||||
return gid, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ func (ins *MysqlInstaller) InitDatabase() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ins *MysqlInstaller) TableDefs() error {
|
||||
func (ins *MysqlInstaller) TableDefs() (err error) {
|
||||
//fmt.Println("Creating the tables")
|
||||
files, _ := ioutil.ReadDir("./schema/mysql/")
|
||||
for _, f := range files {
|
||||
|
@ -115,6 +115,13 @@ func (ins *MysqlInstaller) TableDefs() error {
|
|||
}
|
||||
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 + "'")
|
||||
data, err := ioutil.ReadFile("./schema/mysql/" + f.Name())
|
||||
if err != nil {
|
||||
|
|
17
main.go
17
main.go
|
@ -55,6 +55,12 @@ var allowedFileExts = StringList{
|
|||
var imageFileExts = StringList{
|
||||
"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
|
||||
func (slice StringList) Contains(needle string) bool {
|
||||
|
@ -70,6 +76,16 @@ var staticFiles = make(map[string]SFile)
|
|||
var logWriter = io.MultiWriter(os.Stderr)
|
||||
|
||||
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: Log panics with recover()
|
||||
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: Publish scheduled posts.
|
||||
// TODO: Delete the empty users_groups_scheduler entries
|
||||
|
||||
// TODO: Add a plugin hook here
|
||||
}
|
||||
|
|
497
misc_test.go
497
misc_test.go
|
@ -1,24 +1,26 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func recordMustExist(t *testing.T, err error, errmsg string) {
|
||||
func recordMustExist(t *testing.T, err error, errmsg string, args ...interface{}) {
|
||||
if err == ErrNoRows {
|
||||
t.Error(errmsg)
|
||||
t.Errorf(errmsg, args...)
|
||||
} else if err != nil {
|
||||
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 {
|
||||
t.Error(errmsg)
|
||||
t.Errorf(errmsg, args...)
|
||||
} else if err != ErrNoRows {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -42,50 +44,42 @@ func TestUserStore(t *testing.T) {
|
|||
userStoreTest(t, 3)
|
||||
}
|
||||
func userStoreTest(t *testing.T, newUserID int) {
|
||||
var user *User
|
||||
var err error
|
||||
|
||||
ucache, hasCache := users.(UserCache)
|
||||
if hasCache && ucache.Length() != 0 {
|
||||
t.Error("Initial ucache length isn't zero")
|
||||
// Go doesn't have short-circuiting, so this'll allow us to do one liner tests
|
||||
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")
|
||||
|
||||
if hasCache && ucache.Length() != 0 {
|
||||
t.Error("There shouldn't be anything in the user cache")
|
||||
}
|
||||
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()))
|
||||
|
||||
_, err = users.Get(0)
|
||||
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 {
|
||||
t.Error("There shouldn't be anything in the user cache")
|
||||
}
|
||||
|
||||
user, err = users.Get(1)
|
||||
user, err := users.Get(1)
|
||||
recordMustExist(t, err, "Couldn't find UID #1")
|
||||
|
||||
if user.ID != 1 {
|
||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
||||
}
|
||||
if user.Name != "Admin" {
|
||||
t.Error("user.Name should be 'Admin', not '" + user.Name + "'")
|
||||
}
|
||||
if user.Group != 1 {
|
||||
t.Error("Admin should be in group 1")
|
||||
}
|
||||
expect(t, user.ID == 1, fmt.Sprintf("user.ID should be 1. Got '%d' instead.", user.ID))
|
||||
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")
|
||||
expect(t, user.IsSuperAdmin, "Admin should be a super admin")
|
||||
expect(t, user.IsAdmin, "Admin should be an admin")
|
||||
expect(t, user.IsSuperMod, "Admin should be a super mod")
|
||||
expect(t, user.IsMod, "Admin should be a mod")
|
||||
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))
|
||||
|
||||
if hasCache {
|
||||
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")
|
||||
user, err = ucache.CacheGet(0)
|
||||
_, err = ucache.CacheGet(0)
|
||||
recordMustNotExist(t, err, "UID #0 shouldn't exist, even in the cache")
|
||||
user, err = ucache.CacheGet(1)
|
||||
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 + "'")
|
||||
}
|
||||
|
||||
user, err = ucache.CacheGet(newUserID)
|
||||
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist, even in the cache", newUserID))
|
||||
_, err = ucache.CacheGet(newUserID)
|
||||
recordMustNotExist(t, err, "UID #%d shouldn't exist, even in the cache", newUserID)
|
||||
|
||||
ucache.Flush()
|
||||
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")
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -158,120 +151,281 @@ func userStoreTest(t *testing.T, newUserID int) {
|
|||
expect(t, users.Exists(1), "UID #1 should exist")
|
||||
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
||||
|
||||
if hasCache {
|
||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||
}
|
||||
expect(t, !hasCache || ucache.Length() == 0, fmt.Sprintf("User cache length should be 0, not %d", ucache.Length()))
|
||||
expectIntToBeX(t, users.GlobalCount(), 1, "The number of users should be one, not %d")
|
||||
|
||||
var awaitingActivation = 5
|
||||
uid, err := users.Create("Sam", "ReallyBadPassword", "sam@localhost.loc", awaitingActivation, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
uid, err := users.Create("Sam", "ReallyBadPassword", "sam@localhost.loc", awaitingActivation, false)
|
||||
expectNilErr(t, 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))
|
||||
|
||||
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)
|
||||
}
|
||||
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")
|
||||
|
||||
if hasCache {
|
||||
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
||||
user, err = ucache.CacheGet(newUserID)
|
||||
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d in the cache", newUserID))
|
||||
if user.ID != newUserID {
|
||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
||||
}
|
||||
recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID)
|
||||
expect(t, user.ID == newUserID, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||
}
|
||||
|
||||
err = user.Activate()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expectNilErr(t, err)
|
||||
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
|
||||
if hasCache {
|
||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||
_, 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)
|
||||
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
|
||||
if user.ID != newUserID {
|
||||
t.Errorf("The UID of the user record should be %d", newUserID)
|
||||
}
|
||||
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should be in group "+strconv.Itoa(config.DefaultGroup))
|
||||
recordMustExist(t, err, "Couldn't find UID #%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")
|
||||
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
|
||||
duration, _ := time.ParseDuration("0")
|
||||
|
||||
// TODO: Attempt a double ban, double activation, and double unban
|
||||
err = user.Ban(duration, 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should still be in the default group in this copy")
|
||||
expectNilErr(t, err)
|
||||
expect(t, user.Group == config.DefaultGroup, fmt.Sprintf("Sam should be in group %d, not %d", config.DefaultGroup, user.Group))
|
||||
|
||||
if hasCache {
|
||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||
_, 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)
|
||||
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)
|
||||
}
|
||||
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
|
||||
|
||||
err = user.Unban()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expectNilErr(t, err)
|
||||
expectIntToBeX(t, user.Group, banGroup, "Sam should still be in the ban group in this copy")
|
||||
|
||||
if hasCache {
|
||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||
_, 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)
|
||||
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
|
||||
if user.ID != newUserID {
|
||||
t.Errorf("The UID of the user record should be %d", newUserID)
|
||||
}
|
||||
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should be back in group "+strconv.Itoa(config.DefaultGroup))
|
||||
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||
expectIntToBeX(t, user.ID, newUserID, "The UID of the user record should be %d")
|
||||
|
||||
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, 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()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d should not longer exist", newUserID))
|
||||
expectNilErr(t, err)
|
||||
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d should no longer exist", newUserID))
|
||||
|
||||
if hasCache {
|
||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||
_, 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
|
||||
//ResetTables()
|
||||
_, err = users.Get(newUserID)
|
||||
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) {
|
||||
|
@ -284,7 +438,7 @@ func expectIntToBeX(t *testing.T, item int, expect int, errmsg string) {
|
|||
func expect(t *testing.T, item bool, errmsg string) {
|
||||
if !item {
|
||||
debug.PrintStack()
|
||||
t.Fatalf(errmsg)
|
||||
t.Fatal(errmsg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,18 +463,10 @@ func topicStoreTest(t *testing.T) {
|
|||
var err error
|
||||
|
||||
_, err = topics.Get(-1)
|
||||
if err == nil {
|
||||
t.Error("TID #-1 shouldn't exist")
|
||||
} else if err != ErrNoRows {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recordMustNotExist(t, err, "TID #-1 shouldn't exist")
|
||||
|
||||
_, err = topics.Get(0)
|
||||
if err == nil {
|
||||
t.Error("TID #0 shouldn't exist")
|
||||
} else if err != ErrNoRows {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recordMustNotExist(t, err, "TID #0 shouldn't exist")
|
||||
|
||||
topic, err = topics.Get(1)
|
||||
recordMustExist(t, err, "Couldn't find TID #1")
|
||||
|
@ -361,24 +507,13 @@ func TestForumStore(t *testing.T) {
|
|||
initPlugins()
|
||||
}
|
||||
|
||||
var forum *Forum
|
||||
var err error
|
||||
_, err := fstore.Get(-1)
|
||||
recordMustNotExist(t, err, "FID #-1 shouldn't exist")
|
||||
|
||||
_, err = fstore.Get(-1)
|
||||
if err == nil {
|
||||
t.Error("FID #-1 shouldn't exist")
|
||||
} else if err != ErrNoRows {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = fstore.Get(0)
|
||||
recordMustNotExist(t, err, "FID #0 shouldn't exist")
|
||||
|
||||
forum, err = fstore.Get(0)
|
||||
if err == nil {
|
||||
t.Error("FID #0 shouldn't exist")
|
||||
} else if err != ErrNoRows {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
forum, err = fstore.Get(1)
|
||||
forum, err := fstore.Get(1)
|
||||
recordMustExist(t, err, "Couldn't find FID #1")
|
||||
|
||||
if forum.ID != 1 {
|
||||
|
@ -417,50 +552,95 @@ func TestGroupStore(t *testing.T) {
|
|||
initPlugins()
|
||||
}
|
||||
|
||||
var group *Group
|
||||
var err error
|
||||
|
||||
_, err = gstore.Get(-1)
|
||||
if err == nil {
|
||||
t.Error("GID #-1 shouldn't exist")
|
||||
} else if err != ErrNoRows {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := gstore.Get(-1)
|
||||
recordMustNotExist(t, err, "GID #-1 shouldn't exist")
|
||||
|
||||
// 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")
|
||||
|
||||
if group.ID != 0 {
|
||||
t.Error("group.ID doesn't not match the requested GID. Got '" + strconv.Itoa(group.ID) + "' instead.")
|
||||
}
|
||||
if group.Name != "Unknown" {
|
||||
t.Error("GID #0 is named '" + group.Name + "' and not 'Unknown'")
|
||||
t.Errorf("group.ID doesn't not match the requested GID. Got '%d' instead.", group.ID)
|
||||
}
|
||||
expect(t, group.Name == "Unknown", fmt.Sprintf("GID #0 is named '%s' and not 'Unknown'", group.Name))
|
||||
|
||||
group, err = gstore.Get(1)
|
||||
recordMustExist(t, err, "Couldn't find GID #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)
|
||||
if ok {
|
||||
t.Error("GID #-1 shouldn't exist")
|
||||
}
|
||||
expect(t, !ok, "GID #-1 shouldn't exist")
|
||||
|
||||
// 0 aka Unknown, for system posts and other oddities
|
||||
ok = gstore.Exists(0)
|
||||
if !ok {
|
||||
t.Error("GID #0 should exist")
|
||||
}
|
||||
expect(t, ok, "GID #0 should exist")
|
||||
|
||||
ok = gstore.Exists(1)
|
||||
if !ok {
|
||||
t.Error("GID #1 should exist")
|
||||
}
|
||||
expect(t, ok, "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) {
|
||||
|
@ -471,28 +651,23 @@ func TestReplyStore(t *testing.T) {
|
|||
initPlugins()
|
||||
}
|
||||
|
||||
reply, err := rstore.Get(-1)
|
||||
if err == nil {
|
||||
t.Error("RID #-1 shouldn't exist")
|
||||
}
|
||||
_, err := rstore.Get(-1)
|
||||
recordMustNotExist(t, err, "RID #-1 shouldn't exist")
|
||||
|
||||
reply, err = rstore.Get(0)
|
||||
if err == nil {
|
||||
t.Error("RID #0 shouldn't exist")
|
||||
}
|
||||
_, err = rstore.Get(0)
|
||||
recordMustNotExist(t, err, "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 {
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
if err == nil {
|
||||
t.Error("RID #-1 shouldn't exist")
|
||||
}
|
||||
recordMustNotExist(t, err, "RID #-1 shouldn't exist")
|
||||
|
||||
_, err = prstore.Get(0)
|
||||
if err == nil {
|
||||
t.Error("RID #0 shouldn't exist")
|
||||
}
|
||||
recordMustNotExist(t, err, "RID #0 shouldn't exist")
|
||||
}
|
||||
|
||||
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? */
|
||||
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")
|
||||
hashedPassword, err = BcryptGeneratePasswordNoSalt(realPassword)
|
||||
if err != nil {
|
||||
|
|
|
@ -819,7 +819,10 @@ func routeUnban(w http.ResponseWriter, r *http.Request, user User) {
|
|||
}
|
||||
|
||||
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)
|
||||
return
|
||||
} 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, ":p", "😛", -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>")
|
||||
|
||||
// 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 i = 0
|
||||
for ; len(msgbytes) > (i + 1); i++ {
|
||||
//log.Print("Index:",i)
|
||||
//log.Print("Index: ",i)
|
||||
//log.Print("Index Item: ",msgbytes[i])
|
||||
//log.Print("string(msgbytes[i]): ",string(msgbytes[i]))
|
||||
//log.Print("End Index")
|
||||
if (i == 0 && (msgbytes[0] > 32)) || ((msgbytes[i] < 33) && (msgbytes[i+1] > 32)) {
|
||||
//log.Print("IN")
|
||||
//log.Print(msgbytes[i])
|
||||
//log.Print("IN ",msgbytes[i])
|
||||
if (i != 0) || msgbytes[i] < 33 {
|
||||
i++
|
||||
}
|
||||
|
@ -509,12 +509,12 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
outbytes = append(outbytes, urlClose...)
|
||||
lastItem = i
|
||||
|
||||
//log.Print("string(msgbytes) ",string(msgbytes))
|
||||
//log.Print("msgbytes ",msgbytes)
|
||||
//log.Print(msgbytes[lastItem - 1])
|
||||
//log.Print(lastItem - 1)
|
||||
//log.Print(msgbytes[lastItem])
|
||||
//log.Print("lastItem ",lastItem)
|
||||
//log.Print("string(msgbytes): ",string(msgbytes))
|
||||
//log.Print("msgbytes: ",msgbytes)
|
||||
//log.Print("msgbytes[lastItem - 1]: ",msgbytes[lastItem - 1])
|
||||
//log.Print("lastItem - 1: ",lastItem - 1)
|
||||
//log.Print("msgbytes[lastItem]: ",msgbytes[lastItem])
|
||||
//log.Print("lastItem: ",lastItem)
|
||||
} else if bytes.Equal(msgbytes[i+1:i+5], []byte("rid-")) {
|
||||
outbytes = append(outbytes, msgbytes[lastItem:i]...)
|
||||
i += 5
|
||||
|
@ -589,10 +589,10 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
|
||||
//log.Print(string(msgbytes))
|
||||
//log.Print(msgbytes)
|
||||
//log.Print(msgbytes[lastItem - 1])
|
||||
//log.Print("lastItem - 1",lastItem - 1)
|
||||
//log.Print("msgbytes[lastItem]",msgbytes[lastItem])
|
||||
//log.Print("lastItem",lastItem)
|
||||
//log.Print("msgbytes[lastItem - 1]: ", msgbytes[lastItem - 1])
|
||||
//log.Print("lastItem - 1: ", lastItem - 1)
|
||||
//log.Print("msgbytes[lastItem]: ", msgbytes[lastItem])
|
||||
//log.Print("lastItem: ", lastItem)
|
||||
} else if msgbytes[i] == 'h' || msgbytes[i] == 'f' || msgbytes[i] == 'g' {
|
||||
//log.Print("IN hfg")
|
||||
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:])
|
||||
if msgbytes[i+urlLen] > 32 { // space and invisibles
|
||||
//log.Print("INVALID URL")
|
||||
//log.Print("msgbytes[i+urlLen]", 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("string(msgbytes[i:i+urlLen])", string(msgbytes[i:i+urlLen]))
|
||||
//log.Print("msgbytes[i+urlLen]: ", 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("string(msgbytes[i:i+urlLen]): ", string(msgbytes[i:i+urlLen]))
|
||||
outbytes = append(outbytes, invalidURL...)
|
||||
i += urlLen
|
||||
continue
|
||||
|
@ -673,18 +673,18 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
urlLen := partialURLBytesLen(msgbytes[i:])
|
||||
if msgbytes[i+urlLen] > 32 { // space and invisibles
|
||||
//log.Print("INVALID URL")
|
||||
//log.Print("msgbytes[i+urlLen]", 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("string(msgbytes[i:i+urlLen])", string(msgbytes[i:i+urlLen]))
|
||||
//log.Print("msgbytes[i+urlLen]: ", 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("string(msgbytes[i:i+urlLen]): ", string(msgbytes[i:i+urlLen]))
|
||||
outbytes = append(outbytes, invalidURL...)
|
||||
i += urlLen
|
||||
continue
|
||||
}
|
||||
|
||||
//log.Print("VALID URL")
|
||||
//log.Print("msgbytes[i:i+urlLen]", msgbytes[i:i+urlLen])
|
||||
//log.Print("string(msgbytes[i:i+urlLen])", string(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]))
|
||||
media, ok := parseMediaBytes(msgbytes[i : i+urlLen])
|
||||
if !ok {
|
||||
outbytes = append(outbytes, invalidURL...)
|
||||
|
@ -733,10 +733,10 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
}
|
||||
|
||||
if lastItem != i && len(outbytes) != 0 {
|
||||
//log.Print("lastItem: ",msgbytes[lastItem])
|
||||
//log.Print("lastItem index: ",lastItem)
|
||||
//log.Print("i: ",i)
|
||||
//log.Print("lastItem to end: ",msgbytes[lastItem:])
|
||||
//log.Print("lastItem: ", msgbytes[lastItem])
|
||||
//log.Print("lastItem index: ", lastItem)
|
||||
//log.Print("i: ", i)
|
||||
//log.Print("lastItem to end: ", msgbytes[lastItem:])
|
||||
//log.Print("-----")
|
||||
calclen := len(msgbytes) - 10
|
||||
if calclen <= lastItem {
|
||||
|
@ -756,7 +756,7 @@ func parseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
}
|
||||
|
||||
// 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, ":D", "😃", -1)
|
||||
msg = strings.Replace(msg, ":P", "😛", -1)
|
||||
|
@ -766,7 +766,7 @@ func regexParseMessage(msg string) string {
|
|||
msg = runSshook("parse_assign", msg)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
}*/
|
||||
|
||||
// 6, 7, 8, 6, 2, 7
|
||||
// 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) {
|
||||
_, ok := SimplePanelUserCheck(w, r, &user)
|
||||
if !ok {
|
||||
|
@ -509,24 +508,24 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
|
|||
}
|
||||
group.Forums[fid] = fperms
|
||||
|
||||
perms, err := json.Marshal(fperms)
|
||||
if err != nil {
|
||||
InternalErrorJSQ(err, w, r, isJs)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = addForumPermsToGroupStmt.Exec(gid, fid, permPreset, perms)
|
||||
err = replaceForumPermsForGroup(gid, map[int]string{fid: permPreset}, map[int]ForumPerms{fid: fperms})
|
||||
if err != nil {
|
||||
InternalErrorJSQ(err, w, r, isJs)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Add this and replaceForumPermsForGroup into a transaction?
|
||||
_, err = updateForumStmt.Exec(forum.Name, forum.Desc, forum.Active, "", fid)
|
||||
if err != nil {
|
||||
InternalErrorJSQ(err, w, r, isJs)
|
||||
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 {
|
||||
|
@ -1347,7 +1346,7 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user User) {
|
|||
perPage := 9
|
||||
offset, page, lastPage := pageOffset(stats.Groups, page, perPage)
|
||||
|
||||
// Skip the System group
|
||||
// Skip the 'Unknown' group
|
||||
offset++
|
||||
|
||||
var count int
|
||||
|
@ -1436,15 +1435,16 @@ func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user User, sgi
|
|||
}
|
||||
|
||||
var rank string
|
||||
if group.IsAdmin {
|
||||
switch {
|
||||
case group.IsAdmin:
|
||||
rank = "Admin"
|
||||
} else if group.IsMod {
|
||||
case group.IsMod:
|
||||
rank = "Mod"
|
||||
} else if group.IsBanned {
|
||||
case group.IsBanned:
|
||||
rank = "Banned"
|
||||
} else if group.ID == 6 {
|
||||
case group.ID == 6:
|
||||
rank = "Guest"
|
||||
} else {
|
||||
default:
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = updateGroupRankStmt.Exec(1, 1, 0, gid)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
group.IsAdmin = true
|
||||
group.IsMod = true
|
||||
group.IsBanned = false
|
||||
err = group.ChangeRank(true, true, false)
|
||||
case "Mod":
|
||||
if !user.Perms.EditGroupSuperMod {
|
||||
LocalError("You need the EditGroupSuperMod permission to designate this group as a super-mod group.", w, r, user)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = updateGroupRankStmt.Exec(0, 1, 0, gid)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
group.IsAdmin = false
|
||||
group.IsMod = true
|
||||
group.IsBanned = false
|
||||
err = group.ChangeRank(false, true, false)
|
||||
case "Banned":
|
||||
_, err = updateGroupRankStmt.Exec(0, 0, 1, gid)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
group.IsAdmin = false
|
||||
group.IsMod = false
|
||||
group.IsBanned = true
|
||||
err = group.ChangeRank(false, false, true)
|
||||
case "Guest":
|
||||
LocalError("You can't designate a group as a guest group.", w, r, user)
|
||||
return
|
||||
case "Member":
|
||||
_, err = updateGroupRankStmt.Exec(0, 0, 0, gid)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
group.IsAdmin = false
|
||||
group.IsMod = false
|
||||
group.IsBanned = false
|
||||
err = group.ChangeRank(false, false, false)
|
||||
default:
|
||||
LocalError("Invalid group type.", w, r, user)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = updateGroupStmt.Exec(gname, gtag, gid)
|
||||
|
|
116
permissions.go
116
permissions.go
|
@ -1,9 +1,16 @@
|
|||
package main
|
||||
|
||||
import "log"
|
||||
import "sync"
|
||||
import "strconv"
|
||||
import "encoding/json"
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
// TODO: Refactor the perms system
|
||||
|
||||
var permUpdateMutex sync.Mutex
|
||||
var BlankPerms Perms
|
||||
|
@ -261,10 +268,18 @@ func presetToPermmap(preset string) (out map[string]ForumPerms) {
|
|||
}
|
||||
|
||||
func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
||||
permUpdateMutex.Lock()
|
||||
defer permUpdateMutex.Unlock()
|
||||
tx, err := db.Begin()
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -273,7 +288,16 @@ func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
|||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -282,7 +306,15 @@ func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
|||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -291,23 +323,77 @@ func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
|||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
perms, err = json.Marshal(permmap["guests"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = addForumPermsToGroupStmt.Exec(6, fid, "", perms)
|
||||
// 6 is the ID of the Not Loggedin Group
|
||||
// TODO: Use a shared variable rather than a literal for the group ID
|
||||
err = replaceForumPermsForGroupTx(tx, 6, map[int]string{fid: ""}, map[int]ForumPerms{fid: permmap["guests"]})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
permUpdateMutex.Lock()
|
||||
defer permUpdateMutex.Unlock()
|
||||
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?
|
||||
func rebuildForumPermissions(fid int) error {
|
||||
if dev.DebugMode {
|
||||
|
|
|
@ -2,30 +2,27 @@ package main
|
|||
|
||||
//import "fmt"
|
||||
import (
|
||||
"regexp"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings
|
||||
var markdownUnclosedElement []byte
|
||||
|
||||
var markdownBoldTagOpen, markdownBoldTagClose []byte
|
||||
var markdownItalicTagOpen, markdownItalicTagClose []byte
|
||||
var markdownUnderlineTagOpen, markdownUnderlineTagClose []byte
|
||||
var markdownStrikeTagOpen, markdownStrikeTagClose []byte
|
||||
|
||||
var markdownBoldItalic *regexp.Regexp
|
||||
var markdownBold *regexp.Regexp
|
||||
var markdownItalic *regexp.Regexp
|
||||
var markdownStrike *regexp.Regexp
|
||||
var markdownUnderline *regexp.Regexp
|
||||
var markdownBoldTagOpen []byte
|
||||
var markdownBoldTagClose []byte
|
||||
var markdownItalicTagOpen []byte
|
||||
var markdownItalicTagClose []byte
|
||||
var markdownUnderlineTagOpen []byte
|
||||
var markdownUnderlineTagClose []byte
|
||||
var markdownStrikeTagOpen []byte
|
||||
var markdownStrikeTagClose []byte
|
||||
|
||||
func init() {
|
||||
plugins["markdown"] = NewPlugin("markdown", "Markdown", "Azareal", "http://github.com/Azareal", "", "", "", initMarkdown, nil, deactivateMarkdown, nil, nil)
|
||||
}
|
||||
|
||||
func initMarkdown() error {
|
||||
//plugins["markdown"].AddHook("parse_assign", markdownRegexParse)
|
||||
plugins["markdown"].AddHook("parse_assign", markdownParse)
|
||||
|
||||
markdownUnclosedElement = []byte("<span style='color: red;'>[Unclosed Element]</span>")
|
||||
|
@ -38,35 +35,19 @@ func initMarkdown() error {
|
|||
markdownUnderlineTagClose = []byte("</u>")
|
||||
markdownStrikeTagOpen = []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
|
||||
}
|
||||
|
||||
func deactivateMarkdown() {
|
||||
//plugins["markdown"].RemoveHook("parse_assign", markdownRegexParse)
|
||||
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.
|
||||
// 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 {
|
||||
return strings.TrimSpace(_markdownParse(msg+" ", 0))
|
||||
msg = strings.TrimSpace(_markdownParse(msg+" ", 0))
|
||||
log.Print("final msg: ", msg)
|
||||
return msg
|
||||
}
|
||||
|
||||
// Under Construction!
|
||||
|
@ -77,16 +58,14 @@ func _markdownParse(msg string, n int) string {
|
|||
|
||||
var outbytes []byte
|
||||
var lastElement int
|
||||
//log.Print("enter message loop")
|
||||
//log.Print("Message: %v\n",strings.Replace(msg,"\r","\\r",-1))
|
||||
log.Printf("Initial Message: %+v\n", strings.Replace(msg, "\r", "\\r", -1))
|
||||
|
||||
for index := 0; index < len(msg); index++ {
|
||||
/*//log.Print("--OUTER MARKDOWN LOOP START--")
|
||||
//log.Print("index",index)
|
||||
//log.Print("msg[index]",msg[index])
|
||||
//log.Print("string(msg[index])",string(msg[index]))
|
||||
//log.Print("--OUTER MARKDOWN LOOP END--")
|
||||
//log.Print(" ")*/
|
||||
//log.Print("--OUTER MARKDOWN LOOP START--")
|
||||
//log.Print("index: ", index)
|
||||
//log.Print("msg[index]: ", msg[index])
|
||||
//log.Print("string(msg[index]): ", string(msg[index]))
|
||||
//log.Printf("--OUTER MARKDOWN LOOP END--\n\n")
|
||||
|
||||
switch msg[index] {
|
||||
// TODO: Do something slightly less hacky for skipping URLs
|
||||
|
@ -106,7 +85,7 @@ func _markdownParse(msg string, n int) string {
|
|||
|
||||
index++
|
||||
index = markdownSkipUntilChar(msg, index, '_')
|
||||
if (index-(startIndex+1)) < 2 || index >= len(msg) {
|
||||
if (index-(startIndex+1)) < 1 || index >= len(msg) {
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -116,7 +95,8 @@ func _markdownParse(msg string, n int) string {
|
|||
|
||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||
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...)
|
||||
|
||||
lastElement = index
|
||||
|
@ -129,7 +109,7 @@ func _markdownParse(msg string, n int) string {
|
|||
|
||||
index++
|
||||
index = markdownSkipUntilChar(msg, index, '~')
|
||||
if (index-(startIndex+1)) < 2 || index >= len(msg) {
|
||||
if (index-(startIndex+1)) < 1 || index >= len(msg) {
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -139,28 +119,28 @@ func _markdownParse(msg string, n int) string {
|
|||
|
||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||
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...)
|
||||
|
||||
lastElement = index
|
||||
index--
|
||||
case '*':
|
||||
//log.Print("------")
|
||||
//log.Print("[]byte(msg):",[]byte(msg))
|
||||
//log.Print("len(msg)",len(msg))
|
||||
//log.Print("start index",index)
|
||||
//log.Print("start msg[index]",msg[index])
|
||||
//log.Print("start string(msg[index])",string(msg[index]))
|
||||
//log.Print("start []byte(msg[:index])",[]byte(msg[:index]))
|
||||
//log.Print("[]byte(msg): ", []byte(msg))
|
||||
//log.Print("len(msg): ", len(msg))
|
||||
//log.Print("start index: ", index)
|
||||
//log.Print("start msg[index]: ", msg[index])
|
||||
//log.Print("start string(msg[index]): ", string(msg[index]))
|
||||
//log.Print("start []byte(msg[:index]): ", []byte(msg[:index]))
|
||||
|
||||
var startIndex = index
|
||||
var italic = true
|
||||
var bold bool
|
||||
var bold = false
|
||||
if (index + 2) < len(msg) {
|
||||
//log.Print("start index + 1",index + 1)
|
||||
//log.Print("start msg[index]",msg[index + 1])
|
||||
//log.Print("start string(msg[index])",string(msg[index + 1]))
|
||||
|
||||
//log.Print("start index + 1: ", index + 1)
|
||||
//log.Print("start msg[index]: ", msg[index + 1])
|
||||
//log.Print("start string(msg[index]): ", string(msg[index + 1]))
|
||||
if msg[index+1] == '*' {
|
||||
//log.Print("two asterisks")
|
||||
bold = true
|
||||
|
@ -174,16 +154,16 @@ func _markdownParse(msg string, n int) string {
|
|||
}
|
||||
}
|
||||
|
||||
//log.Print("lastElement",lastElement)
|
||||
//log.Print("startIndex:",startIndex)
|
||||
//log.Print("msg[startIndex]",msg[startIndex])
|
||||
//log.Print("string(msg[startIndex])",string(msg[startIndex]))
|
||||
//log.Print("lastElement: ", lastElement)
|
||||
//log.Print("startIndex: ", startIndex)
|
||||
//log.Print("msg[startIndex]: ", msg[startIndex])
|
||||
//log.Print("string(msg[startIndex]): ", string(msg[startIndex]))
|
||||
|
||||
//log.Print("preabrupt index",index)
|
||||
//log.Print("preabrupt msg[index]",msg[index])
|
||||
//log.Print("preabrupt string(msg[index])",string(msg[index]))
|
||||
//log.Print("preabrupt []byte(msg[:index])",[]byte(msg[:index]))
|
||||
//log.Print("preabrupt msg[:index]",msg[:index])
|
||||
//log.Print("preabrupt index: ", index)
|
||||
//log.Print("preabrupt msg[index]: ", msg[index])
|
||||
//log.Print("preabrupt string(msg[index]): ", string(msg[index]))
|
||||
//log.Print("preabrupt []byte(msg[:index]): ", []byte(msg[:index]))
|
||||
//log.Print("preabrupt msg[:index]: ", msg[:index])
|
||||
|
||||
// Does the string terminate abruptly?
|
||||
if (index + 1) >= len(msg) {
|
||||
|
@ -195,16 +175,15 @@ func _markdownParse(msg string, n int) string {
|
|||
//log.Print("preskip index",index)
|
||||
//log.Print("preskip msg[index]",msg[index])
|
||||
//log.Print("preskip string(msg[index])",string(msg[index]))
|
||||
|
||||
index = markdownSkipUntilAsterisk(msg, index)
|
||||
|
||||
if index >= len(msg) {
|
||||
break
|
||||
}
|
||||
|
||||
//log.Print("index",index)
|
||||
//log.Print("[]byte(msg[:index])",[]byte(msg[:index]))
|
||||
//log.Print("msg[index]",msg[index])
|
||||
//log.Print("index: ", index)
|
||||
//log.Print("[]byte(msg[:index]): ", []byte(msg[:index]))
|
||||
//log.Print("msg[index]: ", msg[index])
|
||||
|
||||
sIndex := startIndex
|
||||
lIndex := index
|
||||
|
@ -213,7 +192,7 @@ func _markdownParse(msg string, n int) string {
|
|||
if (index + 3) >= len(msg) {
|
||||
//log.Print("unclosed markdown element @ exit element")
|
||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
lastElement = startIndex
|
||||
break
|
||||
}
|
||||
|
@ -224,7 +203,7 @@ func _markdownParse(msg string, n int) string {
|
|||
if (index + 2) >= len(msg) {
|
||||
//log.Print("true unclosed markdown element @ exit element")
|
||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
lastElement = startIndex
|
||||
break
|
||||
}
|
||||
|
@ -235,7 +214,7 @@ func _markdownParse(msg string, n int) string {
|
|||
if (index + 1) >= len(msg) {
|
||||
//log.Print("true unclosed markdown element @ exit element")
|
||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
lastElement = startIndex
|
||||
break
|
||||
}
|
||||
|
@ -249,7 +228,7 @@ func _markdownParse(msg string, n int) string {
|
|||
if lIndex <= sIndex {
|
||||
//log.Print("unclosed markdown element @ lIndex <= sIndex")
|
||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
lastElement = startIndex
|
||||
break
|
||||
}
|
||||
|
@ -257,7 +236,7 @@ func _markdownParse(msg string, n int) string {
|
|||
if sIndex < 0 || lIndex < 0 {
|
||||
//log.Print("unclosed markdown element @ sIndex < 0 || lIndex < 0")
|
||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||
outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
//outbytes = append(outbytes, markdownUnclosedElement...)
|
||||
lastElement = startIndex
|
||||
break
|
||||
}
|
||||
|
@ -285,7 +264,8 @@ func _markdownParse(msg string, n int) string {
|
|||
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 {
|
||||
outbytes = append(outbytes, markdownItalicTagClose...)
|
||||
|
@ -296,13 +276,18 @@ func _markdownParse(msg string, n int) string {
|
|||
|
||||
lastElement = index
|
||||
index--
|
||||
case '\\':
|
||||
if (index + 1) < len(msg) {
|
||||
outbytes = append(outbytes, msg[lastElement:index]...)
|
||||
index++
|
||||
lastElement = index
|
||||
}
|
||||
//case '`':
|
||||
//case '_':
|
||||
//case '~':
|
||||
//case 10: // newline
|
||||
}
|
||||
}
|
||||
|
||||
//log.Print("exit message loop")
|
||||
|
||||
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, "[c]hi[/c]", "[c]hi[/c]")
|
||||
if !testing.Short() {
|
||||
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][b]hi[/b]", "<b>hi</b>")
|
||||
//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][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][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]")
|
||||
|
@ -51,9 +51,9 @@ func TestBBCodeRender(t *testing.T) {
|
|||
|
||||
t.Log("Testing bbcodeFullParse")
|
||||
for _, item := range msgList {
|
||||
t.Log("Testing string '" + item.Msg + "'")
|
||||
res = bbcodeFullParse(item.Msg)
|
||||
if res != item.Expects {
|
||||
t.Error("Testing string '" + item.Msg + "'")
|
||||
t.Error("Bad output:", "'"+res+"'")
|
||||
t.Error("Expected:", item.Expects)
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ func TestBBCodeRender(t *testing.T) {
|
|||
t.Log("Testing string '" + msg + "'")
|
||||
res = bbcodeFullParse(msg)
|
||||
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("Expected a number between 0 and 18446744073709551615")
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ func TestBBCodeRender(t *testing.T) {
|
|||
t.Log("Testing string '" + msg + "'")
|
||||
res = bbcodeFullParse(msg)
|
||||
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("Expected a number between 0 and 170141183460469231731687303715884105727")
|
||||
}
|
||||
|
@ -193,19 +193,42 @@ func TestBBCodeRender(t *testing.T) {
|
|||
|
||||
func TestMarkdownRender(t *testing.T) {
|
||||
//t.Skip()
|
||||
err := initMarkdown()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var res string
|
||||
var msgList []MEPair
|
||||
msgList = addMEPair(msgList, "hi", "hi")
|
||||
msgList = addMEPair(msgList, "**h**", "<b>h</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, "*h*", "<i>h</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**", "<i>hi</i>*")
|
||||
msgList = addMEPair(msgList, "**hi***", "<b>hi</b>*")
|
||||
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, "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*~", "<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, "****", "****")
|
||||
|
@ -224,10 +247,11 @@ func TestMarkdownRender(t *testing.T) {
|
|||
msgList = addMEPair(msgList, "*** ***", "<b><i> </i></b>")
|
||||
|
||||
for _, item := range msgList {
|
||||
t.Log("Testing string '" + item.Msg + "'")
|
||||
res = markdownParse(item.Msg)
|
||||
if res != item.Expects {
|
||||
t.Error("Testing string '" + item.Msg + "'")
|
||||
t.Error("Bad output:", "'"+res+"'")
|
||||
//t.Error("Ouput in bytes:", []byte(res))
|
||||
t.Error("Expected:", item.Expects)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ function load_alerts(menu_alerts)
|
|||
}
|
||||
|
||||
var alist = "";
|
||||
var anyAvatar = false
|
||||
for(var i in data.msgs) {
|
||||
var msg = data.msgs[i];
|
||||
var mmsg = msg.msg;
|
||||
|
@ -44,15 +43,13 @@ function load_alerts(menu_alerts)
|
|||
if("sub" in msg) {
|
||||
for(var i = 0; i < msg.sub.length; i++) {
|
||||
mmsg = mmsg.replace("\{"+i+"\}", msg.sub[i]);
|
||||
//console.log("Sub #" + i);
|
||||
//console.log(msg.sub[i]);
|
||||
//console.log("Sub #" + i + ":",msg.sub[i]);
|
||||
}
|
||||
}
|
||||
|
||||
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>";
|
||||
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 {
|
||||
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>");
|
||||
|
@ -62,11 +59,8 @@ function load_alerts(menu_alerts)
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
if(data.msgCount != 0 && data.msgCount != undefined) {
|
||||
alertCounterNode.textContent = data.msgCount;
|
||||
menu_alerts.classList.add("has_alerts");
|
||||
|
@ -88,6 +82,7 @@ function load_alerts(menu_alerts)
|
|||
console.log(magic.responseText);
|
||||
console.log(err);
|
||||
}
|
||||
console.log("error: ",error);
|
||||
alertListNode.innerHTML = "<div class='alertItem'>"+errtxt+"</div>";
|
||||
}
|
||||
});
|
||||
|
@ -176,8 +171,7 @@ $(document).ready(function(){
|
|||
|
||||
var messages = event.data.split('\r');
|
||||
for(var i = 0; i < messages.length; i++) {
|
||||
//console.log("Message:");
|
||||
//console.log(messages[i]);
|
||||
//console.log("Message: ",messages[i]);
|
||||
if(messages[i].startsWith("set ")) {
|
||||
//msgblocks = messages[i].split(' ',3);
|
||||
let msgblocks = SplitN(messages[i]," ",3);
|
||||
|
@ -214,10 +208,10 @@ $(document).ready(function(){
|
|||
let topicStatusInput = $('.topic_status_input').val();
|
||||
let topicContentInput = $('.topic_content_input').val();
|
||||
let formAction = this.form.getAttribute("action");
|
||||
//console.log("New Topic Name: " + topicNameInput);
|
||||
//console.log("New Topic Status: " + topicStatusInput);
|
||||
//console.log("New Topic Content: " + topicContentInput);
|
||||
//console.log("Form Action: " + formAction);
|
||||
//console.log("New Topic Name: ", topicNameInput);
|
||||
//console.log("New Topic Status: ", topicStatusInput);
|
||||
//console.log("New Topic Content: ", topicContentInput);
|
||||
//console.log("Form Action: ", formAction);
|
||||
$.ajax({
|
||||
url: formAction,
|
||||
type: "POST",
|
||||
|
@ -253,7 +247,7 @@ $(document).ready(function(){
|
|||
block.html(newContent);
|
||||
|
||||
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 }
|
||||
});
|
||||
});
|
||||
|
@ -266,8 +260,7 @@ $(document).ready(function(){
|
|||
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>");
|
||||
|
||||
$(".submit_edit").click(function(event)
|
||||
{
|
||||
$(".submit_edit").click(function(event) {
|
||||
event.preventDefault();
|
||||
let blockParent = $(this).closest('.editable_parent');
|
||||
let block = blockParent.find('.editable_block').eq(0);
|
||||
|
@ -275,12 +268,12 @@ $(document).ready(function(){
|
|||
block.html(newContent);
|
||||
|
||||
let formAction = $(this).closest('a').attr("href");
|
||||
//console.log("Form Action: " + formAction);
|
||||
//console.log("Form Action:", formAction);
|
||||
$.ajax({
|
||||
url: formAction + "?session=" + session,
|
||||
type: "POST",
|
||||
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'];
|
||||
var itLen = it.length;
|
||||
var out = "";
|
||||
//console.log("Field Name '" + field_name + "'")
|
||||
//console.log("Field Type",field_type)
|
||||
//console.log("Field Value '" + field_value + "'")
|
||||
//console.log("Field Name:",field_name);
|
||||
//console.log("Field Type:",field_type);
|
||||
//console.log("Field Value:",field_value);
|
||||
for (var i = 0; i < itLen; i++) {
|
||||
var sel = "";
|
||||
if(field_value == i || field_value == it[i]) {
|
||||
|
@ -332,7 +325,7 @@ $(document).ready(function(){
|
|||
//console.log("running .submit_edit event");
|
||||
var out_data = {isJs: "1"}
|
||||
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_type = this.getAttribute("data-type");
|
||||
if(field_type=="list") {
|
||||
|
@ -350,7 +343,7 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
var form_action = $(this).closest('a').attr("href");
|
||||
//console.log("Form Action: " + form_action);
|
||||
//console.log("Form Action:", form_action);
|
||||
//console.log(out_data);
|
||||
$.ajax({ url: form_action + "?session=" + session, type:"POST", dataType:"json", data: out_data });
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
case "function", "operator", "number":
|
||||
// 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()" {
|
||||
token.Contents = "GETUTCDATE()"
|
||||
token.Contents = "GETDATE()"
|
||||
}
|
||||
querystr += " " + token.Contents
|
||||
case "column":
|
||||
|
@ -800,12 +801,21 @@ func (adapter *Mssql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel
|
|||
/* Select */
|
||||
var substituteCount = 0
|
||||
|
||||
for _, column := range processColumns(sel.Columns) {
|
||||
var source, alias string
|
||||
|
||||
// Escape the column names, just in case we've used a reserved keyword
|
||||
var colslice = strings.Split(strings.TrimSpace(sel.Columns), ",")
|
||||
for _, column := range colslice {
|
||||
querystr += "[" + strings.TrimSpace(column) + "],"
|
||||
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 += " FROM [" + sel.Table + "] "
|
||||
|
||||
|
|
|
@ -903,7 +903,7 @@ package main
|
|||
|
||||
import "log"
|
||||
import "database/sql"
|
||||
import "./query_gen/lib"
|
||||
//import "./query_gen/lib"
|
||||
|
||||
// nolint
|
||||
` + stmts + `
|
||||
|
|
|
@ -102,10 +102,9 @@ type DB_Adapter interface {
|
|||
SimpleInsert(name string, table string, columns string, fields string) (string, error)
|
||||
|
||||
// ! DEPRECATED
|
||||
SimpleReplace(name string, table string, columns string, fields string) (string, error)
|
||||
|
||||
// ! 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)
|
||||
//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
|
||||
//SimpleUpsert(name string, table string, columns string, fields 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)
|
||||
Purge(name string, table string) (string, error)
|
||||
|
|
|
@ -75,7 +75,7 @@ func writeStatements(adapter qgen.DB_Adapter) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = writeReplaces(adapter)
|
||||
/*err = writeReplaces(adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ func writeStatements(adapter qgen.DB_Adapter) error {
|
|||
err = writeUpserts(adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}*/
|
||||
|
||||
err = writeUpdates(adapter)
|
||||
if err != nil {
|
||||
|
@ -356,8 +356,6 @@ func writeInserts(adapter qgen.DB_Adapter) error {
|
|||
|
||||
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("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
|
||||
}
|
||||
|
||||
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 = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -385,7 +384,7 @@ func writeUpserts(adapter qgen.DB_Adapter) (err error) {
|
|||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}*/
|
||||
|
||||
func writeUpdates(adapter qgen.DB_Adapter) error {
|
||||
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("updateUserGroup", "users", "group = ?", "uid = ?")
|
||||
|
||||
adapter.SimpleUpdate("updateGroupPerms", "users_groups", "permissions = ?", "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("deleteForumPermsByForum", "forums_permissions", "fid = ?")
|
||||
//adapter.SimpleDelete("deleteForumPermsByForum", "forums_permissions", "fid = ?")
|
||||
|
||||
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 = ?")
|
||||
|
||||
|
@ -501,20 +502,20 @@ func writeSimpleCounts(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_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_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_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""},
|
||||
)
|
||||
)*/
|
||||
|
||||
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 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
// TODO: Move this to *Forum
|
||||
rows, err := getForumTopicsOffsetStmt.Query(fid, offset, config.ItemsPerPage)
|
||||
if err != nil {
|
||||
InternalError(err, w)
|
||||
|
@ -904,10 +906,11 @@ func routeRegisterSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|||
return
|
||||
}
|
||||
|
||||
var active, group int
|
||||
var active bool
|
||||
var group int
|
||||
switch headerLite.Settings["activation_type"] {
|
||||
case 1: // Activate All
|
||||
active = 1
|
||||
active = true
|
||||
group = config.DefaultGroup
|
||||
default: // Anything else. E.g. Admin Activation or Email Activation.
|
||||
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()
|
||||
|
||||
var uid int
|
||||
ucache, ok := users.(UserCache)
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&uid)
|
||||
if err != nil {
|
||||
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 {
|
||||
log.Print("Unable to replace the scheduled group")
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -105,87 +105,90 @@ if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
|||
w.Write(forum_12)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_13)
|
||||
} else {
|
||||
w.Write(forum_14)
|
||||
}
|
||||
} else {
|
||||
w.Write(forum_15)
|
||||
}
|
||||
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_21)
|
||||
}
|
||||
w.Write(forum_22)
|
||||
if len(tmpl_forum_vars.ItemList) != 0 {
|
||||
for _, item := range tmpl_forum_vars.ItemList {
|
||||
w.Write(forum_22)
|
||||
if item.Sticky {
|
||||
w.Write(forum_23)
|
||||
if item.Sticky {
|
||||
w.Write(forum_24)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(forum_24)
|
||||
}
|
||||
}
|
||||
w.Write(forum_25)
|
||||
if item.Creator.Avatar != "" {
|
||||
}
|
||||
}
|
||||
w.Write(forum_26)
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
if item.Creator.Avatar != "" {
|
||||
w.Write(forum_27)
|
||||
}
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
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_35)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(forum_36)
|
||||
if item.Sticky {
|
||||
w.Write(forum_35)
|
||||
}
|
||||
w.Write(forum_36)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(forum_37)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(forum_38)
|
||||
if item.Sticky {
|
||||
w.Write(forum_39)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(forum_38)
|
||||
}
|
||||
}
|
||||
w.Write(forum_39)
|
||||
if item.LastUser.Avatar != "" {
|
||||
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([]byte(item.LastUser.Link))
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write(forum_43)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
}
|
||||
w.Write(forum_44)
|
||||
w.Write([]byte(item.RelativeLastReplyAt))
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(forum_45)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_46)
|
||||
w.Write([]byte(item.RelativeLastReplyAt))
|
||||
w.Write(forum_47)
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_49)
|
||||
}
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_50)
|
||||
}
|
||||
w.Write(forum_51)
|
||||
}
|
||||
w.Write(forum_52)
|
||||
w.Write(footer_0)
|
||||
if len(tmpl_forum_vars.Header.Themes) != 0 {
|
||||
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(`
|
||||
<main>
|
||||
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowblock rowhead topic_list_title_block">
|
||||
<div class="rowitem topic_list_title`)
|
||||
var topics_1 = []byte(` has_opt`)
|
||||
var topics_2 = []byte(`"><h1>All Topics</h1></div>
|
||||
`)
|
||||
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_5 = []byte(`
|
||||
var topics_4 = []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>
|
||||
`)
|
||||
var topics_6 = []byte(`
|
||||
var topics_7 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var topics_7 = []byte(`
|
||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
||||
var topics_8 = []byte(`
|
||||
<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>
|
||||
<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">
|
||||
`)
|
||||
var topics_8 = []byte(`<option `)
|
||||
var topics_9 = []byte(`selected`)
|
||||
var topics_10 = []byte(` value="`)
|
||||
var topics_11 = []byte(`">`)
|
||||
var topics_12 = []byte(`</option>`)
|
||||
var topics_13 = []byte(`
|
||||
var topics_9 = []byte(`<option `)
|
||||
var topics_10 = []byte(`selected`)
|
||||
var topics_11 = []byte(` value="`)
|
||||
var topics_12 = []byte(`">`)
|
||||
var topics_13 = []byte(`</option>`)
|
||||
var topics_14 = []byte(`
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="formrow topic_name_row">
|
||||
|
@ -721,71 +726,73 @@ var topics_13 = []byte(`
|
|||
<div class="formitem">
|
||||
<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;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var topics_15 = []byte(`
|
||||
var topics_16 = []byte(`
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</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">
|
||||
`)
|
||||
var topics_17 = []byte(`<div class="topic_row">
|
||||
var topics_18 = []byte(`<div class="topic_row">
|
||||
<div class="rowitem topic_left passive datarow `)
|
||||
var topics_18 = []byte(`topic_sticky`)
|
||||
var topics_19 = []byte(`topic_closed`)
|
||||
var topics_20 = []byte(`">
|
||||
var topics_19 = []byte(`topic_sticky`)
|
||||
var topics_20 = []byte(`topic_closed`)
|
||||
var topics_21 = []byte(`">
|
||||
<span class="selector"></span>
|
||||
`)
|
||||
var topics_21 = []byte(`<img src="`)
|
||||
var topics_22 = []byte(`" height="64" />`)
|
||||
var topics_23 = []byte(`
|
||||
var topics_22 = []byte(`<img src="`)
|
||||
var topics_23 = []byte(`" height="64" />`)
|
||||
var topics_24 = []byte(`
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var topics_24 = []byte(`">`)
|
||||
var topics_25 = []byte(`</a> `)
|
||||
var topics_26 = []byte(`<a class="rowsmall parent_forum" href="`)
|
||||
var topics_27 = []byte(`">`)
|
||||
var topics_28 = []byte(`</a>`)
|
||||
var topics_29 = []byte(`
|
||||
var topics_25 = []byte(`"><span>`)
|
||||
var topics_26 = []byte(`</span></a> `)
|
||||
var topics_27 = []byte(`<a class="rowsmall parent_forum" href="`)
|
||||
var topics_28 = []byte(`">`)
|
||||
var topics_29 = []byte(`</a>`)
|
||||
var topics_30 = []byte(`
|
||||
<br /><a class="rowsmall starter" href="`)
|
||||
var topics_30 = []byte(`">`)
|
||||
var topics_31 = []byte(`</a>
|
||||
var topics_31 = []byte(`">`)
|
||||
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_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var topics_34 = []byte(`
|
||||
var topics_33 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var topics_34 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var topics_35 = []byte(`
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">`)
|
||||
var topics_35 = []byte(` replies</span><br />
|
||||
<span class="topicCount">x topics</span>
|
||||
var topics_36 = []byte(`</span><br />
|
||||
<span class="likeCount">`)
|
||||
var topics_37 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow `)
|
||||
var topics_36 = []byte(`topic_sticky`)
|
||||
var topics_37 = []byte(`topic_closed`)
|
||||
var topics_38 = []byte(`">
|
||||
var topics_38 = []byte(`topic_sticky`)
|
||||
var topics_39 = []byte(`topic_closed`)
|
||||
var topics_40 = []byte(`">
|
||||
`)
|
||||
var topics_39 = []byte(`<img src="`)
|
||||
var topics_40 = []byte(`" height="64" />`)
|
||||
var topics_41 = []byte(`
|
||||
var topics_41 = []byte(`<img src="`)
|
||||
var topics_42 = []byte(`" height="64" />`)
|
||||
var topics_43 = []byte(`
|
||||
<span>
|
||||
<a href="`)
|
||||
var topics_42 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var topics_43 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">Last: `)
|
||||
var topics_44 = []byte(`</span>
|
||||
var topics_44 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var topics_45 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">`)
|
||||
var topics_46 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>`)
|
||||
var topics_45 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
||||
var topics_46 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||
var topics_47 = []byte(`</div>`)
|
||||
var topics_48 = []byte(`
|
||||
var topics_47 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
||||
var topics_48 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||
var topics_49 = []byte(`</div>`)
|
||||
var topics_50 = []byte(`
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
@ -803,7 +810,7 @@ var forum_8 = []byte(`
|
|||
|
||||
<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`)
|
||||
var forum_9 = []byte(` has_opt`)
|
||||
var forum_10 = []byte(`"><h1>`)
|
||||
|
@ -811,21 +818,26 @@ var forum_11 = []byte(`</h1>
|
|||
</div>
|
||||
`)
|
||||
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_14 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic"><a></a></div>`)
|
||||
var forum_15 = []byte(`
|
||||
var forum_14 = []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>
|
||||
`)
|
||||
var forum_16 = []byte(`
|
||||
var forum_17 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var forum_17 = []byte(`
|
||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;">
|
||||
var forum_18 = []byte(`
|
||||
<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>
|
||||
<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="formitem">
|
||||
<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">
|
||||
<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;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var forum_20 = []byte(`
|
||||
var forum_21 = []byte(`
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var forum_21 = []byte(`
|
||||
var forum_22 = []byte(`
|
||||
<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 `)
|
||||
var forum_23 = []byte(`topic_sticky`)
|
||||
var forum_24 = []byte(`topic_closed`)
|
||||
var forum_25 = []byte(`">
|
||||
var forum_24 = []byte(`topic_sticky`)
|
||||
var forum_25 = []byte(`topic_closed`)
|
||||
var forum_26 = []byte(`">
|
||||
<span class="selector"></span>
|
||||
`)
|
||||
var forum_26 = []byte(`<img src="`)
|
||||
var forum_27 = []byte(`" height="64" />`)
|
||||
var forum_28 = []byte(`
|
||||
var forum_27 = []byte(`<img src="`)
|
||||
var forum_28 = []byte(`" height="64" />`)
|
||||
var forum_29 = []byte(`
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var forum_29 = []byte(`">`)
|
||||
var forum_30 = []byte(`</a>
|
||||
var forum_30 = []byte(`"><span>`)
|
||||
var forum_31 = []byte(`</span></a>
|
||||
<br /><a class="rowsmall starter" href="`)
|
||||
var forum_31 = []byte(`">`)
|
||||
var forum_32 = []byte(`</a>
|
||||
var forum_32 = []byte(`">`)
|
||||
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_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var forum_35 = []byte(`
|
||||
var forum_34 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var forum_35 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var forum_36 = []byte(`
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">`)
|
||||
var forum_36 = []byte(` replies</span><br />
|
||||
<span class="topicCount">x topics</span>
|
||||
var forum_37 = []byte(`</span><br />
|
||||
<span class="likeCount">`)
|
||||
var forum_38 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow `)
|
||||
var forum_37 = []byte(`topic_sticky`)
|
||||
var forum_38 = []byte(`topic_closed`)
|
||||
var forum_39 = []byte(`">
|
||||
var forum_39 = []byte(`topic_sticky`)
|
||||
var forum_40 = []byte(`topic_closed`)
|
||||
var forum_41 = []byte(`">
|
||||
`)
|
||||
var forum_40 = []byte(`<img src="`)
|
||||
var forum_41 = []byte(`" height="64" />`)
|
||||
var forum_42 = []byte(`
|
||||
var forum_42 = []byte(`<img src="`)
|
||||
var forum_43 = []byte(`" height="64" />`)
|
||||
var forum_44 = []byte(`
|
||||
<span>
|
||||
<a href="`)
|
||||
var forum_43 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var forum_44 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">Last: `)
|
||||
var forum_45 = []byte(`</span>
|
||||
var forum_45 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var forum_46 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">`)
|
||||
var forum_47 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>`)
|
||||
var forum_46 = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
||||
var forum_47 = []byte(` <a href="/topics/create/`)
|
||||
var forum_48 = []byte(`">Start one?</a>`)
|
||||
var forum_49 = []byte(`</div>`)
|
||||
var forum_50 = []byte(`
|
||||
var forum_48 = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
||||
var forum_49 = []byte(` <a href="/topics/create/`)
|
||||
var forum_50 = []byte(`">Start one?</a>`)
|
||||
var forum_51 = []byte(`</div>`)
|
||||
var forum_52 = []byte(`
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
|
|
@ -83,107 +83,110 @@ w.Write(topics_2)
|
|||
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
w.Write(topics_3)
|
||||
} else {
|
||||
w.Write(topics_4)
|
||||
}
|
||||
} else {
|
||||
w.Write(topics_5)
|
||||
}
|
||||
w.Write(topics_6)
|
||||
}
|
||||
w.Write(topics_7)
|
||||
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
w.Write(topics_7)
|
||||
w.Write(topics_8)
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
for _, item := range tmpl_topics_vars.ForumList {
|
||||
w.Write(topics_8)
|
||||
if item.ID == tmpl_topics_vars.DefaultForum {
|
||||
w.Write(topics_9)
|
||||
}
|
||||
if item.ID == tmpl_topics_vars.DefaultForum {
|
||||
w.Write(topics_10)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
}
|
||||
w.Write(topics_11)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topics_12)
|
||||
}
|
||||
}
|
||||
w.Write([]byte(item.Name))
|
||||
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_16)
|
||||
}
|
||||
}
|
||||
w.Write(topics_17)
|
||||
if len(tmpl_topics_vars.TopicList) != 0 {
|
||||
for _, item := range tmpl_topics_vars.TopicList {
|
||||
w.Write(topics_17)
|
||||
if item.Sticky {
|
||||
w.Write(topics_18)
|
||||
if item.Sticky {
|
||||
w.Write(topics_19)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(topics_19)
|
||||
}
|
||||
}
|
||||
w.Write(topics_20)
|
||||
if item.Creator.Avatar != "" {
|
||||
}
|
||||
}
|
||||
w.Write(topics_21)
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
if item.Creator.Avatar != "" {
|
||||
w.Write(topics_22)
|
||||
}
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
w.Write(topics_23)
|
||||
w.Write([]byte(item.Link))
|
||||
}
|
||||
w.Write(topics_24)
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(topics_25)
|
||||
if item.ForumName != "" {
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write(topics_26)
|
||||
w.Write([]byte(item.ForumLink))
|
||||
if item.ForumName != "" {
|
||||
w.Write(topics_27)
|
||||
w.Write([]byte(item.ForumName))
|
||||
w.Write([]byte(item.ForumLink))
|
||||
w.Write(topics_28)
|
||||
}
|
||||
w.Write([]byte(item.ForumName))
|
||||
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_34)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(topics_35)
|
||||
if item.Sticky {
|
||||
w.Write(topics_34)
|
||||
}
|
||||
w.Write(topics_35)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(topics_36)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(topics_37)
|
||||
if item.Sticky {
|
||||
w.Write(topics_38)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(topics_37)
|
||||
}
|
||||
}
|
||||
w.Write(topics_38)
|
||||
if item.LastUser.Avatar != "" {
|
||||
w.Write(topics_39)
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
}
|
||||
}
|
||||
w.Write(topics_40)
|
||||
}
|
||||
if item.LastUser.Avatar != "" {
|
||||
w.Write(topics_41)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
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)
|
||||
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write([]byte(item.RelativeLastReplyAt))
|
||||
w.Write(topics_46)
|
||||
}
|
||||
} else {
|
||||
w.Write(topics_47)
|
||||
}
|
||||
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(topics_48)
|
||||
}
|
||||
w.Write(topics_49)
|
||||
}
|
||||
w.Write(topics_50)
|
||||
w.Write(footer_0)
|
||||
if len(tmpl_topics_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_topics_vars.Header.Themes {
|
||||
|
|
|
@ -6,18 +6,22 @@
|
|||
|
||||
<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>
|
||||
{{if ne .CurrentUser.ID 0}}
|
||||
{{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>
|
||||
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic"><a></a></div>{{end}}
|
||||
<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>
|
||||
{{/** 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>
|
||||
{{end}}
|
||||
</div>
|
||||
{{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>
|
||||
<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">
|
||||
|
@ -45,24 +49,25 @@
|
|||
<div id="forum_topic_list" class="rowblock topic_list">
|
||||
{{range .ItemList}}<div class="topic_row">
|
||||
<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}}
|
||||
<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>
|
||||
{{/** 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 .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||
<span class="topicCount">x topics</span>
|
||||
<span class="replyCount">{{.PostCount}}</span><br />
|
||||
<span class="likeCount">{{.LikeCount}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<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}}
|
||||
<span>
|
||||
<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>
|
||||
</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}}
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
{{template "header.html" . }}
|
||||
<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>
|
||||
{{if ne .CurrentUser.ID 0}}
|
||||
{{if .ForumList}}
|
||||
<div class="opt create_topic_opt" title="Create 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}}
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
||||
{{/** 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>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if ne .CurrentUser.ID 0}}
|
||||
{{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>
|
||||
<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">
|
||||
|
@ -45,24 +49,25 @@
|
|||
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
|
||||
{{range .TopicList}}<div class="topic_row">
|
||||
<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}}
|
||||
<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>
|
||||
{{/** 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 .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||
<span class="topicCount">x topics</span>
|
||||
<span class="replyCount">{{.PostCount}}</span><br />
|
||||
<span class="likeCount">{{.LikeCount}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<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}}
|
||||
<span>
|
||||
<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>
|
||||
</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}}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
--header-border-color: hsl(0,0%,85%);
|
||||
--element-border-color: hsl(0,0%,90%);
|
||||
--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;
|
||||
}
|
||||
|
||||
@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 {
|
||||
font-size: 16px;
|
||||
font-family: arial;
|
||||
|
@ -18,7 +30,7 @@ body {
|
|||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: hsl(0,0%,40%);
|
||||
color: var(--primary-link-color);
|
||||
}
|
||||
|
||||
#back {
|
||||
|
@ -53,10 +65,11 @@ li {
|
|||
.menu_overview {
|
||||
font-size: 22px;
|
||||
margin-right: 12px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.menu_overview a:after {
|
||||
margin-left: 14px;
|
||||
margin-right: 4px;
|
||||
margin-left: 13px;
|
||||
margin-right: 5px;
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
|
@ -74,8 +87,37 @@ li {
|
|||
top: -2px;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.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 {
|
||||
content: "\f2c3";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
|
@ -117,8 +159,8 @@ ul {
|
|||
border: 1px solid var(--header-border-color);
|
||||
border-bottom: 2px solid var(--header-border-color);
|
||||
background-color: var(--element-background-color);
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
margin-left: 12px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.rowhead {
|
||||
|
@ -135,6 +177,23 @@ ul {
|
|||
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 {
|
||||
border: none;
|
||||
}
|
||||
|
@ -152,26 +211,37 @@ ul {
|
|||
}
|
||||
|
||||
.topic_list .rowtopic {
|
||||
font-size: 18px;
|
||||
font-size: 17px;
|
||||
color: hsl(0,0%,30%);
|
||||
margin-right: 2px;
|
||||
max-width: 230px;
|
||||
margin-right: 1px;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
}
|
||||
.topic_list .rowtopic span {
|
||||
max-width: 112px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.topic_list .rowsmall {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.topic_list .rowsmall.starter:before {
|
||||
content: "\f007";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 5px;
|
||||
font-size: 16px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.topic_list .rowsmall.starter:before {
|
||||
content: "\f007";
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
margin-right: 5px;
|
||||
font-size: 16px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.topic_list .lastReplyAt {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.topic_list .topic_status_e {
|
||||
|
@ -179,19 +249,50 @@ ul {
|
|||
}
|
||||
|
||||
.topic_left {
|
||||
flex: 1 1 calc(100% - 400px);
|
||||
flex: 1 1 calc(100% - 380px);
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.topic_inner_right {
|
||||
margin-left: auto;
|
||||
margin-left: 15%;
|
||||
margin-right: auto;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.topic_inner_right.rowsmall {
|
||||
font-size: 14px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
/* Experimenting here */
|
||||
.topic_inner_right {
|
||||
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 {
|
||||
flex: 1 1 150px;
|
||||
flex: 1 1 0px; /*150px*/
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
|
@ -205,15 +306,16 @@ ul {
|
|||
|
||||
.topic_right img {
|
||||
border-radius: 30px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
margin-top: 8px;
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.topic_left .topic_inner_left {
|
||||
margin-top: 12px;
|
||||
margin-left: 8px;
|
||||
margin-bottom: 14px;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.topic_right > span {
|
||||
|
@ -222,11 +324,22 @@ ul {
|
|||
}
|
||||
|
||||
.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) {
|
||||
.topic_inner_right {
|
||||
|
|
|
@ -7,10 +7,6 @@
|
|||
"Tag": "WIP",
|
||||
"Sidebars":"right",
|
||||
"Resources": [
|
||||
{
|
||||
"Name":"font-awesome-4.7.0/css/font-awesome.css",
|
||||
"Location":"global"
|
||||
},
|
||||
{
|
||||
"Name":"EQCSS.min.js",
|
||||
"Location":"global"
|
||||
|
|
|
@ -599,6 +599,9 @@ input, select, textarea {
|
|||
.topic_list .lastReplyAt {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.topic_list .lastReplyAt:before {
|
||||
content: "Last: ";
|
||||
}
|
||||
.topic_list .starter:before {
|
||||
content: "Starter: ";
|
||||
}
|
||||
|
|
|
@ -405,6 +405,9 @@ li a {
|
|||
.topic_list .lastReplyAt {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.topic_list .lastReplyAt:before {
|
||||
content: "Last: ";
|
||||
}
|
||||
.topic_list .starter:before {
|
||||
content: "Starter: ";
|
||||
}
|
||||
|
|
|
@ -408,6 +408,9 @@ li a {
|
|||
.topic_list .lastReplyAt {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.topic_list .lastReplyAt:before {
|
||||
content: "Last: ";
|
||||
}
|
||||
.topic_list .starter:before {
|
||||
content: "Starter: ";
|
||||
}
|
||||
|
|
79
user.go
79
user.go
|
@ -9,9 +9,12 @@ package main
|
|||
import (
|
||||
//"log"
|
||||
//"fmt"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"./query_gen/lib"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
|
@ -25,6 +28,7 @@ var CheckPassword = BcryptCheckPassword
|
|||
|
||||
//func(password string) (hashed_password string, salt string, err error)
|
||||
var GeneratePassword = BcryptGeneratePassword
|
||||
var ErrNoTempGroup = errors.New("We couldn't find a temporary group for this user")
|
||||
|
||||
type User struct {
|
||||
ID int
|
||||
|
@ -66,28 +70,61 @@ func (user *User) Ban(duration time.Duration, issuedBy int) error {
|
|||
}
|
||||
|
||||
func (user *User) Unban() error {
|
||||
err := user.RevertGroupUpdate()
|
||||
ucache, ok := users.(UserCache)
|
||||
if ok {
|
||||
ucache.CacheRemove(user.ID)
|
||||
return user.RevertGroupUpdate()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// TODO: Use a transaction to avoid race conditions
|
||||
// Make this more stateless?
|
||||
func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Duration) error {
|
||||
var temporary bool
|
||||
if duration.Nanoseconds() != 0 {
|
||||
temporary = true
|
||||
}
|
||||
|
||||
revertAt := time.Now().Add(duration)
|
||||
_, err := replaceScheduleGroupStmt.Exec(user.ID, gid, issuedBy, revertAt, temporary, user.ID)
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
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)
|
||||
if ok {
|
||||
ucache.CacheRemove(user.ID)
|
||||
|
@ -95,13 +132,24 @@ func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Durat
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO: Use a transaction to avoid race conditions
|
||||
func (user *User) RevertGroupUpdate() error {
|
||||
_, err := replaceScheduleGroupStmt.Exec(user.ID, 0, 0, time.Now(), false, user.ID)
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
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)
|
||||
if ok {
|
||||
ucache.CacheRemove(user.ID)
|
||||
|
@ -157,6 +205,15 @@ func (user *User) ChangeAvatar(avatar string) (err error) {
|
|||
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 {
|
||||
var mod int
|
||||
baseScore := 1
|
||||
|
|
|
@ -24,7 +24,7 @@ type UserStore interface {
|
|||
//BulkGet(ids []int) ([]*User, error)
|
||||
BulkGetMap(ids []int) (map[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
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,7 @@ func (mus *MemoryUserStore) Get(id int) (*User, error) {
|
|||
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)
|
||||
|
||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
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
|
||||
}
|
||||
|
||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||
// Initialise the user
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
|
@ -253,6 +255,7 @@ func (mus *MemoryUserStore) BypassGet(id int) (*User, error) {
|
|||
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)
|
||||
|
||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
|
@ -274,6 +277,7 @@ func (mus *MemoryUserStore) Reload(id int) error {
|
|||
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[0] == '.' {
|
||||
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?
|
||||
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..?
|
||||
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
||||
if err != ErrNoRows {
|
||||
|
@ -405,8 +409,7 @@ func (mus *MemoryUserStore) GetCapacity() int {
|
|||
}
|
||||
|
||||
// GlobalCount returns the total number of users registered on the forums
|
||||
func (mus *MemoryUserStore) GlobalCount() int {
|
||||
var ucount int
|
||||
func (mus *MemoryUserStore) GlobalCount() (ucount int) {
|
||||
err := mus.userCount.QueryRow().Scan(&ucount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
|
@ -548,7 +551,7 @@ func (mus *SQLUserStore) Exists(id int) bool {
|
|||
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..?
|
||||
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
||||
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
|
||||
func (mus *SQLUserStore) GlobalCount() int {
|
||||
var ucount int
|
||||
func (mus *SQLUserStore) GlobalCount() (ucount int) {
|
||||
err := mus.userCount.QueryRow().Scan(&ucount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
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