gosora/patcher/patches.go

963 lines
34 KiB
Go

package main
import (
"bufio"
"database/sql"
"strconv"
"strings"
"unicode"
meta "git.tuxpa.in/a/gosora/common/meta"
qgen "git.tuxpa.in/a/gosora/query_gen"
)
type tblColumn = qgen.DBTableColumn
type tC = tblColumn
type tblKey = qgen.DBTableKey
type tK = tblKey
func init() {
addPatch(0, patch0)
addPatch(1, patch1)
addPatch(2, patch2)
addPatch(3, patch3)
addPatch(4, patch4)
addPatch(5, patch5)
addPatch(6, patch6)
addPatch(7, patch7)
addPatch(8, patch8)
addPatch(9, patch9)
addPatch(10, patch10)
addPatch(11, patch11)
addPatch(12, patch12)
addPatch(13, patch13)
addPatch(14, patch14)
addPatch(15, patch15)
addPatch(16, patch16)
addPatch(17, patch17)
addPatch(18, patch18)
addPatch(19, patch19)
addPatch(20, patch20)
addPatch(21, patch21)
addPatch(22, patch22)
addPatch(23, patch23)
addPatch(24, patch24)
addPatch(25, patch25)
addPatch(26, patch26)
addPatch(27, patch27)
addPatch(28, patch28)
addPatch(29, patch29)
addPatch(30, patch30)
addPatch(31, patch31)
addPatch(32, patch32)
addPatch(33, patch33)
addPatch(34, patch34)
addPatch(35, patch35)
addPatch(36, patch36)
}
func bcol(col string, val bool) qgen.DBTableColumn {
if val {
return tC{col, "boolean", 0, false, false, "1"}
}
return tC{col, "boolean", 0, false, false, "0"}
}
func ccol(col string, size int, sdefault string) qgen.DBTableColumn {
return tC{col, "varchar", size, false, false, sdefault}
}
func patch0(scanner *bufio.Scanner) (err error) {
err = createTable("menus", "", "",
[]tC{
{"mid", "int", 0, false, true, ""},
},
[]tK{
{"mid", "primary", "", false},
},
)
if err != nil {
return err
}
err = createTable("menu_items", "", "",
[]tC{
{"miid", "int", 0, false, true, ""},
{"mid", "int", 0, false, false, ""},
ccol("name", 200, ""),
ccol("htmlID", 200, "''"),
ccol("cssClass", 200, "''"),
ccol("position", 100, ""),
ccol("path", 200, "''"),
ccol("aria", 200, "''"),
ccol("tooltip", 200, "''"),
ccol("tmplName", 200, "''"),
{"order", "int", 0, false, false, "0"},
bcol("guestOnly", false),
bcol("memberOnly", false),
bcol("staffOnly", false),
bcol("adminOnly", false),
},
[]tK{
{"miid", "primary", "", false},
},
)
if err != nil {
return err
}
err = execStmt(qgen.Builder.SimpleInsert("menus", "", ""))
if err != nil {
return err
}
var order int
mOrder := "mid, name, htmlID, cssClass, position, path, aria, tooltip, guestOnly, memberOnly, staffOnly, adminOnly"
addMenuItem := func(data map[string]interface{}) error {
cols, values := qgen.InterfaceMapToInsertStrings(data, mOrder)
err := execStmt(qgen.Builder.SimpleInsert("menu_items", cols+", order", values+","+strconv.Itoa(order)))
order++
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_forums}", "htmlID": "menu_forums", "position": "left", "path": "/forums/", "aria": "{lang.menu_forums_aria}", "tooltip": "{lang.menu_forums_tooltip}"})
if err != nil {
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_topics}", "htmlID": "menu_topics", "cssClass": "menu_topics", "position": "left", "path": "/topics/", "aria": "{lang.menu_topics_aria}", "tooltip": "{lang.menu_topics_tooltip}"})
if err != nil {
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "htmlID": "general_alerts", "cssClass": "menu_alerts", "position": "right", "tmplName": "menu_alerts"})
if err != nil {
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_account}", "cssClass": "menu_account", "position": "left", "path": "/user/edit/critical/", "aria": "{lang.menu_account_aria}", "tooltip": "{lang.menu_account_tooltip}", "memberOnly": true})
if err != nil {
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_profile}", "cssClass": "menu_profile", "position": "left", "path": "{me.Link}", "aria": "{lang.menu_profile_aria}", "tooltip": "{lang.menu_profile_tooltip}", "memberOnly": true})
if err != nil {
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_panel}", "cssClass": "menu_panel menu_account", "position": "left", "path": "/panel/", "aria": "{lang.menu_panel_aria}", "tooltip": "{lang.menu_panel_tooltip}", "memberOnly": true, "staffOnly": true})
if err != nil {
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_logout}", "cssClass": "menu_logout", "position": "left", "path": "/accounts/logout/?session={me.Session}", "aria": "{lang.menu_logout_aria}", "tooltip": "{lang.menu_logout_tooltip}", "memberOnly": true})
if err != nil {
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_register}", "cssClass": "menu_register", "position": "left", "path": "/accounts/create/", "aria": "{lang.menu_register_aria}", "tooltip": "{lang.menu_register_tooltip}", "guestOnly": true})
if err != nil {
return err
}
err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_login}", "cssClass": "menu_login", "position": "left", "path": "/accounts/login/", "aria": "{lang.menu_login_aria}", "tooltip": "{lang.menu_login_tooltip}", "guestOnly": true})
if err != nil {
return err
}
return nil
}
func patch1(scanner *bufio.Scanner) error {
return renameRoutes(map[string]string{
"routeAccountEditCriticalSubmit": "routes.AccountEditCriticalSubmit",
"routeAccountEditAvatar": "routes.AccountEditAvatar",
"routeAccountEditAvatarSubmit": "routes.AccountEditAvatarSubmit",
"routeAccountEditUsername": "routes.AccountEditUsername",
"routeAccountEditUsernameSubmit": "routes.AccountEditUsernameSubmit",
})
}
func patch2(scanner *bufio.Scanner) error {
return renameRoutes(map[string]string{
"routeLogout": "routes.AccountLogout",
"routeShowAttachment": "routes.ShowAttachment",
"routeChangeTheme": "routes.ChangeTheme",
"routeProfileReplyCreateSubmit": "routes.ProfileReplyCreateSubmit",
"routeLikeTopicSubmit": "routes.LikeTopicSubmit",
"routeReplyLikeSubmit": "routes.ReplyLikeSubmit",
"routeDynamic": "routes.DynamicRoute",
"routeUploads": "routes.UploadedFile",
"BadRoute": "routes.BadRoute",
})
}
func patch3(scanner *bufio.Scanner) error {
return createTable("registration_logs", "", "",
[]tC{
{"rlid", "int", 0, false, true, ""},
ccol("username", 100, ""),
ccol("email", 100, ""),
ccol("failureReason", 100, ""),
bcol("success", false), // Did this attempt succeed?
ccol("ipaddress", 200, ""),
{"doneAt", "createdAt", 0, false, false, ""},
},
[]tK{
{"rlid", "primary", "", false},
},
)
}
func patch4(scanner *bufio.Scanner) error {
routes := map[string]string{
"routeReportSubmit": "routes.ReportSubmit",
"routeAccountEditEmail": "routes.AccountEditEmail",
"routeAccountEditEmailTokenSubmit": "routes.AccountEditEmailTokenSubmit",
"routePanelLogsRegs": "panel.LogsRegs",
"routePanelLogsMod": "panel.LogsMod",
"routePanelLogsAdmin": "panel.LogsAdmin",
"routePanelDebug": "panel.Debug",
"routePanelAnalyticsViews": "panel.AnalyticsViews",
"routePanelAnalyticsRouteViews": "panel.AnalyticsRouteViews",
"routePanelAnalyticsAgentViews": "panel.AnalyticsAgentViews",
"routePanelAnalyticsForumViews": "panel.AnalyticsForumViews",
"routePanelAnalyticsSystemViews": "panel.AnalyticsSystemViews",
"routePanelAnalyticsLanguageViews": "panel.AnalyticsLanguageViews",
"routePanelAnalyticsReferrerViews": "panel.AnalyticsReferrerViews",
"routePanelAnalyticsTopics": "panel.AnalyticsTopics",
"routePanelAnalyticsPosts": "panel.AnalyticsPosts",
"routePanelAnalyticsForums": "panel.AnalyticsForums",
"routePanelAnalyticsRoutes": "panel.AnalyticsRoutes",
"routePanelAnalyticsAgents": "panel.AnalyticsAgents",
"routePanelAnalyticsSystems": "panel.AnalyticsSystems",
"routePanelAnalyticsLanguages": "panel.AnalyticsLanguages",
"routePanelAnalyticsReferrers": "panel.AnalyticsReferrers",
"routePanelSettings": "panel.Settings",
"routePanelSettingEdit": "panel.SettingEdit",
"routePanelSettingEditSubmit": "panel.SettingEditSubmit",
"routePanelForums": "panel.Forums",
"routePanelForumsCreateSubmit": "panel.ForumsCreateSubmit",
"routePanelForumsDelete": "panel.ForumsDelete",
"routePanelForumsDeleteSubmit": "panel.ForumsDeleteSubmit",
"routePanelForumsEdit": "panel.ForumsEdit",
"routePanelForumsEditSubmit": "panel.ForumsEditSubmit",
"routePanelForumsEditPermsSubmit": "panel.ForumsEditPermsSubmit",
"routePanelForumsEditPermsAdvance": "panel.ForumsEditPermsAdvance",
"routePanelForumsEditPermsAdvanceSubmit": "panel.ForumsEditPermsAdvanceSubmit",
"routePanelBackups": "panel.Backups",
}
e := renameRoutes(routes)
if e != nil {
return e
}
e = execStmt(qgen.Builder.SimpleDelete("settings", "name='url_tags'"))
if e != nil {
return e
}
return createTable("pages", "utf8mb4", "utf8mb4_general_ci",
[]tC{
{"pid", "int", 0, false, true, ""},
ccol("name", 200, ""),
ccol("title", 200, ""),
{"body", "text", 0, false, false, ""},
{"allowedGroups", "text", 0, false, false, ""},
{"menuID", "int", 0, false, false, "-1"},
},
[]tK{
{"pid", "primary", "", false},
},
)
}
func patch5(scanner *bufio.Scanner) error {
routes := map[string]string{
"routePanelUsers": "panel.Users",
"routePanelUsersEdit": "panel.UsersEdit",
"routePanelUsersEditSubmit": "panel.UsersEditSubmit",
"routes.AccountEditCritical": "routes.AccountEditPassword",
"routes.AccountEditCriticalSubmit": "routes.AccountEditPasswordSubmit",
}
e := renameRoutes(routes)
if e != nil {
return e
}
e = execStmt(qgen.Builder.SimpleUpdate("menu_items", "path='/user/edit/'", "path='/user/edit/critical/'"))
if e != nil {
return e
}
return createTable("users_2fa_keys", "utf8mb4", "utf8mb4_general_ci",
[]tC{
{"uid", "int", 0, false, false, ""},
ccol("secret", 100, ""),
ccol("scratch1", 50, ""),
ccol("scratch2", 50, ""),
ccol("scratch3", 50, ""),
ccol("scratch4", 50, ""),
ccol("scratch5", 50, ""),
ccol("scratch6", 50, ""),
ccol("scratch7", 50, ""),
ccol("scratch8", 50, ""),
{"createdAt", "createdAt", 0, false, false, ""},
},
[]tK{
{"uid", "primary", "", false},
},
)
}
func patch6(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.SimpleInsert("settings", "name, content, type", "'rapid_loading','1','bool'"))
}
func patch7(scanner *bufio.Scanner) error {
return createTable("users_avatar_queue", "", "",
[]tC{
{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
},
[]tK{
{"uid", "primary", "", false},
},
)
}
func renameRoutes(routes map[string]string) error {
// ! Don't reuse this function blindly, it doesn't escape apostrophes
replaceTextWhere := func(replaceThis string, withThis string) error {
return execStmt(qgen.Builder.SimpleUpdate("viewchunks", "route = '"+withThis+"'", "route = '"+replaceThis+"'"))
}
for key, value := range routes {
e := replaceTextWhere(key, value)
if e != nil {
return e
}
}
return nil
}
func patch8(scanner *bufio.Scanner) error {
routes := map[string]string{
"routePanelWordFilter": "panel.WordFilters",
"routePanelWordFiltersCreateSubmit": "panel.WordFiltersCreateSubmit",
"routePanelWordFiltersEdit": "panel.WordFiltersEdit",
"routePanelWordFiltersEditSubmit": "panel.WordFiltersEditSubmit",
"routePanelWordFiltersDeleteSubmit": "panel.WordFiltersDeleteSubmit",
"routePanelPlugins": "panel.Plugins",
"routePanelPluginsActivate": "panel.PluginsActivate",
"routePanelPluginsDeactivate": "panel.PluginsDeactivate",
"routePanelPluginsInstall": "panel.PluginsInstall",
"routePanelGroups": "panel.Groups",
"routePanelGroupsEdit": "panel.GroupsEdit",
"routePanelGroupsEditPerms": "panel.GroupsEditPerms",
"routePanelGroupsEditSubmit": "panel.GroupsEditSubmit",
"routePanelGroupsEditPermsSubmit": "panel.GroupsEditPermsSubmit",
"routePanelGroupsCreateSubmit": "panel.GroupsCreateSubmit",
"routePanelThemes": "panel.Themes",
"routePanelThemesSetDefault": "panel.ThemesSetDefault",
"routePanelThemesMenus": "panel.ThemesMenus",
"routePanelThemesMenusEdit": "panel.ThemesMenusEdit",
"routePanelThemesMenuItemEdit": "panel.ThemesMenuItemEdit",
"routePanelThemesMenuItemEditSubmit": "panel.ThemesMenuItemEditSubmit",
"routePanelThemesMenuItemCreateSubmit": "panel.ThemesMenuItemCreateSubmit",
"routePanelThemesMenuItemDeleteSubmit": "panel.ThemesMenuItemDeleteSubmit",
"routePanelThemesMenuItemOrderSubmit": "panel.ThemesMenuItemOrderSubmit",
"routePanelDashboard": "panel.Dashboard",
}
e := renameRoutes(routes)
if e != nil {
return e
}
return createTable("updates", "", "",
[]tC{
{"dbVersion", "int", 0, false, false, "0"},
}, nil,
)
}
func patch9(scanner *bufio.Scanner) error {
// Table "updates" might not exist due to the installer, so drop it and remake it if so
err := patch8(scanner)
if err != nil {
return err
}
return createTable("login_logs", "", "",
[]tC{
{"lid", "int", 0, false, true, ""},
{"uid", "int", 0, false, false, ""},
bcol("success", false), // Did this attempt succeed?
ccol("ipaddress", 200, ""),
{"doneAt", "createdAt", 0, false, false, ""},
},
[]tK{
{"lid", "primary", "", false},
},
)
}
var acc = qgen.NewAcc
var itoa = strconv.Itoa
func patch10(scanner *bufio.Scanner) error {
err := execStmt(qgen.Builder.AddColumn("topics", tC{"attachCount", "int", 0, false, false, "0"}, nil))
if err != nil {
return err
}
err = execStmt(qgen.Builder.AddColumn("topics", tC{"lastReplyID", "int", 0, false, false, "0"}, nil))
if err != nil {
return err
}
err = acc().Select("topics").Cols("tid").EachInt(func(tid int) error {
stid := itoa(tid)
count, err := acc().Count("attachments").Where("originTable = 'topics' and originID=" + stid).Total()
if err != nil {
return err
}
hasReply := false
err = acc().Select("replies").Cols("rid").Where("tid=" + stid).Orderby("rid DESC").Limit("1").EachInt(func(rid int) error {
hasReply = true
_, err := acc().Update("topics").Set("lastReplyID=?, attachCount=?").Where("tid="+stid).Exec(rid, count)
return err
})
if err != nil {
return err
}
if !hasReply {
_, err = acc().Update("topics").Set("attachCount=?").Where("tid=" + stid).Exec(count)
}
return err
})
if err != nil {
return err
}
_, err = acc().Insert("updates").Columns("dbVersion").Fields("0").Exec()
return err
}
func patch11(scanner *bufio.Scanner) error {
err := execStmt(qgen.Builder.AddColumn("replies", tC{"attachCount", "int", 0, false, false, "0"}, nil))
if err != nil {
return err
}
// Attachments for replies got the topicID rather than the replyID for a while in error, so we want to separate these out
_, err = acc().Update("attachments").Set("originTable='freplies'").Where("originTable='replies'").Exec()
if err != nil {
return err
}
// We could probably do something more efficient, but as there shouldn't be too many sites right now, we can probably cheat a little, otherwise it'll take forever to get things done
return acc().Select("topics").Cols("tid").EachInt(func(tid int) error {
stid := itoa(tid)
count, err := acc().Count("attachments").Where("originTable='topics' and originID=" + stid).Total()
if err != nil {
return err
}
_, err = acc().Update("topics").Set("attachCount=?").Where("tid=" + stid).Exec(count)
return err
})
/*return acc().Select("replies").Cols("rid").EachInt(func(rid int) error {
srid := itoa(rid)
count, err := acc().Count("attachments").Where("originTable='replies' and originID=" + srid).Total()
if err != nil {
return err
}
_, err = acc().Update("replies").Set("attachCount = ?").Where("rid=" + srid).Exec(count)
return err
})*/
}
func patch12(scanner *bufio.Scanner) error {
var e error
addIndex := func(tbl, iname, colname string) {
if e != nil {
return
}
/*e = execStmt(qgen.Builder.RemoveIndex(tbl, iname))
if e != nil {
return
}*/
e = execStmt(qgen.Builder.AddIndex(tbl, iname, colname))
}
addIndex("topics", "parentID", "parentID")
addIndex("replies", "tid", "tid")
addIndex("polls", "parentID", "parentID")
addIndex("likes", "targetItem", "targetItem")
addIndex("emails", "uid", "uid")
addIndex("attachments", "originID", "originID")
addIndex("attachments", "path", "path")
addIndex("activity_stream_matches", "watcher", "watcher")
return e
}
func patch13(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.AddColumn("widgets", tC{"wid", "int", 0, false, true, ""}, &tK{"wid", "primary", "", false}))
}
func patch14(scanner *bufio.Scanner) error {
/*err := execStmt(qgen.Builder.AddKey("topics", "title", tK{"title", "fulltext", "", false}))
if err != nil {
return err
}
err = execStmt(qgen.Builder.AddKey("topics", "content", tK{"content", "fulltext", "", false}))
if err != nil {
return err
}
err = execStmt(qgen.Builder.AddKey("replies", "content", tK{"content", "fulltext", "", false}))
if err != nil {
return err
}*/
return nil
}
func patch15(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.SimpleInsert("settings", "name, content, type", "'google_site_verify','','html-attribute'"))
}
func patch16(scanner *bufio.Scanner) error {
return createTable("password_resets", "", "",
[]tC{
ccol("email", 200, ""),
{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
ccol("validated", 200, ""), // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token
ccol("token", 200, ""),
{"createdAt", "createdAt", 0, false, false, ""},
}, nil,
)
}
func patch17(scanner *bufio.Scanner) error {
err := execStmt(qgen.Builder.AddColumn("attachments", ccol("extra", 200, ""), nil))
if err != nil {
return err
}
err = acc().Select("topics").Cols("tid,parentID").Where("attachCount > 0").Each(func(rows *sql.Rows) error {
var tid, parentID int
err := rows.Scan(&tid, &parentID)
if err != nil {
return err
}
_, err = acc().Update("attachments").Set("sectionID=?").Where("originTable='topics' AND originID=?").Exec(parentID, tid)
return err
})
if err != nil {
return err
}
return acc().Select("replies").Cols("rid,tid").Where("attachCount > 0").Each(func(rows *sql.Rows) error {
var rid, tid, sectionID int
err := rows.Scan(&rid, &tid)
if err != nil {
return err
}
err = acc().Select("topics").Cols("parentID").Where("tid=?").QueryRow(tid).Scan(&sectionID)
if err != nil {
return err
}
_, err = acc().Update("attachments").Set("sectionID=?, extra=?").Where("originTable='replies' AND originID=?").Exec(sectionID, tid, rid)
return err
})
}
func patch18(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.AddColumn("forums", tC{"order", "int", 0, false, false, "0"}, nil))
}
func patch19(scanner *bufio.Scanner) error {
return createTable("memchunks", "", "",
[]tC{
{"count", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
}, nil,
)
}
func patch20(scanner *bufio.Scanner) error {
err := acc().Select("activity_stream_matches").Cols("asid").Each(func(rows *sql.Rows) error {
var asid int
if e := rows.Scan(&asid); e != nil {
return e
}
e := acc().Select("activity_stream").Cols("asid").Where("asid=?").QueryRow(asid).Scan(&asid)
if e != sql.ErrNoRows {
return e
}
_, e = acc().Delete("activity_stream_matches").Where("asid=?").Run(asid)
return e
})
if err != nil {
return err
}
return execStmt(qgen.Builder.AddForeignKey("activity_stream_matches", "asid", "activity_stream", "asid", true))
}
func patch21(scanner *bufio.Scanner) error {
err := execStmt(qgen.Builder.AddColumn("memchunks", tC{"stack", "int", 0, false, false, "0"}, nil))
if err != nil {
return err
}
err = execStmt(qgen.Builder.AddColumn("memchunks", tC{"heap", "int", 0, false, false, "0"}, nil))
if err != nil {
return err
}
err = createTable("meta", "", "",
[]tC{
ccol("name", 200, ""),
ccol("value", 200, ""),
}, nil,
)
if err != nil {
return err
}
return execStmt(qgen.Builder.AddColumn("activity_stream", tC{"createdAt", "createdAt", 0, false, false, ""}, nil))
}
func patch22(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.AddColumn("forums", ccol("tmpl", 200, "''"), nil))
}
func patch23(scanner *bufio.Scanner) error {
err := createTable("conversations", "", "",
[]tC{
{"cid", "int", 0, false, true, ""},
{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
{"createdAt", "createdAt", 0, false, false, ""},
{"lastReplyAt", "datetime", 0, false, false, ""},
{"lastReplyBy", "int", 0, false, false, ""},
},
[]tK{
{"cid", "primary", "", false},
},
)
if err != nil {
return err
}
err = createTable("conversations_posts", "", "",
[]tC{
{"pid", "int", 0, false, true, ""},
{"cid", "int", 0, false, false, ""},
{"createdBy", "int", 0, false, false, ""},
ccol("body", 50, ""),
ccol("post", 50, "''"),
},
[]tK{
{"pid", "primary", "", false},
},
)
if err != nil {
return err
}
return createTable("conversations_participants", "", "",
[]tC{
{"uid", "int", 0, false, false, ""},
{"cid", "int", 0, false, false, ""},
}, nil,
)
}
func patch24(scanner *bufio.Scanner) error {
return createTable("users_groups_promotions", "", "",
[]tC{
{"pid", "int", 0, false, true, ""},
{"from_gid", "int", 0, false, false, ""},
{"to_gid", "int", 0, false, false, ""},
bcol("two_way", false), // If a user no longer meets the requirements for this promotion then they will be demoted if this flag is set
// Requirements
{"level", "int", 0, false, false, ""},
{"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted
},
[]tK{
{"pid", "primary", "", false},
},
)
}
func patch25(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.AddColumn("users_groups_promotions", tC{"posts", "int", 0, false, false, "0"}, nil))
}
func patch26(scanner *bufio.Scanner) error {
return createTable("users_blocks", "", "",
[]tC{
{"blocker", "int", 0, false, false, ""},
{"blockedUser", "int", 0, false, false, ""},
}, nil,
)
}
func patch27(scanner *bufio.Scanner) error {
err := execStmt(qgen.Builder.AddColumn("moderation_logs", tC{"extra", "text", 0, false, false, ""}, nil))
if err != nil {
return err
}
return execStmt(qgen.Builder.AddColumn("administration_logs", tC{"extra", "text", 0, false, false, ""}, nil))
}
func patch28(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.AddColumn("users", tC{"enable_embeds", "int", 0, false, false, "-1"}, nil))
}
// The word counter might run into problems with some languages where words aren't as obviously demarcated, I would advise turning it off in those cases, or if it becomes annoying in general, really.
func WordCount(input string) (count int) {
input = strings.TrimSpace(input)
if input == "" {
return 0
}
var inSpace bool
for _, value := range input {
if unicode.IsSpace(value) || unicode.IsPunct(value) {
if !inSpace {
inSpace = true
}
} else if inSpace {
count++
inSpace = false
}
}
return count + 1
}
func patch29(scanner *bufio.Scanner) error {
f := func(tbl, idCol string) error {
return acc().Select(tbl).Cols(idCol + ",content").Each(func(rows *sql.Rows) error {
var id int
var content string
err := rows.Scan(&id, &content)
if err != nil {
return err
}
_, err = acc().Update(tbl).Set("words=?").Where(idCol+"=?").Exec(WordCount(content), id)
return err
})
}
err := f("topics", "tid")
if err != nil {
return err
}
err = f("replies", "rid")
if err != nil {
return err
}
meta, err := meta.NewDefaultMetaStore(acc())
if err != nil {
return err
}
err = meta.Set("sched", "recalc")
if err != nil {
return err
}
fixCols := func(tbls ...string) error {
for _, tbl := range tbls {
//err := execStmt(qgen.Builder.RenameColumn(tbl, "ipaddress","ip"))
err := execStmt(qgen.Builder.ChangeColumn(tbl, "ipaddress", ccol("ip", 200, "''")))
if err != nil {
return err
}
err = execStmt(qgen.Builder.SetDefaultColumn(tbl, "ip", "varchar", ""))
if err != nil {
return err
}
}
return nil
}
err = fixCols("topics", "replies", "polls_votes", "users_replies")
if err != nil {
return err
}
err = execStmt(qgen.Builder.SetDefaultColumn("replies", "lastEdit", "int", "0"))
if err != nil {
return err
}
err = execStmt(qgen.Builder.SetDefaultColumn("replies", "lastEditBy", "int", "0"))
if err != nil {
return err
}
err = execStmt(qgen.Builder.SetDefaultColumn("users_replies", "lastEdit", "int", "0"))
if err != nil {
return err
}
err = execStmt(qgen.Builder.SetDefaultColumn("users_replies", "lastEditBy", "int", "0"))
if err != nil {
return err
}
return execStmt(qgen.Builder.AddColumn("activity_stream", tC{"extra", "varchar", 200, false, false, "''"}, nil))
}
func patch30(scanner *bufio.Scanner) error {
e := execStmt(qgen.Builder.AddColumn("users_groups_promotions", tC{"registeredFor", "int", 0, false, false, "0"}, nil))
if e != nil {
return e
}
return execStmt(qgen.Builder.SetDefaultColumn("users", "last_ip", "varchar", ""))
}
func patch31(scanner *bufio.Scanner) (e error) {
addKey := func(tbl, col string, tk qgen.DBTableKey) error {
/*e := execStmt(qgen.Builder.RemoveIndex(tbl, col))
if e != nil {
return e
}*/
return execStmt(qgen.Builder.AddKey(tbl, col, tk))
}
e = addKey("topics", "title", tK{"title", "fulltext", "", false})
if e != nil {
return e
}
e = addKey("topics", "content", tK{"content", "fulltext", "", false})
if e != nil {
return e
}
return addKey("replies", "content", tK{"content", "fulltext", "", false})
}
func createTable(tbl, charset, collation string, cols []tC, keys []tK) error {
e := execStmt(qgen.Builder.DropTable(tbl))
if e != nil {
return e
}
return execStmt(qgen.Builder.CreateTable(tbl, charset, collation, cols, keys))
}
func patch32(scanner *bufio.Scanner) error {
return createTable("perfchunks", "", "",
[]tC{
{"low", "int", 0, false, false, "0"},
{"high", "int", 0, false, false, "0"},
{"avg", "int", 0, false, false, "0"},
{"createdAt", "datetime", 0, false, false, ""},
}, nil,
)
}
func patch33(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.AddColumn("viewchunks", tC{"avg", "int", 0, false, false, "0"}, nil))
}
func patch34(scanner *bufio.Scanner) error {
/*err := createTable("tables", "", "",
[]tC{
{"id", "int", 0, false, true, ""},
ccol("name", 200, ""),
},
[]tK{
{"id", "primary", "", false},
{"name", "unique", "", false},
},
)
if err != nil {
return err
}
insert := func(tbl, cols, fields string) {
if err != nil {
return
}
err = execStmt(qgen.Builder.SimpleInsert(tbl, cols, fields))
}
insert("tables", "name", "forums")
insert("tables", "name", "topics")
insert("tables", "name", "replies")
// ! Hold onto freplies for a while longer
insert("tables", "name", "freplies")*/
/*err := execStmt(qgen.Builder.AddColumn("topics", tC{"attachCount", "int", 0, false, false, "0"}, nil))
if err != nil {
return err
}*/
overwriteColumn := func(tbl, col string, tc qgen.DBTableColumn) error {
/*e := execStmt(qgen.Builder.DropColumn(tbl, col))
if e != nil {
return e
}*/
return execStmt(qgen.Builder.AddColumn(tbl, tc, nil))
}
err := overwriteColumn("users", "profile_comments", tC{"profile_comments", "int", 0, false, false, "0"})
if err != nil {
return err
}
err = overwriteColumn("users", "who_can_convo", tC{"who_can_convo", "int", 0, false, false, "0"})
if err != nil {
return err
}
setDefault := func(tbl, col, typ, val string) {
if err != nil {
return
}
err = execStmt(qgen.Builder.SetDefaultColumn(tbl, col, typ, val))
}
setDefault("users_groups", "permissions", "text", "{}")
setDefault("users_groups", "plugin_perms", "text", "{}")
setDefault("forums_permissions", "permissions", "text", "{}")
setDefault("topics", "content", "text", "")
setDefault("topics", "parsed_content", "text", "")
setDefault("replies", "content", "text", "")
setDefault("replies", "parsed_content", "text", "")
//setDefault("revisions", "content", "text", "")
setDefault("users_replies", "content", "text", "")
setDefault("users_replies", "parsed_content", "text", "")
setDefault("pages", "body", "text", "")
setDefault("pages", "allowedGroups", "text", "")
setDefault("moderation_logs", "extra", "text", "")
setDefault("administration_logs", "extra", "text", "")
if err != nil {
return err
}
return nil
}
func patch35(scanner *bufio.Scanner) error {
e := execStmt(qgen.Builder.AddColumn("topics", tC{"weekEvenViews", "int", 0, false, false, "0"}, nil))
if e != nil {
return e
}
return execStmt(qgen.Builder.AddColumn("topics", tC{"weekOddViews", "int", 0, false, false, "0"}, nil))
}
func patch36(scanner *bufio.Scanner) error {
e := createTable("forums_actions", "utf8mb4", "utf8mb4_general_ci",
[]tC{
{"faid", "int", 0, false, true, ""},
{"fid", "int", 0, false, false, ""},
bcol("runOnTopicCreation", false),
{"runDaysAfterTopicCreation", "int", 0, false, false, "0"},
{"runDaysAfterTopicLastReply", "int", 0, false, false, "0"},
ccol("action", 50, ""),
ccol("extra", 200, "''"),
},
[]tK{
{"faid", "primary", "", false},
},
)
if e != nil {
return e
}
return execStmt(qgen.Builder.SimpleInsert("settings", "name, content, type, constraints", "'avatar_visibility','0','list','0-1'"))
}