Profiles are less broken now.
Renamed RouteViewCounterImpl to DefaultRouteViewCounter. Added the bot_routes.go file and moved routeRobotsTxt into it. Added /report/ to robots.txt to stop Googlebot going there. Began work on the sitemaps. I plan to move the routes into their own package, but I don't want to break too many things right now.
This commit is contained in:
parent
bdbd80319f
commit
c7df616f5b
|
@ -0,0 +1,278 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"./common"
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
// TODO: Make this a static file somehow? Is it possible for us to put this file somewhere else?
|
||||
// TODO: Add an API so that plugins can register disallowed areas. E.g. /guilds/join for plugin_guilds
|
||||
func routeRobotsTxt(w http.ResponseWriter, r *http.Request) common.RouteError {
|
||||
_, _ = w.Write([]byte(`User-agent: *
|
||||
Disallow: /panel/
|
||||
Disallow: /topics/create/
|
||||
Disallow: /user/edit/
|
||||
Disallow: /accounts/
|
||||
Disallow: /report/
|
||||
`))
|
||||
return nil
|
||||
}
|
||||
|
||||
var xmlInternalError = []byte(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<error>A problem has occured</error>`)
|
||||
var sitemapPageCap = 40000 // 40k, bump it up to 50k once we gzip this? Does brotli work on sitemaps?
|
||||
|
||||
func writeXMLHeader(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
w.Write([]byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"))
|
||||
}
|
||||
|
||||
// TODO: Keep track of when a sitemap was last modifed and add a lastmod element for it
|
||||
func routeSitemapXml(w http.ResponseWriter, r *http.Request) common.RouteError {
|
||||
var sslBit string
|
||||
if common.Site.EnableSsl {
|
||||
sslBit = "s"
|
||||
}
|
||||
var sitemapItem = func(path string) {
|
||||
w.Write([]byte(`<sitemap>
|
||||
<loc>http` + sslBit + `://` + common.Site.URL + "/" + path + `</loc>
|
||||
</sitemap>
|
||||
`))
|
||||
}
|
||||
writeXMLHeader(w, r)
|
||||
w.Write([]byte("<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"))
|
||||
sitemapItem("sitemaps/topics.xml")
|
||||
sitemapItem("sitemaps/forums.xml")
|
||||
//sitemapItem("sitemaps/users.xml")
|
||||
w.Write([]byte("</sitemapindex>"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type FuzzyRoute struct {
|
||||
Path string
|
||||
Handle func(http.ResponseWriter, *http.Request, int) common.RouteError
|
||||
}
|
||||
|
||||
// TODO: Add a sitemap API and clean things up
|
||||
// TODO: ^-- Make sure that the API is concurrent
|
||||
// TODO: Add a social group sitemap
|
||||
var sitemapRoutes = map[string]func(http.ResponseWriter, *http.Request) common.RouteError{
|
||||
"forums.xml": routeSitemapForums,
|
||||
"topics.xml": routeSitemapTopics,
|
||||
}
|
||||
|
||||
// TODO: Use a router capable of parsing this rather than hard-coding the logic in
|
||||
var fuzzySitemapRoutes = map[string]FuzzyRoute{
|
||||
"topics_page_": FuzzyRoute{"topics_page_(%d).xml", routeSitemapTopic},
|
||||
}
|
||||
|
||||
func sitemapSwitch(w http.ResponseWriter, r *http.Request) common.RouteError {
|
||||
var path = r.URL.Path[len("/sitemaps/"):]
|
||||
for name, fuzzy := range fuzzySitemapRoutes {
|
||||
if strings.HasPrefix(path, name) && strings.HasSuffix(path, ".xml") {
|
||||
var spath = strings.TrimPrefix(path, name)
|
||||
spath = strings.TrimSuffix(spath, ".xml")
|
||||
page, err := strconv.Atoi(spath)
|
||||
if err != nil {
|
||||
if common.Dev.DebugMode {
|
||||
log.Printf("Unable to convert string '%s' to integer in fuzzy route", spath)
|
||||
}
|
||||
return common.NotFound(w, r)
|
||||
}
|
||||
return fuzzy.Handle(w, r, page)
|
||||
}
|
||||
}
|
||||
|
||||
route, ok := sitemapRoutes[path]
|
||||
if !ok {
|
||||
return common.NotFound(w, r)
|
||||
}
|
||||
return route(w, r)
|
||||
}
|
||||
|
||||
func routeSitemapForums(w http.ResponseWriter, r *http.Request) common.RouteError {
|
||||
var sslBit string
|
||||
if common.Site.EnableSsl {
|
||||
sslBit = "s"
|
||||
}
|
||||
var sitemapItem = func(path string) {
|
||||
w.Write([]byte(`<url>
|
||||
<loc>http` + sslBit + `://` + common.Site.URL + path + `</loc>
|
||||
</url>
|
||||
`))
|
||||
}
|
||||
|
||||
group, err := common.Groups.Get(common.GuestUser.Group)
|
||||
if err != nil {
|
||||
log.Print("The guest group doesn't exist for some reason")
|
||||
// TODO: Add XML error handling to errors.go
|
||||
w.WriteHeader(500)
|
||||
w.Write(xmlInternalError)
|
||||
return common.HandledRouteError()
|
||||
}
|
||||
|
||||
writeXMLHeader(w, r)
|
||||
w.Write([]byte("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"))
|
||||
|
||||
for _, fid := range group.CanSee {
|
||||
// Avoid data races by copying the struct into something we can freely mold without worrying about breaking something somewhere else
|
||||
var forum = common.Forums.DirtyGet(fid).Copy()
|
||||
if forum.ParentID == 0 && forum.Name != "" && forum.Active {
|
||||
sitemapItem(common.BuildForumURL(common.NameToSlug(forum.Name), forum.ID))
|
||||
}
|
||||
}
|
||||
|
||||
w.Write([]byte("</urlset>"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Add a global ratelimit. 10 50MB files (smaller if compressed better) per minute?
|
||||
// ? We might have problems with banned users, if they have fewer ViewTopic permissions than guests as they'll be able to see this list. Then again, a banned user could just logout to see it
|
||||
func routeSitemapTopics(w http.ResponseWriter, r *http.Request) common.RouteError {
|
||||
var sslBit string
|
||||
if common.Site.EnableSsl {
|
||||
sslBit = "s"
|
||||
}
|
||||
var sitemapItem = func(path string) {
|
||||
w.Write([]byte(`<sitemap>
|
||||
<loc>http` + sslBit + `://` + common.Site.URL + "/" + path + `</loc>
|
||||
</sitemap>
|
||||
`))
|
||||
}
|
||||
writeXMLHeader(w, r)
|
||||
|
||||
group, err := common.Groups.Get(common.GuestUser.Group)
|
||||
if err != nil {
|
||||
log.Print("The guest group doesn't exist for some reason")
|
||||
// TODO: Add XML error handling to errors.go
|
||||
w.WriteHeader(500)
|
||||
w.Write(xmlInternalError)
|
||||
return common.HandledRouteError()
|
||||
}
|
||||
|
||||
var argList []interface{}
|
||||
var qlist string
|
||||
for _, fid := range group.CanSee {
|
||||
forum := common.Forums.DirtyGet(fid)
|
||||
if forum.Name != "" && forum.Active {
|
||||
argList = append(argList, strconv.Itoa(fid))
|
||||
qlist += "?,"
|
||||
}
|
||||
}
|
||||
if qlist != "" {
|
||||
qlist = qlist[0 : len(qlist)-1]
|
||||
}
|
||||
|
||||
// TODO: Abstract this
|
||||
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "parentID IN("+qlist+")", "")
|
||||
if err != nil {
|
||||
// TODO: Add XML error handling to errors.go
|
||||
w.WriteHeader(500)
|
||||
w.Write(xmlInternalError)
|
||||
common.LogError(err)
|
||||
return common.HandledRouteError()
|
||||
}
|
||||
defer topicCountStmt.Close()
|
||||
|
||||
var topicCount int
|
||||
err = topicCountStmt.QueryRow(argList...).Scan(&topicCount)
|
||||
if err != nil && err != ErrNoRows {
|
||||
// TODO: Add XML error handling to errors.go
|
||||
w.WriteHeader(500)
|
||||
w.Write(xmlInternalError)
|
||||
common.LogError(err)
|
||||
return common.HandledRouteError()
|
||||
}
|
||||
|
||||
var pageCount = topicCount / sitemapPageCap
|
||||
//log.Print("topicCount", topicCount)
|
||||
//log.Print("pageCount", pageCount)
|
||||
w.Write([]byte("<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"))
|
||||
for i := 0; i <= pageCount; i++ {
|
||||
sitemapItem("sitemaps/topics_page_" + strconv.Itoa(i) + ".xml")
|
||||
}
|
||||
w.Write([]byte("</sitemapindex>"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeSitemapTopic(w http.ResponseWriter, r *http.Request, page int) common.RouteError {
|
||||
/*var sslBit string
|
||||
if common.Site.EnableSsl {
|
||||
sslBit = "s"
|
||||
}
|
||||
var sitemapItem = func(path string) {
|
||||
w.Write([]byte(`<url>
|
||||
<loc>http` + sslBit + `://` + common.Site.URL + "/" + path + `</loc>
|
||||
</url>
|
||||
`))
|
||||
}*/
|
||||
|
||||
group, err := common.Groups.Get(common.GuestUser.Group)
|
||||
if err != nil {
|
||||
log.Print("The guest group doesn't exist for some reason")
|
||||
// TODO: Add XML error handling to errors.go
|
||||
w.WriteHeader(500)
|
||||
w.Write(xmlInternalError)
|
||||
return common.HandledRouteError()
|
||||
}
|
||||
|
||||
var argList []interface{}
|
||||
var qlist string
|
||||
for _, fid := range group.CanSee {
|
||||
forum := common.Forums.DirtyGet(fid)
|
||||
if forum.Name != "" && forum.Active {
|
||||
argList = append(argList, strconv.Itoa(fid))
|
||||
qlist += "?,"
|
||||
}
|
||||
}
|
||||
if qlist != "" {
|
||||
qlist = qlist[0 : len(qlist)-1]
|
||||
}
|
||||
|
||||
// TODO: Abstract this
|
||||
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "parentID IN("+qlist+")", "")
|
||||
if err != nil {
|
||||
// TODO: Add XML error handling to errors.go
|
||||
w.WriteHeader(500)
|
||||
w.Write(xmlInternalError)
|
||||
common.LogError(err)
|
||||
return common.HandledRouteError()
|
||||
}
|
||||
defer topicCountStmt.Close()
|
||||
|
||||
var topicCount int
|
||||
err = topicCountStmt.QueryRow(argList...).Scan(&topicCount)
|
||||
if err != nil && err != ErrNoRows {
|
||||
// TODO: Add XML error handling to errors.go
|
||||
w.WriteHeader(500)
|
||||
w.Write(xmlInternalError)
|
||||
common.LogError(err)
|
||||
return common.HandledRouteError()
|
||||
}
|
||||
|
||||
var pageCount = topicCount / sitemapPageCap
|
||||
//log.Print("topicCount", topicCount)
|
||||
//log.Print("pageCount", pageCount)
|
||||
//log.Print("page",page)
|
||||
if page > pageCount {
|
||||
page = pageCount
|
||||
}
|
||||
|
||||
writeXMLHeader(w, r)
|
||||
w.Write([]byte("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"))
|
||||
|
||||
w.Write([]byte("</urlset>"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeSitemapUsers(w http.ResponseWriter, r *http.Request) common.RouteError {
|
||||
writeXMLHeader(w, r)
|
||||
w.Write([]byte("<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"))
|
||||
return nil
|
||||
}
|
|
@ -10,7 +10,8 @@ import (
|
|||
)
|
||||
|
||||
var GlobalViewCounter *ChunkedViewCounter
|
||||
var RouteViewCounter *RouteViewCounterImpl
|
||||
var RouteViewCounter *DefaultRouteViewCounter
|
||||
var TopicViewCounter *DefaultTopicViewCounter
|
||||
|
||||
type ChunkedViewCounter struct {
|
||||
buckets [2]int64
|
||||
|
@ -64,18 +65,18 @@ type RWMutexCounterBucket struct {
|
|||
}
|
||||
|
||||
// The name of the struct clashes with the name of the variable, so we're adding Impl to the end
|
||||
type RouteViewCounterImpl struct {
|
||||
type DefaultRouteViewCounter struct {
|
||||
routeBuckets []*RWMutexCounterBucket //[RouteID]count
|
||||
insert *sql.Stmt
|
||||
}
|
||||
|
||||
func NewRouteViewCounter() (*RouteViewCounterImpl, error) {
|
||||
func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
var routeBuckets = make([]*RWMutexCounterBucket, len(routeMapEnum))
|
||||
for bucketID, _ := range routeBuckets {
|
||||
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||
}
|
||||
counter := &RouteViewCounterImpl{
|
||||
counter := &DefaultRouteViewCounter{
|
||||
routeBuckets: routeBuckets,
|
||||
insert: acc.Insert("viewchunks").Columns("count, createdAt, route").Fields("?,UTC_TIMESTAMP(),?").Prepare(),
|
||||
}
|
||||
|
@ -84,7 +85,7 @@ func NewRouteViewCounter() (*RouteViewCounterImpl, error) {
|
|||
return counter, acc.FirstError()
|
||||
}
|
||||
|
||||
func (counter *RouteViewCounterImpl) Tick() error {
|
||||
func (counter *DefaultRouteViewCounter) Tick() error {
|
||||
for routeID, routeBucket := range counter.routeBuckets {
|
||||
var count int
|
||||
routeBucket.RLock()
|
||||
|
@ -100,7 +101,7 @@ func (counter *RouteViewCounterImpl) Tick() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (counter *RouteViewCounterImpl) insertChunk(count int, route int) error {
|
||||
func (counter *DefaultRouteViewCounter) insertChunk(count int, route int) error {
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -110,7 +111,7 @@ func (counter *RouteViewCounterImpl) insertChunk(count int, route int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (counter *RouteViewCounterImpl) Bump(route int) {
|
||||
func (counter *DefaultRouteViewCounter) Bump(route int) {
|
||||
// TODO: Test this check
|
||||
log.Print("counter.routeBuckets[route]: ", counter.routeBuckets[route])
|
||||
if len(counter.routeBuckets) <= route {
|
||||
|
@ -140,7 +141,7 @@ type ForumViewCounter struct {
|
|||
}*/
|
||||
|
||||
// TODO: Use two odd-even maps for now, and move to something more concurrent later, maybe a sharded map?
|
||||
type TopicViewCounter struct {
|
||||
type DefaultTopicViewCounter struct {
|
||||
oddTopics map[int]*RWMutexCounterBucket // map[tid]struct{counter,sync.RWMutex}
|
||||
evenTopics map[int]*RWMutexCounterBucket
|
||||
oddLock sync.RWMutex
|
||||
|
@ -149,19 +150,19 @@ type TopicViewCounter struct {
|
|||
update *sql.Stmt
|
||||
}
|
||||
|
||||
func NewTopicViewCounter() (*TopicViewCounter, error) {
|
||||
func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
counter := &TopicViewCounter{
|
||||
counter := &DefaultTopicViewCounter{
|
||||
oddTopics: make(map[int]*RWMutexCounterBucket),
|
||||
evenTopics: make(map[int]*RWMutexCounterBucket),
|
||||
update: acc.Update("topics").Set("views = ?").Where("tid = ?").Prepare(), // TODO: Add the views column to the topics table
|
||||
update: acc.Update("topics").Set("views = views + ?").Where("tid = ?").Prepare(),
|
||||
}
|
||||
AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second
|
||||
//AddScheduledSecondTask(counter.Tick)
|
||||
return counter, acc.FirstError()
|
||||
}
|
||||
|
||||
func (counter *TopicViewCounter) Tick() error {
|
||||
func (counter *DefaultTopicViewCounter) Tick() error {
|
||||
counter.oddLock.RLock()
|
||||
for topicID, topic := range counter.oddTopics {
|
||||
var count int
|
||||
|
@ -191,7 +192,7 @@ func (counter *TopicViewCounter) Tick() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (counter *TopicViewCounter) insertChunk(count int, topicID int) error {
|
||||
func (counter *DefaultTopicViewCounter) insertChunk(count int, topicID int) error {
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -200,7 +201,7 @@ func (counter *TopicViewCounter) insertChunk(count int, topicID int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (counter *TopicViewCounter) Bump(topicID int) {
|
||||
func (counter *DefaultTopicViewCounter) Bump(topicID int) {
|
||||
// Is the ID even?
|
||||
if topicID%2 == 0 {
|
||||
counter.evenLock.Lock()
|
||||
|
|
|
@ -181,6 +181,8 @@ func ConvertFriendlyUnit(num int) (int, string) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Make slugs optional for certain languages across the entirety of Gosora?
|
||||
// TODO: Let plugins replace NameToSlug and the URL building logic with their own
|
||||
func NameToSlug(name string) (slug string) {
|
||||
name = strings.TrimSpace(name)
|
||||
name = strings.Replace(name, " ", " ", -1)
|
||||
|
|
|
@ -783,6 +783,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
/*case "/sitemaps": // TODO: Count these views
|
||||
req.URL.Path += extraData
|
||||
err = sitemapSwitch(w,req)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}*/
|
||||
case "/uploads":
|
||||
if extraData == "" {
|
||||
common.NotFound(w,req)
|
||||
|
@ -801,6 +807,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
router.handleError(err,w,req,user)
|
||||
}
|
||||
return
|
||||
/*case "sitemap.xml":
|
||||
err = routeSitemapXml(w,req) // TODO: Count these views
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
return*/
|
||||
}
|
||||
|
||||
if extraData != "" {
|
||||
|
|
6
main.go
6
main.go
|
@ -84,7 +84,11 @@ func afterDBInit() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common.RouteViewCounter, err = common.NewRouteViewCounter()
|
||||
common.RouteViewCounter, err = common.NewDefaultRouteViewCounter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common.TopicViewCounter, err = common.NewDefaultTopicViewCounter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ func createTables(adapter qgen.Adapter) error {
|
|||
qgen.DBTableColumn{"postCount", "int", 0, false, false, "1"},
|
||||
qgen.DBTableColumn{"likeCount", "int", 0, false, false, "0"},
|
||||
qgen.DBTableColumn{"words", "int", 0, false, false, "0"},
|
||||
//qgen.DBTableColumn{"views", "int", 0, false, false, "0"},
|
||||
qgen.DBTableColumn{"views", "int", 0, false, false, "0"},
|
||||
qgen.DBTableColumn{"css_class", "varchar", 100, false, false, "''"},
|
||||
qgen.DBTableColumn{"data", "varchar", 200, false, false, "''"},
|
||||
},
|
||||
|
|
|
@ -283,6 +283,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
var err common.RouteError
|
||||
switch(prefix) {` + out + `
|
||||
/*case "/sitemaps": // TODO: Count these views
|
||||
req.URL.Path += extraData
|
||||
err = sitemapSwitch(w,req)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}*/
|
||||
case "/uploads":
|
||||
if extraData == "" {
|
||||
common.NotFound(w,req)
|
||||
|
@ -301,6 +307,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
router.handleError(err,w,req,user)
|
||||
}
|
||||
return
|
||||
/*case "sitemap.xml":
|
||||
err = routeSitemapXml(w,req) // TODO: Count these views
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
return*/
|
||||
}
|
||||
|
||||
if extraData != "" {
|
||||
|
|
32
routes.go
32
routes.go
|
@ -7,11 +7,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
//"fmt"
|
||||
"bytes"
|
||||
"html"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -74,19 +73,6 @@ func routeStatic(w http.ResponseWriter, r *http.Request) {
|
|||
// Other options instead of io.Copy: io.CopyN(), w.Write(), http.ServeContent()
|
||||
}
|
||||
|
||||
// TODO: Make this a static file somehow? Is it possible for us to put this file somewhere else?
|
||||
// TODO: Add a sitemap
|
||||
// TODO: Add an API so that plugins can register disallowed areas. E.g. /guilds/join for plugin_guilds
|
||||
func routeRobotsTxt(w http.ResponseWriter, r *http.Request) common.RouteError {
|
||||
_, _ = w.Write([]byte(`User-agent: *
|
||||
Disallow: /panel/
|
||||
Disallow: /topics/create/
|
||||
Disallow: /user/edit/
|
||||
Disallow: /accounts/
|
||||
`))
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeOverview(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, ferr := common.UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
|
@ -150,7 +136,7 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
|||
return common.LocalError("Something weird happened", w, r, user)
|
||||
}
|
||||
|
||||
// TODO: Make CanSee a method on *Group with a canSee field?
|
||||
// TODO: Make CanSee a method on *Group with a canSee field? Have a CanSee method on *User to cover the case of superadmins?
|
||||
var canSee []int
|
||||
if user.IsSuperAdmin {
|
||||
canSee, err = common.Forums.GetAllVisibleIDs()
|
||||
|
@ -168,19 +154,16 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
|||
for _, fid := range canSee {
|
||||
forum := common.Forums.DirtyGet(fid)
|
||||
if forum.Name != "" && forum.Active {
|
||||
if forum.ParentType == "" || forum.ParentType == "forum" {
|
||||
// Optimise Quick Topic away for guests
|
||||
if user.Loggedin {
|
||||
fcopy := forum.Copy()
|
||||
// TODO: Add a hook here for plugin_guilds
|
||||
forumList = append(forumList, fcopy)
|
||||
}
|
||||
// This bit's for quick topic, as we don't want unbound forums (e.g. ones in plugin_socialgroups) showing up
|
||||
if (forum.ParentType == "" || forum.ParentType == "forum") && user.Loggedin {
|
||||
fcopy := forum.Copy()
|
||||
// TODO: Add a hook here for plugin_guilds
|
||||
forumList = append(forumList, fcopy)
|
||||
}
|
||||
// ? - Should we be showing plugin_guilds posts on /topics/?
|
||||
// ? - Would it be useful, if we could post in social groups from /topics/?
|
||||
argList = append(argList, strconv.Itoa(fid))
|
||||
qlist += "?,"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,6 +173,7 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
|||
}
|
||||
qlist = qlist[0 : len(qlist)-1]
|
||||
|
||||
// TODO: Abstract this
|
||||
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "parentID IN("+qlist+")", "")
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
This file is here so that Git will include this folder in the repository.
|
|
@ -14,6 +14,7 @@ CREATE TABLE [topics] (
|
|||
[postCount] int DEFAULT 1 not null,
|
||||
[likeCount] int DEFAULT 0 not null,
|
||||
[words] int DEFAULT 0 not null,
|
||||
[views] int DEFAULT 0 not null,
|
||||
[css_class] nvarchar (100) DEFAULT '' not null,
|
||||
[data] nvarchar (200) DEFAULT '' not null,
|
||||
primary key([tid])
|
||||
|
|
|
@ -14,6 +14,7 @@ CREATE TABLE `topics` (
|
|||
`postCount` int DEFAULT 1 not null,
|
||||
`likeCount` int DEFAULT 0 not null,
|
||||
`words` int DEFAULT 0 not null,
|
||||
`views` int DEFAULT 0 not null,
|
||||
`css_class` varchar(100) DEFAULT '' not null,
|
||||
`data` varchar(200) DEFAULT '' not null,
|
||||
primary key(`tid`)
|
||||
|
|
|
@ -14,6 +14,7 @@ CREATE TABLE `topics` (
|
|||
`postCount` int DEFAULT 1 not null,
|
||||
`likeCount` int DEFAULT 0 not null,
|
||||
`words` int DEFAULT 0 not null,
|
||||
`views` int DEFAULT 0 not null,
|
||||
`css_class` varchar (100) DEFAULT '' not null,
|
||||
`data` varchar (200) DEFAULT '' not null,
|
||||
primary key(`tid`)
|
||||
|
|
|
@ -548,7 +548,7 @@ var profile_14 = []byte(`
|
|||
<div class="rowitem passive">
|
||||
<a href="/report/submit/`)
|
||||
var profile_15 = []byte(`?session=`)
|
||||
var profile_16 = []byte(`&type=user" class="profile_menu_item report_item">Report</a>
|
||||
var profile_16 = []byte(`&type=user" class="profile_menu_item report_item" aria-label="Report User" title="Report User"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -559,7 +559,7 @@ var profile_16 = []byte(`&type=user" class="profile_menu_item report_item">Repor
|
|||
var profile_17 = []byte(`
|
||||
<!-- TODO: Inline the display: none; CSS -->
|
||||
<div id="ban_user_head" class="colstack_item colstack_head hash_hide ban_user_hash" style="display: none;">
|
||||
<div class="rowitem"><h1>Ban User</h1></div>
|
||||
<div class="rowitem"><h1><a>Ban User</a></h1></div>
|
||||
</div>
|
||||
<form id="ban_user_form" class="hash_hide ban_user_hash" action="/users/ban/submit/`)
|
||||
var profile_18 = []byte(`?session=`)
|
||||
|
@ -599,7 +599,7 @@ var profile_20 = []byte(`
|
|||
var profile_21 = []byte(`
|
||||
|
||||
<div id="profile_comments_head" class="colstack_item colstack_head hash_hide">
|
||||
<div class="rowitem"><h1>Comments</h1></div>
|
||||
<div class="rowitem"><h1><a>Comments</a></h1></div>
|
||||
</div>
|
||||
<div id="profile_comments" class="colstack_item hash_hide">
|
||||
`)
|
||||
|
@ -665,7 +665,6 @@ var profile_comments_row_29 = []byte(`" class="mod_button" title="Edit Item"><bu
|
|||
var profile_comments_row_30 = []byte(`" class="mod_button" title="Delete Item"><button class="username delete_item trash_label"></button></a>
|
||||
`)
|
||||
var profile_comments_row_31 = []byte(`
|
||||
|
||||
<a class="mod_button" href="/report/submit/`)
|
||||
var profile_comments_row_32 = []byte(`?session=`)
|
||||
var profile_comments_row_33 = []byte(`&type=user-reply"><button class="username report_item flag_label"></button></a>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
{{else}}<a href="#ban_user" class="profile_menu_item">Ban</a>{{end}}
|
||||
</div>{{end}}
|
||||
<div class="rowitem passive">
|
||||
<a href="/report/submit/{{.ProfileOwner.ID}}?session={{.CurrentUser.Session}}&type=user" class="profile_menu_item report_item">Report</a>
|
||||
<a href="/report/submit/{{.ProfileOwner.ID}}?session={{.CurrentUser.Session}}&type=user" class="profile_menu_item report_item" aria-label="Report User" title="Report User"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,7 +31,7 @@
|
|||
{{if .CurrentUser.Perms.BanUsers}}
|
||||
<!-- TODO: Inline the display: none; CSS -->
|
||||
<div id="ban_user_head" class="colstack_item colstack_head hash_hide ban_user_hash" style="display: none;">
|
||||
<div class="rowitem"><h1>Ban User</h1></div>
|
||||
<div class="rowitem"><h1><a>Ban User</a></h1></div>
|
||||
</div>
|
||||
<form id="ban_user_form" class="hash_hide ban_user_hash" action="/users/ban/submit/{{.ProfileOwner.ID}}?session={{.CurrentUser.Session}}" method="post" style="display: none;">
|
||||
{{/** TODO: Put a JS duration calculator here instead of this text? **/}}
|
||||
|
@ -67,7 +67,7 @@
|
|||
{{end}}
|
||||
|
||||
<div id="profile_comments_head" class="colstack_item colstack_head hash_hide">
|
||||
<div class="rowitem"><h1>Comments</h1></div>
|
||||
<div class="rowitem"><h1><a>Comments</a></h1></div>
|
||||
</div>
|
||||
<div id="profile_comments" class="colstack_item hash_hide">
|
||||
{{template "profile_comments_row.html" . }}
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
<a href="/profile/reply/edit/submit/{{.ID}}" class="mod_button" title="Edit Item"><button class="username edit_item edit_label"></button></a>
|
||||
<a href="/profile/reply/delete/submit/{{.ID}}" class="mod_button" title="Delete Item"><button class="username delete_item trash_label"></button></a>
|
||||
{{end}}
|
||||
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label"></button></a>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -860,21 +860,24 @@ select, input, textarea, button {
|
|||
}
|
||||
#profile_left_lane {
|
||||
margin-left: 8px;
|
||||
margin-right: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
#profile_left_pane .topBlock {
|
||||
flex-direction: column;
|
||||
padding-bottom: 18px;
|
||||
padding-bottom: 12px;
|
||||
border: 1px solid var(--element-border-color);
|
||||
border-bottom: 2px solid var(--element-border-color);
|
||||
background-color: var(--element-background-color);
|
||||
}
|
||||
#profile_left_pane .avatarRow {
|
||||
padding: 24px;
|
||||
padding-bottom: 8px;
|
||||
padding: 28px;
|
||||
padding-bottom: 4px;
|
||||
padding-top: 22px;
|
||||
}
|
||||
#profile_left_pane .avatar {
|
||||
border-radius: 80px;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
#profile_left_pane .nameRow {
|
||||
display: flex;
|
||||
|
@ -926,6 +929,12 @@ select, input, textarea, button {
|
|||
#profile_right_lane .topic_reply_form {
|
||||
width: auto;
|
||||
}
|
||||
#profile_comments_head {
|
||||
margin-top: 6px;
|
||||
}
|
||||
#profile_comments {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
#profile_comments .rowitem {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
@ -944,6 +953,22 @@ select, input, textarea, button {
|
|||
#profile_comments .comment .nameAndTitle {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 2px;
|
||||
}
|
||||
#profile_comments .comment .nameAndTitle .user_tag {
|
||||
font-size: 15px;
|
||||
}
|
||||
#profile_comments .comment .content_column {
|
||||
padding-left: 14px;
|
||||
padding-right: 14px;
|
||||
display: flex;
|
||||
width: 100%
|
||||
}
|
||||
#profile_comments .comment .controls {
|
||||
margin-left: auto;
|
||||
}
|
||||
#profile_comments_form .topic_reply_form {
|
||||
border-top: 1px solid var(--element-border-color) !important;
|
||||
}
|
||||
|
||||
.colstack_item .formrow {
|
||||
|
@ -1333,7 +1358,7 @@ select, input, textarea, button {
|
|||
.pin_item:after, .unpin_item:after {
|
||||
content: "\f08d";
|
||||
}
|
||||
.report_item:after {
|
||||
.report_item:not(.profile_menu_item):after {
|
||||
content: "\f024";
|
||||
}
|
||||
.unpin_item, .unlock_item {
|
||||
|
@ -1382,7 +1407,7 @@ select, input, textarea, button {
|
|||
margin-left: -3px;
|
||||
margin-right: -3px;
|
||||
}
|
||||
.edit_item, .button_container .open_edit, .delete_item, .pin_item, .unpin_item, .lock_item, .unlock_item, .ip_item_button, .report_item {
|
||||
.edit_item, .button_container .open_edit, .delete_item, .pin_item, .unpin_item, .lock_item, .unlock_item, .ip_item_button, .report_item:not(.profile_menu_item) {
|
||||
display: none;
|
||||
}
|
||||
.button_menu:after {
|
||||
|
|
|
@ -702,6 +702,9 @@ input, select, textarea {
|
|||
float: right;
|
||||
font-weight: normal;
|
||||
}
|
||||
#profile_left_pane .report_item:after {
|
||||
content: "Report";
|
||||
}
|
||||
#profile_left_lane .profileName {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
|
|
@ -741,6 +741,9 @@ button.username {
|
|||
float: right;
|
||||
font-weight: normal;
|
||||
}
|
||||
#profile_left_pane .report_item:after {
|
||||
content: "Report";
|
||||
}
|
||||
#profile_right_lane {
|
||||
width: calc(100% - 230px);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"Creator": "Azareal",
|
||||
"FullImage": "tempra-conflux.png",
|
||||
"MobileFriendly": true,
|
||||
"BgAvatars":true,
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Docks":["rightSidebar"],
|
||||
"Templates": [
|
||||
|
|
|
@ -574,6 +574,9 @@ button.username {
|
|||
float: right;
|
||||
font-weight: normal;
|
||||
}
|
||||
#profile_left_pane .report_item:after {
|
||||
content: "Report";
|
||||
}
|
||||
|
||||
/* Media Queries */
|
||||
|
||||
|
|
|
@ -800,6 +800,9 @@ button.username {
|
|||
#profile_left_lane .profileName {
|
||||
font-size: 18px;
|
||||
}
|
||||
#profile_left_lane .report_item:after {
|
||||
content: "Report";
|
||||
}
|
||||
#profile_right_lane {
|
||||
width: calc(100% - 245px);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue