package bm import ( "context" "database/sql" "errors" chatcontracts "git.golem15.com/golem15/sm-chat-plugin/contracts" chanclasses "git.golem15.com/golem15/sm-chatchannels-plugin/classes" "git.golem15.com/golem15/summercms/modules/backpack" "git.golem15.com/golem15/summercms/modules/lagoon" "git.golem15.com/jakub/sm-bm-plugin/classes" "gorm.io/gorm" ) // The chat config keys and their fallbacks (config/config.yaml). const ( configChatGroup = "jz.bm.chat.group" configChatModeratorGroup = "jz.bm.chat.moderator_group" configChatMaxUpload = "jz.bm.chat.max_upload_bytes" configChatMaxEmbeds = "jz.bm.chat.max_embeds" defaultChatGroup = "studenci" defaultChatModeratorGroup = "moderatorzy" defaultChatMaxUpload = 62914560 defaultChatMaxEmbeds = 3 ) // registerChat makes jz.bm the host of the BM chat: it registers the bm // context alias on golem15.chat's registry and installs the channel access // policy. Without golem15.chat it does nothing, so jz.bm keeps working (and // its tests keep activating) without the chat plugins; jz.bm does not // Require them. func (p *Plugin) registerChat(app *backpack.App) error { if app == nil || !app.HasPlugin("golem15.chat") { return nil } reg, ok := app.Lookup[*chatcontracts.Registry]() if !ok || reg == nil { return errors.New("jz.bm: golem15.chat must be listed before jz.bm in summer.yaml") } if err := reg.Add(p.ID(), chatcontracts.Registration{Alias: classes.ChatContextType, Loader: p.loadChatContext}); err != nil { return err } return p.installChatAccess(app) } // loadChatContext is the loader of the bm alias. The only context is id 1; // any other id is unknown, which golem15.chat answers with its not-a-member // error. func (p *Plugin) loadChatContext(ctx context.Context, db *gorm.DB, id uint) (chatcontracts.Context, error) { return classes.ChatContextLoader(p.chatGroup, p.chatModeratorGroup)(ctx, db, id) } // installChatAccess installs BM's access policy on golem15.chatchannels' // channel service. The service is published from chatchannels' own // database hook, which runs before this one because chatchannels boots // first; under a database-less boot (tests, route:list) neither runs. func (p *Plugin) installChatAccess(app *backpack.App) error { if app == nil || !app.HasPlugin("golem15.chatchannels") { return nil } return lagoon.OnDatabase(app, func(_ *sql.DB, _ *gorm.DB) error { svc, ok := app.Lookup[*chanclasses.ChannelService]() if !ok || svc == nil { return errors.New("jz.bm: golem15.chatchannels must be listed before jz.bm in summer.yaml") } svc.SetAccessResolver(classes.NewChatAccess(p.database, p.chatGroup(), nil)) return nil }) } // database returns the published application database, or nil. func (p *Plugin) database() *gorm.DB { if p.app == nil { return nil } db, _ := p.app.Lookup[*gorm.DB]() return db } // chatGroup is the code of the user group whose members are in the chat. func (p *Plugin) chatGroup() string { return p.configString(configChatGroup, defaultChatGroup) } // chatModeratorGroup is the code of the user group whose members moderate // the chat's channels. func (p *Plugin) chatModeratorGroup() string { return p.configString(configChatModeratorGroup, defaultChatModeratorGroup) } // chatMaxUploadBytes is the total attachment size of one message. func (p *Plugin) chatMaxUploadBytes() int { return p.configInt(configChatMaxUpload, defaultChatMaxUpload) } // chatMaxEmbeds is the number of entity embeds one message may carry. func (p *Plugin) chatMaxEmbeds() int { return p.configInt(configChatMaxEmbeds, defaultChatMaxEmbeds) } func (p *Plugin) configString(key, fallback string) string { if p.app == nil || p.app.Config == nil { return fallback } if v := p.app.Config.String(key); v != "" { return v } return fallback } func (p *Plugin) configInt(key string, fallback int) int { if p.app == nil || p.app.Config == nil { return fallback } if v := p.app.Config.Int(key); v > 0 { return v } return fallback }