WIP Conversations templates.

WIP Conversations structs.
WIP Unbound conversations routes.
Shorten more variable names.
Eliminate a few characters here and there.
Track down widget tick errors more easily.

Add the convos_head title phrase.
Add the convo_head title phrase.
Add the unit phrase.
Add the account_menu_messages phrase.
Add the convos_head phrase.
Add the convo_head phrase.
Add the create_convo_head phrase.
Add the create_convo_button phrase.
Add the profile_send_message phrase.
Add the panel_dashboard_disk_unknown phrase.
This commit is contained in:
Azareal 2019-07-27 09:29:42 +10:00
parent 597240233f
commit a287ccc3d6
12 changed files with 262 additions and 38 deletions

View File

@ -1,8 +1,11 @@
package counters package counters
import "database/sql" import (
import "github.com/Azareal/Gosora/common" "database/sql"
import "github.com/Azareal/Gosora/query_gen"
"github.com/Azareal/Gosora/common"
qgen "github.com/Azareal/Gosora/query_gen"
)
var RouteViewCounter *DefaultRouteViewCounter var RouteViewCounter *DefaultRouteViewCounter

View File

@ -23,38 +23,38 @@ func NewDefaultMenuStore() *DefaultMenuStore {
} }
// TODO: Add actual support for multiple menus // TODO: Add actual support for multiple menus
func (store *DefaultMenuStore) GetAllMap() (out map[int]*MenuListHolder) { func (s *DefaultMenuStore) GetAllMap() (out map[int]*MenuListHolder) {
out = make(map[int]*MenuListHolder) out = make(map[int]*MenuListHolder)
for mid, atom := range store.menus { for mid, atom := range s.menus {
out[mid] = atom.Load().(*MenuListHolder) out[mid] = atom.Load().(*MenuListHolder)
} }
return out return out
} }
func (store *DefaultMenuStore) Get(mid int) (*MenuListHolder, error) { func (s *DefaultMenuStore) Get(mid int) (*MenuListHolder, error) {
aStore, ok := store.menus[mid] aStore, ok := s.menus[mid]
if ok { if ok {
return aStore.Load().(*MenuListHolder), nil return aStore.Load().(*MenuListHolder), nil
} }
return nil, ErrNoRows return nil, ErrNoRows
} }
func (store *DefaultMenuStore) Items(mid int) (mlist MenuItemList, err error) { func (s *DefaultMenuStore) Items(mid int) (mlist MenuItemList, err error) {
err = qgen.NewAcc().Select("menu_items").Columns("miid, name, htmlID, cssClass, position, path, aria, tooltip, order, tmplName, guestOnly, memberOnly, staffOnly, adminOnly").Where("mid = " + strconv.Itoa(mid)).Orderby("order ASC").Each(func(rows *sql.Rows) error { err = qgen.NewAcc().Select("menu_items").Columns("miid, name, htmlID, cssClass, position, path, aria, tooltip, order, tmplName, guestOnly, memberOnly, staffOnly, adminOnly").Where("mid = " + strconv.Itoa(mid)).Orderby("order ASC").Each(func(rows *sql.Rows) error {
var mitem = MenuItem{MenuID: mid} i := MenuItem{MenuID: mid}
err := rows.Scan(&mitem.ID, &mitem.Name, &mitem.HTMLID, &mitem.CSSClass, &mitem.Position, &mitem.Path, &mitem.Aria, &mitem.Tooltip, &mitem.Order, &mitem.TmplName, &mitem.GuestOnly, &mitem.MemberOnly, &mitem.SuperModOnly, &mitem.AdminOnly) err := rows.Scan(&i.ID, &i.Name, &i.HTMLID, &i.CSSClass, &i.Position, &i.Path, &i.Aria, &i.Tooltip, &i.Order, &i.TmplName, &i.GuestOnly, &i.MemberOnly, &i.SuperModOnly, &i.AdminOnly)
if err != nil { if err != nil {
return err return err
} }
store.itemStore.Add(mitem) s.itemStore.Add(i)
mlist = append(mlist, mitem) mlist = append(mlist, i)
return nil return nil
}) })
return mlist, err return mlist, err
} }
func (store *DefaultMenuStore) Load(mid int) error { func (s *DefaultMenuStore) Load(mid int) error {
mlist, err := store.Items(mid) mlist, err := s.Items(mid)
if err != nil { if err != nil {
return err return err
} }
@ -64,12 +64,12 @@ func (store *DefaultMenuStore) Load(mid int) error {
return err return err
} }
var aStore = &atomic.Value{} aStore := &atomic.Value{}
aStore.Store(hold) aStore.Store(hold)
store.menus[mid] = aStore s.menus[mid] = aStore
return nil return nil
} }
func (store *DefaultMenuStore) ItemStore() *DefaultMenuItemStore { func (s *DefaultMenuStore) ItemStore() *DefaultMenuItemStore {
return store.itemStore return s.itemStore
} }

