Begin work on making the analytics panes somewhat usable when having JavaScript disabled.

Very minor refactoring here and there across the software.
Save some allocations here and there.
This commit is contained in:
Azareal 2019-10-28 09:13:24 +10:00
parent 7b09a3aff5
commit 4d9dc76392
35 changed files with 126 additions and 110 deletions

View File

@ -18,10 +18,11 @@ type DefaultActivityStream struct {
} }
func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivityStream, error) { func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivityStream, error) {
as := "activity_stream"
return &DefaultActivityStream{ return &DefaultActivityStream{
add: acc.Insert("activity_stream").Columns("actor, targetUser, event, elementType, elementID, createdAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(), add: acc.Insert(as).Columns("actor, targetUser, event, elementType, elementID, createdAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
get: acc.Select("activity_stream").Columns("actor, targetUser, event, elementType, elementID, createdAt").Where("asid = ?").Prepare(), get: acc.Select(as).Columns("actor, targetUser, event, elementType, elementID, createdAt").Where("asid = ?").Prepare(),
count: acc.Count("activity_stream").Prepare(), count: acc.Count(as).Prepare(),
}, acc.FirstError() }, acc.FirstError()
} }

View File

@ -47,7 +47,7 @@ func init() {
qgen.DBInsert{"activity_stream_matches", "watcher, asid", ""}, qgen.DBInsert{"activity_stream_matches", "watcher, asid", ""},
qgen.DBJoin{"activity_stream", "activity_subscriptions", "activity_subscriptions.user, activity_stream.asid", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""}, qgen.DBJoin{"activity_stream", "activity_subscriptions", "activity_subscriptions.user, activity_stream.asid", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""},
), ),
notifyOne: acc.Insert("activity_stream_matches").Columns("watcher, asid").Fields("?,?").Prepare(), notifyOne: acc.Insert("activity_stream_matches").Columns("watcher,asid").Fields("?,?").Prepare(),
getWatchers: acc.SimpleInnerJoin("activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""), getWatchers: acc.SimpleInnerJoin("activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""),
} }
return acc.FirstError() return acc.FirstError()
@ -224,8 +224,7 @@ func notifyWatchers(asid int) {
} }
uids = append(uids, uid) uids = append(uids, uid)
} }
err = rows.Err() if err = rows.Err(); err != nil {
if err != nil {
LogError(err) LogError(err)
return return
} }

View File

@ -1,7 +1,7 @@
/* /*
* *
* Gosora Authentication Interface * Gosora Authentication Interface
* Copyright Azareal 2017 - 2019 * Copyright Azareal 2017 - 2020
* *
*/ */
package common package common
@ -145,6 +145,7 @@ func (auth *DefaultAuth) ValidateMFAToken(mfaToken string, uid int) error {
if ok { if ok {
return nil return nil
} }
for i, scratch := range mfaItem.Scratch { for i, scratch := range mfaItem.Scratch {
if subtle.ConstantTimeCompare([]byte(scratch), []byte(mfaToken)) == 1 { if subtle.ConstantTimeCompare([]byte(scratch), []byte(mfaToken)) == 1 {
err = mfaItem.BurnScratch(i) err = mfaItem.BurnScratch(i)
@ -155,6 +156,7 @@ func (auth *DefaultAuth) ValidateMFAToken(mfaToken string, uid int) error {
return nil return nil
} }
} }
return ErrWrongMFAToken return ErrWrongMFAToken
} }

View File

