Refactored a few bits and pieces.

Added Riot as a dependency, I'm still deciding over whether to use this or Bleve.
Tweaked the topic view for Cosora.
Fixed a crash bug in the group creator.
Moved a few things from permissions.go into the ForumPermsStore, more to come here!
Added more tests.
Refactored the MySQL Query Generator.
Refactored the BBCode Parser.
Moved the presets into the Phrase System.
This commit is contained in:
Azareal 2017-11-05 01:04:57 +00:00
parent d0363f3eb1
commit d0ffc4be78
20 changed files with 508 additions and 585 deletions

View File

@ -63,11 +63,11 @@ func initDatabase() (err error) {
} }
log.Print("Loading the forum permissions.") log.Print("Loading the forum permissions.")
err = buildForumPermissions() fpstore = NewForumPermsStore()
err = fpstore.Init()
if err != nil { if err != nil {
return err return err
} }
fpstore = NewForumPermsStore()
log.Print("Loading the settings.") log.Print("Loading the settings.")
err = LoadSettings() err = LoadSettings()

View File

@ -1,5 +1,10 @@
package main package main
import (
"encoding/json"
"log"
)
var fpstore *ForumPermsStore var fpstore *ForumPermsStore
type ForumPermsStore struct { type ForumPermsStore struct {
@ -9,6 +14,156 @@ func NewForumPermsStore() *ForumPermsStore {
return &ForumPermsStore{} return &ForumPermsStore{}
} }
func (fps *ForumPermsStore) Init() error {
fids, err := fstore.GetAllIDs()
if err != nil {
return err
}
if dev.SuperDebug {
log.Print("fids: ", fids)
}
rows, err := getForumsPermissionsStmt.Query()
if err != nil {
return err
}
defer rows.Close()
if dev.DebugMode {
log.Print("Adding the forum permissions")
if dev.SuperDebug {
log.Print("forumPerms[gid][fid]")
}
}
// Temporarily store the forum perms in a map before transferring it to a much faster and thread-safe slice
forumPerms = make(map[int]map[int]ForumPerms)
for rows.Next() {
var gid, fid int
var perms []byte
var pperms ForumPerms
err = rows.Scan(&gid, &fid, &perms)
if err != nil {
return err
}
if dev.SuperDebug {
log.Print("perms: ", string(perms))
}
err = json.Unmarshal(perms, &pperms)
if err != nil {
return err
}
pperms.ExtData = make(map[string]bool)
pperms.Overrides = true
_, ok := forumPerms[gid]
if !ok {
forumPerms[gid] = make(map[int]ForumPerms)
}
if dev.SuperDebug {
log.Print("gid: ", gid)
log.Print("fid: ", fid)
log.Printf("perms: %+v\n", pperms)
}
forumPerms[gid][fid] = pperms
}
return fps.cascadePermSetToGroups(forumPerms, fids)
}
// TODO: Need a more thread-safe way of doing this. Possibly with sync.Map?
func (fps *ForumPermsStore) Reload(fid int) error {
if dev.DebugMode {
log.Printf("Reloading the forum permissions for forum #%d", fid)
}
fids, err := fstore.GetAllIDs()
if err != nil {
return err
}
rows, err := db.Query("select gid, permissions from forums_permissions where fid = ? order by gid asc", fid)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var gid int
var perms []byte
var pperms ForumPerms
err := rows.Scan(&gid, &perms)
if err != nil {
return err
}
err = json.Unmarshal(perms, &pperms)
if err != nil {
return err
}
pperms.ExtData = make(map[string]bool)
pperms.Overrides = true
_, ok := forumPerms[gid]
if !ok {
forumPerms[gid] = make(map[int]ForumPerms)
}
forumPerms[gid][fid] = pperms
}
return fps.cascadePermSetToGroups(forumPerms, fids)
}
func (fps *ForumPermsStore) cascadePermSetToGroups(forumPerms map[int]map[int]ForumPerms, fids []int) error {
groups, err := gstore.GetAll()
if err != nil {
return err
}
for _, group := range groups {
if dev.DebugMode {
log.Printf("Updating the forum permissions for Group #%d", group.ID)
}
group.Forums = []ForumPerms{BlankForumPerms}
group.CanSee = []int{}
fps.cascadePermSetToGroup(forumPerms, group, fids)
if dev.SuperDebug {
log.Printf("group.CanSee (length %d): %+v \n", len(group.CanSee), group.CanSee)
log.Printf("group.Forums (length %d): %+v\n", len(group.Forums), group.Forums)
}
}
return nil
}
func (fps *ForumPermsStore) cascadePermSetToGroup(forumPerms map[int]map[int]ForumPerms, group *Group, fids []int) {
for _, fid := range fids {
if dev.SuperDebug {
log.Printf("Forum #%+v\n", fid)
}
forumPerm, ok := forumPerms[group.ID][fid]
if ok {
//log.Print("Overriding permissions for forum #%d",fid)
group.Forums = append(group.Forums, forumPerm)
} else {
//log.Printf("Inheriting from group defaults for forum #%d",fid)
forumPerm = BlankForumPerms
group.Forums = append(group.Forums, forumPerm)
}
if forumPerm.Overrides {
if forumPerm.ViewTopic {
group.CanSee = append(group.CanSee, fid)
}
} else if group.Perms.ViewTopic {
group.CanSee = append(group.CanSee, fid)
}
if dev.SuperDebug {
log.Print("group.ID: ", group.ID)
log.Printf("forumPerm: %+v\n", forumPerm)
log.Print("group.CanSee: ", group.CanSee)
}
}
}
func (fps *ForumPermsStore) Get(fid int, gid int) (fperms ForumPerms, err error) { func (fps *ForumPermsStore) Get(fid int, gid int) (fperms ForumPerms, err error) {
// TODO: Add a hook here and have plugin_guilds use it // TODO: Add a hook here and have plugin_guilds use it
group, err := gstore.Get(gid) group, err := gstore.Get(gid)

View File

@ -157,21 +157,24 @@ func (mgs *MemoryGroupStore) Reload(id int) error {
func (mgs *MemoryGroupStore) initGroup(group *Group) error { func (mgs *MemoryGroupStore) initGroup(group *Group) error {
err := json.Unmarshal(group.PermissionsText, &group.Perms) err := json.Unmarshal(group.PermissionsText, &group.Perms)
if err != nil { if err != nil {
log.Printf("group: %+v\n", group)
log.Print("bad group perms: ", group.PermissionsText)
return err return err
} }
if dev.DebugMode { if dev.DebugMode {
log.Print(group.Name + ": ") log.Printf(group.Name+": %+v\n", group.Perms)
log.Printf("%+v\n", group.Perms)
} }
err = json.Unmarshal(group.PluginPermsText, &group.PluginPerms) err = json.Unmarshal(group.PluginPermsText, &group.PluginPerms)
if err != nil { if err != nil {
log.Printf("group: %+v\n", group)
log.Print("bad group plugin perms: ", group.PluginPermsText)
return err return err
} }
if dev.DebugMode { if dev.DebugMode {
log.Print(group.Name + ": ") log.Printf(group.Name+": %+v\n", group.PluginPerms)
log.Printf("%+v\n", group.PluginPerms)
} }
//group.Perms.ExtData = make(map[string]bool) //group.Perms.ExtData = make(map[string]bool)
// TODO: Can we optimise the bit where this cascades down to the user now? // TODO: Can we optimise the bit where this cascades down to the user now?
if group.IsAdmin || group.IsMod { if group.IsAdmin || group.IsMod {
@ -204,7 +207,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
} }
defer tx.Rollback() defer tx.Rollback()
insertTx, err := qgen.Builder.SimpleInsertTx(tx, "users_groups", "name, tag, is_admin, is_mod, is_banned, permissions", "?,?,?,?,?,?") insertTx, err := qgen.Builder.SimpleInsertTx(tx, "users_groups", "name, tag, is_admin, is_mod, is_banned, permissions, plugin_perms", "?,?,?,?,?,?,'{}'")
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -279,7 +282,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
mgs.Unlock() mgs.Unlock()
for _, forum := range fdata { for _, forum := range fdata {
err = rebuildForumPermissions(forum.ID) err = fpstore.Reload(forum.ID)
if err != nil { if err != nil {
return gid, err return gid, err
} }

View File

@ -22,6 +22,9 @@ go get -u gopkg.in/sourcemap.v1
echo "Installing OttoJS" echo "Installing OttoJS"
go get -u github.com/robertkrimen/otto go get -u github.com/robertkrimen/otto
echo "Installing the Riot Search Engine"
go get -u github.com/robertkrimen/otto
echo "Building the installer" echo "Building the installer"
cd ./install cd ./install

View File

@ -71,6 +71,13 @@ if %errorlevel% neq 0 (
exit /b %errorlevel% exit /b %errorlevel%
) )
echo Installing the Riot Search Engine
go get -u github.com/go-ego/riot
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the installer echo Building the installer
go generate go generate

View File

@ -42,6 +42,16 @@
"SettingLabels": { "SettingLabels": {
"activation_type": "Activate All,Email Activation,Admin Approval" "activation_type": "Activate All,Email Activation,Admin Approval"
}, },
"PermPresets": {
"all":"Public",
"announce":"Announcements",
"members":"Member Only",
"staff":"Staff Only",
"admins":"Admin Only",
"archive":"Archive",
"custom":"Custom",
"unknown":"Unknown"
},
"Accounts": { "Accounts": {
"VerifyEmailSubject": "Validate Your Email @ {{name}}", "VerifyEmailSubject": "Validate Your Email @ {{name}}",
"VerifyEmailBody": "Dear {{username}}, following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. {{schema}}://{{url}}/user/edit/token/{{token}}\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused." "VerifyEmailBody": "Dear {{username}}, following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. {{schema}}://{{url}}/user/edit/token/{{token}}\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused."

View File

@ -421,6 +421,8 @@ func userStoreTest(t *testing.T, newUserID int) {
_, err = users.Get(newUserID) _, err = users.Get(newUserID)
recordMustNotExist(t, err, "UID #%d shouldn't exist", newUserID) recordMustNotExist(t, err, "UID #%d shouldn't exist", newUserID)
// TODO: Add tests for the Cache* methods
} }
// TODO: Add an error message to this? // TODO: Add an error message to this?
@ -445,6 +447,36 @@ func expect(t *testing.T, item bool, errmsg string) {
} }
} }
func TestPermsMiddleware(t *testing.T) {
if !gloinited {
err := gloinit()
if err != nil {
t.Fatal(err)
}
}
if !pluginsInited {
initPlugins()
}
dummyResponseRecorder := httptest.NewRecorder()
bytesBuffer := bytes.NewBuffer([]byte(""))
dummyRequest := httptest.NewRequest("", "/forum/1", bytesBuffer)
user := getDummyUser()
ferr := SuperModOnly(dummyResponseRecorder, dummyRequest, *user)
expect(t, ferr != nil, "Blank users shouldn't be supermods")
user.IsSuperMod = false
ferr = SuperModOnly(dummyResponseRecorder, dummyRequest, *user)
expect(t, ferr != nil, "Non-supermods shouldn't be allowed through supermod gates")
user.IsSuperMod = true
ferr = SuperModOnly(dummyResponseRecorder, dummyRequest, *user)
expect(t, ferr == nil, "Supermods should be allowed through supermod gates")
// TODO: Loop over the Control Panel routes and make sure only supermods can get in
}
func TestTopicStore(t *testing.T) { func TestTopicStore(t *testing.T) {
if !gloinited { if !gloinited {
err := gloinit() err := gloinit()
@ -525,31 +557,44 @@ func TestForumStore(t *testing.T) {
if forum.ID != 1 { if forum.ID != 1 {
t.Error("forum.ID doesn't not match the requested FID. Got '" + strconv.Itoa(forum.ID) + "' instead.'") t.Error("forum.ID doesn't not match the requested FID. Got '" + strconv.Itoa(forum.ID) + "' instead.'")
} }
if forum.Name != "Reports" { // TODO: Check the preset and forum permissions
t.Error("FID #0 is named '" + forum.Name + "' and not 'Reports'") expect(t, forum.Name == "Reports", fmt.Sprintf("FID #0 is named '%s' and not 'Reports'", forum.Name))
} expect(t, !forum.Active, fmt.Sprintf("The reports forum shouldn't be active"))
var expectDesc = "All the reports go here"
expect(t, forum.Desc == expectDesc, fmt.Sprintf("The forum description should be '%s' not '%s'", expectDesc, forum.Desc))
forum, err = fstore.Get(2) forum, err = fstore.Get(2)
recordMustExist(t, err, "Couldn't find FID #1") recordMustExist(t, err, "Couldn't find FID #1")
_ = forum expect(t, forum.ID == 2, fmt.Sprintf("The FID should be 2 not %d", forum.ID))
expect(t, forum.Name == "General", fmt.Sprintf("The name of the forum should be 'General' not '%s'", forum.Name))
expect(t, forum.Active, fmt.Sprintf("The general forum should be active"))
expectDesc = "A place for general discussions which don't fit elsewhere"
expect(t, forum.Desc == expectDesc, fmt.Sprintf("The forum description should be '%s' not '%s'", expectDesc, forum.Desc))
ok := fstore.Exists(-1) ok := fstore.Exists(-1)
if ok { expect(t, !ok, "FID #-1 shouldn't exist")
t.Error("FID #-1 shouldn't exist")
}
ok = fstore.Exists(0) ok = fstore.Exists(0)
if ok { expect(t, !ok, "FID #0 shouldn't exist")
t.Error("FID #0 shouldn't exist")
}
ok = fstore.Exists(1) ok = fstore.Exists(1)
if !ok { expect(t, ok, "FID #1 should exist")
t.Error("FID #1 should exist")
// TODO: Test forum creation
// TODO: Test forum deletion
// TODO: Test forum update
}
// TODO: Implement this
func TestForumPermsStore(t *testing.T) {
if !gloinited {
gloinit()
}
if !pluginsInited {
initPlugins()
} }
} }
// TODO: Test the group permissions
func TestGroupStore(t *testing.T) { func TestGroupStore(t *testing.T) {
if !gloinited { if !gloinited {
gloinit() gloinit()
@ -646,7 +691,19 @@ func TestGroupStore(t *testing.T) {
expect(t, group.IsMod, "This should be a mod group") expect(t, group.IsMod, "This should be a mod group")
expect(t, !group.IsBanned, "This shouldn't be a ban group") expect(t, !group.IsBanned, "This shouldn't be a ban group")
// Make sure the data is static
gstore.Reload(gid)
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 // TODO: Test group deletion
// TODO: Test group reload
// TODO: Test group cache set
} }
func TestReplyStore(t *testing.T) { func TestReplyStore(t *testing.T) {
@ -714,6 +771,7 @@ func TestSlugs(t *testing.T) {
msgList = addMEPair(msgList, "--", "untitled") msgList = addMEPair(msgList, "--", "untitled")
msgList = addMEPair(msgList, "é", "é") msgList = addMEPair(msgList, "é", "é")
msgList = addMEPair(msgList, "-é-", "é") msgList = addMEPair(msgList, "-é-", "é")
msgList = addMEPair(msgList, "-你好-", "untitled")
for _, item := range msgList { for _, item := range msgList {
t.Log("Testing string '" + item.Msg + "'") t.Log("Testing string '" + item.Msg + "'")

View File

@ -338,7 +338,6 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user User, sfi
if err == ErrNoRows { if err == ErrNoRows {
return LocalError("The forum you're trying to edit doesn't exist.", w, r, user) return LocalError("The forum you're trying to edit doesn't exist.", w, r, user)
} else if err != nil { } else if err != nil {
return InternalError(err, w, r) return InternalError(err, w, r)
} }
@ -465,6 +464,8 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
return InternalErrorJSQ(err, w, r, isJs) return InternalErrorJSQ(err, w, r, isJs)
} }
// ! IMPORTANT
// TODO: Refactor this
forumUpdateMutex.Lock() forumUpdateMutex.Lock()
defer forumUpdateMutex.Unlock() defer forumUpdateMutex.Unlock()
if changed { if changed {
@ -488,7 +489,6 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
} }
err = fstore.Reload(fid) err = fstore.Reload(fid)
if err != nil { if err != nil {
// TODO: Log this? -- Another admin might have deleted it
return LocalErrorJSQ("Unable to reload forum", w, r, user, isJs) return LocalErrorJSQ("Unable to reload forum", w, r, user, isJs)
} }
} }

View File

@ -349,7 +349,7 @@ func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
permUpdateMutex.Lock() permUpdateMutex.Lock()
defer permUpdateMutex.Unlock() defer permUpdateMutex.Unlock()
return rebuildForumPermissions(fid) return fpstore.Reload(fid)
} }
func replaceForumPermsForGroup(gid int, presetSet map[int]string, permSets map[int]ForumPerms) error { func replaceForumPermsForGroup(gid int, presetSet map[int]string, permSets map[int]ForumPerms) error {
@ -393,163 +393,7 @@ func replaceForumPermsForGroupTx(tx *sql.Tx, gid int, presetSets map[int]string,
return nil return nil
} }
// TODO: Need a more thread-safe way of doing this. Possibly with sync.Map? // TODO: Refactor this and write tests for it
func rebuildForumPermissions(fid int) error {
if dev.DebugMode {
log.Print("Loading the forum permissions")
}
fids, err := fstore.GetAllIDs()
if err != nil {
return err
}
rows, err := db.Query("select gid, permissions from forums_permissions where fid = ? order by gid asc", fid)
if err != nil {
return err
}
defer rows.Close()
if dev.DebugMode {
log.Print("Updating the forum permissions")
}
for rows.Next() {
var gid int
var perms []byte
var pperms ForumPerms
err := rows.Scan(&gid, &perms)
if err != nil {
return err
}
err = json.Unmarshal(perms, &pperms)
if err != nil {
return err
}
pperms.ExtData = make(map[string]bool)
pperms.Overrides = true
_, ok := forumPerms[gid]
if !ok {
forumPerms[gid] = make(map[int]ForumPerms)
}
forumPerms[gid][fid] = pperms
}
return cascadePermSetToGroups(forumPerms, fids)
}
func buildForumPermissions() error {
fids, err := fstore.GetAllIDs()
if err != nil {
return err
}
if dev.SuperDebug {
log.Print("fids: ", fids)
}
rows, err := getForumsPermissionsStmt.Query()
if err != nil {
return err
}
defer rows.Close()
if dev.DebugMode {
log.Print("Adding the forum permissions")
if dev.SuperDebug {
log.Print("forumPerms[gid][fid]")
}
}
// Temporarily store the forum perms in a map before transferring it to a much faster and thread-safe slice
forumPerms = make(map[int]map[int]ForumPerms)
for rows.Next() {
var gid, fid int
var perms []byte
var pperms ForumPerms
err = rows.Scan(&gid, &fid, &perms)
if err != nil {
return err
}
if dev.SuperDebug {
log.Print("perms: ", string(perms))
}
err = json.Unmarshal(perms, &pperms)
if err != nil {
return err
}
pperms.ExtData = make(map[string]bool)
pperms.Overrides = true
_, ok := forumPerms[gid]
if !ok {
forumPerms[gid] = make(map[int]ForumPerms)
}
if dev.SuperDebug {
log.Print("gid: ", gid)
log.Print("fid: ", fid)
log.Printf("perms: %+v;", pperms)
}
forumPerms[gid][fid] = pperms
}
return cascadePermSetToGroups(forumPerms, fids)
}
func cascadePermSetToGroups(forumPerms map[int]map[int]ForumPerms, fids []int) error {
groups, err := gstore.GetAll()
if err != nil {
return err
}
for _, group := range groups {
if dev.DebugMode {
log.Printf("Updating the forum permissions for Group #%d", group.ID)
}
group.Forums = []ForumPerms{BlankForumPerms}
group.CanSee = []int{}
cascadePermSetToGroup(forumPerms, group, fids)
if dev.SuperDebug {
log.Printf("group.CanSee %+v\n", group.CanSee)
log.Printf("group.Forums %+v\n", group.Forums)
log.Print("len(group.CanSee): ", len(group.CanSee))
log.Print("len(group.Forums): ", len(group.Forums)) // This counts blank aka 0
}
}
return nil
}
func cascadePermSetToGroup(forumPerms map[int]map[int]ForumPerms, group *Group, fids []int) {
for _, fid := range fids {
if dev.SuperDebug {
log.Printf("Forum #%+v\n", fid)
}
forumPerm, ok := forumPerms[group.ID][fid]
if ok {
// Override group perms
//log.Print("Overriding permissions for forum #" + strconv.Itoa(fid))
group.Forums = append(group.Forums, forumPerm)
} else {
// Inherit from Group
//log.Print("Inheriting from default for forum #" + strconv.Itoa(fid))
forumPerm = BlankForumPerms
group.Forums = append(group.Forums, forumPerm)
}
if forumPerm.Overrides {
if forumPerm.ViewTopic {
group.CanSee = append(group.CanSee, fid)
}
} else if group.Perms.ViewTopic {
group.CanSee = append(group.CanSee, fid)
}
if dev.SuperDebug {
log.Print("group.ID: ", group.ID)
log.Printf("forumPerm: %+v\n", forumPerm)
log.Print("group.CanSee: ", group.CanSee)
}
}
}
func forumPermsToGroupForumPreset(fperms ForumPerms) string { func forumPermsToGroupForumPreset(fperms ForumPerms) string {
if !fperms.Overrides { if !fperms.Overrides {
return "default" return "default"
@ -614,24 +458,12 @@ func stripInvalidPreset(preset string) string {
// TODO: Move this into the phrase system? // TODO: Move this into the phrase system?
func presetToLang(preset string) string { func presetToLang(preset string) string {
switch preset { phrases := GetAllPermPresets()
case "all": phrase, ok := phrases[preset]
return "Public" if !ok {
case "announce": phrase = phrases["unknown"]
return "Announcements"
case "members":
return "Member Only"
case "staff":
return "Staff Only"
case "admins":
return "Admin Only"
case "archive":
return "Archive"
case "custom":
return "Custom"
default:
return ""
} }
return phrase
} }
// TODO: Is this racey? // TODO: Is this racey?

View File

@ -39,6 +39,7 @@ type LanguagePack struct {
GlobalPerms map[string]string GlobalPerms map[string]string
LocalPerms map[string]string LocalPerms map[string]string
SettingLabels map[string]string SettingLabels map[string]string
PermPresets map[string]string
Accounts map[string]string // TODO: Apply these phrases in the software proper Accounts map[string]string // TODO: Apply these phrases in the software proper
} }
@ -139,6 +140,10 @@ func GetAllSettingLabels() map[string]string {
return currentLangPack.Load().(*LanguagePack).SettingLabels return currentLangPack.Load().(*LanguagePack).SettingLabels
} }
func GetAllPermPresets() map[string]string {
return currentLangPack.Load().(*LanguagePack).PermPresets
}
func GetAccountPhrase(name string) string { func GetAccountPhrase(name string) string {
res, ok := currentLangPack.Load().(*LanguagePack).Accounts[name] res, ok := currentLangPack.Load().(*LanguagePack).Accounts[name]
if !ok { if !ok {

View File

@ -261,8 +261,7 @@ func bbcodeFullParse(msg string) string {
i += 6 i += 6
} }
//if msglen >= (i+5) { //if msglen >= (i+5) {
// log.Print("boo2") // log.Print("boo2: ", string(msgbytes[i:i+5]))
// log.Print(string(msgbytes[i:i+5]))
//} //}
complexBbc = true complexBbc = true
} }
@ -317,73 +316,15 @@ func bbcodeFullParse(msg string) string {
//log.Print("BBCode Pre:","`"+string(msgbytes)+"`") //log.Print("BBCode Pre:","`"+string(msgbytes)+"`")
//log.Print("----") //log.Print("----")
for ; i < len(msgbytes); i++ { for ; i < len(msgbytes); i++ {
MainLoop:
if msgbytes[i] == '[' { if msgbytes[i] == '[' {
OuterComplex:
if msgbytes[i+1] == 'u' { if msgbytes[i+1] == 'u' {
if msgbytes[i+2] == 'r' && msgbytes[i+3] == 'l' && msgbytes[i+4] == ']' { if msgbytes[i+2] == 'r' && msgbytes[i+3] == 'l' && msgbytes[i+4] == ']' {
start = i + 5 i, start, lastTag, outbytes = bbcodeParseURL(i, start, lastTag, msgbytes, outbytes)
outbytes = append(outbytes, msgbytes[lastTag:i]...) continue
i = start
i += partialURLBytesLen(msgbytes[start:])
//log.Print("Partial Bytes:",string(msgbytes[start:]))
//log.Print("-----")
if !bytes.Equal(msgbytes[i:i+6], []byte("[/url]")) {
//log.Print("Invalid Bytes:",string(msgbytes[i:i+6]))
//log.Print("-----")
outbytes = append(outbytes, invalidURL...)
goto MainLoop
}
outbytes = append(outbytes, urlOpen...)
outbytes = append(outbytes, msgbytes[start:i]...)
outbytes = append(outbytes, urlOpen2...)
outbytes = append(outbytes, msgbytes[start:i]...)
outbytes = append(outbytes, urlClose...)
i += 6
lastTag = i
} }
} else if msgbytes[i+1] == 'r' { } else if msgbytes[i+1] == 'r' {
if bytes.Equal(msgbytes[i+2:i+6], []byte("and]")) { if bytes.Equal(msgbytes[i+2:i+6], []byte("and]")) {
outbytes = append(outbytes, msgbytes[lastTag:i]...) i, start, lastTag, outbytes = bbcodeParseRand(i, start, lastTag, msgbytes, outbytes)
start = i + 6
i = start
for ; ; i++ {
if msgbytes[i] == '[' {
if !bytes.Equal(msgbytes[i+1:i+7], []byte("/rand]")) {
outbytes = append(outbytes, bbcodeMissingTag...)
goto OuterComplex
}
break
} else if (len(msgbytes) - 1) < (i + 10) {
outbytes = append(outbytes, bbcodeMissingTag...)
goto OuterComplex
}
}
number, err := strconv.ParseInt(string(msgbytes[start:i]), 10, 64)
if err != nil {
outbytes = append(outbytes, bbcodeInvalidNumber...)
goto MainLoop
}
// TODO: Add support for negative numbers?
if number < 0 {
outbytes = append(outbytes, bbcodeNoNegative...)
goto MainLoop
}
var dat []byte
if number == 0 {
dat = []byte("0")
} else {
dat = []byte(strconv.FormatInt((random.Int63n(number)), 10))
}
outbytes = append(outbytes, dat...)
//log.Print("Outputted the random number")
i += 7
lastTag = i
} }
} }
} }
@ -411,3 +352,71 @@ func bbcodeFullParse(msg string) string {
return msg return msg
} }
func bbcodeParseURL(i int, start int, lastTag int, msgbytes []byte, outbytes []byte) (int, int, int, []byte) {
start = i + 5
outbytes = append(outbytes, msgbytes[lastTag:i]...)
i = start
i += partialURLBytesLen(msgbytes[start:])
//log.Print("Partial Bytes: ", string(msgbytes[start:]))
//log.Print("-----")
if !bytes.Equal(msgbytes[i:i+6], []byte("[/url]")) {
//log.Print("Invalid Bytes: ", string(msgbytes[i:i+6]))
//log.Print("-----")
outbytes = append(outbytes, invalidURL...)
return i, start, lastTag, outbytes
}
outbytes = append(outbytes, urlOpen...)
outbytes = append(outbytes, msgbytes[start:i]...)
outbytes = append(outbytes, urlOpen2...)
outbytes = append(outbytes, msgbytes[start:i]...)
outbytes = append(outbytes, urlClose...)
i += 6
lastTag = i
return i, start, lastTag, outbytes
}
func bbcodeParseRand(i int, start int, lastTag int, msgbytes []byte, outbytes []byte) (int, int, int, []byte) {
outbytes = append(outbytes, msgbytes[lastTag:i]...)
start = i + 6
i = start
for ; ; i++ {
if msgbytes[i] == '[' {
if !bytes.Equal(msgbytes[i+1:i+7], []byte("/rand]")) {
outbytes = append(outbytes, bbcodeMissingTag...)
return i, start, lastTag, outbytes
}
break
} else if (len(msgbytes) - 1) < (i + 10) {
outbytes = append(outbytes, bbcodeMissingTag...)
return i, start, lastTag, outbytes
}
}
number, err := strconv.ParseInt(string(msgbytes[start:i]), 10, 64)
if err != nil {
outbytes = append(outbytes, bbcodeInvalidNumber...)
return i, start, lastTag, outbytes
}
// TODO: Add support for negative numbers?
if number < 0 {
outbytes = append(outbytes, bbcodeNoNegative...)
return i, start, lastTag, outbytes
}
var dat []byte
if number == 0 {
dat = []byte("0")
} else {
dat = []byte(strconv.FormatInt((random.Int63n(number)), 10))
}
outbytes = append(outbytes, dat...)
//log.Print("Outputted the random number")
i += 7
lastTag = i
return i, start, lastTag, outbytes
}

View File

@ -48,6 +48,8 @@ func TestBBCodeRender(t *testing.T) {
msgList = addMEPair(msgList, "[quote][b]hi[/b][/quote]", "<span class='postQuote'><b>hi</b></span>") msgList = addMEPair(msgList, "[quote][b]hi[/b][/quote]", "<span class='postQuote'><b>hi</b></span>")
msgList = addMEPair(msgList, "[quote][b]h[/b][/quote]", "<span class='postQuote'><b>h</b></span>") msgList = addMEPair(msgList, "[quote][b]h[/b][/quote]", "<span class='postQuote'><b>h</b></span>")
msgList = addMEPair(msgList, "[quote][b][/b][/quote]", "<span class='postQuote'><b></b></span>") msgList = addMEPair(msgList, "[quote][b][/b][/quote]", "<span class='postQuote'><b></b></span>")
msgList = addMEPair(msgList, "-你好-", "-你好-")
msgList = addMEPair(msgList, "[i]-你好-[/i]", "<i>-你好-</i>") // TODO: More of these Unicode tests? Emoji, Chinese, etc.?
t.Log("Testing bbcodeFullParse") t.Log("Testing bbcodeFullParse")
for _, item := range msgList { for _, item := range msgList {
@ -249,6 +251,8 @@ func TestMarkdownRender(t *testing.T) {
msgList = addMEPair(msgList, "* *", "<i> </i>") msgList = addMEPair(msgList, "* *", "<i> </i>")
msgList = addMEPair(msgList, "** **", "<b> </b>") msgList = addMEPair(msgList, "** **", "<b> </b>")
msgList = addMEPair(msgList, "*** ***", "<b><i> </i></b>") msgList = addMEPair(msgList, "*** ***", "<b><i> </i></b>")
msgList = addMEPair(msgList, "-你好-", "-你好-")
msgList = addMEPair(msgList, "*-你好-*", "<i>-你好-</i>") // TODO: More of these Unicode tests? Emoji, Chinese, etc.?
for _, item := range msgList { for _, item := range msgList {
res = markdownParse(item.Msg) res = markdownParse(item.Msg)

View File

@ -118,21 +118,7 @@ func (adapter *MysqlAdapter) SimpleInsert(name string, table string, columns str
return "", errors.New("No input data found for SimpleInsert") return "", errors.New("No input data found for SimpleInsert")
} }
var querystr = "INSERT INTO `" + table + "`(" var querystr = "INSERT INTO `" + table + "`(" + adapter.buildColumns(columns) + ") VALUES ("
// Escape the column names, just in case we've used a reserved keyword
for _, column := range processColumns(columns) {
if column.Type == "function" {
querystr += column.Left + ","
} else {
querystr += "`" + column.Left + "`,"
}
}
// Remove the trailing comma
querystr = querystr[0 : len(querystr)-1]
querystr += ") VALUES ("
for _, field := range processFields(fields) { for _, field := range processFields(fields) {
nameLen := len(field.Name) nameLen := len(field.Name)
if field.Name[0] == '"' && field.Name[nameLen-1] == '"' && nameLen >= 3 { if field.Name[0] == '"' && field.Name[nameLen-1] == '"' && nameLen >= 3 {
@ -149,6 +135,18 @@ func (adapter *MysqlAdapter) SimpleInsert(name string, table string, columns str
return querystr + ")", nil return querystr + ")", nil
} }
func (adapter *MysqlAdapter) buildColumns(columns string) (querystr string) {
// Escape the column names, just in case we've used a reserved keyword
for _, column := range processColumns(columns) {
if column.Type == "function" {
querystr += column.Left + ","
} else {
querystr += "`" + column.Left + "`,"
}
}
return querystr[0 : len(querystr)-1]
}
// ! DEPRECATED // ! DEPRECATED
func (adapter *MysqlAdapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) { func (adapter *MysqlAdapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) {
if name == "" { if name == "" {
@ -164,20 +162,7 @@ func (adapter *MysqlAdapter) SimpleReplace(name string, table string, columns st
return "", errors.New("No input data found for SimpleInsert") return "", errors.New("No input data found for SimpleInsert")
} }
var querystr = "REPLACE INTO `" + table + "`(" var querystr = "REPLACE INTO `" + table + "`(" + adapter.buildColumns(columns) + ") VALUES ("
// Escape the column names, just in case we've used a reserved keyword
for _, column := range processColumns(columns) {
if column.Type == "function" {
querystr += column.Left + ","
} else {
querystr += "`" + column.Left + "`,"
}
}
// Remove the trailing comma
querystr = querystr[0 : len(querystr)-1]
querystr += ") VALUES ("
for _, field := range processFields(fields) { for _, field := range processFields(fields) {
querystr += field.Name + "," querystr += field.Name + ","
} }
@ -260,29 +245,13 @@ func (adapter *MysqlAdapter) SimpleUpdate(name string, table string, set string,
} }
querystr += "," querystr += ","
} }
// Remove the trailing comma
querystr = querystr[0 : len(querystr)-1] querystr = querystr[0 : len(querystr)-1]
// Add support for BETWEEN x.x whereStr, err := adapter.buildWhere(where)
if len(where) != 0 { if err != nil {
querystr += " WHERE" return querystr, err
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
}
}
querystr += " AND"
}
querystr = querystr[0 : len(querystr)-4]
} }
querystr += whereStr
adapter.pushStatement(name, "update", querystr) adapter.pushStatement(name, "update", querystr)
return querystr, nil return querystr, nil
@ -335,32 +304,8 @@ func (adapter *MysqlAdapter) Purge(name string, table string) (string, error) {
return "DELETE FROM `" + table + "`", nil return "DELETE FROM `" + table + "`", nil
} }
func (adapter *MysqlAdapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) { // TODO: Add support for BETWEEN x.x
if name == "" { func (adapter *MysqlAdapter) buildWhere(where string) (querystr string, err error) {
return "", errors.New("You need a name for this statement")
}
if table == "" {
return "", errors.New("You need a name for this table")
}
if len(columns) == 0 {
return "", errors.New("No columns found for SimpleSelect")
}
// Slice up the user friendly strings into something easier to process
var colslice = strings.Split(strings.TrimSpace(columns), ",")
var querystr = "SELECT "
// Escape the column names, just in case we've used a reserved keyword
for _, column := range colslice {
querystr += "`" + strings.TrimSpace(column) + "`,"
}
// Remove the trailing comma
querystr = querystr[0 : len(querystr)-1]
querystr += " FROM `" + table + "`"
// Add support for BETWEEN x.x
if len(where) != 0 { if len(where) != 0 {
querystr += " WHERE" querystr += " WHERE"
for _, loc := range processWhere(where) { for _, loc := range processWhere(where) {
@ -373,14 +318,17 @@ func (adapter *MysqlAdapter) SimpleSelect(name string, table string, columns str
case "string": case "string":
querystr += " '" + token.Contents + "'" querystr += " '" + token.Contents + "'"
default: default:
panic("This token doesn't exist o_o") return querystr, errors.New("This token doesn't exist o_o")
} }
} }
querystr += " AND" querystr += " AND"
} }
querystr = querystr[0 : len(querystr)-4] querystr = querystr[0 : len(querystr)-4]
} }
return querystr, nil
}
func (adapter *MysqlAdapter) buildOrderby(orderby string) (querystr string) {
if len(orderby) != 0 { if len(orderby) != 0 {
querystr += " ORDER BY " querystr += " ORDER BY "
for _, column := range processOrderby(orderby) { for _, column := range processOrderby(orderby) {
@ -389,11 +337,36 @@ func (adapter *MysqlAdapter) SimpleSelect(name string, table string, columns str
} }
querystr = querystr[0 : len(querystr)-1] querystr = querystr[0 : len(querystr)-1]
} }
return querystr
if limit != "" {
querystr += " LIMIT " + limit
} }
func (adapter *MysqlAdapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) {
if name == "" {
return "", errors.New("You need a name for this statement")
}
if table == "" {
return "", errors.New("You need a name for this table")
}
if len(columns) == 0 {
return "", errors.New("No columns found for SimpleSelect")
}
var querystr = "SELECT "
// Slice up the user friendly strings into something easier to process
var colslice = strings.Split(strings.TrimSpace(columns), ",")
for _, column := range colslice {
querystr += "`" + strings.TrimSpace(column) + "`,"
}
querystr = querystr[0 : len(querystr)-1]
whereStr, err := adapter.buildWhere(where)
if err != nil {
return querystr, err
}
querystr += " FROM `" + table + "`" + whereStr + adapter.buildOrderby(orderby) + adapter.buildLimit(limit)
querystr = strings.TrimSpace(querystr) querystr = strings.TrimSpace(querystr)
adapter.pushStatement(name, "select", querystr) adapter.pushStatement(name, "select", querystr)
return querystr, nil return querystr, nil
@ -435,54 +408,15 @@ func (adapter *MysqlAdapter) SimpleLeftJoin(name string, table1 string, table2 s
} }
querystr += source + alias + "," querystr += source + alias + ","
} }
// Remove the trailing comma // Remove the trailing comma
querystr = querystr[0 : len(querystr)-1] querystr = querystr[0 : len(querystr)-1]
querystr += " FROM `" + table1 + "` LEFT JOIN `" + table2 + "` ON " whereStr, err := adapter.buildJoinWhere(where)
for _, joiner := range processJoiner(joiners) { if err != nil {
querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND " return querystr, err
}
// Remove the trailing AND
querystr = querystr[0 : len(querystr)-4]
// Add support for BETWEEN x.x
if len(where) != 0 {
querystr += " WHERE"
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
querystr += " " + token.Contents
case "column":
halves := strings.Split(token.Contents, ".")
if len(halves) == 2 {
querystr += " `" + halves[0] + "`.`" + halves[1] + "`"
} else {
querystr += " `" + token.Contents + "`"
}
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
}
}
querystr += " AND"
}
querystr = querystr[0 : len(querystr)-4]
} }
if len(orderby) != 0 { querystr += " FROM `" + table1 + "` LEFT JOIN `" + table2 + "` ON " + adapter.buildJoiners(joiners) + whereStr + adapter.buildOrderby(orderby) + adapter.buildLimit(limit)
querystr += " ORDER BY "
for _, column := range processOrderby(orderby) {
querystr += column.Column + " " + strings.ToUpper(column.Order) + ","
}
querystr = querystr[0 : len(querystr)-1]
}
if limit != "" {
querystr += " LIMIT " + limit
}
querystr = strings.TrimSpace(querystr) querystr = strings.TrimSpace(querystr)
adapter.pushStatement(name, "select", querystr) adapter.pushStatement(name, "select", querystr)
@ -529,50 +463,12 @@ func (adapter *MysqlAdapter) SimpleInnerJoin(name string, table1 string, table2
// Remove the trailing comma // Remove the trailing comma
querystr = querystr[0 : len(querystr)-1] querystr = querystr[0 : len(querystr)-1]
querystr += " FROM `" + table1 + "` INNER JOIN `" + table2 + "` ON " whereStr, err := adapter.buildJoinWhere(where)
for _, joiner := range processJoiner(joiners) { if err != nil {
querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND " return querystr, err
}
// Remove the trailing AND
querystr = querystr[0 : len(querystr)-4]
// Add support for BETWEEN x.x
if len(where) != 0 {
querystr += " WHERE"
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
querystr += " " + token.Contents
case "column":
halves := strings.Split(token.Contents, ".")
if len(halves) == 2 {
querystr += " `" + halves[0] + "`.`" + halves[1] + "`"
} else {
querystr += " `" + token.Contents + "`"
}
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
}
}
querystr += " AND"
}
querystr = querystr[0 : len(querystr)-4]
} }
if len(orderby) != 0 { querystr += " FROM `" + table1 + "` INNER JOIN `" + table2 + "` ON " + adapter.buildJoiners(joiners) + whereStr + adapter.buildOrderby(orderby) + adapter.buildLimit(limit)
querystr += " ORDER BY "
for _, column := range processOrderby(orderby) {
querystr += column.Column + " " + strings.ToUpper(column.Order) + ","
}
querystr = querystr[0 : len(querystr)-1]
}
if limit != "" {
querystr += " LIMIT " + limit
}
querystr = strings.TrimSpace(querystr) querystr = strings.TrimSpace(querystr)
adapter.pushStatement(name, "select", querystr) adapter.pushStatement(name, "select", querystr)
@ -581,7 +477,6 @@ func (adapter *MysqlAdapter) SimpleInnerJoin(name string, table1 string, table2
func (adapter *MysqlAdapter) SimpleInsertSelect(name string, ins DB_Insert, sel DB_Select) (string, error) { func (adapter *MysqlAdapter) SimpleInsertSelect(name string, ins DB_Insert, sel DB_Select) (string, error) {
/* Insert Portion */ /* Insert Portion */
var querystr = "INSERT INTO `" + ins.Table + "`(" var querystr = "INSERT INTO `" + ins.Table + "`("
// Escape the column names, just in case we've used a reserved keyword // Escape the column names, just in case we've used a reserved keyword
@ -613,40 +508,12 @@ func (adapter *MysqlAdapter) SimpleInsertSelect(name string, ins DB_Insert, sel
} }
querystr = querystr[0 : len(querystr)-1] querystr = querystr[0 : len(querystr)-1]
querystr += " FROM `" + sel.Table + "`" whereStr, err := adapter.buildWhere(sel.Where)
if err != nil {
// Add support for BETWEEN x.x return querystr, err
if len(sel.Where) != 0 {
querystr += " WHERE"
for _, loc := range processWhere(sel.Where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
}
}
querystr += " AND"
}
querystr = querystr[0 : len(querystr)-4]
} }
if len(sel.Orderby) != 0 { querystr += " FROM `" + sel.Table + "`" + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit)
querystr += " ORDER BY "
for _, column := range processOrderby(sel.Orderby) {
querystr += column.Column + " " + strings.ToUpper(column.Order) + ","
}
querystr = querystr[0 : len(querystr)-1]
}
if sel.Limit != "" {
querystr += " LIMIT " + sel.Limit
}
querystr = strings.TrimSpace(querystr) querystr = strings.TrimSpace(querystr)
adapter.pushStatement(name, "insert", querystr) adapter.pushStatement(name, "insert", querystr)
@ -655,7 +522,6 @@ func (adapter *MysqlAdapter) SimpleInsertSelect(name string, ins DB_Insert, sel
func (adapter *MysqlAdapter) SimpleInsertLeftJoin(name string, ins DB_Insert, sel DB_Join) (string, error) { func (adapter *MysqlAdapter) SimpleInsertLeftJoin(name string, ins DB_Insert, sel DB_Join) (string, error) {
/* Insert Portion */ /* Insert Portion */
var querystr = "INSERT INTO `" + ins.Table + "`(" var querystr = "INSERT INTO `" + ins.Table + "`("
// Escape the column names, just in case we've used a reserved keyword // Escape the column names, just in case we've used a reserved keyword
@ -689,16 +555,32 @@ func (adapter *MysqlAdapter) SimpleInsertLeftJoin(name string, ins DB_Insert, se
} }
querystr = querystr[0 : len(querystr)-1] querystr = querystr[0 : len(querystr)-1]
querystr += " FROM `" + sel.Table1 + "` LEFT JOIN `" + sel.Table2 + "` ON " whereStr, err := adapter.buildJoinWhere(sel.Where)
for _, joiner := range processJoiner(sel.Joiners) { if err != nil {
return querystr, err
}
querystr += " FROM `" + sel.Table1 + "` LEFT JOIN `" + sel.Table2 + "` ON " + adapter.buildJoiners(sel.Joiners) + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit)
querystr = strings.TrimSpace(querystr)
adapter.pushStatement(name, "insert", querystr)
return querystr, nil
}
// TODO: Make this more consistent with the other build* methods?
func (adapter *MysqlAdapter) buildJoiners(joiners string) (querystr string) {
for _, joiner := range processJoiner(joiners) {
querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND " querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND "
} }
querystr = querystr[0 : len(querystr)-4] // Remove the trailing AND
return querystr[0 : len(querystr)-4]
}
// Add support for BETWEEN x.x // Add support for BETWEEN x.x
if len(sel.Where) != 0 { func (adapter *MysqlAdapter) buildJoinWhere(where string) (querystr string, err error) {
if len(where) != 0 {
querystr += " WHERE" querystr += " WHERE"
for _, loc := range processWhere(sel.Where) { for _, loc := range processWhere(where) {
for _, token := range loc.Expr { for _, token := range loc.Expr {
switch token.Type { switch token.Type {
case "function", "operator", "number", "substitute": case "function", "operator", "number", "substitute":
@ -713,34 +595,25 @@ func (adapter *MysqlAdapter) SimpleInsertLeftJoin(name string, ins DB_Insert, se
case "string": case "string":
querystr += " '" + token.Contents + "'" querystr += " '" + token.Contents + "'"
default: default:
panic("This token doesn't exist o_o") return querystr, errors.New("This token doesn't exist o_o")
} }
} }
querystr += " AND" querystr += " AND"
} }
querystr = querystr[0 : len(querystr)-4] querystr = querystr[0 : len(querystr)-4]
} }
if len(sel.Orderby) != 0 {
querystr += " ORDER BY "
for _, column := range processOrderby(sel.Orderby) {
querystr += column.Column + " " + strings.ToUpper(column.Order) + ","
}
querystr = querystr[0 : len(querystr)-1]
}
if sel.Limit != "" {
querystr += " LIMIT " + sel.Limit
}
querystr = strings.TrimSpace(querystr)
adapter.pushStatement(name, "insert", querystr)
return querystr, nil return querystr, nil
} }
func (adapter *MysqlAdapter) buildLimit(limit string) (querystr string) {
if limit != "" {
querystr += " LIMIT " + limit
}
return querystr
}
func (adapter *MysqlAdapter) SimpleInsertInnerJoin(name string, ins DB_Insert, sel DB_Join) (string, error) { func (adapter *MysqlAdapter) SimpleInsertInnerJoin(name string, ins DB_Insert, sel DB_Join) (string, error) {
/* Insert Portion */ /* Insert Portion */
var querystr = "INSERT INTO `" + ins.Table + "`(" var querystr = "INSERT INTO `" + ins.Table + "`("
// Escape the column names, just in case we've used a reserved keyword // Escape the column names, just in case we've used a reserved keyword
@ -774,56 +647,19 @@ func (adapter *MysqlAdapter) SimpleInsertInnerJoin(name string, ins DB_Insert, s
} }
querystr = querystr[0 : len(querystr)-1] querystr = querystr[0 : len(querystr)-1]
querystr += " FROM `" + sel.Table1 + "` INNER JOIN `" + sel.Table2 + "` ON " whereStr, err := adapter.buildJoinWhere(sel.Where)
for _, joiner := range processJoiner(sel.Joiners) { if err != nil {
querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND " return querystr, err
}
querystr = querystr[0 : len(querystr)-4]
// Add support for BETWEEN x.x
if len(sel.Where) != 0 {
querystr += " WHERE"
for _, loc := range processWhere(sel.Where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
querystr += " " + token.Contents
case "column":
halves := strings.Split(token.Contents, ".")
if len(halves) == 2 {
querystr += " `" + halves[0] + "`.`" + halves[1] + "`"
} else {
querystr += " `" + token.Contents + "`"
}
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
}
}
querystr += " AND"
}
querystr = querystr[0 : len(querystr)-4]
} }
if len(sel.Orderby) != 0 { querystr += " FROM `" + sel.Table1 + "` INNER JOIN `" + sel.Table2 + "` ON " + adapter.buildJoiners(sel.Joiners) + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit)
querystr += " ORDER BY "
for _, column := range processOrderby(sel.Orderby) {
querystr += column.Column + " " + strings.ToUpper(column.Order) + ","
}
querystr = querystr[0 : len(querystr)-1]
}
if sel.Limit != "" {
querystr += " LIMIT " + sel.Limit
}
querystr = strings.TrimSpace(querystr) querystr = strings.TrimSpace(querystr)
adapter.pushStatement(name, "insert", querystr) adapter.pushStatement(name, "insert", querystr)
return querystr, nil return querystr, nil
} }
func (adapter *MysqlAdapter) SimpleCount(name string, table string, where string, limit string) (string, error) { func (adapter *MysqlAdapter) SimpleCount(name string, table string, where string, limit string) (querystr string, err error) {
if name == "" { if name == "" {
return "", errors.New("You need a name for this statement") return "", errors.New("You need a name for this statement")
} }
@ -831,31 +667,12 @@ func (adapter *MysqlAdapter) SimpleCount(name string, table string, where string
return "", errors.New("You need a name for this table") return "", errors.New("You need a name for this table")
} }
var querystr = "SELECT COUNT(*) AS `count` FROM `" + table + "`" querystr = "SELECT COUNT(*) AS `count` FROM `" + table + "`"
whereStr, err := adapter.buildWhere(where)
// TODO: Add support for BETWEEN x.x if err != nil {
if len(where) != 0 { return "", err
querystr += " WHERE"
//log.Print("SimpleCount: ", name)
//log.Print("where: ", where)
//log.Print("processWhere: ", processWhere(where))
for _, loc := range processWhere(where) {
for _, token := range loc.Expr {
switch token.Type {
case "function", "operator", "number", "substitute":
querystr += " " + token.Contents
case "column":
querystr += " `" + token.Contents + "`"
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
}
}
querystr += " AND"
}
querystr = querystr[0 : len(querystr)-4]
} }
querystr += whereStr
if limit != "" { if limit != "" {
querystr += " LIMIT " + limit querystr += " LIMIT " + limit

View File

@ -351,7 +351,7 @@ var topic_alt_29 = []byte(`</textarea>
<div class="button_container"> <div class="button_container">
`) `)
var topic_alt_30 = []byte(`<a href="/topic/like/submit/`) var topic_alt_30 = []byte(`<a href="/topic/like/submit/`)
var topic_alt_31 = []byte(`" class="action_button like_item">+1</a>`) var topic_alt_31 = []byte(`" class="action_button like_item add_like">+1</a>`)
var topic_alt_32 = []byte(`<a href="/topic/edit/`) var topic_alt_32 = []byte(`<a href="/topic/edit/`)
var topic_alt_33 = []byte(`" class="action_button open_edit">Edit</a>`) var topic_alt_33 = []byte(`" class="action_button open_edit">Edit</a>`)
var topic_alt_34 = []byte(`<a href="/topic/delete/submit/`) var topic_alt_34 = []byte(`<a href="/topic/delete/submit/`)
@ -420,7 +420,7 @@ var topic_alt_72 = []byte(`</div>
<div class="button_container"> <div class="button_container">
`) `)
var topic_alt_73 = []byte(`<a href="/reply/like/submit/`) var topic_alt_73 = []byte(`<a href="/reply/like/submit/`)
var topic_alt_74 = []byte(`" class="action_button like_item">+1</a>`) var topic_alt_74 = []byte(`" class="action_button like_item add_like">+1</a>`)
var topic_alt_75 = []byte(`<a href="/reply/edit/submit/`) var topic_alt_75 = []byte(`<a href="/reply/edit/submit/`)
var topic_alt_76 = []byte(`" class="action_button edit_item">Edit</a>`) var topic_alt_76 = []byte(`" class="action_button edit_item">Edit</a>`)
var topic_alt_77 = []byte(`<a href="/reply/delete/submit/`) var topic_alt_77 = []byte(`<a href="/reply/delete/submit/`)

View File

@ -34,7 +34,7 @@
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea> <textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
<div class="button_container"> <div class="button_container">
{{if .CurrentUser.Loggedin}} {{if .CurrentUser.Loggedin}}
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="action_button like_item">+1</a>{{end}} {{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="action_button like_item add_like">+1</a>{{end}}
{{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit">Edit</a>{{end}} {{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit">Edit</a>{{end}}
{{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}" class="action_button delete_item">Delete</a>{{end}} {{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}" class="action_button delete_item">Delete</a>{{end}}
{{if .CurrentUser.Perms.CloseTopic}} {{if .CurrentUser.Perms.CloseTopic}}
@ -68,7 +68,7 @@
<div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div> <div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div>
<div class="button_container"> <div class="button_container">
{{if $.CurrentUser.Loggedin}} {{if $.CurrentUser.Loggedin}}
{{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}" class="action_button like_item">+1</a>{{end}} {{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}" class="action_button like_item add_like">+1</a>{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="action_button edit_item">Edit</a>{{end}} {{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="action_button edit_item">Edit</a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="action_button delete_item">Delete</a>{{end}} {{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="action_button delete_item">Delete</a>{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item">Report</a> <a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item">Report</a>

View File

@ -411,13 +411,20 @@ select, input, textarea {
.topic_reply_form { .topic_reply_form {
margin: 0px; margin: 0px;
width: 100%; width: 100%;
height: min-content;
} }
.topic_reply_form .trumbowyg-button-pane:after { .topic_reply_form .trumbowyg-button-pane:after {
display: none; display: none;
} }
.topic_reply_form .trumbowyg-box {
min-height: auto;
}
.topic_reply_form .trumbowyg-editor { .topic_reply_form .trumbowyg-editor {
border-left: none; border-left: none;
border-right: none; border-right: none;
min-height: 103px;
max-height: 200px;
overflow-y: scroll;
} }
.topic_reply_form .quick_button_row { .topic_reply_form .quick_button_row {
margin-bottom: 7px; margin-bottom: 7px;
@ -673,13 +680,21 @@ select, input, textarea {
content: " likes"; content: " likes";
margin-right: 6px; margin-right: 6px;
} }
.created_at:before, .ip_item:before {
.post_item .add_like:after, .created_at:before, .ip_item:before {
border-left: 1px solid var(--element-border-color); border-left: 1px solid var(--element-border-color);
content: ""; content: "";
margin-right: 10px;
margin-top: 1px; margin-top: 1px;
margin-bottom: 1px; margin-bottom: 1px;
} }
.created_at:before, .ip_item:before {
margin-right: 10px;
}
.post_item .add_like:after {
margin-left: 10px;
margin-right: 5px;
}
.created_at { .created_at {
margin-right: 10px; margin-right: 10px;
} }

View File

@ -9,8 +9,13 @@ $(document).ready(function(){
$(".topic_create_form").addClass("selectedInput"); $(".topic_create_form").addClass("selectedInput");
}); });
//$.trumbowyg.svgPath = false; //$.trumbowyg.svgPath = false;
$('#input_content').trumbowyg({ $('.topic_create_form #input_content').trumbowyg({
btns: [['viewHTML'],['undo','redo'],['formatting'],['strong','em','del'],['link'],['insertImage'],['unorderedList','orderedList'],['removeformat']], btns: [['viewHTML'],['undo','redo'],['formatting'],['strong','em','del'],['link'],['insertImage'],['unorderedList','orderedList'],['removeformat']],
//hideButtonTexts: true //hideButtonTexts: true
}); });
$('.topic_reply_form #input_content').trumbowyg({
btns: [['viewHTML'],['undo','redo'],['formatting'],['strong','em','del'],['link'],['insertImage'],['unorderedList','orderedList'],['removeformat']],
autogrow: true,
//hideButtonTexts: true
});
}); });

View File

@ -21,3 +21,6 @@ go get -u gopkg.in/sourcemap.v1
echo "Updating OttoJS" echo "Updating OttoJS"
go get -u github.com/robertkrimen/otto go get -u github.com/robertkrimen/otto
echo "Updating the Riot Search Engine"
go get -u github.com/go-ego/riot

View File

@ -68,5 +68,12 @@ if %errorlevel% neq 0 (
exit /b %errorlevel% exit /b %errorlevel%
) )
echo Installing the Riot Search Engine
go get -u github.com/go-ego/riot
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo The dependencies were successfully updated echo The dependencies were successfully updated
pause pause

42
user.go
View File

@ -228,11 +228,11 @@ func (user *User) ChangeGroup(group int) (err error) {
return err return err
} }
func (user *User) increasePostStats(wcount int, topic bool) error { func (user *User) increasePostStats(wcount int, topic bool) (err error) {
var mod int var mod int
baseScore := 1 baseScore := 1
if topic { if topic {
_, err := incrementUserTopicsStmt.Exec(1, user.ID) _, err = incrementUserTopicsStmt.Exec(1, user.ID)
if err != nil { if err != nil {
return err return err
} }
@ -241,24 +241,19 @@ func (user *User) increasePostStats(wcount int, topic bool) error {
settings := settingBox.Load().(SettingBox) settings := settingBox.Load().(SettingBox)
if wcount >= settings["megapost_min_words"].(int) { if wcount >= settings["megapost_min_words"].(int) {
_, err := incrementUserMegapostsStmt.Exec(1, 1, 1, user.ID) _, err = incrementUserMegapostsStmt.Exec(1, 1, 1, user.ID)
if err != nil {
return err
}
mod = 4 mod = 4
} else if wcount >= settings["bigpost_min_words"].(int) { } else if wcount >= settings["bigpost_min_words"].(int) {
_, err := incrementUserBigpostsStmt.Exec(1, 1, user.ID) _, err = incrementUserBigpostsStmt.Exec(1, 1, user.ID)
if err != nil {
return err
}
mod = 1 mod = 1
} else { } else {
_, err := incrementUserPostsStmt.Exec(1, user.ID) _, err = incrementUserPostsStmt.Exec(1, user.ID)
}
if err != nil { if err != nil {
return err return err
} }
}
_, err := incrementUserScoreStmt.Exec(baseScore+mod, user.ID) _, err = incrementUserScoreStmt.Exec(baseScore+mod, user.ID)
if err != nil { if err != nil {
return err return err
} }
@ -269,11 +264,11 @@ func (user *User) increasePostStats(wcount int, topic bool) error {
return err return err
} }
func (user *User) decreasePostStats(wcount int, topic bool) error { func (user *User) decreasePostStats(wcount int, topic bool) (err error) {
var mod int var mod int
baseScore := -1 baseScore := -1
if topic { if topic {
_, err := incrementUserTopicsStmt.Exec(-1, user.ID) _, err = incrementUserTopicsStmt.Exec(-1, user.ID)
if err != nil { if err != nil {
return err return err
} }
@ -282,24 +277,19 @@ func (user *User) decreasePostStats(wcount int, topic bool) error {
settings := settingBox.Load().(SettingBox) settings := settingBox.Load().(SettingBox)
if wcount >= settings["megapost_min_words"].(int) { if wcount >= settings["megapost_min_words"].(int) {
_, err := incrementUserMegapostsStmt.Exec(-1, -1, -1, user.ID) _, err = incrementUserMegapostsStmt.Exec(-1, -1, -1, user.ID)
if err != nil {
return err
}
mod = 4 mod = 4
} else if wcount >= settings["bigpost_min_words"].(int) { } else if wcount >= settings["bigpost_min_words"].(int) {
_, err := incrementUserBigpostsStmt.Exec(-1, -1, user.ID) _, err = incrementUserBigpostsStmt.Exec(-1, -1, user.ID)
if err != nil {
return err
}
mod = 1 mod = 1
} else { } else {
_, err := incrementUserPostsStmt.Exec(-1, user.ID) _, err = incrementUserPostsStmt.Exec(-1, user.ID)
}
if err != nil { if err != nil {
return err return err
} }
}
_, err := incrementUserScoreStmt.Exec(baseScore-mod, user.ID) _, err = incrementUserScoreStmt.Exec(baseScore-mod, user.ID)
if err != nil { if err != nil {
return err return err
} }