github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/patcher/patches.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "database/sql" 6 "strconv" 7 "strings" 8 "unicode" 9 10 meta "github.com/Azareal/Gosora/common/meta" 11 qgen "github.com/Azareal/Gosora/query_gen" 12 ) 13 14 type tblColumn = qgen.DBTableColumn 15 type tC = tblColumn 16 type tblKey = qgen.DBTableKey 17 type tK = tblKey 18 19 func init() { 20 addPatch(0, patch0) 21 addPatch(1, patch1) 22 addPatch(2, patch2) 23 addPatch(3, patch3) 24 addPatch(4, patch4) 25 addPatch(5, patch5) 26 addPatch(6, patch6) 27 addPatch(7, patch7) 28 addPatch(8, patch8) 29 addPatch(9, patch9) 30 addPatch(10, patch10) 31 addPatch(11, patch11) 32 addPatch(12, patch12) 33 addPatch(13, patch13) 34 addPatch(14, patch14) 35 addPatch(15, patch15) 36 addPatch(16, patch16) 37 addPatch(17, patch17) 38 addPatch(18, patch18) 39 addPatch(19, patch19) 40 addPatch(20, patch20) 41 addPatch(21, patch21) 42 addPatch(22, patch22) 43 addPatch(23, patch23) 44 addPatch(24, patch24) 45 addPatch(25, patch25) 46 addPatch(26, patch26) 47 addPatch(27, patch27) 48 addPatch(28, patch28) 49 addPatch(29, patch29) 50 addPatch(30, patch30) 51 addPatch(31, patch31) 52 addPatch(32, patch32) 53 addPatch(33, patch33) 54 addPatch(34, patch34) 55 addPatch(35, patch35) 56 addPatch(36, patch36) 57 } 58 59 func bcol(col string, val bool) qgen.DBTableColumn { 60 if val { 61 return tC{col, "boolean", 0, false, false, "1"} 62 } 63 return tC{col, "boolean", 0, false, false, "0"} 64 } 65 func ccol(col string, size int, sdefault string) qgen.DBTableColumn { 66 return tC{col, "varchar", size, false, false, sdefault} 67 } 68 69 func patch0(scanner *bufio.Scanner) (err error) { 70 err = createTable("menus", "", "", 71 []tC{ 72 {"mid", "int", 0, false, true, ""}, 73 }, 74 []tK{ 75 {"mid", "primary", "", false}, 76 }, 77 ) 78 if err != nil { 79 return err 80 } 81 82 err = createTable("menu_items", "", "", 83 []tC{ 84 {"miid", "int", 0, false, true, ""}, 85 {"mid", "int", 0, false, false, ""}, 86 ccol("name", 200, ""), 87 ccol("htmlID", 200, "''"), 88 ccol("cssClass", 200, "''"), 89 ccol("position", 100, ""), 90 ccol("path", 200, "''"), 91 ccol("aria", 200, "''"), 92 ccol("tooltip", 200, "''"), 93 ccol("tmplName", 200, "''"), 94 {"order", "int", 0, false, false, "0"}, 95 96 bcol("guestOnly", false), 97 bcol("memberOnly", false), 98 bcol("staffOnly", false), 99 bcol("adminOnly", false), 100 }, 101 []tK{ 102 {"miid", "primary", "", false}, 103 }, 104 ) 105 if err != nil { 106 return err 107 } 108 109 err = execStmt(qgen.Builder.SimpleInsert("menus", "", "")) 110 if err != nil { 111 return err 112 } 113 114 var order int 115 mOrder := "mid, name, htmlID, cssClass, position, path, aria, tooltip, guestOnly, memberOnly, staffOnly, adminOnly" 116 addMenuItem := func(data map[string]interface{}) error { 117 cols, values := qgen.InterfaceMapToInsertStrings(data, mOrder) 118 err := execStmt(qgen.Builder.SimpleInsert("menu_items", cols+", order", values+","+strconv.Itoa(order))) 119 order++ 120 return err 121 } 122 123 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}"}) 124 if err != nil { 125 return err 126 } 127 128 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}"}) 129 if err != nil { 130 return err 131 } 132 133 err = addMenuItem(map[string]interface{}{"mid": 1, "htmlID": "general_alerts", "cssClass": "menu_alerts", "position": "right", "tmplName": "menu_alerts"}) 134 if err != nil { 135 return err 136 } 137 138 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}) 139 if err != nil { 140 return err 141 } 142 143 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}) 144 if err != nil { 145 return err 146 } 147 148 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}) 149 if err != nil { 150 return err 151 } 152 153 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}) 154 if err != nil { 155 return err 156 } 157 158 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}) 159 if err != nil { 160 return err 161 } 162 163 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}) 164 if err != nil { 165 return err 166 } 167 168 return nil 169 } 170 171 func patch1(scanner *bufio.Scanner) error { 172 return renameRoutes(map[string]string{ 173 "routeAccountEditCriticalSubmit": "routes.AccountEditCriticalSubmit", 174 "routeAccountEditAvatar": "routes.AccountEditAvatar", 175 "routeAccountEditAvatarSubmit": "routes.AccountEditAvatarSubmit", 176 "routeAccountEditUsername": "routes.AccountEditUsername", 177 "routeAccountEditUsernameSubmit": "routes.AccountEditUsernameSubmit", 178 }) 179 } 180 181 func patch2(scanner *bufio.Scanner) error { 182 return renameRoutes(map[string]string{ 183 "routeLogout": "routes.AccountLogout", 184 "routeShowAttachment": "routes.ShowAttachment", 185 "routeChangeTheme": "routes.ChangeTheme", 186 "routeProfileReplyCreateSubmit": "routes.ProfileReplyCreateSubmit", 187 "routeLikeTopicSubmit": "routes.LikeTopicSubmit", 188 "routeReplyLikeSubmit": "routes.ReplyLikeSubmit", 189 "routeDynamic": "routes.DynamicRoute", 190 "routeUploads": "routes.UploadedFile", 191 "BadRoute": "routes.BadRoute", 192 }) 193 } 194 195 func patch3(scanner *bufio.Scanner) error { 196 return createTable("registration_logs", "", "", 197 []tC{ 198 {"rlid", "int", 0, false, true, ""}, 199 ccol("username", 100, ""), 200 ccol("email", 100, ""), 201 ccol("failureReason", 100, ""), 202 bcol("success", false), // Did this attempt succeed? 203 ccol("ipaddress", 200, ""), 204 {"doneAt", "createdAt", 0, false, false, ""}, 205 }, 206 []tK{ 207 {"rlid", "primary", "", false}, 208 }, 209 ) 210 } 211 212 func patch4(scanner *bufio.Scanner) error { 213 routes := map[string]string{ 214 "routeReportSubmit": "routes.ReportSubmit", 215 "routeAccountEditEmail": "routes.AccountEditEmail", 216 "routeAccountEditEmailTokenSubmit": "routes.AccountEditEmailTokenSubmit", 217 "routePanelLogsRegs": "panel.LogsRegs", 218 "routePanelLogsMod": "panel.LogsMod", 219 "routePanelLogsAdmin": "panel.LogsAdmin", 220 "routePanelDebug": "panel.Debug", 221 "routePanelAnalyticsViews": "panel.AnalyticsViews", 222 "routePanelAnalyticsRouteViews": "panel.AnalyticsRouteViews", 223 "routePanelAnalyticsAgentViews": "panel.AnalyticsAgentViews", 224 "routePanelAnalyticsForumViews": "panel.AnalyticsForumViews", 225 "routePanelAnalyticsSystemViews": "panel.AnalyticsSystemViews", 226 "routePanelAnalyticsLanguageViews": "panel.AnalyticsLanguageViews", 227 "routePanelAnalyticsReferrerViews": "panel.AnalyticsReferrerViews", 228 "routePanelAnalyticsTopics": "panel.AnalyticsTopics", 229 "routePanelAnalyticsPosts": "panel.AnalyticsPosts", 230 "routePanelAnalyticsForums": "panel.AnalyticsForums", 231 "routePanelAnalyticsRoutes": "panel.AnalyticsRoutes", 232 "routePanelAnalyticsAgents": "panel.AnalyticsAgents", 233 "routePanelAnalyticsSystems": "panel.AnalyticsSystems", 234 "routePanelAnalyticsLanguages": "panel.AnalyticsLanguages", 235 "routePanelAnalyticsReferrers": "panel.AnalyticsReferrers", 236 "routePanelSettings": "panel.Settings", 237 "routePanelSettingEdit": "panel.SettingEdit", 238 "routePanelSettingEditSubmit": "panel.SettingEditSubmit", 239 "routePanelForums": "panel.Forums", 240 "routePanelForumsCreateSubmit": "panel.ForumsCreateSubmit", 241 "routePanelForumsDelete": "panel.ForumsDelete", 242 "routePanelForumsDeleteSubmit": "panel.ForumsDeleteSubmit", 243 "routePanelForumsEdit": "panel.ForumsEdit", 244 "routePanelForumsEditSubmit": "panel.ForumsEditSubmit", 245 "routePanelForumsEditPermsSubmit": "panel.ForumsEditPermsSubmit", 246 "routePanelForumsEditPermsAdvance": "panel.ForumsEditPermsAdvance", 247 "routePanelForumsEditPermsAdvanceSubmit": "panel.ForumsEditPermsAdvanceSubmit", 248 "routePanelBackups": "panel.Backups", 249 } 250 e := renameRoutes(routes) 251 if e != nil { 252 return e 253 } 254 255 e = execStmt(qgen.Builder.SimpleDelete("settings", "name='url_tags'")) 256 if e != nil { 257 return e 258 } 259 260 return createTable("pages", "utf8mb4", "utf8mb4_general_ci", 261 []tC{ 262 {"pid", "int", 0, false, true, ""}, 263 ccol("name", 200, ""), 264 ccol("title", 200, ""), 265 {"body", "text", 0, false, false, ""}, 266 {"allowedGroups", "text", 0, false, false, ""}, 267 {"menuID", "int", 0, false, false, "-1"}, 268 }, 269 []tK{ 270 {"pid", "primary", "", false}, 271 }, 272 ) 273 } 274 275 func patch5(scanner *bufio.Scanner) error { 276 routes := map[string]string{ 277 "routePanelUsers": "panel.Users", 278 "routePanelUsersEdit": "panel.UsersEdit", 279 "routePanelUsersEditSubmit": "panel.UsersEditSubmit", 280 "routes.AccountEditCritical": "routes.AccountEditPassword", 281 "routes.AccountEditCriticalSubmit": "routes.AccountEditPasswordSubmit", 282 } 283 e := renameRoutes(routes) 284 if e != nil { 285 return e 286 } 287 288 e = execStmt(qgen.Builder.SimpleUpdate("menu_items", "path='/user/edit/'", "path='/user/edit/critical/'")) 289 if e != nil { 290 return e 291 } 292 293 return createTable("users_2fa_keys", "utf8mb4", "utf8mb4_general_ci", 294 []tC{ 295 {"uid", "int", 0, false, false, ""}, 296 ccol("secret", 100, ""), 297 ccol("scratch1", 50, ""), 298 ccol("scratch2", 50, ""), 299 ccol("scratch3", 50, ""), 300 ccol("scratch4", 50, ""), 301 ccol("scratch5", 50, ""), 302 ccol("scratch6", 50, ""), 303 ccol("scratch7", 50, ""), 304 ccol("scratch8", 50, ""), 305 {"createdAt", "createdAt", 0, false, false, ""}, 306 }, 307 []tK{ 308 {"uid", "primary", "", false}, 309 }, 310 ) 311 } 312 313 func patch6(scanner *bufio.Scanner) error { 314 return execStmt(qgen.Builder.SimpleInsert("settings", "name, content, type", "'rapid_loading','1','bool'")) 315 } 316 317 func patch7(scanner *bufio.Scanner) error { 318 return createTable("users_avatar_queue", "", "", 319 []tC{ 320 {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key 321 }, 322 []tK{ 323 {"uid", "primary", "", false}, 324 }, 325 ) 326 } 327 328 func renameRoutes(routes map[string]string) error { 329 // ! Don't reuse this function blindly, it doesn't escape apostrophes 330 replaceTextWhere := func(replaceThis string, withThis string) error { 331 return execStmt(qgen.Builder.SimpleUpdate("viewchunks", "route = '"+withThis+"'", "route = '"+replaceThis+"'")) 332 } 333 334 for key, value := range routes { 335 e := replaceTextWhere(key, value) 336 if e != nil { 337 return e 338 } 339 } 340 341 return nil 342 } 343 344 func patch8(scanner *bufio.Scanner) error { 345 routes := map[string]string{ 346 "routePanelWordFilter": "panel.WordFilters", 347 "routePanelWordFiltersCreateSubmit": "panel.WordFiltersCreateSubmit", 348 "routePanelWordFiltersEdit": "panel.WordFiltersEdit", 349 "routePanelWordFiltersEditSubmit": "panel.WordFiltersEditSubmit", 350 "routePanelWordFiltersDeleteSubmit": "panel.WordFiltersDeleteSubmit", 351 "routePanelPlugins": "panel.Plugins", 352 "routePanelPluginsActivate": "panel.PluginsActivate", 353 "routePanelPluginsDeactivate": "panel.PluginsDeactivate", 354 "routePanelPluginsInstall": "panel.PluginsInstall", 355 "routePanelGroups": "panel.Groups", 356 "routePanelGroupsEdit": "panel.GroupsEdit", 357 "routePanelGroupsEditPerms": "panel.GroupsEditPerms", 358 "routePanelGroupsEditSubmit": "panel.GroupsEditSubmit", 359 "routePanelGroupsEditPermsSubmit": "panel.GroupsEditPermsSubmit", 360 "routePanelGroupsCreateSubmit": "panel.GroupsCreateSubmit", 361 "routePanelThemes": "panel.Themes", 362 "routePanelThemesSetDefault": "panel.ThemesSetDefault", 363 "routePanelThemesMenus": "panel.ThemesMenus", 364 "routePanelThemesMenusEdit": "panel.ThemesMenusEdit", 365 "routePanelThemesMenuItemEdit": "panel.ThemesMenuItemEdit", 366 "routePanelThemesMenuItemEditSubmit": "panel.ThemesMenuItemEditSubmit", 367 "routePanelThemesMenuItemCreateSubmit": "panel.ThemesMenuItemCreateSubmit", 368 "routePanelThemesMenuItemDeleteSubmit": "panel.ThemesMenuItemDeleteSubmit", 369 "routePanelThemesMenuItemOrderSubmit": "panel.ThemesMenuItemOrderSubmit", 370 "routePanelDashboard": "panel.Dashboard", 371 } 372 e := renameRoutes(routes) 373 if e != nil { 374 return e 375 } 376 377 return createTable("updates", "", "", 378 []tC{ 379 {"dbVersion", "int", 0, false, false, "0"}, 380 }, nil, 381 ) 382 } 383 384 func patch9(scanner *bufio.Scanner) error { 385 // Table "updates" might not exist due to the installer, so drop it and remake it if so 386 err := patch8(scanner) 387 if err != nil { 388 return err 389 } 390 391 return createTable("login_logs", "", "", 392 []tC{ 393 {"lid", "int", 0, false, true, ""}, 394 {"uid", "int", 0, false, false, ""}, 395 bcol("success", false), // Did this attempt succeed? 396 ccol("ipaddress", 200, ""), 397 {"doneAt", "createdAt", 0, false, false, ""}, 398 }, 399 []tK{ 400 {"lid", "primary", "", false}, 401 }, 402 ) 403 } 404 405 var acc = qgen.NewAcc 406 var itoa = strconv.Itoa 407 408 func patch10(scanner *bufio.Scanner) error { 409 err := execStmt(qgen.Builder.AddColumn("topics", tC{"attachCount", "int", 0, false, false, "0"}, nil)) 410 if err != nil { 411 return err 412 } 413 err = execStmt(qgen.Builder.AddColumn("topics", tC{"lastReplyID", "int", 0, false, false, "0"}, nil)) 414 if err != nil { 415 return err 416 } 417 418 err = acc().Select("topics").Cols("tid").EachInt(func(tid int) error { 419 stid := itoa(tid) 420 count, err := acc().Count("attachments").Where("originTable = 'topics' and originID=" + stid).Total() 421 if err != nil { 422 return err 423 } 424 425 hasReply := false 426 err = acc().Select("replies").Cols("rid").Where("tid=" + stid).Orderby("rid DESC").Limit("1").EachInt(func(rid int) error { 427 hasReply = true 428 _, err := acc().Update("topics").Set("lastReplyID=?, attachCount=?").Where("tid="+stid).Exec(rid, count) 429 return err 430 }) 431 if err != nil { 432 return err 433 } 434 if !hasReply { 435 _, err = acc().Update("topics").Set("attachCount=?").Where("tid=" + stid).Exec(count) 436 } 437 return err 438 }) 439 if err != nil { 440 return err 441 } 442 443 _, err = acc().Insert("updates").Columns("dbVersion").Fields("0").Exec() 444 return err 445 } 446 447 func patch11(scanner *bufio.Scanner) error { 448 err := execStmt(qgen.Builder.AddColumn("replies", tC{"attachCount", "int", 0, false, false, "0"}, nil)) 449 if err != nil { 450 return err 451 } 452 453 // Attachments for replies got the topicID rather than the replyID for a while in error, so we want to separate these out 454 _, err = acc().Update("attachments").Set("originTable='freplies'").Where("originTable='replies'").Exec() 455 if err != nil { 456 return err 457 } 458 459 // 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 460 return acc().Select("topics").Cols("tid").EachInt(func(tid int) error { 461 stid := itoa(tid) 462 count, err := acc().Count("attachments").Where("originTable='topics' and originID=" + stid).Total() 463 if err != nil { 464 return err 465 } 466 _, err = acc().Update("topics").Set("attachCount=?").Where("tid=" + stid).Exec(count) 467 return err 468 }) 469 470 /*return acc().Select("replies").Cols("rid").EachInt(func(rid int) error { 471 srid := itoa(rid) 472 count, err := acc().Count("attachments").Where("originTable='replies' and originID=" + srid).Total() 473 if err != nil { 474 return err 475 } 476 _, err = acc().Update("replies").Set("attachCount = ?").Where("rid=" + srid).Exec(count) 477 return err 478 })*/ 479 } 480 481 func patch12(scanner *bufio.Scanner) error { 482 var e error 483 addIndex := func(tbl, iname, colname string) { 484 if e != nil { 485 return 486 } 487 /*e = execStmt(qgen.Builder.RemoveIndex(tbl, iname)) 488 if e != nil { 489 return 490 }*/ 491 e = execStmt(qgen.Builder.AddIndex(tbl, iname, colname)) 492 } 493 addIndex("topics", "parentID", "parentID") 494 addIndex("replies", "tid", "tid") 495 addIndex("polls", "parentID", "parentID") 496 addIndex("likes", "targetItem", "targetItem") 497 addIndex("emails", "uid", "uid") 498 addIndex("attachments", "originID", "originID") 499 addIndex("attachments", "path", "path") 500 addIndex("activity_stream_matches", "watcher", "watcher") 501 return e 502 } 503 504 func patch13(scanner *bufio.Scanner) error { 505 return execStmt(qgen.Builder.AddColumn("widgets", tC{"wid", "int", 0, false, true, ""}, &tK{"wid", "primary", "", false})) 506 } 507 508 func patch14(scanner *bufio.Scanner) error { 509 /*err := execStmt(qgen.Builder.AddKey("topics", "title", tK{"title", "fulltext", "", false})) 510 if err != nil { 511 return err 512 } 513 err = execStmt(qgen.Builder.AddKey("topics", "content", tK{"content", "fulltext", "", false})) 514 if err != nil { 515 return err 516 } 517 err = execStmt(qgen.Builder.AddKey("replies", "content", tK{"content", "fulltext", "", false})) 518 if err != nil { 519 return err 520 }*/ 521 522 return nil 523 } 524 525 func patch15(scanner *bufio.Scanner) error { 526 return execStmt(qgen.Builder.SimpleInsert("settings", "name, content, type", "'google_site_verify','','html-attribute'")) 527 } 528 529 func patch16(scanner *bufio.Scanner) error { 530 return createTable("password_resets", "", "", 531 []tC{ 532 ccol("email", 200, ""), 533 {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key 534 ccol("validated", 200, ""), // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token 535 ccol("token", 200, ""), 536 {"createdAt", "createdAt", 0, false, false, ""}, 537 }, nil, 538 ) 539 } 540 541 func patch17(scanner *bufio.Scanner) error { 542 err := execStmt(qgen.Builder.AddColumn("attachments", ccol("extra", 200, ""), nil)) 543 if err != nil { 544 return err 545 } 546 547 err = acc().Select("topics").Cols("tid,parentID").Where("attachCount > 0").Each(func(rows *sql.Rows) error { 548 var tid, parentID int 549 err := rows.Scan(&tid, &parentID) 550 if err != nil { 551 return err 552 } 553 _, err = acc().Update("attachments").Set("sectionID=?").Where("originTable='topics' AND originID=?").Exec(parentID, tid) 554 return err 555 }) 556 if err != nil { 557 return err 558 } 559 560 return acc().Select("replies").Cols("rid,tid").Where("attachCount > 0").Each(func(rows *sql.Rows) error { 561 var rid, tid, sectionID int 562 err := rows.Scan(&rid, &tid) 563 if err != nil { 564 return err 565 } 566 err = acc().Select("topics").Cols("parentID").Where("tid=?").QueryRow(tid).Scan(§ionID) 567 if err != nil { 568 return err 569 } 570 _, err = acc().Update("attachments").Set("sectionID=?, extra=?").Where("originTable='replies' AND originID=?").Exec(sectionID, tid, rid) 571 return err 572 }) 573 } 574 575 func patch18(scanner *bufio.Scanner) error { 576 return execStmt(qgen.Builder.AddColumn("forums", tC{"order", "int", 0, false, false, "0"}, nil)) 577 } 578 579 func patch19(scanner *bufio.Scanner) error { 580 return createTable("memchunks", "", "", 581 []tC{ 582 {"count", "int", 0, false, false, "0"}, 583 {"createdAt", "datetime", 0, false, false, ""}, 584 }, nil, 585 ) 586 } 587 588 func patch20(scanner *bufio.Scanner) error { 589 err := acc().Select("activity_stream_matches").Cols("asid").Each(func(rows *sql.Rows) error { 590 var asid int 591 if e := rows.Scan(&asid); e != nil { 592 return e 593 } 594 e := acc().Select("activity_stream").Cols("asid").Where("asid=?").QueryRow(asid).Scan(&asid) 595 if e != sql.ErrNoRows { 596 return e 597 } 598 _, e = acc().Delete("activity_stream_matches").Where("asid=?").Run(asid) 599 return e 600 }) 601 if err != nil { 602 return err 603 } 604 605 return execStmt(qgen.Builder.AddForeignKey("activity_stream_matches", "asid", "activity_stream", "asid", true)) 606 } 607 608 func patch21(scanner *bufio.Scanner) error { 609 err := execStmt(qgen.Builder.AddColumn("memchunks", tC{"stack", "int", 0, false, false, "0"}, nil)) 610 if err != nil { 611 return err 612 } 613 614 err = execStmt(qgen.Builder.AddColumn("memchunks", tC{"heap", "int", 0, false, false, "0"}, nil)) 615 if err != nil { 616 return err 617 } 618 619 err = createTable("meta", "", "", 620 []tC{ 621 ccol("name", 200, ""), 622 ccol("value", 200, ""), 623 }, nil, 624 ) 625 if err != nil { 626 return err 627 } 628 629 return execStmt(qgen.Builder.AddColumn("activity_stream", tC{"createdAt", "createdAt", 0, false, false, ""}, nil)) 630 } 631 632 func patch22(scanner *bufio.Scanner) error { 633 return execStmt(qgen.Builder.AddColumn("forums", ccol("tmpl", 200, "''"), nil)) 634 } 635 636 func patch23(scanner *bufio.Scanner) error { 637 err := createTable("conversations", "", "", 638 []tC{ 639 {"cid", "int", 0, false, true, ""}, 640 {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key 641 {"createdAt", "createdAt", 0, false, false, ""}, 642 {"lastReplyAt", "datetime", 0, false, false, ""}, 643 {"lastReplyBy", "int", 0, false, false, ""}, 644 }, 645 []tK{ 646 {"cid", "primary", "", false}, 647 }, 648 ) 649 if err != nil { 650 return err 651 } 652 653 err = createTable("conversations_posts", "", "", 654 []tC{ 655 {"pid", "int", 0, false, true, ""}, 656 {"cid", "int", 0, false, false, ""}, 657 {"createdBy", "int", 0, false, false, ""}, 658 ccol("body", 50, ""), 659 ccol("post", 50, "''"), 660 }, 661 []tK{ 662 {"pid", "primary", "", false}, 663 }, 664 ) 665 if err != nil { 666 return err 667 } 668 669 return createTable("conversations_participants", "", "", 670 []tC{ 671 {"uid", "int", 0, false, false, ""}, 672 {"cid", "int", 0, false, false, ""}, 673 }, nil, 674 ) 675 } 676 677 func patch24(scanner *bufio.Scanner) error { 678 return createTable("users_groups_promotions", "", "", 679 []tC{ 680 {"pid", "int", 0, false, true, ""}, 681 {"from_gid", "int", 0, false, false, ""}, 682 {"to_gid", "int", 0, false, false, ""}, 683 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 684 685 // Requirements 686 {"level", "int", 0, false, false, ""}, 687 {"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted 688 }, 689 []tK{ 690 {"pid", "primary", "", false}, 691 }, 692 ) 693 } 694 695 func patch25(scanner *bufio.Scanner) error { 696 return execStmt(qgen.Builder.AddColumn("users_groups_promotions", tC{"posts", "int", 0, false, false, "0"}, nil)) 697 } 698 699 func patch26(scanner *bufio.Scanner) error { 700 return createTable("users_blocks", "", "", 701 []tC{ 702 {"blocker", "int", 0, false, false, ""}, 703 {"blockedUser", "int", 0, false, false, ""}, 704 }, nil, 705 ) 706 } 707 708 func patch27(scanner *bufio.Scanner) error { 709 err := execStmt(qgen.Builder.AddColumn("moderation_logs", tC{"extra", "text", 0, false, false, ""}, nil)) 710 if err != nil { 711 return err 712 } 713 return execStmt(qgen.Builder.AddColumn("administration_logs", tC{"extra", "text", 0, false, false, ""}, nil)) 714 } 715 716 func patch28(scanner *bufio.Scanner) error { 717 return execStmt(qgen.Builder.AddColumn("users", tC{"enable_embeds", "int", 0, false, false, "-1"}, nil)) 718 } 719 720 // 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. 721 func WordCount(input string) (count int) { 722 input = strings.TrimSpace(input) 723 if input == "" { 724 return 0 725 } 726 727 var inSpace bool 728 for _, value := range input { 729 if unicode.IsSpace(value) || unicode.IsPunct(value) { 730 if !inSpace { 731 inSpace = true 732 } 733 } else if inSpace { 734 count++ 735 inSpace = false 736 } 737 } 738 739 return count + 1 740 } 741 742 func patch29(scanner *bufio.Scanner) error { 743 f := func(tbl, idCol string) error { 744 return acc().Select(tbl).Cols(idCol + ",content").Each(func(rows *sql.Rows) error { 745 var id int 746 var content string 747 err := rows.Scan(&id, &content) 748 if err != nil { 749 return err 750 } 751 _, err = acc().Update(tbl).Set("words=?").Where(idCol+"=?").Exec(WordCount(content), id) 752 return err 753 }) 754 } 755 err := f("topics", "tid") 756 if err != nil { 757 return err 758 } 759 err = f("replies", "rid") 760 if err != nil { 761 return err 762 } 763 764 meta, err := meta.NewDefaultMetaStore(acc()) 765 if err != nil { 766 return err 767 } 768 err = meta.Set("sched", "recalc") 769 if err != nil { 770 return err 771 } 772 773 fixCols := func(tbls ...string) error { 774 for _, tbl := range tbls { 775 //err := execStmt(qgen.Builder.RenameColumn(tbl, "ipaddress","ip")) 776 err := execStmt(qgen.Builder.ChangeColumn(tbl, "ipaddress", ccol("ip", 200, "''"))) 777 if err != nil { 778 return err 779 } 780 err = execStmt(qgen.Builder.SetDefaultColumn(tbl, "ip", "varchar", "")) 781 if err != nil { 782 return err 783 } 784 } 785 return nil 786 } 787 err = fixCols("topics", "replies", "polls_votes", "users_replies") 788 if err != nil { 789 return err 790 } 791 792 err = execStmt(qgen.Builder.SetDefaultColumn("replies", "lastEdit", "int", "0")) 793 if err != nil { 794 return err 795 } 796 err = execStmt(qgen.Builder.SetDefaultColumn("replies", "lastEditBy", "int", "0")) 797 if err != nil { 798 return err 799 } 800 err = execStmt(qgen.Builder.SetDefaultColumn("users_replies", "lastEdit", "int", "0")) 801 if err != nil { 802 return err 803 } 804 err = execStmt(qgen.Builder.SetDefaultColumn("users_replies", "lastEditBy", "int", "0")) 805 if err != nil { 806 return err 807 } 808 809 return execStmt(qgen.Builder.AddColumn("activity_stream", tC{"extra", "varchar", 200, false, false, "''"}, nil)) 810 811 } 812 813 func patch30(scanner *bufio.Scanner) error { 814 e := execStmt(qgen.Builder.AddColumn("users_groups_promotions", tC{"registeredFor", "int", 0, false, false, "0"}, nil)) 815 if e != nil { 816 return e 817 } 818 return execStmt(qgen.Builder.SetDefaultColumn("users", "last_ip", "varchar", "")) 819 } 820 821 func patch31(scanner *bufio.Scanner) (e error) { 822 addKey := func(tbl, col string, tk qgen.DBTableKey) error { 823 /*e := execStmt(qgen.Builder.RemoveIndex(tbl, col)) 824 if e != nil { 825 return e 826 }*/ 827 return execStmt(qgen.Builder.AddKey(tbl, col, tk)) 828 } 829 e = addKey("topics", "title", tK{"title", "fulltext", "", false}) 830 if e != nil { 831 return e 832 } 833 e = addKey("topics", "content", tK{"content", "fulltext", "", false}) 834 if e != nil { 835 return e 836 } 837 return addKey("replies", "content", tK{"content", "fulltext", "", false}) 838 } 839 840 func createTable(tbl, charset, collation string, cols []tC, keys []tK) error { 841 e := execStmt(qgen.Builder.DropTable(tbl)) 842 if e != nil { 843 return e 844 } 845 return execStmt(qgen.Builder.CreateTable(tbl, charset, collation, cols, keys)) 846 } 847 848 func patch32(scanner *bufio.Scanner) error { 849 return createTable("perfchunks", "", "", 850 []tC{ 851 {"low", "int", 0, false, false, "0"}, 852 {"high", "int", 0, false, false, "0"}, 853 {"avg", "int", 0, false, false, "0"}, 854 {"createdAt", "datetime", 0, false, false, ""}, 855 }, nil, 856 ) 857 } 858 859 func patch33(scanner *bufio.Scanner) error { 860 return execStmt(qgen.Builder.AddColumn("viewchunks", tC{"avg", "int", 0, false, false, "0"}, nil)) 861 } 862 863 func patch34(scanner *bufio.Scanner) error { 864 /*err := createTable("tables", "", "", 865 []tC{ 866 {"id", "int", 0, false, true, ""}, 867 ccol("name", 200, ""), 868 }, 869 []tK{ 870 {"id", "primary", "", false}, 871 {"name", "unique", "", false}, 872 }, 873 ) 874 if err != nil { 875 return err 876 } 877 insert := func(tbl, cols, fields string) { 878 if err != nil { 879 return 880 } 881 err = execStmt(qgen.Builder.SimpleInsert(tbl, cols, fields)) 882 } 883 insert("tables", "name", "forums") 884 insert("tables", "name", "topics") 885 insert("tables", "name", "replies") 886 // ! Hold onto freplies for a while longer 887 insert("tables", "name", "freplies")*/ 888 /*err := execStmt(qgen.Builder.AddColumn("topics", tC{"attachCount", "int", 0, false, false, "0"}, nil)) 889 if err != nil { 890 return err 891 }*/ 892 overwriteColumn := func(tbl, col string, tc qgen.DBTableColumn) error { 893 /*e := execStmt(qgen.Builder.DropColumn(tbl, col)) 894 if e != nil { 895 return e 896 }*/ 897 return execStmt(qgen.Builder.AddColumn(tbl, tc, nil)) 898 } 899 err := overwriteColumn("users", "profile_comments", tC{"profile_comments", "int", 0, false, false, "0"}) 900 if err != nil { 901 return err 902 } 903 err = overwriteColumn("users", "who_can_convo", tC{"who_can_convo", "int", 0, false, false, "0"}) 904 if err != nil { 905 return err 906 } 907 908 setDefault := func(tbl, col, typ, val string) { 909 if err != nil { 910 return 911 } 912 err = execStmt(qgen.Builder.SetDefaultColumn(tbl, col, typ, val)) 913 } 914 setDefault("users_groups", "permissions", "text", "{}") 915 setDefault("users_groups", "plugin_perms", "text", "{}") 916 setDefault("forums_permissions", "permissions", "text", "{}") 917 setDefault("topics", "content", "text", "") 918 setDefault("topics", "parsed_content", "text", "") 919 setDefault("replies", "content", "text", "") 920 setDefault("replies", "parsed_content", "text", "") 921 //setDefault("revisions", "content", "text", "") 922 setDefault("users_replies", "content", "text", "") 923 setDefault("users_replies", "parsed_content", "text", "") 924 setDefault("pages", "body", "text", "") 925 setDefault("pages", "allowedGroups", "text", "") 926 setDefault("moderation_logs", "extra", "text", "") 927 setDefault("administration_logs", "extra", "text", "") 928 if err != nil { 929 return err 930 } 931 932 return nil 933 } 934 935 func patch35(scanner *bufio.Scanner) error { 936 e := execStmt(qgen.Builder.AddColumn("topics", tC{"weekEvenViews", "int", 0, false, false, "0"}, nil)) 937 if e != nil { 938 return e 939 } 940 return execStmt(qgen.Builder.AddColumn("topics", tC{"weekOddViews", "int", 0, false, false, "0"}, nil)) 941 } 942 943 func patch36(scanner *bufio.Scanner) error { 944 e := createTable("forums_actions", "utf8mb4", "utf8mb4_general_ci", 945 []tC{ 946 {"faid", "int", 0, false, true, ""}, 947 {"fid", "int", 0, false, false, ""}, 948 bcol("runOnTopicCreation", false), 949 {"runDaysAfterTopicCreation", "int", 0, false, false, "0"}, 950 {"runDaysAfterTopicLastReply", "int", 0, false, false, "0"}, 951 ccol("action", 50, ""), 952 ccol("extra", 200, "''"), 953 }, 954 []tK{ 955 {"faid", "primary", "", false}, 956 }, 957 ) 958 if e != nil { 959 return e 960 } 961 return execStmt(qgen.Builder.SimpleInsert("settings", "name, content, type, constraints", "'avatar_visibility','0','list','0-1'")) 962 }