@ -34,9 +34,10 @@ type MemoryForumPermsStore struct {
func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) { func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) {
acc := qgen.NewAcc() acc := qgen.NewAcc()
fp := "forums_permissions"
return &MemoryForumPermsStore{ return &MemoryForumPermsStore{
getByForum: acc.Select("forums_permissions").Columns("gid, permissions").Where("fid = ?").Orderby("gid ASC").Prepare(), getByForum: acc.Select(fp).Columns("gid, permissions").Where("fid = ?").Orderby("gid ASC").Prepare(),
getByForumGroup: acc.Select("forums_permissions").Columns("permissions").Where("fid = ? AND gid = ?").Prepare(), getByForumGroup: acc.Select(fp).Columns("permissions").Where("fid = ? AND gid = ?").Prepare(),
evenForums: make(map[int]map[int]*ForumPerms), evenForums: make(map[int]map[int]*ForumPerms),
oddForums: make(map[int]map[int]*ForumPerms), oddForums: make(map[int]map[int]*ForumPerms),

View File

@ -138,8 +138,9 @@ func (s *MemoryPollCache) RemoveUnsafe(id int) error {
// Flush removes all the polls from the cache, useful for tests. // Flush removes all the polls from the cache, useful for tests.
func (s *MemoryPollCache) Flush() { func (s *MemoryPollCache) Flush() {
m := make(map[int]*Poll)
s.Lock() s.Lock()
s.items = make(map[int]*Poll) s.items = m
s.length = 0 s.length = 0
s.Unlock() s.Unlock()
} }

View File

@ -45,10 +45,11 @@ var settingStmts SettingStmts
func init() { func init() {
SettingBox.Store(SettingMap(make(map[string]interface{}))) SettingBox.Store(SettingMap(make(map[string]interface{})))
DbInits.Add(func(acc *qgen.Accumulator) error { DbInits.Add(func(acc *qgen.Accumulator) error {
s := "settings"
settingStmts = SettingStmts{ settingStmts = SettingStmts{
getAll: acc.Select("settings").Columns("name, content, type, constraints").Prepare(), getAll: acc.Select(s).Columns("name, content, type, constraints").Prepare(),
get: acc.Select("settings").Columns("content, type, constraints").Where("name = ?").Prepare(), get: acc.Select(s).Columns("content, type, constraints").Where("name = ?").Prepare(),
update: acc.Update("settings").Set("content = ?").Where("name = ?").Prepare(), update: acc.Update(s).Set("content = ?").Where("name = ?").Prepare(),
} }
return acc.FirstError() return acc.FirstError()
}) })
@ -67,8 +68,8 @@ func LoadSettings() error {
return err return err
} }
for _, setting := range settings { for _, s := range settings {
err = sBox.ParseSetting(setting.Name, setting.Content, setting.Type, setting.Constraint) err = sBox.ParseSetting(s.Name, s.Content, s.Type, s.Constraint)
if err != nil { if err != nil {
return err return err
} }
@ -80,7 +81,7 @@ func LoadSettings() error {
// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions. // TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
func (sBox SettingMap) ParseSetting(sname string, scontent string, stype string, constraint string) (err error) { func (sBox SettingMap) ParseSetting(sname string, scontent string, stype string, constraint string) (err error) {
var ssBox = map[string]interface{}(sBox) ssBox := map[string]interface{}(sBox)
switch stype { switch stype {
case "bool": case "bool":
ssBox[sname] = (scontent == "1") ssBox[sname] = (scontent == "1")

View File

@ -88,7 +88,7 @@ func (tList *DefaultTopicList) Tick() error {
canSee[i] = byte(item) canSee[i] = byte(item)
} }
var canSeeInt = make([]int, len(canSee)) canSeeInt := make([]int, len(canSee))
copy(canSeeInt, group.CanSee) copy(canSeeInt, group.CanSee)
sCanSee := string(canSee) sCanSee := string(canSee)
permTree[sCanSee] = canSeeInt permTree[sCanSee] = canSeeInt
@ -161,7 +161,7 @@ func (tList *DefaultTopicList) GetListByCanSee(canSee []int, page int, orderby s
} }
} }
var inSlice = func(haystack []int, needle int) bool { inSlice := func(haystack []int, needle int) bool {
for _, item := range haystack { for _, item := range haystack {
if needle == item { if needle == item {
return true return true
@ -200,7 +200,7 @@ func (tList *DefaultTopicList) GetList(page int, orderby string, filterIDs []int
return nil, nil, Paginator{nil, 1, 1}, err return nil, nil, Paginator{nil, 1, 1}, err
} }
var inSlice = func(haystack []int, needle int) bool { inSlice := func(haystack []int, needle int) bool {
for _, item := range haystack { for _, item := range haystack {
if needle == item { if needle == item {
return true return true
@ -282,43 +282,43 @@ func (tList *DefaultTopicList) getList(page int, orderby string, argList []inter
reqUserList := make(map[int]bool) reqUserList := make(map[int]bool)
for rows.Next() { for rows.Next() {
// TODO: Embed Topic structs in TopicsRow to make it easier for us to reuse this work in the topic cache // TODO: Embed Topic structs in TopicsRow to make it easier for us to reuse this work in the topic cache
topic := TopicsRow{} t := TopicsRow{}
err := rows.Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.IsClosed, &topic.Sticky, &topic.CreatedAt, &topic.LastReplyAt, &topic.LastReplyBy, &topic.LastReplyID, &topic.ParentID, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.AttachCount, &topic.Poll, &topic.Data) err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IsClosed, &t.Sticky, &t.CreatedAt, &t.LastReplyAt, &t.LastReplyBy, &t.LastReplyID, &t.ParentID, &t.ViewCount, &t.PostCount, &t.LikeCount, &t.AttachCount, &t.Poll, &t.Data)
if err != nil { if err != nil {
return nil, Paginator{nil, 1, 1}, err return nil, Paginator{nil, 1, 1}, err
} }
topic.Link = BuildTopicURL(NameToSlug(topic.Title), topic.ID) t.Link = BuildTopicURL(NameToSlug(t.Title), t.ID)
// TODO: Pass forum to something like topicItem.Forum and use that instead of these two properties? Could be more flexible. // TODO: Pass forum to something like topicItem.Forum and use that instead of these two properties? Could be more flexible.
forum := Forums.DirtyGet(topic.ParentID) forum := Forums.DirtyGet(t.ParentID)
topic.ForumName = forum.Name t.ForumName = forum.Name
topic.ForumLink = forum.Link t.ForumLink = forum.Link
// TODO: Create a specialised function with a bit less overhead for getting the last page for a post count // TODO: Create a specialised function with a bit less overhead for getting the last page for a post count
_, _, lastPage := PageOffset(topic.PostCount, 1, Config.ItemsPerPage) _, _, lastPage := PageOffset(t.PostCount, 1, Config.ItemsPerPage)
topic.LastPage = lastPage t.LastPage = lastPage
// TODO: Rename this Vhook to better reflect moving the topic list from /routes/ to /common/ // TODO: Rename this Vhook to better reflect moving the topic list from /routes/ to /common/
GetHookTable().Vhook("topics_topic_row_assign", &topic, &forum) GetHookTable().Vhook("topics_topic_row_assign", &t, &forum)
topicList = append(topicList, &topic) topicList = append(topicList, &t)
reqUserList[topic.CreatedBy] = true reqUserList[t.CreatedBy] = true
reqUserList[topic.LastReplyBy] = true reqUserList[t.LastReplyBy] = true
//log.Print("rlen: ", rlen) //log.Print("rlen: ", rlen)
//log.Print("rcap: ", rcap) //log.Print("rcap: ", rcap)
//log.Print("topic.PostCount: ", topic.PostCount) //log.Print("topic.PostCount: ", t.PostCount)
//log.Print("topic.PostCount == 2 && rlen < rcap: ", topic.PostCount == 2 && rlen < rcap) //log.Print("topic.PostCount == 2 && rlen < rcap: ", topic.PostCount == 2 && rlen < rcap)
// Avoid the extra queries on topic list pages, if we already have what we want... // Avoid the extra queries on topic list pages, if we already have what we want...
var hRids = false hRids := false
if tcache != nil { if tcache != nil {
if t, err := tcache.Get(topic.ID); err == nil { if t, err := tcache.Get(t.ID); err == nil {
hRids = len(t.Rids) != 0 hRids = len(t.Rids) != 0
} }
} }
if topic.PostCount == 2 && rlen < rcap && !hRids && page < 5 { if t.PostCount == 2 && rlen < rcap && !hRids && page < 5 {
rids, err := GetRidsForTopic(topic.ID, 0) rids, err := GetRidsForTopic(t.ID, 0)
if err != nil { if err != nil {
return nil, Paginator{nil, 1, 1}, err return nil, Paginator{nil, 1, 1}, err
} }
@ -329,12 +329,12 @@ func (tList *DefaultTopicList) getList(page int, orderby string, argList []inter
} }
_, _ = Rstore.Get(rids[0]) _, _ = Rstore.Get(rids[0])
rlen++ rlen++
topic.Rids = []int{rids[0]} t.Rids = []int{rids[0]}
} }
if tcache != nil { if tcache != nil {
if _, err := tcache.Get(topic.ID); err == sql.ErrNoRows { if _, err := tcache.Get(t.ID); err == sql.ErrNoRows {
_ = tcache.Set(topic.Topic()) _ = tcache.Set(t.Topic())
} }
} }
} }
@ -344,7 +344,7 @@ func (tList *DefaultTopicList) getList(page int, orderby string, argList []inter
} }
// Convert the user ID map to a slice, then bulk load the users // Convert the user ID map to a slice, then bulk load the users
var idSlice = make([]int, len(reqUserList)) idSlice := make([]int, len(reqUserList))
var i int var i int
for userID := range reqUserList { for userID := range reqUserList {
idSlice[i] = userID idSlice[i] = userID

View File

@ -1,4 +1,4 @@
/* Copyright Azareal 2017 - 2019 */ /* Copyright Azareal 2017 - 2020 */
package common package common
import ( import (

View File

@ -223,6 +223,7 @@ func (h *WsHubImpl) UserCount() (count int) {
h.evenUserLock.RLock() h.evenUserLock.RLock()
count += len(h.evenOnlineUsers) count += len(h.evenOnlineUsers)
h.evenUserLock.RUnlock() h.evenUserLock.RUnlock()
h.oddUserLock.RLock() h.oddUserLock.RLock()
count += len(h.oddOnlineUsers) count += len(h.oddOnlineUsers)
h.oddUserLock.RUnlock() h.oddUserLock.RUnlock()
@ -236,6 +237,7 @@ func (h *WsHubImpl) HasUser(uid int) (exists bool) {
if exists { if exists {
return exists return exists
} }
h.oddUserLock.RLock() h.oddUserLock.RLock()
_, exists = h.oddOnlineUsers[uid] _, exists = h.oddOnlineUsers[uid]
h.oddUserLock.RUnlock() h.oddUserLock.RUnlock()

View File

@ -100,6 +100,7 @@ func (u *WSUser) AddSocket(conn *websocket.Conn, page string) {
func (u *WSUser) RemoveSocket(conn *websocket.Conn) { func (u *WSUser) RemoveSocket(conn *websocket.Conn) {
u.Lock() u.Lock()
defer u.Unlock()
if len(u.Sockets) < 6 { if len(u.Sockets) < 6 {
for i, socket := range u.Sockets { for i, socket := range u.Sockets {
if socket == nil { if socket == nil {
@ -107,7 +108,6 @@ func (u *WSUser) RemoveSocket(conn *websocket.Conn) {
} }
if socket.conn == conn { if socket.conn == conn {
u.Sockets[i] = nil u.Sockets[i] = nil
u.Unlock()
//fmt.Printf("%+v\n", wsUser.Sockets) //fmt.Printf("%+v\n", wsUser.Sockets)
return return
} }
@ -123,8 +123,6 @@ func (u *WSUser) RemoveSocket(conn *websocket.Conn) {
} }
u.Sockets = append(u.Sockets[:key], u.Sockets[key+1:]...) u.Sockets = append(u.Sockets[:key], u.Sockets[key+1:]...)
//fmt.Printf("%+v\n", u.Sockets) //fmt.Printf("%+v\n", u.Sockets)
u.Unlock()
} }
func (u *WSUser) SetPageForSocket(conn *websocket.Conn, page string) error { func (u *WSUser) SetPageForSocket(conn *websocket.Conn, page string) error {

View File

@ -87,8 +87,8 @@ type Member struct {
User c.User User c.User
} }
func PrebuildTmplList(user c.User, header *c.Header) c.CTmpl { func PrebuildTmplList(user c.User, h *c.Header) c.CTmpl {
var guildList = []*Guild{ guildList := []*Guild{
&Guild{ &Guild{
ID: 1, ID: 1,
Name: "lol", Name: "lol",
@ -104,7 +104,7 @@ func PrebuildTmplList(user c.User, header *c.Header) c.CTmpl {
Forums: []*c.Forum{c.Forums.DirtyGet(1)}, Forums: []*c.Forum{c.Forums.DirtyGet(1)},
}, },
} }
listPage := ListPage{"Guild List", user, header, guildList} listPage := ListPage{"Guild List", user, h, guildList}
return c.CTmpl{"guilds_guild_list", "guilds_guild_list.html", "templates/", "guilds.ListPage", listPage, []string{"./extend/guilds/lib"}} return c.CTmpl{"guilds_guild_list", "guilds_guild_list.html", "templates/", "guilds.ListPage", listPage, []string{"./extend/guilds/lib"}}
} }

View File

@ -879,7 +879,7 @@ function mainInit(){
for(let i = 0; i < elems.length; i++) { for(let i = 0; i < elems.length; i++) {
let elem = elems[i]; let elem = elems[i];
if(elem.nodeName=="SELECT") { if(elem.nodeName=="SELECT") {
s += elem.name + "=" + elem.options[elem.selectedIndex].getAttribute("val") + "&"; s += elem.name + "=" + elem.options[elem.selectedIndex].getAttribute("value") + "&";
} }
// TODO: Implement other element types... // TODO: Implement other element types...
} }

View File

@ -6,8 +6,8 @@ type RouteSubset struct {
func (set *RouteSubset) Before(lines ...string) *RouteSubset { func (set *RouteSubset) Before(lines ...string) *RouteSubset {
for _, line := range lines { for _, line := range lines {
for _, route := range set.RouteList { for _, r := range set.RouteList {
route.RunBefore = append(route.RunBefore, Runnable{line, false}) r.RunBefore = append(r.RunBefore, Runnable{line, false})
} }
} }
return set return set
@ -15,8 +15,8 @@ func (set *RouteSubset) Before(lines ...string) *RouteSubset {
func (set *RouteSubset) LitBefore(lines ...string) *RouteSubset { func (set *RouteSubset) LitBefore(lines ...string) *RouteSubset {
for _, line := range lines { for _, line := range lines {
for _, route := range set.RouteList { for _, r := range set.RouteList {
route.RunBefore = append(route.RunBefore, Runnable{line, true}) r.RunBefore = append(r.RunBefore, Runnable{line, true})
} }
} }
return set return set

View File

@ -103,7 +103,7 @@ func SettingEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, snam
return c.NoPermissions(w, r, user) return c.NoPermissions(w, r, user)
} }
scontent := c.SanitiseBody(r.PostFormValue("setting-value")) scontent := c.SanitiseBody(r.PostFormValue("value"))
rerr := headerLite.Settings.Update(sname, scontent) rerr := headerLite.Settings.Update(sname, scontent)
if rerr != nil { if rerr != nil {
return rerr return rerr

View File

@ -13,4 +13,4 @@
<div class="rowitem passive"><a href="/user/convos/">{{lang "account_menu_messages"}}</a></div> <div class="rowitem passive"><a href="/user/convos/">{{lang "account_menu_messages"}}</a></div>
{{/** TODO: Add an alerts page with pagination to go through alerts which either don't fit in the alerts drop-down or which have already been dismissed. Bear in mind though that dismissed alerts older than two weeks might be purged to save space and to speed up the database **/}} {{/** TODO: Add an alerts page with pagination to go through alerts which either don't fit in the alerts drop-down or which have already been dismissed. Bear in mind though that dismissed alerts older than two weeks might be purged to save space and to speed up the database **/}}
</div> </div>
</nav> </nav>

View File

@ -24,7 +24,7 @@
<div class="levelBit"> <div class="levelBit">
<a href="/user/levels/">{{level .CurrentUser.Level}}</a> <a href="/user/levels/">{{level .CurrentUser.Level}}</a>
</div> </div>
<div class="progressWrap" style="width: {{.Percentage}}%;"> <div class="progressWrap" style="width:{{.Percentage}}%;">
<div>{{.CurrentScore}} / {{.NextScore}}</div> <div>{{.CurrentScore}} / {{.NextScore}}</div>
</div> </div>
</div> </div>

View File

@ -20,10 +20,8 @@
<div class="rowitem rowmsg" style="white-space:pre-wrap;">{{lang "account_mfa_scratch_explanation"}}</div> <div class="rowitem rowmsg" style="white-space:pre-wrap;">{{lang "account_mfa_scratch_explanation"}}</div>
</div> </div>
<div id="panel_mfa_scratches" class="colstack_item rowlist"> <div id="panel_mfa_scratches" class="colstack_item rowlist">
{{range .Something}} {{range .Something}}<div class="rowitem">{{.}}</div>{{end}}
<div class="rowitem">{{.}}</div>
{{end}}
</div> </div>
</main> </main>
</div> </div>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -23,4 +23,4 @@
</div> </div>
</main> </main>
</div> </div>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -18,9 +18,9 @@
<div class="formitem formlabel"><a>Visibility</a></div> <div class="formitem formlabel"><a>Visibility</a></div>
<div class="formitem"> <div class="formitem">
<select name="group_privacy"> <select name="group_privacy">
<option val="0">Public</option> <option value=0>Public</option>
<option val="1">Protected</option> <option value=1>Protected</option>
<option val="2">Private</option> <option value=2>Private</option>
</select> </select>
</div> </div>
</div> </div>
@ -31,4 +31,4 @@
</div> </div>
</main> </main>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -29,12 +29,12 @@
top: 4px; top: 4px;
} }
.menuItem a { .menuItem a {
color: black; color: black;
text-decoration: none; text-decoration: none;
} }
.rightMenu { .rightMenu {
float: right; float: right;
border-right: none; border-right: none;
border-left: 1px solid #ccc; border-left: 1px solid #ccc;
} }
.sgBackdrop { .sgBackdrop {
@ -45,4 +45,4 @@
padding-top: calc(150px - 38px); padding-top: calc(150px - 38px);
border-bottom: none; border-bottom: none;
} }
</style> </style>

View File

@ -16,12 +16,12 @@
<div class="menuItem rightMenu"><a href="#">Edit</a></div> <div class="menuItem rightMenu"><a href="#">Edit</a></div>
<div class="menuItem rightMenu"><a href="/guild/join/{{.Guild.ID}}">Join</a></div> <div class="menuItem rightMenu"><a href="/guild/join/{{.Guild.ID}}">Join</a></div>
</nav> </nav>
<div style="clear: both;"></div> <div style="clear:both;"></div>
</div> </div>
<main id="socialgroups_member_list" class="rowblock member_list" style="position: relative;z-index: 50;"> <main id="socialgroups_member_list" class="rowblock member_list" style="position:relative;z-index:50;">
{{range .ItemList}}<div class="rowitem passive datarow" style="background-image: url({{.User.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 78px;{{if .Offline}}background-color: #eaeaea;{{else if gt .Rank 0}}background-color: #e6f3ff;{{end}}"> {{range .ItemList}}<div class="rowitem passive datarow" style="background-image:url({{.User.Avatar}});background-position:left;background-repeat:no-repeat;background-size:64px;padding-left:78px;{{if .Offline}}background-color:#eaeaea;{{else if gt .Rank 0}}background-color:#e6f3ff;{{end}}">
<span style="float: right;"> <span style="float:right;">
<span class="rank" style="font-size: 15px;">{{.RankString}}</span><br /> <span class="rank" style="font-size:15px;">{{.RankString}}</span><br />
<span class="joinedAt rowsmall">{{.JoinedAt}}</span> <span class="joinedAt rowsmall">{{.JoinedAt}}</span>
</span> </span>
<span> <span>
@ -32,4 +32,4 @@
</div> </div>
{{end}} {{end}}
</main> </main>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -25,4 +25,4 @@
</form> </form>
</div> </div>
</main> </main>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -18,4 +18,4 @@
</form> </form>
</div> </div>
</main> </main>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -2,10 +2,11 @@
<div class="rowitem"> <div class="rowitem">
<h1>{{lang "panel_statistics_active_memory_head"}}</h1> <h1>{{lang "panel_statistics_active_memory_head"}}</h1>
<select form="timeRangeForm" class="typeSelector to_right autoSubmitRedirect" name="mtype"> <select form="timeRangeForm" class="typeSelector to_right autoSubmitRedirect" name="mtype">
<option val="0"{{if eq .MemType 0}} selected{{end}}>{{lang "panel_statistics_memory_type_total"}}</option> <option value="0"{{if eq .MemType 0}} selected{{end}}>{{lang "panel_statistics_memory_type_total"}}</option>
<option val="1"{{if eq .MemType 1}} selected{{end}}>{{lang "panel_statistics_memory_type_stack"}}</option> <option value="1"{{if eq .MemType 1}} selected{{end}}>{{lang "panel_statistics_memory_type_stack"}}</option>
<option val="2"{{if eq .MemType 2}} selected{{end}}>{{lang "panel_statistics_memory_type_heap"}}</option> <option value="2"{{if eq .MemType 2}} selected{{end}}>{{lang "panel_statistics_memory_type_heap"}}</option>
</select> </select>
<noscript><input form="timeRangeForm" type="submit" /></noscript>
{{template "panel_analytics_time_range_month.html" . }} {{template "panel_analytics_time_range_month.html" . }}
</div> </div>
</div> </div>

View File

@ -2,9 +2,10 @@
<div class="rowitem"> <div class="rowitem">
<h1>{{lang "panel_statistics_referrers_head"}}</h1> <h1>{{lang "panel_statistics_referrers_head"}}</h1>
<select form="timeRangeForm" class="spamSelector to_right autoSubmitRedirect" name="spam"> <select form="timeRangeForm" class="spamSelector to_right autoSubmitRedirect" name="spam">
<option val="0"{{if not .ShowSpam}} selected{{end}}>{{lang "panel_statistics_spam_hide"}}</option> <option value="0"{{if not .ShowSpam}} selected{{end}}>{{lang "panel_statistics_spam_hide"}}</option>
<option val="1"{{if .ShowSpam}} selected{{end}}>{{lang "panel_statistics_spam_show"}}</option> <option value="1"{{if .ShowSpam}} selected{{end}}>{{lang "panel_statistics_spam_show"}}</option>
</select> </select>
<noscript><input form="timeRangeForm" type="submit" /></noscript>
{{template "panel_analytics_time_range.html" . }} {{template "panel_analytics_time_range.html" . }}
</div> </div>
</div> </div>

View File

@ -1,10 +1,11 @@
<select form="timeRangeForm" class="timeRangeSelector to_right autoSubmitRedirect" name="timeRange"> <select form="timeRangeForm" class="timeRangeSelector to_right autoSubmitRedirect" name="timeRange">
<option val="one-year"{{if eq .TimeRange "one-year"}} selected{{end}}>{{lang "panel_statistics_time_range_one_year"}}</option> <option value="one-year"{{if eq .TimeRange "one-year"}} selected{{end}}>{{lang "panel_statistics_time_range_one_year"}}</option>
<option val="three-months"{{if eq .TimeRange "three-months"}} selected{{end}}>{{lang "panel_statistics_time_range_three_months"}}</option> <option value="three-months"{{if eq .TimeRange "three-months"}} selected{{end}}>{{lang "panel_statistics_time_range_three_months"}}</option>
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>{{lang "panel_statistics_time_range_one_month"}}</option> <option value="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>{{lang "panel_statistics_time_range_one_month"}}</option>
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>{{lang "panel_statistics_time_range_one_week"}}</option> <option value="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>{{lang "panel_statistics_time_range_one_week"}}</option>
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>{{lang "panel_statistics_time_range_two_days"}}</option> <option value="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>{{lang "panel_statistics_time_range_two_days"}}</option>
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>{{lang "panel_statistics_time_range_one_day"}}</option> <option value="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>{{lang "panel_statistics_time_range_one_day"}}</option>
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>{{lang "panel_statistics_time_range_twelve_hours"}}</option> <option value="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>{{lang "panel_statistics_time_range_twelve_hours"}}</option>
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>{{lang "panel_statistics_time_range_six_hours"}}</option> <option value="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>{{lang "panel_statistics_time_range_six_hours"}}</option>
</select> </select>
<noscript><input form="timeRangeForm" type="submit" /></noscript>

View File

@ -1,8 +1,9 @@
<select form="timeRangeForm" class="timeRangeSelector to_right autoSubmitRedirect" name="timeRange"> <select form="timeRangeForm" class="timeRangeSelector to_right autoSubmitRedirect" name="timeRange">
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>{{lang "panel_statistics_time_range_one_month"}}</option> <option value="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>{{lang "panel_statistics_time_range_one_month"}}</option>
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>{{lang "panel_statistics_time_range_one_week"}}</option> <option value="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>{{lang "panel_statistics_time_range_one_week"}}</option>
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>{{lang "panel_statistics_time_range_two_days"}}</option> <option value="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>{{lang "panel_statistics_time_range_two_days"}}</option>
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>{{lang "panel_statistics_time_range_one_day"}}</option> <option value="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>{{lang "panel_statistics_time_range_one_day"}}</option>
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>{{lang "panel_statistics_time_range_twelve_hours"}}</option> <option value="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>{{lang "panel_statistics_time_range_twelve_hours"}}</option>
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>{{lang "panel_statistics_time_range_six_hours"}}</option> <option value="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>{{lang "panel_statistics_time_range_six_hours"}}</option>
</select> </select>
<noscript><input form="timeRangeForm" type="submit" /></noscript>

View File

@ -7,7 +7,7 @@
<div class="formrow"> <div class="formrow">
<div class="formitem formlabel"><a>{{lang "panel_setting_value"}}</a></div> <div class="formitem formlabel"><a>{{lang "panel_setting_value"}}</a></div>
<div class="formitem"> <div class="formitem">
<select name="setting-value"> <select name="value">
{{range .ItemList}}<option{{if .Selected}} selected{{end}} value="{{.Value}}">{{.Label}}</option>{{end}} {{range .ItemList}}<option{{if .Selected}} selected{{end}} value="{{.Value}}">{{.Label}}</option>{{end}}
</select> </select>
</div> </div>
@ -16,19 +16,19 @@
<div class="formrow"> <div class="formrow">
<div class="formitem formlabel"><a>{{lang "panel_setting_value"}}</a></div> <div class="formitem formlabel"><a>{{lang "panel_setting_value"}}</a></div>
<div class="formitem"> <div class="formitem">
<select name="setting-value"> <select name="value">
<option{{if eq .Setting.Content "1"}} selected{{end}} value="1">{{lang "option_yes"}}</option> <option{{if eq .Setting.Content "1"}} selected{{end}} value=1>{{lang "option_yes"}}</option>
<option{{if eq .Setting.Content "0"}} selected{{end}} value="0">{{lang "option_no"}}</option> <option{{if eq .Setting.Content "0"}} selected{{end}} value=0>{{lang "option_no"}}</option>
</select> </select>
</div> </div>
</div> </div>
{{else if eq .Setting.Type "textarea"}} {{else if eq .Setting.Type "textarea"}}
<div class="formrow"> <div class="formrow">
<div class="formitem"><textarea name="setting-value">{{.Setting.Content}}</textarea></div> <div class="formitem"><textarea name="value">{{.Setting.Content}}</textarea></div>
</div> </div>
{{else}}<div class="formrow"> {{else}}<div class="formrow">
<div class="formitem formlabel"><a>{{lang "panel_setting_value"}}</a></div> <div class="formitem formlabel"><a>{{lang "panel_setting_value"}}</a></div>
<div class="formitem"><input name="setting-value" type="text" value="{{.Setting.Content}}" /></div> <div class="formitem"><input name="value" type="text" value="{{.Setting.Content}}"/></div>
</div>{{end}} </div>{{end}}
<div class="formrow form_button_row"> <div class="formrow form_button_row">
<div class="formitem"><button name="panel-button" class="formbutton">{{lang "panel_setting_update_button"}}</button></div> <div class="formitem"><button name="panel-button" class="formbutton">{{lang "panel_setting_update_button"}}</button></div>

View File

@ -29,7 +29,7 @@
{{if .Poll.ID}}{{template "topic_poll.html" . }}{{end}} {{if .Poll.ID}}{{template "topic_poll.html" . }}{{end}}
<article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="{{lang "topic.opening_post_aria"}}"> <article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="{{lang "topic.opening_post_aria"}}">
<div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="background-image:url({{.Topic.Avatar}}), url(/s/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position:0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat,repeat-y;"> <div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="background-image:url({{.Topic.Avatar}}),url(/s/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position:0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat,repeat-y;">
<div class="hide_on_edit topic_content user_content" itemprop="text">{{.Topic.ContentHTML}}</div> <div class="hide_on_edit topic_content user_content" itemprop="text">{{.Topic.ContentHTML}}</div>
{{if .CurrentUser.Loggedin}}<textarea name="topic_content" class="show_on_edit topic_content_input edit_source">{{.Topic.Content}}</textarea>{{end}} {{if .CurrentUser.Loggedin}}<textarea name="topic_content" class="show_on_edit topic_content_input edit_source">{{.Topic.Content}}</textarea>{{end}}

View File

@ -121,5 +121,4 @@
{{end}} {{end}}
</main> </main>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -1,9 +1,13 @@
<div class="rowblock topic_reply_container"> <div class="rowblock topic_reply_container">
<div class="userinfo" aria-label="{{lang "topic.your_information"}}"> <div class="userinfo" aria-label="{{lang "topic.your_information"}}">
<div class="avatar_item" style="background-image:url({{.CurrentUser.Avatar}}),url(/s/white-dot.jpg);background-position:0px -10px;">&nbsp;</div> <div class="avatar_item" style="background-image:url({{.CurrentUser.Avatar}}),url(/s/white-dot.jpg);">&nbsp;</div>
<div class="user_meta"> <div class="user_meta">
<a href="{{.CurrentUser.Link}}" class="the_name" rel="author">{{.CurrentUser.Name}}</a> <a href="{{.CurrentUser.Link}}" class="the_name" rel="author">{{.CurrentUser.Name}}</a>
{{if .CurrentUser.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.CurrentUser.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .CurrentUser.Level}}</div><div class="tag_post"></div></div>{{end}} <div class="tag_block">
<div class="tag_pre"></div>
{{if .CurrentUser.Tag}}<div class="post_tag">{{.CurrentUser.Tag}}</div>{{else}}<div class="post_tag post_level">{{level .CurrentUser.Level}}</div>{{end}}
<div class="tag_post"></div>
</div>
</div> </div>
</div> </div>
<div class="rowblock topic_reply_form quick_create_form" aria-label="{{lang "topic.reply_aria"}}"> <div class="rowblock topic_reply_form quick_create_form" aria-label="{{lang "topic.reply_aria"}}">

View File

@ -1,7 +1,11 @@
<div class="userinfo" aria-label="{{lang "topic.userinfo_aria"}}"> <div class="userinfo" aria-label="{{lang "topic.userinfo_aria"}}">
<div class="avatar_item" style="background-image:url({{.Avatar}}), url(/s/white-dot.jpg);background-position:0px -10px;">&nbsp;</div> <div class="avatar_item" style="background-image:url({{.Avatar}}),url(/s/white-dot.jpg);">&nbsp;</div>
<div class="user_meta"> <div class="user_meta">
<a href="{{.UserLink}}" class="the_name" rel="author">{{.CreatedByName}}</a> <a href="{{.UserLink}}" class="the_name" rel="author">{{.CreatedByName}}</a>
{{if .Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .Level}}</div><div class="tag_post"></div></div>{{end}} <div class="tag_block">
<div class="tag_pre"></div>
{{if .Tag}}<div class="post_tag">{{.Tag}}</div>{{else}}<div class="post_tag post_level">{{level .Level}}</div>{{end}}
<div class="tag_post"></div>
</div>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
<article class="rowblock post_container poll" aria-level="{{lang "topic.poll_aria"}}"> <article class="rowblock post_container poll" aria-level="{{lang "topic.poll_aria"}}">
<div class="rowitem passive editable_parent post_item poll_item {{.Topic.ClassName}}" style="background-image:url({{.Topic.Avatar}}), url(/s/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position:0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat,repeat-y;"> <div class="rowitem passive editable_parent post_item poll_item {{.Topic.ClassName}}" style="background-image:url({{.Topic.Avatar}}),url(/s/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position:0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat,repeat-y;">
<div class="topic_content user_content"> <div class="topic_content user_content">
{{range .Poll.QuickOptions}} {{range .Poll.QuickOptions}}
<div class="poll_option"> <div class="poll_option">

View File

@ -1059,6 +1059,7 @@ blockquote:first-child {
width: 84px; width: 84px;
height: 84px; height: 84px;
margin-bottom: 12px; margin-bottom: 12px;
background-position: 0px -10px;
background-size: 120px; background-size: 120px;
} }
.the_name, .userinfo .tag_block { .the_name, .userinfo .tag_block {

View File

@ -837,6 +837,7 @@ blockquote:first-child {
border-radius: 36px; border-radius: 36px;
height: 58px; height: 58px;
width: 58px; width: 58px;
background-position: 0px -10px;
background-size: 78px; background-size: 78px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;