2017-05-29 14:52:37 +00:00
/* Copyright Azareal 2016 - 2018 */
2017-01-01 15:45:43 +00:00
package main
2017-05-29 14:52:37 +00:00
import (
//"fmt"
2017-06-25 09:56:39 +00:00
"bytes"
2017-09-03 04:50:31 +00:00
"encoding/json"
2017-08-13 11:22:34 +00:00
"errors"
2017-05-29 14:52:37 +00:00
"io/ioutil"
2017-09-03 04:50:31 +00:00
"log"
"mime"
2017-05-29 14:52:37 +00:00
"net/http"
2017-09-03 04:50:31 +00:00
"os"
"path/filepath"
"reflect"
"strings"
2017-09-10 16:57:22 +00:00
"sync"
"sync/atomic"
2017-06-25 09:56:39 +00:00
"text/template"
2017-05-29 14:52:37 +00:00
)
2017-01-01 15:45:43 +00:00
2017-09-03 04:50:31 +00:00
var themes = make ( map [ string ] Theme )
2017-09-10 16:57:22 +00:00
var defaultThemeBox atomic . Value
var changeDefaultThemeMutex sync . Mutex
2017-01-01 15:45:43 +00:00
2017-09-22 02:21:17 +00:00
// TODO: Use this when the default theme doesn't exist
var fallbackTheme = "shadow"
2017-09-03 04:50:31 +00:00
//var overridenTemplates map[string]interface{} = make(map[string]interface{})
var overridenTemplates = make ( map [ string ] bool )
type Theme struct {
Name string
FriendlyName string
Version string
Creator string
FullImage string
2017-01-07 06:31:04 +00:00
MobileFriendly bool
2017-09-03 04:50:31 +00:00
Disabled bool
2017-02-28 09:27:28 +00:00
HideFromThemes bool
2017-09-03 04:50:31 +00:00
ForkOf string
Tag string
URL string
Sidebars string // Allowed Values: left, right, both, false
2017-07-29 10:36:39 +00:00
//DisableMinifier // Is this really a good idea? I don't think themes should be fighting against the minifier
2017-09-03 04:50:31 +00:00
Settings map [ string ] ThemeSetting
Templates [ ] TemplateMapping
TemplatesMap map [ string ] string
TmplPtr map [ string ] interface { }
Resources [ ] ThemeResource
2017-06-25 09:56:39 +00:00
ResourceTemplates * template . Template
2017-05-29 14:52:37 +00:00
2017-01-01 15:45:43 +00:00
// This variable should only be set and unset by the system, not the theme meta file
Active bool
}
2017-09-03 04:50:31 +00:00
type ThemeSetting struct {
2017-01-01 15:45:43 +00:00
FriendlyName string
2017-09-03 04:50:31 +00:00
Options [ ] string
2017-01-01 15:45:43 +00:00
}
2017-09-03 04:50:31 +00:00
type TemplateMapping struct {
Name string
2017-01-01 15:45:43 +00:00
Source string
//When string
}
2017-09-03 04:50:31 +00:00
type ThemeResource struct {
Name string
2017-06-19 08:06:54 +00:00
Location string
}
2017-09-23 19:57:13 +00:00
func init ( ) {
defaultThemeBox . Store ( fallbackTheme )
}
// ? - Delete themes which no longer exist in the themes folder from the database?
2017-06-14 09:55:47 +00:00
func LoadThemes ( ) error {
2017-09-10 16:57:22 +00:00
changeDefaultThemeMutex . Lock ( )
2017-09-18 17:03:52 +00:00
rows , err := getThemesStmt . Query ( )
2017-06-14 09:55:47 +00:00
if err != nil {
return err
}
defer rows . Close ( )
var uname string
var defaultThemeSwitch bool
for rows . Next ( ) {
err = rows . Scan ( & uname , & defaultThemeSwitch )
if err != nil {
return err
}
// Was the theme deleted at some point?
theme , ok := themes [ uname ]
if ! ok {
continue
}
if defaultThemeSwitch {
2017-09-23 19:57:13 +00:00
log . Print ( "Loading the default theme '" + theme . Name + "'" )
2017-06-14 09:55:47 +00:00
theme . Active = true
2017-09-23 19:57:13 +00:00
defaultThemeBox . Store ( theme . Name )
2017-09-03 04:50:31 +00:00
mapThemeTemplates ( theme )
2017-06-14 09:55:47 +00:00
} else {
2017-09-23 19:57:13 +00:00
log . Print ( "Loading the theme '" + theme . Name + "'" )
2017-06-14 09:55:47 +00:00
theme . Active = false
}
2017-09-03 04:50:31 +00:00
2017-06-14 09:55:47 +00:00
themes [ uname ] = theme
}
2017-09-10 16:57:22 +00:00
changeDefaultThemeMutex . Unlock ( )
2017-09-03 04:50:31 +00:00
return rows . Err ( )
2017-06-14 09:55:47 +00:00
}
2017-09-03 04:50:31 +00:00
func initThemes ( ) error {
2017-01-01 15:45:43 +00:00
themeFiles , err := ioutil . ReadDir ( "./themes" )
if err != nil {
2017-09-03 04:50:31 +00:00
return err
2017-01-01 15:45:43 +00:00
}
2017-05-29 14:52:37 +00:00
2017-01-01 15:45:43 +00:00
for _ , themeFile := range themeFiles {
if ! themeFile . IsDir ( ) {
continue
}
2017-05-29 14:52:37 +00:00
2017-01-01 15:45:43 +00:00
themeName := themeFile . Name ( )
log . Print ( "Adding theme '" + themeName + "'" )
themeFile , err := ioutil . ReadFile ( "./themes/" + themeName + "/theme.json" )
if err != nil {
2017-09-03 04:50:31 +00:00
return err
2017-01-01 15:45:43 +00:00
}
2017-05-29 14:52:37 +00:00
2017-01-01 15:45:43 +00:00
var theme Theme
2017-01-07 06:31:04 +00:00
err = json . Unmarshal ( themeFile , & theme )
if err != nil {
2017-09-03 04:50:31 +00:00
return err
2017-01-07 06:31:04 +00:00
}
2017-05-29 14:52:37 +00:00
2017-01-01 15:45:43 +00:00
theme . Active = false // Set this to false, just in case someone explicitly overrode this value in the JSON file
2017-05-29 14:52:37 +00:00
Added Quick Topic.
Added Attachments.
Added Attachment Media Embeds.
Renamed a load of *Store and *Cache methods to reduce the amount of unneccesary typing.
Added petabytes as a unit and cleaned up a few of the friendly units.
Refactored the username change logic to make it easier to maintain.
Refactored the avatar change logic to make it easier to maintain.
Shadow now uses CSS Variables for most of it's colours. We have plans to transpile this to support older browsers later on!
Snuck some CSS Variables into Tempra Conflux.
Added the GroupCache interface to MemoryGroupStore.
Added the Length method to MemoryGroupStore.
Added support for a site short name.
Added the UploadFiles permission.
Renamed more functions.
Fixed the background for the left gutter on the postbit for Tempra Simple and Shadow.
Added support for if statements operating on int8, int16, int32, int32, int64, uint, uint8, uint16, uint32, uint64, float32, and float64 for the template compiler.
Added support for if statements operating on slices and maps for the template compiler.
Fixed a security exploit in reply editing.
Fixed a bug in the URL detector in the parser where it couldn't find URLs with non-standard ports.
Fixed buttons having blue outlines on focus on Shadow.
Refactored the topic creation logic to make it easier to maintain.
Made a few responsive fixes, but there's still more to do in the following commits!
2017-10-05 10:20:28 +00:00
// 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
_ , err = os . Stat ( "./themes/" + theme . Name + "/public/" )
if err != nil {
if os . IsNotExist ( err ) {
return 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 err
}
}
2017-01-07 06:31:04 +00:00
if theme . FullImage != "" {
2017-07-17 10:23:42 +00:00
if dev . DebugMode {
2017-05-07 08:31:41 +00:00
log . Print ( "Adding theme image" )
}
2017-09-03 04:50:31 +00:00
err = addStaticFile ( "./themes/" + themeName + "/" + theme . FullImage , "./themes/" + themeName )
2017-01-07 06:31:04 +00:00
if err != nil {
2017-09-03 04:50:31 +00:00
return err
2017-01-07 06:31:04 +00:00
}
}
2017-05-29 14:52:37 +00:00
2017-09-23 19:57:13 +00:00
theme . TemplatesMap = make ( map [ string ] string )
theme . TmplPtr = make ( map [ string ] interface { } )
if theme . Templates != nil {
for _ , themeTmpl := range theme . Templates {
theme . TemplatesMap [ themeTmpl . Name ] = themeTmpl . Source
theme . TmplPtr [ themeTmpl . Name ] = tmplPtrMap [ "o_" + themeTmpl . Source ]
}
}
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 )
if err != nil {
return err
}
2017-01-01 15:45:43 +00:00
themes [ theme . Name ] = theme
}
2017-09-03 04:50:31 +00:00
return nil
2017-01-01 15:45:43 +00:00
}
2017-09-03 04:50:31 +00:00
func addThemeStaticFiles ( theme Theme ) error {
2017-09-10 16:57:22 +00:00
// 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?
2017-09-03 04:50:31 +00:00
return filepath . Walk ( "./themes/" + theme . Name + "/public" , func ( path string , f os . FileInfo , err error ) error {
2017-07-17 10:23:42 +00:00
if dev . DebugMode {
2017-06-25 09:56:39 +00:00
log . Print ( "Attempting to add static file '" + path + "' for default theme '" + theme . Name + "'" )
}
2017-01-01 15:45:43 +00:00
if err != nil {
return err
}
if f . IsDir ( ) {
return nil
}
2017-05-29 14:52:37 +00:00
2017-09-03 04:50:31 +00:00
path = strings . Replace ( path , "\\" , "/" , - 1 )
2017-01-01 15:45:43 +00:00
data , err := ioutil . ReadFile ( path )
if err != nil {
return err
}
2017-05-29 14:52:37 +00:00
2017-09-03 04:50:31 +00:00
var ext = filepath . Ext ( path )
2017-06-25 09:56:39 +00:00
//log.Print("path ",path)
//log.Print("ext ",ext)
2017-07-17 10:23:42 +00:00
if ext == ".css" && len ( data ) != 0 {
2017-06-25 09:56:39 +00:00
var b bytes . Buffer
2017-09-03 04:50:31 +00:00
var pieces = strings . Split ( path , "/" )
var filename = pieces [ len ( pieces ) - 1 ]
2017-06-25 09:56:39 +00:00
//log.Print("filename ", filename)
2017-09-18 17:03:52 +00:00
err = theme . ResourceTemplates . ExecuteTemplate ( & b , filename , CSSData { ComingSoon : "We don't have any data to pass you yet!" } )
2017-06-25 09:56:39 +00:00
if err != nil {
return err
}
data = b . Bytes ( )
2017-05-07 08:31:41 +00:00
}
2017-06-25 09:56:39 +00:00
2017-09-03 04:50:31 +00:00
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 ) }
2017-05-29 14:52:37 +00:00
2017-07-17 10:23:42 +00:00
if dev . DebugMode {
2017-09-03 04:50:31 +00:00
log . Print ( "Added the '/" + theme . Name + path + "' static file for theme " + theme . Name + "." )
2017-06-25 09:56:39 +00:00
}
2017-01-01 15:45:43 +00:00
return nil
} )
}
2017-09-03 04:50:31 +00:00
func mapThemeTemplates ( theme Theme ) {
2017-01-01 15:45:43 +00:00
if theme . Templates != nil {
for _ , themeTmpl := range theme . Templates {
if themeTmpl . Name == "" {
log . Fatal ( "Invalid destination template name" )
}
if themeTmpl . Source == "" {
log . Fatal ( "Invalid source template name" )
}
2017-05-29 14:52:37 +00:00
2017-01-07 06:31:04 +00:00
// `go generate` is one possibility for letting plugins inject custom page structs, but it would simply add another step of compilation. It might be simpler than the current build process from the perspective of the administrator?
2017-05-29 14:52:37 +00:00
2017-09-03 04:50:31 +00:00
destTmplPtr , ok := tmplPtrMap [ themeTmpl . Name ]
2017-01-01 15:45:43 +00:00
if ! ok {
2017-06-16 10:41:30 +00:00
return
2017-01-01 15:45:43 +00:00
}
2017-09-03 04:50:31 +00:00
sourceTmplPtr , ok := tmplPtrMap [ themeTmpl . Source ]
2017-01-01 15:45:43 +00:00
if ! ok {
log . Fatal ( "The source template doesn't exist!" )
}
2017-05-29 14:52:37 +00:00
2017-09-03 04:50:31 +00:00
switch dTmplPtr := destTmplPtr . ( type ) {
case * func ( TopicPage , http . ResponseWriter ) :
switch sTmplPtr := sourceTmplPtr . ( type ) {
case * func ( TopicPage , http . ResponseWriter ) :
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
overridenTemplates [ themeTmpl . Name ] = true
* dTmplPtr = * sTmplPtr
default :
log . Fatal ( "The source and destination templates are incompatible" )
}
case * func ( TopicsPage , http . ResponseWriter ) :
switch sTmplPtr := sourceTmplPtr . ( type ) {
case * func ( TopicsPage , http . ResponseWriter ) :
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
overridenTemplates [ themeTmpl . Name ] = true
* dTmplPtr = * sTmplPtr
default :
log . Fatal ( "The source and destination templates are incompatible" )
}
case * func ( ForumPage , http . ResponseWriter ) :
switch sTmplPtr := sourceTmplPtr . ( type ) {
case * func ( ForumPage , http . ResponseWriter ) :
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
overridenTemplates [ themeTmpl . Name ] = true
* dTmplPtr = * sTmplPtr
default :
log . Fatal ( "The source and destination templates are incompatible" )
}
case * func ( ForumsPage , http . ResponseWriter ) :
switch sTmplPtr := sourceTmplPtr . ( type ) {
case * func ( ForumsPage , http . ResponseWriter ) :
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
overridenTemplates [ themeTmpl . Name ] = true
* dTmplPtr = * sTmplPtr
default :
log . Fatal ( "The source and destination templates are incompatible" )
}
case * func ( ProfilePage , http . ResponseWriter ) :
switch sTmplPtr := sourceTmplPtr . ( type ) {
case * func ( ProfilePage , http . ResponseWriter ) :
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
overridenTemplates [ themeTmpl . Name ] = true
* dTmplPtr = * sTmplPtr
2017-01-01 15:45:43 +00:00
default :
2017-09-03 04:50:31 +00:00
log . Fatal ( "The source and destination templates are incompatible" )
}
case * func ( CreateTopicPage , http . ResponseWriter ) :
switch sTmplPtr := sourceTmplPtr . ( type ) {
case * func ( CreateTopicPage , http . ResponseWriter ) :
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
overridenTemplates [ themeTmpl . Name ] = true
* dTmplPtr = * sTmplPtr
default :
log . Fatal ( "The source and destination templates are incompatible" )
}
case * func ( Page , http . ResponseWriter ) :
switch sTmplPtr := sourceTmplPtr . ( type ) {
case * func ( Page , http . ResponseWriter ) :
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
overridenTemplates [ themeTmpl . Name ] = true
* dTmplPtr = * sTmplPtr
default :
log . Fatal ( "The source and destination templates are incompatible" )
}
default :
log . Fatal ( "Unknown destination template type!" )
2017-01-01 15:45:43 +00:00
}
}
}
}
2017-09-03 04:50:31 +00:00
func resetTemplateOverrides ( ) {
2017-01-01 15:45:43 +00:00
log . Print ( "Resetting the template overrides" )
2017-05-29 14:52:37 +00:00
2017-09-03 04:50:31 +00:00
for name := range overridenTemplates {
2017-01-01 15:45:43 +00:00
log . Print ( "Resetting '" + name + "' template override" )
2017-05-29 14:52:37 +00:00
2017-09-03 04:50:31 +00:00
originPointer , ok := tmplPtrMap [ "o_" + name ]
2017-01-01 15:45:43 +00:00
if ! ok {
//log.Fatal("The origin template doesn't exist!")
log . Print ( "The origin template doesn't exist!" )
return
}
2017-05-29 14:52:37 +00:00
2017-09-03 04:50:31 +00:00
destTmplPtr , ok := tmplPtrMap [ name ]
2017-01-01 15:45:43 +00:00
if ! ok {
//log.Fatal("The destination template doesn't exist!")
log . Print ( "The destination template doesn't exist!" )
return
}
2017-05-29 14:52:37 +00:00
2017-01-01 16:14:38 +00:00
// Not really a pointer, more of a function handle, an artifact from one of the earlier versions of themes.go
2017-09-03 04:50:31 +00:00
switch oPtr := originPointer . ( type ) {
case func ( TopicPage , http . ResponseWriter ) :
switch dPtr := destTmplPtr . ( type ) {
case * func ( TopicPage , http . ResponseWriter ) :
* dPtr = oPtr
2017-01-01 15:45:43 +00:00
default :
2017-09-03 04:50:31 +00:00
log . Fatal ( "The origin and destination templates are incompatible" )
}
case func ( TopicsPage , http . ResponseWriter ) :
switch dPtr := destTmplPtr . ( type ) {
case * func ( TopicsPage , http . ResponseWriter ) :
* dPtr = oPtr
default :
log . Fatal ( "The origin and destination templates are incompatible" )
}
case func ( ForumPage , http . ResponseWriter ) :
switch dPtr := destTmplPtr . ( type ) {
case * func ( ForumPage , http . ResponseWriter ) :
* dPtr = oPtr
default :
log . Fatal ( "The origin and destination templates are incompatible" )
}
case func ( ForumsPage , http . ResponseWriter ) :
switch dPtr := destTmplPtr . ( type ) {
case * func ( ForumsPage , http . ResponseWriter ) :
* dPtr = oPtr
default :
log . Fatal ( "The origin and destination templates are incompatible" )
}
case func ( ProfilePage , http . ResponseWriter ) :
switch dPtr := destTmplPtr . ( type ) {
case * func ( ProfilePage , http . ResponseWriter ) :
* dPtr = oPtr
default :
log . Fatal ( "The origin and destination templates are incompatible" )
}
case func ( CreateTopicPage , http . ResponseWriter ) :
switch dPtr := destTmplPtr . ( type ) {
case * func ( CreateTopicPage , http . ResponseWriter ) :
* dPtr = oPtr
default :
log . Fatal ( "The origin and destination templates are incompatible" )
}
case func ( Page , http . ResponseWriter ) :
switch dPtr := destTmplPtr . ( type ) {
case * func ( Page , http . ResponseWriter ) :
* dPtr = oPtr
default :
log . Fatal ( "The origin and destination templates are incompatible" )
}
default :
log . Fatal ( "Unknown destination template type!" )
2017-01-01 15:45:43 +00:00
}
log . Print ( "The template override was reset" )
}
2017-09-03 04:50:31 +00:00
overridenTemplates = make ( map [ string ] bool )
2017-01-01 15:45:43 +00:00
log . Print ( "All of the template overrides have been reset" )
}
2017-08-13 11:22:34 +00:00
// NEW method of doing theme templates to allow one user to have a different theme to another. Under construction.
2017-09-10 16:57:22 +00:00
// TODO: Generate the type switch instead of writing it by hand
// TODO: Cut the number of types in half
2017-08-13 11:22:34 +00:00
func RunThemeTemplate ( theme string , template string , pi interface { } , w http . ResponseWriter ) {
2017-09-03 04:50:31 +00:00
switch tmplO := GetThemeTemplate ( theme , template ) . ( type ) {
case * func ( TopicPage , http . ResponseWriter ) :
var tmpl = * tmplO
tmpl ( pi . ( TopicPage ) , w )
case * func ( TopicsPage , http . ResponseWriter ) :
var tmpl = * tmplO
tmpl ( pi . ( TopicsPage ) , w )
case * func ( ForumPage , http . ResponseWriter ) :
var tmpl = * tmplO
tmpl ( pi . ( ForumPage ) , w )
case * func ( ForumsPage , http . ResponseWriter ) :
var tmpl = * tmplO
tmpl ( pi . ( ForumsPage ) , w )
case * func ( ProfilePage , http . ResponseWriter ) :
var tmpl = * tmplO
tmpl ( pi . ( ProfilePage ) , w )
case * func ( CreateTopicPage , http . ResponseWriter ) :
var tmpl = * tmplO
tmpl ( pi . ( CreateTopicPage ) , w )
case * func ( Page , http . ResponseWriter ) :
var tmpl = * tmplO
tmpl ( pi . ( Page ) , w )
case func ( TopicPage , http . ResponseWriter ) :
tmplO ( pi . ( TopicPage ) , w )
case func ( TopicsPage , http . ResponseWriter ) :
tmplO ( pi . ( TopicsPage ) , w )
case func ( ForumPage , http . ResponseWriter ) :
tmplO ( pi . ( ForumPage ) , w )
case func ( ForumsPage , http . ResponseWriter ) :
tmplO ( pi . ( ForumsPage ) , w )
case func ( ProfilePage , http . ResponseWriter ) :
tmplO ( pi . ( ProfilePage ) , w )
case func ( CreateTopicPage , http . ResponseWriter ) :
tmplO ( pi . ( CreateTopicPage ) , w )
case func ( Page , http . ResponseWriter ) :
tmplO ( pi . ( Page ) , w )
case string :
2017-09-10 16:57:22 +00:00
mapping , ok := themes [ defaultThemeBox . Load ( ) . ( string ) ] . TemplatesMap [ template ]
2017-09-03 04:50:31 +00:00
if ! ok {
mapping = template
}
err := templates . ExecuteTemplate ( w , mapping + ".html" , pi )
if err != nil {
LogError ( err )
}
default :
log . Print ( "theme " , theme )
log . Print ( "template " , template )
log . Print ( "pi " , pi )
log . Print ( "tmplO " , tmplO )
valueOf := reflect . ValueOf ( tmplO )
log . Print ( "initial valueOf.Type()" , valueOf . Type ( ) )
for valueOf . Kind ( ) == reflect . Interface || valueOf . Kind ( ) == reflect . Ptr {
valueOf = valueOf . Elem ( )
log . Print ( "valueOf.Elem().Type() " , valueOf . Type ( ) )
}
log . Print ( "deferenced valueOf.Type() " , valueOf . Type ( ) )
log . Print ( "valueOf.Kind() " , valueOf . Kind ( ) )
LogError ( errors . New ( "Unknown template type" ) )
2017-08-13 11:22:34 +00:00
}
}
2017-09-03 04:50:31 +00:00
// GetThemeTemplate attempts to get the template for a specific theme, otherwise it falls back on the default template pointer, which if absent will fallback onto the template interpreter
2017-08-13 11:22:34 +00:00
func GetThemeTemplate ( theme string , template string ) interface { } {
tmpl , ok := themes [ theme ] . TmplPtr [ template ]
2017-09-03 04:50:31 +00:00
if ok {
2017-08-13 11:22:34 +00:00
return tmpl
}
2017-09-03 04:50:31 +00:00
tmpl , ok = tmplPtrMap [ template ]
if ok {
return tmpl
}
return template
2017-08-13 11:22:34 +00:00
}
2017-09-03 04:50:31 +00:00
// CreateThemeTemplate creates a theme template on the current default theme
2017-08-13 11:22:34 +00:00
func CreateThemeTemplate ( theme string , name string ) {
2017-09-03 04:50:31 +00:00
themes [ theme ] . TmplPtr [ name ] = func ( pi Page , w http . ResponseWriter ) {
2017-09-10 16:57:22 +00:00
mapping , ok := themes [ defaultThemeBox . Load ( ) . ( string ) ] . TemplatesMap [ name ]
2017-08-13 11:22:34 +00:00
if ! ok {
mapping = name
}
2017-09-03 04:50:31 +00:00
err := templates . ExecuteTemplate ( w , mapping + ".html" , pi )
2017-08-13 11:22:34 +00:00
if err != nil {
2017-09-03 04:50:31 +00:00
InternalError ( err , w )
2017-08-13 11:22:34 +00:00
}
}
}
2017-09-10 16:57:22 +00:00
func GetDefaultThemeName ( ) string {
return defaultThemeBox . Load ( ) . ( string )
}
func SetDefaultThemeName ( name string ) {
defaultThemeBox . Store ( name )
}