gosora/common/theme_list.go

322 lines
9.9 KiB
Go
Raw Normal View History

package common
Happy 100th commit! For the 100th commit, I've revamped a good portion of the user interface, and we don't plan on stopping! There's more to come! Each theme has more of a distinct feel in the control panel now. I moved a large portion of the inline CSS into global.css, tweaked some core code which didn't line up, and added CSS text insertions to make things more flexible. Revamped the alerts interface. We have more changes coming up! Added screenshots for Tempra Cursive and Tempra Conflux Mobile to the README. Added a .htaccess file, just in case someone plops Gosora in Apache's /www/ folder to stop the contents of config.go from becoming publically visible. Never put Gosora in your /www/ folder, Gosora is a standalone program which does NOT use Apache or any other webserver. Fixed a bug in the inline forum editor. Added a hand-written Markdown parser which is much faster than the previous Regex based one. This is still a work in progress. Added support for strikethrough and underline elements to the Markdown parser. Fixed the missing semicolons in global.js Revamped the form CSS. Author bits on the theme manager now link to the author's website. Improved the profiles a little. The code in the stylesheets now have a more consistent style. Fixed a bug in the Cosmo theme relating to the fact that Gosora doesn't have sidebars yet. There are many more changes which aren't listed here. If weirdness regarding lines or spacing occurs, I'm experimenting with a new text editor. I hope to have this fixed by the next commit.
2017-05-29 14:52:37 +00:00
import (
2022-02-21 03:53:13 +00:00
"database/sql"
"encoding/json"
"errors"
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"sync/atomic"
qgen "git.tuxpa.in/a/gosora/query_gen"
Happy 100th commit! For the 100th commit, I've revamped a good portion of the user interface, and we don't plan on stopping! There's more to come! Each theme has more of a distinct feel in the control panel now. I moved a large portion of the inline CSS into global.css, tweaked some core code which didn't line up, and added CSS text insertions to make things more flexible. Revamped the alerts interface. We have more changes coming up! Added screenshots for Tempra Cursive and Tempra Conflux Mobile to the README. Added a .htaccess file, just in case someone plops Gosora in Apache's /www/ folder to stop the contents of config.go from becoming publically visible. Never put Gosora in your /www/ folder, Gosora is a standalone program which does NOT use Apache or any other webserver. Fixed a bug in the inline forum editor. Added a hand-written Markdown parser which is much faster than the previous Regex based one. This is still a work in progress. Added support for strikethrough and underline elements to the Markdown parser. Fixed the missing semicolons in global.js Revamped the form CSS. Author bits on the theme manager now link to the author's website. Improved the profiles a little. The code in the stylesheets now have a more consistent style. Fixed a bug in the Cosmo theme relating to the fact that Gosora doesn't have sidebars yet. There are many more changes which aren't listed here. If weirdness regarding lines or spacing occurs, I'm experimenting with a new text editor. I hope to have this fixed by the next commit.
2017-05-29 14:52:37 +00:00
)
// TODO: Something more thread-safe
type ThemeList map[string]*Theme
UNSTABLE: Began work on the Nox Theme. Removed the Tempra Cursive Theme. You can now do bulk moderation actions with Shadow. Added: Argon2 as a dependency. The EmailStore. The ReportStore. The Copy method to *Setting. The AddColumn method to the query builder and adapters. The textarea setting type. More logging to better debug issues. The GetOffset method to the UserStore. Removed: Sortable from Code Climate's Analysis. MemberCheck and memberCheck as they're obsolete now. The obsolete url_tags setting. The BcryptGeneratePasswordNoSalt function. Some redundant fields from some of the page structs. Revamped: The Control Panel Setting List and Editor. Refactored: The password hashing logic to make it more amenable to multiple hashing algorithms. The email portion of the Account Manager. The Control Panel User List. The report system. simplePanelUserCheck and simpleUserCheck to remove the duplicated logic as the two do the exact same thing. Fixed: Missing slugs in the profile links in the User Manager. A few template initialisers potentially reducing the number of odd template edge cases. Some problems with the footer. Custom selection colour not applying to images on Shadow. The avatars of the bottom row of the topic list on Conflux leaking out. Other: Moved the startTime variable into package common and exported it. Moved the password hashing logic from user.go to auth.go Split common/themes.go into common/theme.go and common/theme_list.go Replaced the SettingLabels phrase category with the more generic SettingPhrases category. Moved a load of routes, including panel ones into the routes and panel packages. Hid the notifications link from the Account Menu. Moved more inline CSS into the CSS files and made things a little more flexible here and there. Continued work on PgSQL, still a ways away. Guests now have a default avatar like everyone else. Tweaked some of the font sizes on Cosora to make the text look a little nicer. Partially implemented the theme dock override logic. Partially implemented a "symlink" like feature for theme directories. ... And a bunch of other things I might have missed. You will need to run this update script / patcher for this commit. Warning: This is an "unstable commit", therefore some things may be a little less stable than I'd like. For instance, the Shadow Theme is a little broken in this commit.
2018-05-27 09:36:35 +00:00
var Themes ThemeList = make(map[string]*Theme) // ? Refactor this into a store?
var DefaultThemeBox atomic.Value
var ChangeDefaultThemeMutex sync.Mutex
var ThemesSlice []*Theme
// TODO: Fallback to a random theme if this doesn't exist, so admins can remove themes they don't use
// TODO: Use this when the default theme doesn't exist
var fallbackTheme = "cosora"
UNSTABLE: Began work on the Nox Theme. Removed the Tempra Cursive Theme. You can now do bulk moderation actions with Shadow. Added: Argon2 as a dependency. The EmailStore. The ReportStore. The Copy method to *Setting. The AddColumn method to the query builder and adapters. The textarea setting type. More logging to better debug issues. The GetOffset method to the UserStore. Removed: Sortable from Code Climate's Analysis. MemberCheck and memberCheck as they're obsolete now. The obsolete url_tags setting. The BcryptGeneratePasswordNoSalt function. Some redundant fields from some of the page structs. Revamped: The Control Panel Setting List and Editor. Refactored: The password hashing logic to make it more amenable to multiple hashing algorithms. The email portion of the Account Manager. The Control Panel User List. The report system. simplePanelUserCheck and simpleUserCheck to remove the duplicated logic as the two do the exact same thing. Fixed: Missing slugs in the profile links in the User Manager. A few template initialisers potentially reducing the number of odd template edge cases. Some problems with the footer. Custom selection colour not applying to images on Shadow. The avatars of the bottom row of the topic list on Conflux leaking out. Other: Moved the startTime variable into package common and exported it. Moved the password hashing logic from user.go to auth.go Split common/themes.go into common/theme.go and common/theme_list.go Replaced the SettingLabels phrase category with the more generic SettingPhrases category. Moved a load of routes, including panel ones into the routes and panel packages. Hid the notifications link from the Account Menu. Moved more inline CSS into the CSS files and made things a little more flexible here and there. Continued work on PgSQL, still a ways away. Guests now have a default avatar like everyone else. Tweaked some of the font sizes on Cosora to make the text look a little nicer. Partially implemented the theme dock override logic. Partially implemented a "symlink" like feature for theme directories. ... And a bunch of other things I might have missed. You will need to run this update script / patcher for this commit. Warning: This is an "unstable commit", therefore some things may be a little less stable than I'd like. For instance, the Shadow Theme is a little broken in this commit.
2018-05-27 09:36:35 +00:00
var overridenTemplates = make(map[string]bool) // ? What is this used for?
type ThemeStmts struct {
2022-02-21 03:32:53 +00:00
getAll *sql.Stmt
isDefault *sql.Stmt
update *sql.Stmt
add *sql.Stmt
}
var themeStmts ThemeStmts
func init() {
2022-02-21 03:32:53 +00:00
DbInits.Add(func(acc *qgen.Accumulator) error {
t, cols := "themes", "uname,default"
themeStmts = ThemeStmts{
getAll: acc.Select(t).Columns(cols).Prepare(),
isDefault: acc.Select(t).Columns("default").Where("uname=?").Prepare(),
update: acc.Update(t).Set("default=?").Where("uname=?").Prepare(),
add: acc.Insert(t).Columns(cols).Fields("?,?").Prepare(),
}
return acc.FirstError()
})
}
UNSTABLE: Began work on the Nox Theme. Removed the Tempra Cursive Theme. You can now do bulk moderation actions with Shadow. Added: Argon2 as a dependency. The EmailStore. The ReportStore. The Copy method to *Setting. The AddColumn method to the query builder and adapters. The textarea setting type. More logging to better debug issues. The GetOffset method to the UserStore. Removed: Sortable from Code Climate's Analysis. MemberCheck and memberCheck as they're obsolete now. The obsolete url_tags setting. The BcryptGeneratePasswordNoSalt function. Some redundant fields from some of the page structs. Revamped: The Control Panel Setting List and Editor. Refactored: The password hashing logic to make it more amenable to multiple hashing algorithms. The email portion of the Account Manager. The Control Panel User List. The report system. simplePanelUserCheck and simpleUserCheck to remove the duplicated logic as the two do the exact same thing. Fixed: Missing slugs in the profile links in the User Manager. A few template initialisers potentially reducing the number of odd template edge cases. Some problems with the footer. Custom selection colour not applying to images on Shadow. The avatars of the bottom row of the topic list on Conflux leaking out. Other: Moved the startTime variable into package common and exported it. Moved the password hashing logic from user.go to auth.go Split common/themes.go into common/theme.go and common/theme_list.go Replaced the SettingLabels phrase category with the more generic SettingPhrases category. Moved a load of routes, including panel ones into the routes and panel packages. Hid the notifications link from the Account Menu. Moved more inline CSS into the CSS files and made things a little more flexible here and there. Continued work on PgSQL, still a ways away. Guests now have a default avatar like everyone else. Tweaked some of the font sizes on Cosora to make the text look a little nicer. Partially implemented the theme dock override logic. Partially implemented a "symlink" like feature for theme directories. ... And a bunch of other things I might have missed. You will need to run this update script / patcher for this commit. Warning: This is an "unstable commit", therefore some things may be a little less stable than I'd like. For instance, the Shadow Theme is a little broken in this commit.
2018-05-27 09:36:35 +00:00
func NewThemeList() (themes ThemeList, err error) {
2022-02-21 03:32:53 +00:00
themes = make(map[string]*Theme)
themeFiles, err := ioutil.ReadDir("./themes")
if err != nil {
return themes, err
}
if len(themeFiles) == 0 {
return themes, errors.New("You don't have any themes")
}
var lastTheme, defaultTheme string
for _, themeFile := range themeFiles {
if !themeFile.IsDir() {
continue
}
themeName := themeFile.Name()
log.Printf("Adding theme '%s'", themeName)
themePath := "./themes/" + themeName
themeFile, err := ioutil.ReadFile(themePath + "/theme.json")
if err != nil {
return themes, err
}
th := &Theme{}
err = json.Unmarshal(themeFile, th)
if err != nil {
return themes, err
}
if th.Name == "" {
return themes, errors.New("Theme " + themePath + " doesn't have a name set in theme.json")
}
if th.Name == fallbackTheme {
defaultTheme = fallbackTheme
}
lastTheme = th.Name
// TODO: Implement the static file part of this and fsnotify
if th.Path != "" {
log.Print("Resolving redirect to " + th.Path)
themeFile, err := ioutil.ReadFile(th.Path + "/theme.json")
if err != nil {
return themes, err
}
th = &Theme{Path: th.Path}
err = json.Unmarshal(themeFile, th)
if err != nil {
return themes, err
}
} else {
th.Path = themePath
}
th.Active = false // Set this to false, just in case someone explicitly overrode this value in the JSON file
// TODO: Let the theme specify where it's resources are via the JSON file?
// TODO: Let the theme inherit CSS from another theme?
// ? - This might not be too helpful, as it only searches for /public/ and not if /public/ is empty. Still, it might help some people with a slightly less cryptic error
log.Print(th.Path + "/public/")
_, err = os.Stat(th.Path + "/public/")
if err != nil {
if os.IsNotExist(err) {
return themes, errors.New("We couldn't find this theme's resources. E.g. the /public/ folder.")
} else {
log.Print("We weren't able to access this theme's resources due to a permissions issue or some other problem")
return themes, err
}
}
if th.FullImage != "" {
DebugLog("Adding theme image")
err = StaticFiles.Add(th.Path+"/"+th.FullImage, themePath)
if err != nil {
return themes, err
}
}
th.TemplatesMap = make(map[string]string)
th.TmplPtr = make(map[string]interface{})
if th.Templates != nil {
for _, themeTmpl := range th.Templates {
th.TemplatesMap[themeTmpl.Name] = themeTmpl.Source
th.TmplPtr[themeTmpl.Name] = TmplPtrMap["o_"+themeTmpl.Source]
}
}
th.IntTmplHandle = DefaultTemplates
overrides, err := ioutil.ReadDir(th.Path + "/overrides/")
if err != nil && !os.IsNotExist(err) {
return themes, err
}
if len(overrides) > 0 {
overCount := 0
th.OverridenMap = make(map[string]bool)
for _, override := range overrides {
if override.IsDir() {
continue
}
ext := filepath.Ext(themePath + "/overrides/" + override.Name())
log.Print("attempting to add " + themePath + "/overrides/" + override.Name())
if ext != ".html" {
log.Print("not a html file")
continue
}
overCount++
nosuf := strings.TrimSuffix(override.Name(), ext)
th.OverridenTemplates = append(th.OverridenTemplates, nosuf)
th.OverridenMap[nosuf] = true
//th.TmplPtr[nosuf] = TmplPtrMap["o_"+nosuf]
log.Print("succeeded")
}
localTmpls := template.New("")
err = loadTemplates(localTmpls, th.Name)
if err != nil {
return themes, err
}
th.IntTmplHandle = localTmpls
log.Printf("theme.OverridenTemplates: %+v\n", th.OverridenTemplates)
log.Printf("theme.IntTmplHandle: %+v\n", th.IntTmplHandle)
} else {
log.Print("no overrides for " + th.Name)
}
for i, res := range th.Resources {
ext := filepath.Ext(res.Name)
switch ext {
case ".css":
res.Type = ResTypeSheet
case ".js":
res.Type = ResTypeScript
}
switch res.Location {
case "global":
res.LocID = LocGlobal
case "frontend":
res.LocID = LocFront
case "panel":
res.LocID = LocPanel
}
th.Resources[i] = res
}
for _, dock := range th.Docks {
if id, ok := DockToID[dock]; ok {
th.DocksID = append(th.DocksID, id)
}
}
// TODO: Bind the built template, or an interpreted one for any dock overrides this theme has
themes[th.Name] = th
ThemesSlice = append(ThemesSlice, th)
}
if defaultTheme == "" {
defaultTheme = lastTheme
}
DefaultThemeBox.Store(defaultTheme)
return themes, nil
}
UNSTABLE: Began work on the Nox Theme. Removed the Tempra Cursive Theme. You can now do bulk moderation actions with Shadow. Added: Argon2 as a dependency. The EmailStore. The ReportStore. The Copy method to *Setting. The AddColumn method to the query builder and adapters. The textarea setting type. More logging to better debug issues. The GetOffset method to the UserStore. Removed: Sortable from Code Climate's Analysis. MemberCheck and memberCheck as they're obsolete now. The obsolete url_tags setting. The BcryptGeneratePasswordNoSalt function. Some redundant fields from some of the page structs. Revamped: The Control Panel Setting List and Editor. Refactored: The password hashing logic to make it more amenable to multiple hashing algorithms. The email portion of the Account Manager. The Control Panel User List. The report system. simplePanelUserCheck and simpleUserCheck to remove the duplicated logic as the two do the exact same thing. Fixed: Missing slugs in the profile links in the User Manager. A few template initialisers potentially reducing the number of odd template edge cases. Some problems with the footer. Custom selection colour not applying to images on Shadow. The avatars of the bottom row of the topic list on Conflux leaking out. Other: Moved the startTime variable into package common and exported it. Moved the password hashing logic from user.go to auth.go Split common/themes.go into common/theme.go and common/theme_list.go Replaced the SettingLabels phrase category with the more generic SettingPhrases category. Moved a load of routes, including panel ones into the routes and panel packages. Hid the notifications link from the Account Menu. Moved more inline CSS into the CSS files and made things a little more flexible here and there. Continued work on PgSQL, still a ways away. Guests now have a default avatar like everyone else. Tweaked some of the font sizes on Cosora to make the text look a little nicer. Partially implemented the theme dock override logic. Partially implemented a "symlink" like feature for theme directories. ... And a bunch of other things I might have missed. You will need to run this update script / patcher for this commit. Warning: This is an "unstable commit", therefore some things may be a little less stable than I'd like. For instance, the Shadow Theme is a little broken in this commit.
2018-05-27 09:36:35 +00:00
// TODO: Make the initThemes and LoadThemes functions less confusing
// ? - Delete themes which no longer exist in the themes folder from the database?
func (t ThemeList) LoadActiveStatus() error {
2022-02-21 03:32:53 +00:00
ChangeDefaultThemeMutex.Lock()
defer ChangeDefaultThemeMutex.Unlock()
rows, e := themeStmts.getAll.Query()
if e != nil {
return e
}
defer rows.Close()
var uname string
var defaultThemeSwitch bool
for rows.Next() {
e = rows.Scan(&uname, &defaultThemeSwitch)
if e != nil {
return e
}
// Was the theme deleted at some point?
theme, ok := t[uname]
if !ok {
continue
}
if defaultThemeSwitch {
DebugLogf("Loading the default theme '%s'", theme.Name)
theme.Active = true
DefaultThemeBox.Store(theme.Name)
theme.MapTemplates()
} else {
DebugLogf("Loading the theme '%s'", theme.Name)
theme.Active = false
}
t[uname] = theme
}
return rows.Err()
}
func (t ThemeList) LoadStaticFiles() error {
2022-02-21 03:32:53 +00:00
for _, theme := range t {
if e := theme.LoadStaticFiles(); e != nil {
return e
}
}
return nil
}
func ResetTemplateOverrides() {
2022-02-21 03:32:53 +00:00
log.Print("Resetting the template overrides")
for name := range overridenTemplates {
log.Print("Resetting '" + name + "' template override")
originPointer, ok := TmplPtrMap["o_"+name]
if !ok {
log.Print("The origin template doesn't exist!")
return
}
destTmplPtr, ok := TmplPtrMap[name]
if !ok {
log.Print("The destination template doesn't exist!")
return
}
// Not really a pointer, more of a function handle, an artifact from one of the earlier versions of themes.go
oPtr, ok := originPointer.(func(interface{}, io.Writer) error)
if !ok {
log.Print("name: ", name)
LogError(errors.New("Unknown destination template type!"))
return
}
dPtr, ok := destTmplPtr.(*func(interface{}, io.Writer) error)
if !ok {
LogError(errors.New("The source and destination templates are incompatible"))
return
}
*dPtr = oPtr
log.Print("The template override was reset")
}
overridenTemplates = make(map[string]bool)
log.Print("All of the template overrides have been reset")
}
// CreateThemeTemplate creates a theme template on the current default theme
func CreateThemeTemplate(theme, name string) {
2022-02-21 03:32:53 +00:00
Themes[theme].TmplPtr[name] = func(pi Page, w http.ResponseWriter) error {
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap[name]
if !ok {
mapping = name
}
return DefaultTemplates.ExecuteTemplate(w, mapping+".html", pi)
}
}
func GetDefaultThemeName() string {
2022-02-21 03:32:53 +00:00
return DefaultThemeBox.Load().(string)
}
func SetDefaultThemeName(name string) {
2022-02-21 03:32:53 +00:00
DefaultThemeBox.Store(name)
}