diff --git a/README.md b/README.md index c37d6950..df740496 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Several important features for saving memory in the templates system may have to # Advanced Installation -An example of running the commands directly on Windows. +An example of running the commands directly on Windows. We're looking into reducing the number of commands you need to type, for instance, you could invoke the update-deps batch or shell files to install / update all of the dependencies instead of typing each `get get -u` Linux is similar, however you might need to use cd and mv a bit more like in the shell files due to the differences in go build across platforms. Additionally, Linux doesn't require `StackExchange/wmi` or ``/x/sys/windows` @@ -168,7 +168,7 @@ We're looking for ways to clean-up the plugin system so that all of them (except More images in the /images/ folder. Beware though, some of them are *really* outdated. -# Dependencies (a few of these like Riot aren't currently in use, but we anticipate that we'll need some sort of search engine library in the very immediate future) +# Dependencies (a few of these like Rez aren't currently in use, but we anticipate that we'll need some sort of search engine library in the very immediate future) * Go 1.9 @@ -194,8 +194,6 @@ More images in the /images/ folder. Beware though, some of them are *really* out * ithub.com/denisenkom/go-mssqldb For interfacing with MSSQL. You will be able to pick this instead of MSSQL soon. -* github.com/go-ego/riot A search engine library. - * github.com/bamiaux/rez An image resizer (e.g. for spitting out thumbnails) * github.com/fsnotify/fsnotify A library for watching events on the file system. diff --git a/common/errors.go b/common/errors.go index 7b5efec5..cb17260c 100644 --- a/common/errors.go +++ b/common/errors.go @@ -1,7 +1,6 @@ package common import "log" - import "sync" import "net/http" import "runtime/debug" @@ -32,10 +31,6 @@ type RouteErrorImpl struct { handled bool } -/*func NewRouteError(msg string, system bool, json bool) RouteError { - return &RouteErrorImpl{msg, system, json, false} -}*/ - func (err *RouteErrorImpl) Type() string { // System errors may contain sensitive information we don't want the user to see if err.system { @@ -80,21 +75,9 @@ func LogWarning(err error) { // InternalError is the main function for handling internal errors, while simultaneously printing out a page for the end-user to let them know that *something* has gone wrong // ? - Add a user parameter? func InternalError(err error, w http.ResponseWriter, r *http.Request) RouteError { - log.Print(err) - debug.PrintStack() - - // TODO: Centralise the user struct somewhere else - user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0} - pi := Page{"Internal Server Error", user, DefaultHeaderVar(), tList, "A problem has occurred in the system."} - err = Templates.ExecuteTemplate(w, "error.html", pi) - if err != nil { - log.Print(err) - } - - errorBufferMutex.Lock() - defer errorBufferMutex.Unlock() - errorBuffer = append(errorBuffer, err) - log.Fatal("") + pi := Page{"Internal Server Error", GuestUser, DefaultHeaderVar(), tList, "A problem has occurred in the system."} + handleErrorTemplate(w, r, pi) + LogError(err) return HandledRouteError() } @@ -112,26 +95,14 @@ func InternalErrorJSQ(err error, w http.ResponseWriter, r *http.Request, isJs bo func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) RouteError { w.WriteHeader(500) _, _ = w.Write([]byte(`{"errmsg":"A problem has occurred in the system."}`)) - errorBufferMutex.Lock() - defer errorBufferMutex.Unlock() - errorBuffer = append(errorBuffer, err) - log.Fatal(err) + LogError(err) return HandledRouteError() } func PreError(errmsg string, w http.ResponseWriter, r *http.Request) RouteError { w.WriteHeader(500) - user := User{ID: 0, Group: 6, Perms: GuestPerms} - pi := Page{"Error", user, DefaultHeaderVar(), tList, errmsg} - if PreRenderHooks["pre_render_error"] != nil { - if RunPreRenderHook("pre_render_error", w, r, &user, &pi) { - return nil - } - } - err := Templates.ExecuteTemplate(w, "error.html", pi) - if err != nil { - LogError(err) - } + pi := Page{"Error", GuestUser, DefaultHeaderVar(), tList, errmsg} + handleErrorTemplate(w, r, pi) return HandledRouteError() } @@ -152,15 +123,7 @@ func PreErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, isJs boo func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, user User) RouteError { w.WriteHeader(500) pi := Page{"Local Error", user, DefaultHeaderVar(), tList, errmsg} - if PreRenderHooks["pre_render_error"] != nil { - if RunPreRenderHook("pre_render_error", w, r, &user, &pi) { - return nil - } - } - err := Templates.ExecuteTemplate(w, "error.html", pi) - if err != nil { - LogError(err) - } + handleErrorTemplate(w, r, pi) return HandledRouteError() } @@ -182,16 +145,7 @@ func LocalErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) RouteEr func NoPermissions(w http.ResponseWriter, r *http.Request, user User) RouteError { w.WriteHeader(403) pi := Page{"Local Error", user, DefaultHeaderVar(), tList, "You don't have permission to do that."} - // TODO: What to do about this hook? - if PreRenderHooks["pre_render_error"] != nil { - if RunPreRenderHook("pre_render_error", w, r, &user, &pi) { - return nil - } - } - err := Templates.ExecuteTemplate(w, "error.html", pi) - if err != nil { - LogError(err) - } + handleErrorTemplate(w, r, pi) return HandledRouteError() } @@ -212,15 +166,7 @@ func NoPermissionsJS(w http.ResponseWriter, r *http.Request, user User) RouteErr func Banned(w http.ResponseWriter, r *http.Request, user User) RouteError { w.WriteHeader(403) pi := Page{"Banned", user, DefaultHeaderVar(), tList, "You have been banned from this site."} - if PreRenderHooks["pre_render_error"] != nil { - if RunPreRenderHook("pre_render_error", w, r, &user, &pi) { - return nil - } - } - err := Templates.ExecuteTemplate(w, "error.html", pi) - if err != nil { - LogError(err) - } + handleErrorTemplate(w, r, pi) return HandledRouteError() } @@ -252,15 +198,7 @@ func LoginRequiredJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bo func LoginRequired(w http.ResponseWriter, r *http.Request, user User) RouteError { w.WriteHeader(401) pi := Page{"Local Error", user, DefaultHeaderVar(), tList, "You need to login to do that."} - if PreRenderHooks["pre_render_error"] != nil { - if RunPreRenderHook("pre_render_error", w, r, &user, &pi) { - return nil - } - } - err := Templates.ExecuteTemplate(w, "error.html", pi) - if err != nil { - LogError(err) - } + handleErrorTemplate(w, r, pi) return HandledRouteError() } @@ -292,30 +230,14 @@ func SecurityError(w http.ResponseWriter, r *http.Request, user User) RouteError // ? - Add a JSQ and JS version of this? // ? - Add a user parameter? func NotFound(w http.ResponseWriter, r *http.Request) RouteError { - w.WriteHeader(404) - // TODO: Centralise the user struct somewhere else - user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0} - pi := Page{"Not Found", user, DefaultHeaderVar(), tList, "The requested page doesn't exist."} - err := Templates.ExecuteTemplate(w, "error.html", pi) - if err != nil { - LogError(err) - } - return HandledRouteError() + return CustomError("The requested page doesn't exist.", 404, "Not Found", w, r, GuestUser) } // CustomError lets us make custom error types which aren't covered by the generic functions above func CustomError(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User) RouteError { w.WriteHeader(errcode) pi := Page{errtitle, user, DefaultHeaderVar(), tList, errmsg} - if PreRenderHooks["pre_render_error"] != nil { - if RunPreRenderHook("pre_render_error", w, r, &user, &pi) { - return nil - } - } - err := Templates.ExecuteTemplate(w, "error.html", pi) - if err != nil { - LogError(err) - } + handleErrorTemplate(w, r, pi) return HandledRouteError() } @@ -324,12 +246,25 @@ func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.Response if !isJs { return CustomError(errmsg, errcode, errtitle, w, r, user) } - return CustomErrorJS(errmsg, errcode, errtitle, w, r, user) + return CustomErrorJS(errmsg, errcode, w, r, user) } // CustomErrorJS is the pure JSON version of CustomError -func CustomErrorJS(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User) RouteError { +func CustomErrorJS(errmsg string, errcode int, w http.ResponseWriter, r *http.Request, user User) RouteError { w.WriteHeader(errcode) _, _ = w.Write([]byte(`{"errmsg":"` + errmsg + `"}`)) return HandledRouteError() } + +func handleErrorTemplate(w http.ResponseWriter, r *http.Request, pi Page) { + // TODO: What to do about this hook? + if PreRenderHooks["pre_render_error"] != nil { + if RunPreRenderHook("pre_render_error", w, r, &pi.CurrentUser, &pi) { + return + } + } + err := Templates.ExecuteTemplate(w, "error.html", pi) + if err != nil { + LogError(err) + } +} diff --git a/common/files.go b/common/files.go index 569f592e..44e68586 100644 --- a/common/files.go +++ b/common/files.go @@ -2,9 +2,9 @@ package common import ( "bytes" - "log" "mime" "strings" + "sync" //"errors" "compress/gzip" "io/ioutil" @@ -16,6 +16,7 @@ import ( type SFileList map[string]SFile var StaticFiles SFileList = make(map[string]SFile) +var staticFileMutex sync.RWMutex type SFile struct { Data []byte @@ -48,11 +49,9 @@ func (list SFileList) Init() error { var ext = filepath.Ext("/public/" + path) gzipData := compressBytesGzip(data) - list["/static/"+path] = SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)} + list.Set("/static/"+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) - if Dev.DebugMode { - log.Print("Added the '" + path + "' static file.") - } + debugLogf("Added the '%s' static file.", path) return nil }) } @@ -75,14 +74,25 @@ func (list SFileList) Add(path string, prefix string) error { path = strings.TrimPrefix(path, prefix) gzipData := compressBytesGzip(data) - list["/static"+path] = SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)} + list.Set("/static"+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) - if Dev.DebugMode { - log.Printf("Added the '%s' static file", path) - } + debugLogf("Added the '%s' static file", path) return nil } +func (list SFileList) Get(name string) (file SFile, exists bool) { + staticFileMutex.RLock() + defer staticFileMutex.RUnlock() + file, exists = list[name] + return file, exists +} + +func (list SFileList) Set(name string, data SFile) { + staticFileMutex.Lock() + defer staticFileMutex.Unlock() + list[name] = data +} + func compressBytesGzip(in []byte) []byte { var buff bytes.Buffer gz := gzip.NewWriter(&buff) diff --git a/common/pages.go b/common/pages.go index cebea15c..15a9f883 100644 --- a/common/pages.go +++ b/common/pages.go @@ -14,8 +14,8 @@ type HeaderVars struct { Widgets PageWidgets Site *site Settings SettingMap - Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed? - Theme Theme + Themes map[string]*Theme // TODO: Use a slice containing every theme instead of the main map for speed? + Theme *Theme //TemplateName string // TODO: Use this to move template calls to the router rather than duplicating them over and over and over? Zone string Writer http.ResponseWriter @@ -151,8 +151,8 @@ type PanelThemesPage struct { CurrentUser User Header *HeaderVars Stats PanelStats - PrimaryThemes []Theme - VariantThemes []Theme + PrimaryThemes []*Theme + VariantThemes []*Theme } type PanelUserPage struct { diff --git a/common/routes_common.go b/common/routes_common.go index bfcef757..9f814c8e 100644 --- a/common/routes_common.go +++ b/common/routes_common.go @@ -97,7 +97,7 @@ func cascadeForumPerms(fperms *ForumPerms, user *User) { // Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with // TODO: Do a panel specific theme? func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, stats PanelStats, rerr RouteError) { - var theme Theme + var theme = &Theme{Name: ""} cookie, err := r.Cookie("current_theme") if err == nil { @@ -116,6 +116,7 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerV Themes: Themes, Theme: theme, Zone: "panel", + Writer: w, } // TODO: We should probably initialise headerVars.ExtData @@ -189,7 +190,7 @@ func simpleUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header // TODO: Add the ability for admins to restrict certain themes to certain groups? func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, rerr RouteError) { - var theme Theme + var theme = &Theme{Name: ""} cookie, err := r.Cookie("current_theme") if err == nil { @@ -208,6 +209,7 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars * Themes: Themes, Theme: theme, Zone: "frontend", + Writer: w, } if user.IsBanned { diff --git a/common/themes.go b/common/themes.go index ad524cbf..cca2f6c2 100644 --- a/common/themes.go +++ b/common/themes.go @@ -22,9 +22,9 @@ import ( "../query_gen/lib" ) -type ThemeList map[string]Theme // ? Use pointers instead? +type ThemeList map[string]*Theme -var Themes ThemeList = make(map[string]Theme) +var Themes ThemeList = make(map[string]*Theme) var DefaultThemeBox atomic.Value var ChangeDefaultThemeMutex sync.Mutex @@ -45,7 +45,6 @@ type Theme struct { Tag string URL string Docks []string // Allowed Values: leftSidebar, rightSidebar, footer - AboutSegment bool // ? - Should this be a theme var instead? Settings map[string]ThemeSetting Templates []TemplateMapping TemplatesMap map[string]string @@ -117,7 +116,7 @@ func (themes ThemeList) LoadActiveStatus() error { log.Printf("Loading the default theme '%s'", theme.Name) theme.Active = true DefaultThemeBox.Store(theme.Name) - MapThemeTemplates(theme) + theme.MapTemplates() } else { log.Printf("Loading the theme '%s'", theme.Name) theme.Active = false @@ -147,8 +146,8 @@ func InitThemes() error { return err } - var theme Theme - err = json.Unmarshal(themeFile, &theme) + var theme = &Theme{Name: ""} + err = json.Unmarshal(themeFile, theme) if err != nil { return err } @@ -185,21 +184,25 @@ func InitThemes() error { } } - theme.ResourceTemplates = template.New("") - template.Must(theme.ResourceTemplates.ParseGlob("./themes/" + theme.Name + "/public/*.css")) - - // It should be safe for us to load the files for all the themes in memory, as-long as the admin hasn't setup a ridiculous number of themes - err = AddThemeStaticFiles(theme) + err = theme.LoadStaticFiles() if err != nil { return err } - Themes[theme.Name] = theme } return nil } -func AddThemeStaticFiles(theme Theme) error { +// TODO: It might be unsafe to call the template parsing functions with fsnotify, do something more concurrent +func (theme *Theme) LoadStaticFiles() error { + theme.ResourceTemplates = template.New("") + template.Must(theme.ResourceTemplates.ParseGlob("./themes/" + theme.Name + "/public/*.css")) + + // It should be safe for us to load the files for all the themes in memory, as-long as the admin hasn't setup a ridiculous number of themes + return theme.AddThemeStaticFiles() +} + +func (theme *Theme) AddThemeStaticFiles() error { // TODO: Use a function instead of a closure to make this more testable? What about a function call inside the closure to take the theme variable into account? return filepath.Walk("./themes/"+theme.Name+"/public", func(path string, f os.FileInfo, err error) error { debugLog("Attempting to add static file '" + path + "' for default theme '" + theme.Name + "'") @@ -230,14 +233,14 @@ func AddThemeStaticFiles(theme Theme) error { path = strings.TrimPrefix(path, "themes/"+theme.Name+"/public") gzipData := compressBytesGzip(data) - StaticFiles["/static/"+theme.Name+path] = SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)} + StaticFiles.Set("/static/"+theme.Name+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) debugLog("Added the '/" + theme.Name + path + "' static file for theme " + theme.Name + ".") return nil }) } -func MapThemeTemplates(theme Theme) { +func (theme *Theme) MapTemplates() { if theme.Templates != nil { for _, themeTmpl := range theme.Templates { if themeTmpl.Name == "" { diff --git a/install-linux b/install-linux index 61e0fe55..451908d2 100644 --- a/install-linux +++ b/install-linux @@ -22,9 +22,6 @@ go get -u gopkg.in/sourcemap.v1 echo "Installing OttoJS" go get -u github.com/robertkrimen/otto -echo "Installing the Riot Search Engine" -go get -u github.com/robertkrimen/otto - echo "Installing the Rez Image Resizer" go get -u github.com/bamiaux/rez diff --git a/install.bat b/install.bat index 1421cfaa..34c22631 100644 --- a/install.bat +++ b/install.bat @@ -71,13 +71,6 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) -echo Installing the Riot Search Engine -go get -u github.com/go-ego/riot -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - echo Installing the Rez Image Resizer go get -u github.com/bamiaux/rez if %errorlevel% neq 0 ( diff --git a/main.go b/main.go index 6a8f7b29..32621db9 100644 --- a/main.go +++ b/main.go @@ -13,10 +13,12 @@ import ( "net/http" "os" "os/signal" + "strings" "syscall" "time" //"runtime/pprof" "./common" + "github.com/fsnotify/fsnotify" ) var version = common.Version{Major: 0, Minor: 1, Patch: 0, Tag: "dev"} @@ -55,13 +57,11 @@ func afterDBInit() (err error) { if err != nil { return err } - log.Print("Initialising the widgets") err = common.InitWidgets() if err != nil { return err } - log.Print("Initialising the authentication system") common.Auth, err = common.NewDefaultAuth() if err != nil { @@ -72,12 +72,10 @@ func afterDBInit() (err error) { if err != nil { return err } - common.ModLogs, err = common.NewModLogStore() if err != nil { return err } - common.AdminLogs, err = common.NewAdminLogStore() if err != nil { return err @@ -158,6 +156,65 @@ func main() { log.Fatal(err) } + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + + go func() { + for { + select { + case event := <-watcher.Events: + log.Println("event:", event) + if event.Op&fsnotify.Write == fsnotify.Write { + log.Println("modified file:", event.Name) + var pathBits = strings.Split(event.Name, "\\") + if len(pathBits) > 0 { + if pathBits[0] == "themes" { + var themeName string + if len(pathBits) >= 1 { + themeName = pathBits[1] + } + + if len(pathBits) >= 2 && pathBits[2] == "public" { + // TODO: Handle new themes freshly plopped into the folder? + theme, ok := common.Themes[themeName] + if ok { + err = theme.LoadStaticFiles() + if err != nil { + common.LogError(err) + } + } + } + + } + } + } else if event.Op&fsnotify.Create == fsnotify.Create { + log.Println("new file:", event.Name) + } + case err := <-watcher.Errors: + log.Println("error:", err) + } + } + }() + + // TODO: Keep tabs on the theme stuff, and the langpacks + err = watcher.Add("./public") + if err != nil { + log.Fatal(err) + } + err = watcher.Add("./templates") + if err != nil { + log.Fatal(err) + } + for _, theme := range common.Themes { + err = watcher.Add("./themes/" + theme.Name + "/public") + if err != nil { + log.Fatal(err) + } + } + // Run this goroutine once a second secondTicker := time.NewTicker(1 * time.Second) fifteenMinuteTicker := time.NewTicker(15 * time.Minute) diff --git a/member_routes.go b/member_routes.go index eb692951..101685d5 100644 --- a/member_routes.go +++ b/member_routes.go @@ -42,7 +42,6 @@ func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User, return common.NoPermissions(w, r, user) } headerVars.Zone = "create_topic" - headerVars.Writer = w // Lock this to the forum being linked? // Should we always put it in strictmode when it's linked from another forum? Well, the user might end up changing their mind on what forum they want to post in and it would be a hassle, if they had to switch pages, even if it is a single click for many (exc. mobile) diff --git a/panel_routes.go b/panel_routes.go index 15a37809..b2730270 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -1473,7 +1473,7 @@ func routePanelThemes(w http.ResponseWriter, r *http.Request, user common.User) return common.NoPermissions(w, r, user) } - var pThemeList, vThemeList []common.Theme + var pThemeList, vThemeList []*common.Theme for _, theme := range common.Themes { if theme.HideFromThemes { continue @@ -1557,7 +1557,7 @@ func routePanelThemesSetDefault(w http.ResponseWriter, r *http.Request, user com common.DefaultThemeBox.Store(uname) common.ResetTemplateOverrides() - common.MapThemeTemplates(theme) + theme.MapTemplates() common.ChangeDefaultThemeMutex.Unlock() http.Redirect(w, r, "/panel/themes/", http.StatusSeeOther) diff --git a/routes.go b/routes.go index 178407aa..cc017e43 100644 --- a/routes.go +++ b/routes.go @@ -42,8 +42,7 @@ func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { // GET functions func routeStatic(w http.ResponseWriter, r *http.Request) { - //log.Print("Outputting static file '" + r.URL.Path + "'") - file, ok := common.StaticFiles[r.URL.Path] + file, ok := common.StaticFiles.Get(r.URL.Path) if !ok { if common.Dev.DebugMode { log.Printf("Failed to find '%s'", r.URL.Path) @@ -61,11 +60,8 @@ func routeStatic(w http.ResponseWriter, r *http.Request) { } h.Set("Last-Modified", file.FormattedModTime) h.Set("Content-Type", file.Mimetype) - //Cache-Control: max-age=31536000 - h.Set("Cache-Control", cacheControlMaxAge) + h.Set("Cache-Control", cacheControlMaxAge) //Cache-Control: max-age=31536000 h.Set("Vary", "Accept-Encoding") - //http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file) - //w.Write(file.Data) if strings.Contains(h.Get("Accept-Encoding"), "gzip") { h.Set("Content-Encoding", "gzip") h.Set("Content-Length", strconv.FormatInt(file.GzipLength, 10)) @@ -74,15 +70,9 @@ func routeStatic(w http.ResponseWriter, r *http.Request) { h.Set("Content-Length", strconv.FormatInt(file.Length, 10)) // Avoid doing a type conversion every time? io.Copy(w, bytes.NewReader(file.Data)) } - //io.CopyN(w, bytes.NewReader(file.Data), staticFiles[r.URL.Path].Length) + // Other options instead of io.Copy: io.CopyN(), w.Write(), http.ServeContent() } -// Deprecated: Test route for stopping the server during a performance analysis -/*func routeExit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError{ - db.Close() - os.Exit(0) -}*/ - // 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 @@ -102,7 +92,6 @@ func routeOverview(w http.ResponseWriter, r *http.Request, user common.User) com return ferr } headerVars.Zone = "overview" - headerVars.Writer = w pi := common.Page{common.GetTitlePhrase("overview"), user, headerVars, tList, nil} if common.PreRenderHooks["pre_render_overview"] != nil { @@ -129,7 +118,6 @@ func routeCustomPage(w http.ResponseWriter, r *http.Request, user common.User) c return common.NotFound(w, r) } headerVars.Zone = "custom_page" - headerVars.Writer = w pi := common.Page{common.GetTitlePhrase("page"), user, headerVars, tList, nil} if common.PreRenderHooks["pre_render_custom_page"] != nil { @@ -151,7 +139,6 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo return ferr } headerVars.Zone = "topics" - headerVars.Writer = w // TODO: Add a function for the qlist stuff var qlist string @@ -337,7 +324,6 @@ func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid s return common.InternalError(err, w, r) } headerVars.Zone = "view_forum" - headerVars.Writer = w // Calculate the offset var offset int @@ -424,7 +410,6 @@ func routeForums(w http.ResponseWriter, r *http.Request, user common.User) commo return ferr } headerVars.Zone = "forums" - headerVars.Writer = w var err error var forumList []common.Forum @@ -514,7 +499,6 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm return common.NoPermissions(w, r, user) } headerVars.Zone = "view_topic" - headerVars.Writer = w topic.ContentHTML = common.ParseMessage(topic.Content, topic.ParentID, "forums") topic.ContentLines = strings.Count(topic.Content, "\n") diff --git a/themes/cosora/public/main.css b/themes/cosora/public/main.css index d866cde8..459c6d62 100644 --- a/themes/cosora/public/main.css +++ b/themes/cosora/public/main.css @@ -495,6 +495,12 @@ select, input, textarea, button { background-color: var(--element-background-color); padding: 12px; } +/* TODO: Refactor bgavatars to make the avatars rounded */ +.rowlist.bgavatars .rowitem { + background-repeat: no-repeat; + background-size: 40px; + padding-left: 46px; +} .topic_list .rowtopic { font-size: 17px; @@ -959,4 +965,41 @@ select, input, textarea, button { .topic_inner_right { display: none; } +} +@media(max-width: 520px) { + .topic_list { + display: flex; + flex-wrap: wrap; + } + .topic_list .topic_row { + display: block; + width: calc(50% - 6px); + float: left; + } + .topic_list .topic_row:nth-child(odd) { + margin-right: 12px; + } + .topic_left { + margin-bottom: 0px; + border-bottom: none; + border-right: 1px solid var(--element-border-color); + } + .topic_left .parent_forum { + display: none; + } + .topic_right.rowitem { + border-top: none; + border-left: 1px solid var(--element-border-color); + background-color: hsl(0,0%,90%); + } + .topic_right br, .topic_right img { + display: none; + } + .topic_right.topic_sticky { + border-bottom: 2px solid var(--element-border-color); + } + .topic_right > span { + margin-top: 6px; + margin-bottom: 6px; + } } \ No newline at end of file diff --git a/themes/cosora/theme.json b/themes/cosora/theme.json index 1386f16c..3718d748 100644 --- a/themes/cosora/theme.json +++ b/themes/cosora/theme.json @@ -6,7 +6,6 @@ "URL": "github.com/Azareal/Gosora", "Tag": "WIP", "Docks":["rightSidebar","footer"], - "AboutSegment":true, "Templates": [ { "Name": "topic", diff --git a/update-deps-linux b/update-deps-linux index 3bc02af2..21d1305a 100644 --- a/update-deps-linux +++ b/update-deps-linux @@ -22,9 +22,6 @@ go get -u gopkg.in/sourcemap.v1 echo "Updating OttoJS" go get -u github.com/robertkrimen/otto -echo "Updating the Riot Search Engine" -go get -u github.com/go-ego/riot - echo "Updating the Rez Image Resizer" go get -u github.com/bamiaux/rez diff --git a/update-deps.bat b/update-deps.bat index 00d36b6d..510cb126 100644 --- a/update-deps.bat +++ b/update-deps.bat @@ -68,13 +68,6 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) -echo Updating the Riot Search Engine -go get -u github.com/go-ego/riot -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - echo Updating the Rez Image Resizer go get -u github.com/bamiaux/rez if %errorlevel% neq 0 (