gosora/common/page_store.go
Azareal 6935637867 Cascade delete attachments properly.
Cascade delete replied to topic events for replies properly.
Cascade delete likes on topic posts properly.
Cascade delete replies and their children properly.
Recalculate user stats properly when items are deleted.
Users can now unlike topic opening posts.
Add a recalculator to fix abnormalities across upgrades.
Try fixing a last_ip daily update bug.

Add Existable interface.
Add Delete method to LikeStore.
Add Each, Exists, Create, CountUser, CountMegaUser and CountBigUser methods to ReplyStore.
Add CountUser, CountMegaUser, CountBigUser methods to TopicStore.
Add Each method to UserStore.
Add Add, Delete and DeleteResource methods to SubscriptionStore.
Add Delete, DeleteByParams, DeleteByParamsExtra and AidsByParamsExtra methods to ActivityStream.
Add Exists method to ProfileReplyStore.

Add DropColumn, RenameColumn and ChangeColumn to the database adapters.

Shorten ipaddress column names to ip.
- topics table.
- replies table
- users_replies table.
- polls_votes table.
Add extra column to activity_stream table.
Fix an issue upgrading sites to MariaDB 10.3 from older versions of Gosora. Please report any other issues you find.

You need to run the updater / patcher for this commit.
2020-01-31 17:22:08 +10:00

176 lines
4.6 KiB
Go

package common
import (
"database/sql"
"strconv"
"strings"
qgen "github.com/Azareal/Gosora/query_gen"
)
type CustomPageStmts struct {
update *sql.Stmt
create *sql.Stmt
}
var customPageStmts CustomPageStmts
func init() {
DbInits.Add(func(acc *qgen.Accumulator) error {
customPageStmts = CustomPageStmts{
update: acc.Update("pages").Set("name=?,title=?,body=?,allowedGroups=?,menuID=?").Where("pid=?").Prepare(),
create: acc.Insert("pages").Columns("name,title,body,allowedGroups,menuID").Fields("?,?,?,?,?").Prepare(),
}
return acc.FirstError()
})
}
type CustomPage struct {
ID int
Name string // TODO: Let admins put pages in "virtual subdirectories"
Title string
Body string
AllowedGroups []int
MenuID int
}
func BlankCustomPage() *CustomPage {
return new(CustomPage)
}
func (p *CustomPage) AddAllowedGroup(gid int) {
p.AllowedGroups = append(p.AllowedGroups, gid)
}
func (p *CustomPage) getRawAllowedGroups() (rawAllowedGroups string) {
for _, group := range p.AllowedGroups {
rawAllowedGroups += strconv.Itoa(group) + ","
}
if len(rawAllowedGroups) > 0 {
rawAllowedGroups = rawAllowedGroups[:len(rawAllowedGroups)-1]
}
return rawAllowedGroups
}
func (p *CustomPage) Commit() error {
_, err := customPageStmts.update.Exec(p.Name, p.Title, p.Body, p.getRawAllowedGroups(), p.MenuID, p.ID)
Pages.Reload(p.ID)
return err
}
func (p *CustomPage) Create() (int, error) {
res, err := customPageStmts.create.Exec(p.Name, p.Title, p.Body, p.getRawAllowedGroups(), p.MenuID)
if err != nil {
return 0, err
}
pid64, err := res.LastInsertId()
return int(pid64), err
}
var Pages PageStore
// Holds the custom pages, but doesn't include the template pages in /pages/ which are a lot more flexible yet harder to use and which are too risky security-wise to make editable in the Control Panel
type PageStore interface {
Count() (count int)
Get(id int) (*CustomPage, error)
GetByName(name string) (*CustomPage, error)
GetOffset(offset, perPage int) (pages []*CustomPage, err error)
Reload(id int) error
Delete(id int) error
}
// TODO: Add a cache to this to save on the queries
type DefaultPageStore struct {
get *sql.Stmt
getByName *sql.Stmt
getOffset *sql.Stmt
count *sql.Stmt
delete *sql.Stmt
}
func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) {
pa := "pages"
return &DefaultPageStore{
get: acc.Select(pa).Columns("name, title, body, allowedGroups, menuID").Where("pid=?").Prepare(),
getByName: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Where("name=?").Prepare(),
getOffset: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Orderby("pid DESC").Limit("?,?").Prepare(),
count: acc.Count(pa).Prepare(),
delete: acc.Delete(pa).Where("pid=?").Prepare(),
}, acc.FirstError()
}
func (s *DefaultPageStore) Count() (count int) {
err := s.count.QueryRow().Scan(&count)
if err != nil {
LogError(err)
}
return count
}
func (s *DefaultPageStore) parseAllowedGroups(raw string, page *CustomPage) error {
if raw == "" {
return nil
}
for _, sgroup := range strings.Split(raw, ",") {
group, err := strconv.Atoi(sgroup)
if err != nil {
return err
}
page.AddAllowedGroup(group)
}
return nil
}
func (s *DefaultPageStore) Get(id int) (*CustomPage, error) {
p := &CustomPage{ID: id}
rawAllowedGroups := ""
err := s.get.QueryRow(id).Scan(&p.Name, &p.Title, &p.Body, &rawAllowedGroups, &p.MenuID)
if err != nil {
return nil, err
}
return p, s.parseAllowedGroups(rawAllowedGroups, p)
}
func (s *DefaultPageStore) GetByName(name string) (*CustomPage, error) {
p := BlankCustomPage()
rawAllowedGroups := ""
err := s.getByName.QueryRow(name).Scan(&p.ID, &p.Name, &p.Title, &p.Body, &rawAllowedGroups, &p.MenuID)
if err != nil {
return nil, err
}
return p, s.parseAllowedGroups(rawAllowedGroups, p)
}
func (s *DefaultPageStore) GetOffset(offset, perPage int) (pages []*CustomPage, err error) {
rows, err := s.getOffset.Query(offset, perPage)
if err != nil {
return pages, err
}
defer rows.Close()
for rows.Next() {
p := &CustomPage{ID: 0}
rawAllowedGroups := ""
err := rows.Scan(&p.ID, &p.Name, &p.Title, &p.Body, &rawAllowedGroups, &p.MenuID)
if err != nil {
return pages, err
}
err = s.parseAllowedGroups(rawAllowedGroups, p)
if err != nil {
return pages, err
}
pages = append(pages, p)
}
return pages, rows.Err()
}
// Always returns nil as there's currently no cache
func (s *DefaultPageStore) Reload(id int) error {
return nil
}
func (s *DefaultPageStore) Delete(id int) error {
_, err := s.delete.Exec(id)
return err
}