View File

@ -283,6 +283,23 @@ type ResetPage struct {
MFA bool MFA bool
} }
type ConvoListPage struct {
*Header
Convos []*Conversation
Paginator
}
type ConvoViewPage struct {
*Header
Posts []*ConversationPost
Paginator
}
type ConvoCreatePage struct {
*Header
RecpName string
}
/* WIP for dyntmpl */ /* WIP for dyntmpl */
type Panel struct { type Panel struct {
*BasePanelPage *BasePanelPage

View File

@ -60,10 +60,9 @@ func (tList *DefaultTopicList) Tick() error {
} }
//fmt.Println("building topic list") //fmt.Println("building topic list")
var oddLists = make(map[int]*TopicListHolder) oddLists := make(map[int]*TopicListHolder)
var evenLists = make(map[int]*TopicListHolder) evenLists := make(map[int]*TopicListHolder)
addList := func(gid int, holder *TopicListHolder) {
var addList = func(gid int, holder *TopicListHolder) {
if gid%2 == 0 { if gid%2 == 0 {
evenLists[gid] = holder evenLists[gid] = holder
} else { } else {
@ -76,15 +75,15 @@ func (tList *DefaultTopicList) Tick() error {
return err return err
} }
var gidToCanSee = make(map[int]string) gidToCanSee := make(map[int]string)
var permTree = make(map[string][]int) // [string(canSee)]canSee permTree := make(map[string][]int) // [string(canSee)]canSee
for _, group := range allGroups { for _, group := range allGroups {
// ? - Move the user count check to instance initialisation? Might require more book-keeping, particularly when a user moves into a zero user group // ? - Move the user count check to instance initialisation? Might require more book-keeping, particularly when a user moves into a zero user group
if group.UserCount == 0 && group.ID != GuestUser.Group { if group.UserCount == 0 && group.ID != GuestUser.Group {
continue continue
} }
var canSee = make([]byte, len(group.CanSee)) canSee := make([]byte, len(group.CanSee))
for i, item := range group.CanSee { for i, item := range group.CanSee {
canSee[i] = byte(item) canSee[i] = byte(item)
} }
@ -96,7 +95,7 @@ func (tList *DefaultTopicList) Tick() error {
gidToCanSee[group.ID] = sCanSee gidToCanSee[group.ID] = sCanSee
} }
var canSeeHolders = make(map[string]*TopicListHolder) canSeeHolders := make(map[string]*TopicListHolder)
for name, canSee := range permTree { for name, canSee := range permTree {
topicList, forumList, paginator, err := tList.GetListByCanSee(canSee, 1, "", nil) topicList, forumList, paginator, err := tList.GetListByCanSee(canSee, 1, "", nil)
if err != nil { if err != nil {

View File

@ -57,8 +57,7 @@ var wolLastUsers []*User
func wolTick(widget *Widget) error { func wolTick(widget *Widget) error {
w := httptest.NewRecorder() w := httptest.NewRecorder()
users, ucount := wolGetUsers() users, ucount := wolGetUsers()
inOld := func(id int) bool {
var inOld = func(id int) bool {
for _, user := range wolLastUsers { for _, user := range wolLastUsers {
if id == user.ID { if id == user.ID {
return true return true

View File

@ -11,6 +11,7 @@ import (
"sync/atomic" "sync/atomic"
min "github.com/Azareal/Gosora/common/templates" min "github.com/Azareal/Gosora/common/templates"
"github.com/pkg/errors"
) )
// TODO: Clean this file up // TODO: Clean this file up
@ -292,12 +293,12 @@ func releaseWidgets(widgets []*Widget) {
// TODO: Use atomics // TODO: Use atomics
func setDock(dock string, widgets []*Widget) { func setDock(dock string, widgets []*Widget) {
var dockHandle = func(dockWidgets []*Widget) { dockHandle := func(dockWidgets []*Widget) {
widgetUpdateMutex.Lock() widgetUpdateMutex.Lock()
DebugLog(dock, widgets) DebugLog(dock, widgets)
releaseWidgets(dockWidgets) releaseWidgets(dockWidgets)
} }
var dockHandle2 = func(dockWidgets WidgetDock) WidgetDock { dockHandle2 := func(dockWidgets WidgetDock) WidgetDock {
dockHandle(dockWidgets.Items) dockHandle(dockWidgets.Items)
if dockWidgets.Scheduler == nil { if dockWidgets.Scheduler == nil {
dockWidgets.Scheduler = &WidgetScheduler{} dockWidgets.Scheduler = &WidgetScheduler{}
@ -335,23 +336,23 @@ type WidgetScheduler struct {
store atomic.Value store atomic.Value
} }
func (schedule *WidgetScheduler) Add(widget *Widget) { func (s *WidgetScheduler) Add(widget *Widget) {
schedule.widgets = append(schedule.widgets, widget) s.widgets = append(s.widgets, widget)
} }
func (schedule *WidgetScheduler) Store() { func (s *WidgetScheduler) Store() {
schedule.store.Store(schedule.widgets) s.store.Store(s.widgets)
} }
func (schedule *WidgetScheduler) Tick() error { func (s *WidgetScheduler) Tick() error {
widgets := schedule.store.Load().([]*Widget) widgets := s.store.Load().([]*Widget)
for _, widget := range widgets { for _, widget := range widgets {
if widget.TickFunc == nil { if widget.TickFunc == nil {
continue continue
} }
err := widget.TickFunc(widget) err := widget.TickFunc(widget)
if err != nil { if err != nil {
return err return errors.WithStack(err)
} }
} }
return nil return nil

View File

@ -143,6 +143,8 @@
"account_logins":"Logins", "account_logins":"Logins",
"account_penalties":"Penalties", "account_penalties":"Penalties",
"account_level_list":"Level Progress", "account_level_list":"Level Progress",
"convos_head":"Conversations",
"convo_head":"Conversation",
"panel_dashboard":"Control Panel Dashboard", "panel_dashboard":"Control Panel Dashboard",
"panel_forums":"Forum Manager", "panel_forums":"Forum Manager",
@ -328,6 +330,7 @@
"TmplPhrases": { "TmplPhrases": {
"pipe":"|", "pipe":"|",
"unit":"%.1f%s",
"menu_forums":"Forums", "menu_forums":"Forums",
"menu_topics":"Topics", "menu_topics":"Topics",
@ -492,6 +495,7 @@
"account_menu_notifications":"Notifications", "account_menu_notifications":"Notifications",
"account_menu_logins":"Logins", "account_menu_logins":"Logins",
"account_menu_penalties":"Penalties", "account_menu_penalties":"Penalties",
"account_menu_messages":"Messages",
"account_coming_soon":"Coming Soon", "account_coming_soon":"Coming Soon",
@ -530,6 +534,11 @@
"account_logins_success":"Successful Login", "account_logins_success":"Successful Login",
"account_logins_failure":"Failed Login", "account_logins_failure":"Failed Login",
"convos_head":"Conversations",
"convo_head":"Conversation",
"create_convo_head":"Create Conversation",
"create_convo_button":"Create Convo",
"areyousure_head":"Are you sure?", "areyousure_head":"Are you sure?",
"areyousure_continue":"Continue", "areyousure_continue":"Continue",
@ -681,6 +690,7 @@
"paginator.next_page_aria":"Go to the next page", "paginator.next_page_aria":"Go to the next page",
"profile_login_for_options":"Login for options", "profile_login_for_options":"Login for options",
"profile_send_message":"Send Message",
"profile_add_friend":"Add Friend", "profile_add_friend":"Add Friend",
"profile_unban":"Unban", "profile_unban":"Unban",
"profile_ban":"Ban", "profile_ban":"Ban",
@ -768,6 +778,7 @@
"panel_dashboard_memused":"Mem: %.1f%s", "panel_dashboard_memused":"Mem: %.1f%s",
"panel_dashboard_memused_desc":"The amount of memory likely being used by this instance", "panel_dashboard_memused_desc":"The amount of memory likely being used by this instance",
"panel_dashboard_disk":"Disk: %.1f%s", "panel_dashboard_disk":"Disk: %.1f%s",
"panel_dashboard_disk_unknown":"Disk: ??",
"panel_dashboard_disk_desc":"The amount of disk space being used by this instance", "panel_dashboard_disk_desc":"The amount of disk space being used by this instance",
"panel_dashboard_online": "%d%s online", "panel_dashboard_online": "%d%s online",
"panel_dashboard_online_desc":"The number of people who are currently online", "panel_dashboard_online_desc":"The number of people who are currently online",

View File

@ -30,7 +30,7 @@ func init() {
func ShowAttachment(w http.ResponseWriter, r *http.Request, user c.User, filename string) c.RouteError { func ShowAttachment(w http.ResponseWriter, r *http.Request, user c.User, filename string) c.RouteError {
filename = c.Stripslashes(filename) filename = c.Stripslashes(filename)
var ext = filepath.Ext("./attachs/" + filename) ext := filepath.Ext("./attachs/" + filename)
if !c.AllowedFileExts.Contains(strings.TrimPrefix(ext, ".")) { if !c.AllowedFileExts.Contains(strings.TrimPrefix(ext, ".")) {
return c.LocalError("Bad extension", w, r, user) return c.LocalError("Bad extension", w, r, user)
} }

148
routes/convos.go Normal file
View File

@ -0,0 +1,148 @@
package routes
import (
"database/sql"
"net/http"
"strconv"
"strings"
c "github.com/Azareal/Gosora/common"
"github.com/Azareal/Gosora/common/phrases"
)
func Convos(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError {
accountEditHead("convos", w, r, &user, header)
ccount := c.Convos.GetUserCount(user.ID)
page, _ := strconv.Atoi(r.FormValue("page"))
offset, page, lastPage := c.PageOffset(ccount, page, c.Config.ItemsPerPage)
pageList := c.Paginate(page, lastPage, 5)
convos, err := c.Convos.GetUser(user.ID, offset)
if err == sql.ErrNoRows {
return c.NotFound(w, r, header)
} else if err != nil {
return c.InternalError(err, w, r)
}
pi := c.Account{header, "dashboard", "convos", c.ConvoListPage{header, convos, c.Paginator{pageList, page, lastPage}}}
return renderTemplate("account", w, r, header, pi)
}
func Convo(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header, scid string) c.RouteError {
accountEditHead("convo", w, r, &user, header)
cid, err := strconv.Atoi(scid)
if err != nil {
return c.LocalError(phrases.GetErrorPhrase("id_must_be_integer"), w, r, user)
}
convo, err := c.Convos.Get(cid)
if err == sql.ErrNoRows {
return c.NotFound(w, r, header)
} else if err != nil {
return c.InternalError(err, w, r)
}
pcount := convo.PostsCount()
if pcount == 0 {
return c.NotFound(w, r, header)
}
page, _ := strconv.Atoi(r.FormValue("page"))
offset, page, lastPage := c.PageOffset(pcount, page, c.Config.ItemsPerPage)
pageList := c.Paginate(page, lastPage, 5)
posts, err := convo.Posts(offset)
// TODO: Report a better error for no posts
if err == sql.ErrNoRows {
return c.NotFound(w, r, header)
} else if err != nil {
return c.InternalError(err, w, r)
}
pi := c.Account{header, "dashboard", "convo", c.ConvoViewPage{header, posts, c.Paginator{pageList, page, lastPage}}}
return renderTemplate("account", w, r, header, pi)
}
func ConvosCreate(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError {
accountEditHead("create_convo", w, r, &user, header)
recpName := ""
pi := c.Account{header, "dashboard", "create_convo", c.ConvoCreatePage{header, recpName}}
return renderTemplate("account", w, r, header, pi)
}
func ConvosCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
_, ferr := c.SimpleUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
recps := c.SanitiseSingleLine(r.PostFormValue("recp"))
body := c.PreparseMessage(r.PostFormValue("body"))
rlist := []int{}
max := 10 // max number of recipients that can be added at once
for i, recp := range strings.Split(recps, ",") {
if i >= max {
break
}
u, err := c.Users.GetByName(recp)
if err == sql.ErrNoRows {
return c.LocalError("One of the recipients doesn't exist", w, r, user)
} else if err != nil {
return c.InternalError(err, w, r)
}
rlist = append(rlist, u.ID)
}
cid, err := c.Convos.Create(body, user.ID, rlist)
if err != nil {
return c.InternalError(err, w, r)
}
http.Redirect(w, r, "/user/convo/"+strconv.Itoa(cid), http.StatusSeeOther)
return nil
}
/*type ConversationPost struct {
ID int
CID int
Body string
Post string // aes, ''
}*/
func ConvosDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, scid string) c.RouteError {
_, ferr := c.SimpleUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
cid, err := strconv.Atoi(scid)
if err != nil {
return c.LocalError(phrases.GetErrorPhrase("id_must_be_integer"), w, r, user)
}
err = c.Convos.Delete(cid)
if err != nil {
return c.InternalError(err, w, r)
}
http.Redirect(w, r, "/user/convos/", http.StatusSeeOther)
return nil
}
func ConvosCreateReplySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
_, ferr := c.SimpleUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
http.Redirect(w, r, "/user/convo/id", http.StatusSeeOther)
return nil
}
func ConvosDeleteReplySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
_, ferr := c.SimpleUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
http.Redirect(w, r, "/user/convo/id", http.StatusSeeOther)
return nil
}

10
templates/convo.html Normal file
View File

@ -0,0 +1,10 @@
<div class="colstack_item colstack_head rowhead">
<div class="rowitem">
<h1>{{lang "convo_head"}}</h1>
</div>
</div>
{{range .Posts}}
<div class="colstack_item">
<div class="rowitem">{{.Body}}</div>
</div>
{{end}}

17
templates/convos.html Normal file
View File

@ -0,0 +1,17 @@
<div class="colstack_item colstack_head rowhead">
<div class="rowitem">
<h1>{{lang "convos_head"}}</h1>
<h2>Create Convo</h2>
</div>
</div>
<div class="colstack_item">
{{range .Convos}}
<div class="rowitem">
<span class="to_left">
<a href="/user/convo/{{.ID}}">Message</a>
</span>
<span title="{{abstime .LastReplyAt}}" class="to_right">{{reltime .LastReplyAt}}</span>
</div>{{end}}
<div style="clear:both;"></div>
</div>
{{template "paginator.html" . }}

View File

@ -0,0 +1,19 @@
<div class="colstack_item colstack_head rowhead">
<div class="rowitem">
<h1>{{lang "create_convo_head"}}</h1>
</div>
</div>
<div class="colstack_item the_form">
<form action="/user/convos/create/submit/?session={{.CurrentUser.Session}}" method="post">
<div class="formrow real_first_child">
<div class="formitem formlabel"><a>Recipient/s</a></div>
<div class="formitem"><input name="recp" type="text"{{if .RecpName}} value="{{.RecpName}}"{{end}} /></div>
</div>
<div class="formrow">
<div class="formitem"><textarea name="body"></textarea></div>
</div>
<div class="formrow">
<div class="formitem"><button name="panel-button" class="formbutton form_middle_button">{{lang "create_convo_button"}}</button></div>
</div>
</form>
</div>