Added the theme system.

You can now switch between themes in the Control Panel.
The Alternate Post Layout teased in various screenshots and bits of inaccessible code is now the Tempra Conflux theme, while the original post layout is the Tempa Simple theme.
Dramatically increased the speed at which the static files are served.
Added a benchmark for static files.
Eliminated the name parameter in various custom page structs.
Added the TopicsPage struct for the /topics/ route.
Added the ForumPage struct for the /forum/{id} route.
Added the ForumsPage struct for the /forums/ route.
The static file route now serves 404s when a file doesn't exist instead of nearly crashing the server.
Reduced the number of unnecessary allocations on some of the routes.
Added gradients to Tempra Conflux.
Added position: sticky; to the userinfo blocks in Tempra Conflux.
Added a notice on the generated template files.
This commit is contained in:
Azareal 2017-01-01 15:45:43 +00:00
parent bcaa646f68
commit e47450d372
39 changed files with 1187 additions and 167 deletions

View File

@ -99,8 +99,15 @@ CREATE TABLE `plugins`(
unique(`uname`)
);
CREATE TABLE `themes`(
`uname` varchar(200) not null,
`default` tinyint DEFAULT 0 not null,
unique(`uname`)
);
INSERT INTO settings(`name`,`content`,`type`) VALUES ('url_tags','1','bool');
INSERT INTO settings(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3',);
INSERT INTO themes(`uname`,`default`) VALUES ('tempra-simple',1);
INSERT INTO users(`name`,`email`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`,`message`)
VALUES ('Admin','admin@localhost',1,1,NOW(),NOW(),'');
@ -118,6 +125,7 @@ EditUserGroupSuperMod
EditUserGroupAdmin
ManageForums
EditSettings
ManageThemes
ManagePlugins
ViewIPs
@ -132,11 +140,11 @@ PinTopic
CloseTopic
*/
INSERT INTO users_groups(`name`,`permissions`,`active`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManagePlugins":true,"ViewIPs":true,"ViewTopic":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}',1,1,1,"Admin");
INSERT INTO users_groups(`name`,`permissions`,`is_mod`,`tag`) VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":false,"EditUser":true,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":true,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManagePlugins":false,"ViewIPs":true,"ViewTopic":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}',1,"Mod");
INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":true,"EditTopic":false,"DeleteTopic":false,"CreateReply":true,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}');
INSERT INTO users_groups(`name`,`permissions`,`is_banned`) VALUES ('Banned','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":false,"EditTopic":false,"DeleteTopic":false,"CreateReply":false,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}',1);
INSERT INTO users_groups(`name`,`permissions`,`is_banned`) VALUES ('Awaiting Activation','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":false,"EditTopic":false,"DeleteTopic":false,"CreateReply":false,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}');
INSERT INTO users_groups(`name`,`permissions`,`active`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewIPs":true,"ViewTopic":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}',1,1,1,"Admin");
INSERT INTO users_groups(`name`,`permissions`,`is_mod`,`tag`) VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":false,"EditUser":true,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":true,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManageThemes":false,"ManagePlugins":false,"ViewIPs":true,"ViewTopic":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}',1,"Mod");
INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManageThemes":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":true,"EditTopic":false,"DeleteTopic":false,"CreateReply":true,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}');
INSERT INTO users_groups(`name`,`permissions`,`is_banned`) VALUES ('Banned','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManageThemes":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":false,"EditTopic":false,"DeleteTopic":false,"CreateReply":false,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}',1);
INSERT INTO users_groups(`name`,`permissions`,`is_banned`) VALUES ('Awaiting Activation','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManageThemes":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":false,"EditTopic":false,"DeleteTopic":false,"CreateReply":false,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}');
INSERT INTO forums(`name`,`lastTopicTime`) VALUES ('General',NOW());
INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`)

View File

@ -0,0 +1,19 @@
{
"Name": "tempra-simple",
"FriendlyName": "Tempra Simple",
"Version": "0.0.1",
"Creator": "Azareal",
"Settings": {
"PostLayout": {
"FriendlyName":"Post Layout",
"Options": ["Compact","Alternate"]
}
},
"Templates": [
{
"Name": "topic",
"Source": "topic_alt",
"When": "PostLayout=Alternate"
}
]
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" ?>
<theme>
<name>tempra-simple</name>
<friendlyName>Tempra Simple</friendlyName>
<version>0.0.1</version>
<creator url="http://github.com/Azareal">Azareal</creator>
<settings>
<name>PostLayout</name>
<friendlyName>Post Layout</name>
<options>
<option>Compact</option>
<option>Alternate</option>
</options>
</settings>
<templates>
<template name="topic" src="topic_alt" when="PostLayout=Alternate"></template>
</templates>
</theme>

View File

@ -1,5 +1,6 @@
/* Copyright Azareal 2016 - 2017 */
package main
import "log"
var plugins map[string]Plugin = make(map[string]Plugin)
var hooks map[string]func(interface{})interface{} = make(map[string]func(interface{})interface{})
@ -24,6 +25,16 @@ type Plugin struct
hooks[name] = handler
}*/
func init_plugins() {
for name, body := range plugins {
log.Print("Added plugin " + name)
if body.Active {
log.Print("Initialised plugin " + name)
plugins[name].Init()
}
}
}
func add_hook(name string, handler interface{}) {
switch h := handler.(type) {
case func(interface{})interface{}:

View File

@ -31,8 +31,8 @@ func BenchmarkTopicTemplate(b *testing.B) {
replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""})
replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""})
tpage := TopicPage{"Topic Blah","topic",user,noticeList,replyList,topic,false}
tpage2 := TopicPage{"Topic Blah","topic",admin,noticeList,replyList,topic,false}
tpage := TopicPage{"Topic Blah",user,noticeList,replyList,topic,false}
tpage2 := TopicPage{"Topic Blah",admin,noticeList,replyList,topic,false}
w := ioutil.Discard
b.Run("compiled_useradmin", func(b *testing.B) {
@ -65,7 +65,7 @@ func BenchmarkTopicsTemplate(b *testing.B) {
var noticeList map[int]string = make(map[int]string)
noticeList[0] = "test"
var topicList []interface{}
var topicList []TopicUser
topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""})
topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""})
topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""})
@ -77,8 +77,8 @@ func BenchmarkTopicsTemplate(b *testing.B) {
topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""})
topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""})
tpage := Page{"Topic Blah","topic",user,noticeList,topicList,0}
tpage2 := Page{"Topic Blah","topic",admin,noticeList,topicList,0}
tpage := TopicsPage{"Topic Blah",user,noticeList,topicList,0}
tpage2 := TopicsPage{"Topic Blah",admin,noticeList,topicList,0}
w := ioutil.Discard
b.Run("compiled_useradmin", func(b *testing.B) {
@ -138,6 +138,10 @@ func BenchmarkRoute(b *testing.B) {
forums_req_admin.AddCookie(&admin_session_cookie)
forums_handler := http.HandlerFunc(route_forums)
static_w := httptest.NewRecorder()
static_req := httptest.NewRequest("get","/static/global.js",bytes.NewReader(nil))
static_handler := http.HandlerFunc(route_static)
debug = false
nogrouplog = true
@ -146,16 +150,18 @@ func BenchmarkRoute(b *testing.B) {
log.SetOutput(discard)
var err error
init_database(err);
init_database(err)
external_sites["YT"] = "https://www.youtube.com/"
hooks["trow_assign"] = nil
hooks["rrow_assign"] = nil
init_plugins()
for name, body := range plugins {
if body.Active {
plugins[name].Init()
}
b.Run("static_files", func(b *testing.B) {
for i := 0; i < b.N; i++ {
static_w.Body.Reset()
static_handler.ServeHTTP(static_w,static_req)
}
})
b.Run("topic_admin", func(b *testing.B) {
for i := 0; i < b.N; i++ {

Binary file not shown.

View File

@ -30,6 +30,7 @@ type Perms struct
EditUserGroupAdmin bool
ManageForums bool // This could be local, albeit limited for per-forum managers
EditSettings bool
ManageThemes bool
ManagePlugins bool
ViewIPs bool
@ -86,6 +87,7 @@ func init() {
EditUserGroupAdmin: true,
ManageForums: true,
EditSettings: true,
ManageThemes: true,
ManagePlugins: true,
ViewIPs: true,

BIN
images/bench_round3.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

BIN
images/panel-theme-list.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

53
main.go
View File

@ -31,52 +31,54 @@ var external_sites map[string]string = make(map[string]string)
var groups map[int]Group = make(map[int]Group)
var forums map[int]Forum = make(map[int]Forum)
var static_files map[string]SFile = make(map[string]SFile)
var ctemplates []string
var template_topic_handle func(TopicPage,io.Writer) = nil
var template_topic_origin_handle func(TopicPage,io.Writer) = nil
var template_topic_alt_handle func(TopicPage,io.Writer) = nil
var template_topics_handle func(Page,io.Writer) = nil
var template_forum_handle func(Page,io.Writer) = nil
var template_forums_handle func(Page,io.Writer) = nil
var template_topics_handle func(TopicsPage,io.Writer) = nil
var template_forum_handle func(ForumPage,io.Writer) = nil
var template_forums_handle func(ForumsPage,io.Writer) = nil
var template_profile_handle func(ProfilePage,io.Writer) = nil
func compile_templates() {
var c CTemplateSet
user := User{0,"","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""}
user := User{62,"","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""}
var noticeList map[int]string = make(map[int]string)
noticeList[0] = "test"
log.Print("Compiling the templates")
topic := TopicUser{0,"",template.HTML(""),0,false,false,"",0,"","","",no_css_tmpl,0,"","","",""}
topic := TopicUser{1,"Blah",template.HTML("Hey there!"),0,false,false,"",0,"","","",no_css_tmpl,0,"","","",""}
var replyList []Reply
replyList = append(replyList, Reply{0,0,"",template.HTML(""),0,"","",0,0,"",no_css_tmpl,0,"","","",""})
replyList = append(replyList, Reply{0,0,"",template.HTML("Yo!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""})
var varList map[string]VarItem = make(map[string]VarItem)
tpage := TopicPage{"Title","name",user,noticeList,replyList,topic,false}
tpage := TopicPage{"Title",user,noticeList,replyList,topic,false}
topic_id_tmpl := c.compile_template("topic.html","templates/","TopicPage", tpage, varList)
topic_id_alt_tmpl := c.compile_template("topic_alt.html","templates/","TopicPage", tpage, varList)
varList = make(map[string]VarItem)
ppage := ProfilePage{"Title",user,noticeList,replyList,user,false}
ppage := ProfilePage{"User 526",user,noticeList,replyList,user,false}
profile_tmpl := c.compile_template("profile.html","templates/","ProfilePage", ppage, varList)
var forumList []interface{}
var forumList []Forum
for _, forum := range forums {
if forum.Active {
forumList = append(forumList, forum)
}
}
varList = make(map[string]VarItem)
pi := Page{"Forum List","forums",user,noticeList,forumList,0}
forums_tmpl := c.compile_template("forums.html","templates/","Page", pi, varList)
forums_page := ForumsPage{"Forum List",user,noticeList,forumList,0}
forums_tmpl := c.compile_template("forums.html","templates/","ForumsPage", forums_page, varList)
var topicList []interface{}
var topicList []TopicUser
topicList = append(topicList, TopicUser{1,"Topic Title","The topic content.",1,false,false,"",1,"open","Admin","","",0,"","","",""})
pi = Page{"Topic List","topics",user,noticeList,topicList,""}
topics_tmpl := c.compile_template("topics.html","templates/","Page", pi, varList)
topics_page := TopicsPage{"Topic List",user,noticeList,topicList,""}
topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList)
//topics_tmpl := c.compile_template("topics.html","templates/","Page", pi, varList)
pi = Page{"General Forum","forum",user,noticeList,topicList,"There aren't any topics in this forum yet."}
forum_tmpl := c.compile_template("forum.html","templates/","Page", pi, varList)
forum_page := ForumPage{"General Forum",user,noticeList,topicList,"There aren't any topics in this forum yet."}
forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList)
log.Print("Writing the templates")
write_template("topic", topic_id_tmpl)
@ -102,8 +104,9 @@ func write_template(name string, content string) {
}
func main(){
init_themes()
var err error
init_database(err);
init_database(err)
compile_templates()
log.Print("Loading the static files.")
@ -132,19 +135,11 @@ func main(){
hooks["rrow_assign"] = nil
templates.ParseGlob("pages/*")
for name, body := range plugins {
log.Print("Added plugin " + name)
if body.Active {
log.Print("Initialised plugin " + name)
plugins[name].Init()
}
}
init_plugins()
// In a directory to stop it clashing with the other paths
http.HandleFunc("/static/", route_static)
//http.HandleFunc("/static/", route_fstatic)
//fs_p := http.FileServer(http.Dir("./public"))
//http.Handle("/static/", http.StripPrefix("/static/",fs_p))
fs_u := http.FileServer(http.Dir("./uploads"))
http.Handle("/uploads/", http.StripPrefix("/uploads/",fs_u))
@ -205,6 +200,8 @@ func main(){
http.HandleFunc("/panel/settings/", route_panel_settings)
http.HandleFunc("/panel/settings/edit/", route_panel_setting)
http.HandleFunc("/panel/settings/edit/submit/", route_panel_setting_edit)
http.HandleFunc("/panel/themes/", route_panel_themes)
http.HandleFunc("/panel/themes/default/", route_panel_themes_default)
http.HandleFunc("/panel/plugins/", route_panel_plugins)
http.HandleFunc("/panel/plugins/activate/", route_panel_plugins_activate)
http.HandleFunc("/panel/plugins/deactivate/", route_panel_plugins_deactivate)

View File

@ -42,8 +42,6 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
var is_closed bool
if topic_status == "closed" {
is_closed = true
} else {
is_closed = false
}
topic_content := html.EscapeString(r.PostFormValue("topic_content"))
@ -910,7 +908,6 @@ func route_panel_plugins_activate(w http.ResponseWriter, r *http.Request){
NoPermissions(w,r,user)
return
}
if r.FormValue("session") != user.Session {
SecurityError(w,r,user)
return
@ -1243,3 +1240,95 @@ func route_panel_groups(w http.ResponseWriter, r *http.Request){
pi := Page{"Group Manager","panel-groups",user,noticeList,groupList,0}
templates.ExecuteTemplate(w,"panel-groups.html", pi)
}
func route_panel_themes(w http.ResponseWriter, r *http.Request){
user, noticeList, ok := SessionCheck(w,r)
if !ok {
return
}
if !user.Is_Super_Mod || !user.Perms.ManageThemes {
NoPermissions(w,r,user)
return
}
var themeList []interface{}
for _, theme := range themes {
themeList = append(themeList, theme)
}
pi := Page{"Theme Manager","panel-themes",user,noticeList,themeList,0}
templates.ExecuteTemplate(w,"panel-themes.html", pi)
}
func route_panel_themes_default(w http.ResponseWriter, r *http.Request){
user, ok := SimpleSessionCheck(w,r)
if !ok {
return
}
if !user.Is_Super_Mod || !user.Perms.ManageThemes {
NoPermissions(w,r,user)
return
}
if r.FormValue("session") != user.Session {
SecurityError(w,r,user)
return
}
uname := r.URL.Path[len("/panel/themes/default/"):]
theme, ok := themes[uname]
if !ok {
LocalError("The theme isn't registered in the system",w,r,user)
return
}
var isDefault bool
err := db.QueryRow("SELECT `default` from `themes` where `uname` = ?", uname).Scan(&isDefault)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r,user)
return
}
has_theme := err != sql.ErrNoRows
if has_theme {
if isDefault {
LocalError("The theme is already active",w,r,user)
return
}
_, err = update_theme_stmt.Exec(1, uname)
if err != nil {
InternalError(err,w,r,user)
return
}
} else {
_, err := add_theme_stmt.Exec(uname,1)
if err != nil {
InternalError(err,w,r,user)
return
}
}
_, err = update_theme_stmt.Exec(0, defaultTheme)
if err != nil {
InternalError(err,w,r,user)
return
}
log.Print("Setting theme '" + theme.Name + "' as the default theme")
theme.Active = true
themes[uname] = theme
dTheme, ok := themes[defaultTheme]
if !ok {
log.Fatal("The default theme is missing")
return
}
dTheme.Active = false
themes[defaultTheme] = dTheme
defaultTheme = uname
reset_template_overrides()
add_theme_static_files(uname)
map_theme_templates(theme)
http.Redirect(w,r,"/panel/themes/",http.StatusSeeOther)
}

View File

@ -42,6 +42,8 @@ var update_setting_stmt *sql.Stmt
var add_plugin_stmt *sql.Stmt
var update_plugin_stmt *sql.Stmt
var update_user_stmt *sql.Stmt
var add_theme_stmt *sql.Stmt
var update_theme_stmt *sql.Stmt
func init_database(err error) {
if(dbpassword != ""){
@ -253,6 +255,18 @@ func init_database(err error) {
log.Fatal(err)
}
log.Print("Preparing add_theme statement.")
add_theme_stmt, err = db.Prepare("INSERT INTO `themes`(`uname`,`default`) VALUES(?,?)")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing update_theme statement.")
update_theme_stmt, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing update_user statement.")
update_user_stmt, err = db.Prepare("UPDATE `users` SET `name` = ?, `email` = ?, `group` = ? WHERE `uid` = ?")
if err != nil {
@ -381,4 +395,42 @@ func init_database(err error) {
if err != nil {
log.Fatal(err)
}
log.Print("Loading the themes.")
rows, err = db.Query("SELECT `uname`, `default` FROM `themes`")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var defaultThemeSwitch bool
for rows.Next() {
err := rows.Scan(&uname, &defaultThemeSwitch)
if err != nil {
log.Fatal(err)
}
// Was the theme deleted at some point?
theme, ok := themes[uname]
if !ok {
continue
}
if defaultThemeSwitch {
log.Print("Loading the theme '" + theme.Name + "'")
theme.Active = true
defaultTheme = uname
add_theme_static_files(uname)
map_theme_templates(theme)
} else {
theme.Active = false
}
themes[uname] = theme
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}

View File

@ -15,7 +15,6 @@ type Page struct
type TopicPage struct
{
Title string
Name string
CurrentUser User
NoticeList map[int]string
ItemList []Reply
@ -23,6 +22,33 @@ type TopicPage struct
ExtData interface{}
}
type TopicsPage struct
{
Title string
CurrentUser User
NoticeList map[int]string
ItemList []TopicUser
ExtData interface{}
}
type ForumPage struct
{
Title string
CurrentUser User
NoticeList map[int]string
ItemList []TopicUser
ExtData interface{}
}
type ForumsPage struct
{
Title string
CurrentUser User
NoticeList map[int]string
ItemList []Forum
ExtData interface{}
}
type ProfilePage struct
{
Title string
@ -36,7 +62,6 @@ type ProfilePage struct
type PageSimple struct
{
Title string
Name string
Something interface{}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

108
routes.go
View File

@ -24,18 +24,27 @@ var nList map[int]string
// GET functions
func route_static(w http.ResponseWriter, r *http.Request){
//name := r.URL.Path[len("/static/"):]
if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && static_files[r.URL.Path].Info.ModTime().Before(t.Add(1*time.Second)) {
//log.Print("Outputting static file '" + r.URL.Path + "'")
file, ok := static_files[r.URL.Path]
if !ok {
w.WriteHeader(http.StatusNotFound)
return
}
// Surely, there's a more efficient way of doing this?
if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && file.Info.ModTime().Before(t.Add(1 * time.Second)) {
w.WriteHeader(http.StatusNotModified)
return
}
h := w.Header()
h.Set("Last-Modified", static_files[r.URL.Path].FormattedModTime)
h.Set("Content-Type", static_files[r.URL.Path].Mimetype)
h.Set("Content-Length", strconv.FormatInt(static_files[r.URL.Path].Length, 10))
//http.ServeContent(w,r,r.URL.Path,static_files[r.URL.Path].Info.ModTime(),static_files[r.URL.Path])
//w.Write(static_files[r.URL.Path].Data)
io.Copy(w, bytes.NewReader(static_files[r.URL.Path].Data))
//io.CopyN(w, bytes.NewReader(static_files[r.URL.Path].Data), static_files[r.URL.Path].Length)
h.Set("Last-Modified", file.FormattedModTime)
h.Set("Content-Type", file.Mimetype)
h.Set("Content-Length", strconv.FormatInt(file.Length, 10)) // Avoid doing a type conversion every time?
//http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file)
//w.Write(file.Data)
io.Copy(w, bytes.NewReader(file.Data)) // Use w.Write instead?
//io.CopyN(w, bytes.NewReader(file.Data), static_files[r.URL.Path].Length)
}
func route_fstatic(w http.ResponseWriter, r *http.Request){
@ -83,21 +92,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){
return
}*/
var(
topicList []interface{}
tid int
title string
content string
createdBy int
is_closed bool
sticky bool
createdAt string
parentID int
status string
name string
avatar string
)
var topicList []TopicUser
rows, err := get_topic_list_stmt.Query()
if err != nil {
InternalError(err,w,r,user)
@ -105,27 +100,27 @@ func route_topics(w http.ResponseWriter, r *http.Request){
}
defer rows.Close()
topicItem := TopicUser{ID: 0,}
for rows.Next() {
err := rows.Scan(&tid, &title, &content, &createdBy, &is_closed, &sticky, &createdAt, &parentID, &name, &avatar)
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.ParentID, &topicItem.CreatedByName, &topicItem.Avatar)
if err != nil {
InternalError(err,w,r,user)
return
}
if is_closed {
status = "closed"
if topicItem.Is_Closed {
topicItem.Status = "shut"
} else {
status = "open"
topicItem.Status = "open"
}
if avatar != "" {
if avatar[0] == '.' {
avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar
if topicItem.Avatar != "" {
if topicItem.Avatar[0] == '.' {
topicItem.Avatar = "/uploads/avatar_" + strconv.Itoa(topicItem.CreatedBy) + topicItem.Avatar
}
} else {
avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(createdBy),1)
topicItem.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topicItem.CreatedBy),1)
}
topicItem := TopicUser{tid,title,content,createdBy,is_closed,sticky, createdAt,parentID,status,name,avatar,"",0,"","","",""}
if hooks["trow_assign"] != nil {
topicItem = run_hook("trow_assign", topicItem).(TopicUser)
}
@ -137,7 +132,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){
return
}
pi := Page{"Topic List","topics",user,noticeList,topicList,0}
pi := TopicsPage{"Topic List",user,noticeList,topicList,0}
if template_topics_handle != nil {
template_topics_handle(pi,w)
} else {
@ -154,20 +149,7 @@ func route_forum(w http.ResponseWriter, r *http.Request){
return
}
var(
topicList []interface{}
tid int
title string
content string
createdBy int
is_closed bool
sticky bool
createdAt string
parentID int
status string
name string
avatar string
)
var topicList []TopicUser
fid, err := strconv.Atoi(r.URL.Path[len("/forum/"):])
if err != nil {
@ -192,27 +174,27 @@ func route_forum(w http.ResponseWriter, r *http.Request){
}
defer rows.Close()
topicItem := TopicUser{ID: 0}
for rows.Next() {
err := rows.Scan(&tid, &title, &content, &createdBy, &is_closed, &sticky, &createdAt, &parentID, &name, &avatar)
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.ParentID, &topicItem.CreatedByName, &topicItem.Avatar)
if err != nil {
InternalError(err,w,r,user)
return
}
if is_closed {
status = "closed"
if topicItem.Is_Closed {
topicItem.Status = "shut"
} else {
status = "open"
topicItem.Status = "open"
}
if avatar != "" {
if avatar[0] == '.' {
avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar
if topicItem.Avatar != "" {
if topicItem.Avatar[0] == '.' {
topicItem.Avatar = "/uploads/avatar_" + strconv.Itoa(topicItem.CreatedBy) + topicItem.Avatar
}
} else {
avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(createdBy),1)
topicItem.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topicItem.CreatedBy),1)
}
topicItem := TopicUser{tid,title,content,createdBy,is_closed,sticky,createdAt,parentID,status,name,avatar,"",0,"","","",""}
if hooks["trow_assign"] != nil {
topicItem = run_hook("trow_assign", topicItem).(TopicUser)
}
@ -224,7 +206,7 @@ func route_forum(w http.ResponseWriter, r *http.Request){
return
}
pi := Page{forums[fid].Name,"forum",user,noticeList,topicList,0}
pi := ForumPage{forums[fid].Name,user,noticeList,topicList,0}
if template_forum_handle != nil {
template_forum_handle(pi,w)
} else {
@ -241,14 +223,14 @@ func route_forums(w http.ResponseWriter, r *http.Request){
return
}
var forumList []interface{}
var forumList []Forum
for _, forum := range forums {
if forum.Active {
forumList = append(forumList, forum)
}
}
pi := Page{"Forum List","forums",user,noticeList,forumList,0}
pi := ForumsPage{"Forum List",user,noticeList,forumList,0}
if template_forums_handle != nil {
template_forums_handle(pi,w)
} else {
@ -315,7 +297,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
topic.ContentLines = strings.Count(content,"\n")
if topic.Is_Closed {
topic.Status = "closed"
topic.Status = "shut"
// We don't want users posting in locked topics...
if !user.Is_Mod {
@ -407,9 +389,9 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
return
}
tpage := TopicPage{topic.Title,"topic",user,noticeList,replyList,topic,0}
if template_topic_handle != nil { //if template_topic_alt_handle != nil {
template_topic_handle(tpage,w) //template_topic_alt_handle(tpage,w)
tpage := TopicPage{topic.Title,user,noticeList,replyList,topic,0}
if template_topic_handle != nil {
template_topic_handle(tpage,w)
} else {
err = templates.ExecuteTemplate(w,"topic.html", tpage)
if err != nil {

View File

@ -1,12 +1,17 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "io"
import "strconv"
func init() {
template_forum_handle = template_forum
template_forum_handle = template_forum
//o_template_forum_handle = template_forum
ctemplates = append(ctemplates,"forum")
tmpl_ptr_map["forum"] = &template_forum_handle
tmpl_ptr_map["o_forum"] = template_forum
}
func template_forum(tmpl_forum_vars Page, w io.Writer) {
func template_forum(tmpl_forum_vars ForumPage, w io.Writer) {
w.Write([]byte(`<!doctype html>
<html lang="en">
<head>
@ -68,20 +73,20 @@ w.Write([]byte(`
if len(tmpl_forum_vars.ItemList) != 0 {
for _, item := range tmpl_forum_vars.ItemList {
w.Write([]byte(`<div class="rowitem passive" style="`))
if item.(TopicUser).Avatar != "" {
w.Write([]byte(`background-image: url(` + item.(TopicUser).Avatar + `);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`))
if item.Avatar != "" {
w.Write([]byte(`background-image: url(` + item.Avatar + `);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`))
}
if item.(TopicUser).Sticky {
if item.Sticky {
w.Write([]byte(`background-color: #FFFFCC;`))
} else {
if item.(TopicUser).Is_Closed {
if item.Is_Closed {
w.Write([]byte(`background-color: #eaeaea;`))
}
}
w.Write([]byte(`">
<a href="/topic/` + strconv.Itoa(item.(TopicUser).ID) + `">` + item.(TopicUser).Title + `</a> `))
if item.(TopicUser).Is_Closed {
w.Write([]byte(`<span class="username topic_status_e topic_status_closed" style="float: right;">closed</span>
<a href="/topic/` + strconv.Itoa(item.ID) + `">` + item.Title + `</a> `))
if item.Is_Closed {
w.Write([]byte(`<span class="username topic_status_e topic_status_closed" style="float: right;">shut</span>
`))
} else {
w.Write([]byte(`<span class="username hide_on_micro topic_status_e topic_status_open" style="float: right;">open</span>`))

View File

@ -1,12 +1,17 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "io"
import "strconv"
func init() {
template_forums_handle = template_forums
template_forums_handle = template_forums
//o_template_forums_handle = template_forums
ctemplates = append(ctemplates,"forums")
tmpl_ptr_map["forums"] = &template_forums_handle
tmpl_ptr_map["o_forums"] = template_forums
}
func template_forums(tmpl_forums_vars Page, w io.Writer) {
func template_forums(tmpl_forums_vars ForumsPage, w io.Writer) {
w.Write([]byte(`<!doctype html>
<html lang="en">
<head>
@ -65,8 +70,8 @@ w.Write([]byte(`
if len(tmpl_forums_vars.ItemList) != 0 {
for _, item := range tmpl_forums_vars.ItemList {
w.Write([]byte(`<div class="rowitem">
<a href="/forum/` + strconv.Itoa(item.(Forum).ID) + `" style="font-size: 20px;position:relative;top: -2px;font-weight: normal;text-transform: none;">` + item.(Forum).Name + `</a>
<a href="/topic/` + strconv.Itoa(item.(Forum).LastTopicID) + `" style="font-weight: normal;text-transform: none;float: right;">` + item.(Forum).LastTopic + ` <small style="font-size: 12px;">` + item.(Forum).LastTopicTime + `</small></a>
<a href="/forum/` + strconv.Itoa(item.ID) + `" style="font-size: 20px;position:relative;top: -2px;font-weight: normal;text-transform: none;">` + item.Name + `</a>
<a href="/topic/` + strconv.Itoa(item.LastTopicID) + `" style="font-weight: normal;text-transform: none;float: right;">` + item.LastTopic + ` <small style="font-size: 12px;">` + item.LastTopicTime + `</small></a>
</div>
`))
}

View File

@ -1,9 +1,14 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "strconv"
import "io"
import "strconv"
func init() {
template_profile_handle = template_profile
template_profile_handle = template_profile
//o_template_profile_handle = template_profile
ctemplates = append(ctemplates,"profile")
tmpl_ptr_map["profile"] = &template_profile_handle
tmpl_ptr_map["o_profile"] = template_profile
}
func template_profile(tmpl_profile_vars ProfilePage, w io.Writer) {

View File

@ -1,10 +1,15 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "io"
import "strconv"
import "html/template"
func init() {
template_topic_handle = template_topic
template_topic_handle = template_topic
//o_template_topic_handle = template_topic
ctemplates = append(ctemplates,"topic")
tmpl_ptr_map["topic"] = &template_topic_handle
tmpl_ptr_map["o_topic"] = template_topic
}
func template_topic(tmpl_topic_vars TopicPage, w io.Writer) {
@ -91,7 +96,7 @@ w.Write([]byte(`
<input class='show_on_edit topic_name_input' name="topic_name" value='` + tmpl_topic_vars.Topic.Title + `' type="text" />
<select name="topic_status" class='show_on_edit topic_status_input' style='float: right;'>
<option>open</option>
<option>closed</option>
<option>shut</option>
</select>
<button name="topic-button" class="formbutton show_on_edit submit_edit">Update</button>
`))

View File

@ -1,10 +1,15 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "strconv"
import "html/template"
import "io"
import "strconv"
func init() {
template_topic_alt_handle = template_topic_alt
template_topic_alt_handle = template_topic_alt
//o_template_topic_alt_handle = template_topic_alt
ctemplates = append(ctemplates,"topic_alt")
tmpl_ptr_map["topic_alt"] = &template_topic_alt_handle
tmpl_ptr_map["o_topic_alt"] = template_topic_alt
}
func template_topic_alt(tmpl_topic_alt_vars TopicPage, w io.Writer) {
@ -63,15 +68,17 @@ w.Write([]byte(`<div class="alert">` + item + `</div>`))
w.Write([]byte(`
<div class="rowblock">
<form action='/topic/edit/submit/` + strconv.Itoa(tmpl_topic_alt_vars.Topic.ID) + `' method="post">
<div class="rowitem"`))
<div class="rowitem" style="`))
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write([]byte(` style="background-color: #FFFFEA;"`))
w.Write([]byte(`background-color: #FFFFEA;background: linear-gradient(to bottom, hsl(60, 70%, 96%), hsl(60, 70%, 89%)), url('/static/fabric-base-simple-alpha.png');`))
} else {
if tmpl_topic_alt_vars.Topic.Is_Closed {
w.Write([]byte(` style="background-color: #eaeaea;"`))
w.Write([]byte(`background-color: #eaeaea;background: linear-gradient(to bottom, #eaeaea, hsl(0,0%,79%));`))
} else {
w.Write([]byte(`background: linear-gradient(to bottom, white, hsl(0, 0%, 93%));`))
}
}
w.Write([]byte(`>
w.Write([]byte(`">
<a class='topic_name hide_on_edit'>` + tmpl_topic_alt_vars.Topic.Title + `</a>
<span class='username hide_on_micro topic_status_e topic_status_` + tmpl_topic_alt_vars.Topic.Status + ` hide_on_edit' style="font-weight:normal;float: right;">` + tmpl_topic_alt_vars.Topic.Status + `</span>
<span class="username hide_on_micro" style="border-right: 0;font-weight: normal;float: right;">Status</span>
@ -91,7 +98,7 @@ w.Write([]byte(`
<input class='show_on_edit topic_name_input' name="topic_name" value='` + tmpl_topic_alt_vars.Topic.Title + `' type="text" />
<select name="topic_status" class='show_on_edit topic_status_input' style='float: right;'>
<option>open</option>
<option>closed</option>
<option>shut</option>
</select>
<button name="topic-button" class="formbutton show_on_edit submit_edit">Update</button>
`))
@ -104,7 +111,7 @@ w.Write([]byte(`
<style type="text/css">.rowitem:last-child .content_container { margin-bottom: 5px !important; }</style>
<div class="rowblock post_container" style="border-top: none;">
<div class="rowitem passive deletable_block editable_parent post_item" style="background-color: #eaeaea;padding-top: 4px;padding-left: 5px;clear: both;border-bottom: none;padding-right: 4px;padding-bottom: 2px;">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;position: sticky;top: 4px;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="avatar_item" style="background-image: url(` + tmpl_topic_alt_vars.Topic.Avatar + `), url(/static/white-dot.jpg);background-position: 0px -10px;background-repeat: no-repeat, repeat-y;background-size: 128px;width: 128px;height: 100%;min-height: 128px;border-style: solid;border-color: #eaeaea;border-width: 1px;">&nbsp;</div>
<div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">` + tmpl_topic_alt_vars.Topic.CreatedByName + `</div>
</div>
@ -118,7 +125,7 @@ if len(tmpl_topic_alt_vars.ItemList) != 0 {
for _, item := range tmpl_topic_alt_vars.ItemList {
w.Write([]byte(`
<div class="rowitem passive deletable_block editable_parent post_item" style="background-color: #eaeaea;padding-top: 4px;padding-left: 5px;clear: both;border-bottom: none;padding-right: 4px;padding-bottom: 2px;">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;position: sticky;top: 4px;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="avatar_item" style="background-image: url(` + item.Avatar + `), url(/static/white-dot.jpg);background-position: 0px -10px;background-repeat: no-repeat, repeat-y;background-size: 128px;width: 128px;height: 100%;min-height: 128px;border-style: solid;border-color: #eaeaea;border-width: 1px;">&nbsp;</div>
<div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">` + item.CreatedByName + `</div>
</div>
@ -146,7 +153,7 @@ w.Write([]byte(`</div>
`))
if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
w.Write([]byte(`
<div class="rowblock">
<div class="rowblock" style="border-top: none;">
<form action="/reply/create/" method="post">
<input name="tid" value='` + strconv.Itoa(tmpl_topic_alt_vars.Topic.ID) + `' type="hidden" />
<div class="formrow">

View File

@ -1,12 +1,17 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "io"
import "strconv"
func init() {
template_topics_handle = template_topics
template_topics_handle = template_topics
//o_template_topics_handle = template_topics
ctemplates = append(ctemplates,"topics")
tmpl_ptr_map["topics"] = &template_topics_handle
tmpl_ptr_map["o_topics"] = template_topics
}
func template_topics(tmpl_topics_vars Page, w io.Writer) {
func template_topics(tmpl_topics_vars TopicsPage, w io.Writer) {
w.Write([]byte(`<!doctype html>
<html lang="en">
<head>
@ -68,20 +73,20 @@ w.Write([]byte(`
if len(tmpl_topics_vars.ItemList) != 0 {
for _, item := range tmpl_topics_vars.ItemList {
w.Write([]byte(`<div class="rowitem passive" style="`))
if item.(TopicUser).Avatar != "" {
w.Write([]byte(`background-image: url(` + item.(TopicUser).Avatar + `);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`))
if item.Avatar != "" {
w.Write([]byte(`background-image: url(` + item.Avatar + `);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`))
}
if item.(TopicUser).Sticky {
if item.Sticky {
w.Write([]byte(`background-color: #FFFFCC;`))
} else {
if item.(TopicUser).Is_Closed {
if item.Is_Closed {
w.Write([]byte(`background-color: #eaeaea;`))
}
}
w.Write([]byte(`">
<a href="/topic/` + strconv.Itoa(item.(TopicUser).ID) + `">` + item.(TopicUser).Title + `</a> `))
if item.(TopicUser).Is_Closed {
w.Write([]byte(`<span class="username topic_status_e topic_status_closed" style="float: right;">closed</span>
<a href="/topic/` + strconv.Itoa(item.ID) + `">` + item.Title + `</a> `))
if item.Is_Closed {
w.Write([]byte(`<span class="username topic_status_e topic_status_closed" style="float: right;">shut</span>
`))
} else {
w.Write([]byte(`<span class="username hide_on_micro topic_status_e topic_status_open" style="float: right;">open</span>`))

View File

@ -8,6 +8,9 @@ import "path/filepath"
import "io/ioutil"
import "text/template/parse"
var ctemplates []string
var tmpl_ptr_map map[string]interface{} = make(map[string]interface{})
type VarItem struct
{
Name string
@ -76,7 +79,6 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
if err != nil {
log.Fatal(err)
}
if debug {
fmt.Println(name)
}
@ -90,7 +92,6 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
if debug {
fmt.Println(c.tlist)
}
c.localVars = make(map[string]map[string]VarItemReflect)
c.localVars[fname] = make(map[string]VarItemReflect)
c.localVars[fname]["."] = VarItemReflect{".",varholder,holdreflect}
@ -126,11 +127,14 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
varString += "var " + varItem.Name + " " + varItem.Type + " = " + varItem.Destination + "\n"
}
out = "package main\n" + importList + c.pVarList + "\nfunc init() {\ntemplate_" + fname +"_handle = template_" + fname + "\n}\n\nfunc template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w io.Writer) {\n" + varString + out + "}\n"
fout := "/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n"
fout += "package main\n" + importList + c.pVarList + "\n"
fout += "func init() {\n\ttemplate_" + fname +"_handle = template_" + fname + "\n\t//o_template_" + fname +"_handle = template_" + fname + "\n\tctemplates = append(ctemplates,\"" + fname + "\")\n\ttmpl_ptr_map[\"" + fname + "\"] = &template_" + fname + "_handle\n\ttmpl_ptr_map[\"o_" + fname + "\"] = template_" + fname + "\n}\n\n"
fout += "func template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w io.Writer) {\n" + varString + out + "}\n"
out = strings.Replace(out,`))
fout = strings.Replace(fout,`))
w.Write([]byte(`," + ",-1)
out = strings.Replace(out,"` + `","",-1)
fout = strings.Replace(fout,"` + `","",-1)
for index, count := range c.stats {
fmt.Println(index + ": " + strconv.Itoa(count))
@ -139,9 +143,9 @@ w.Write([]byte(`," + ",-1)
if debug {
fmt.Println("Output!")
fmt.Println(out)
fmt.Println(fout)
}
return out
return fout
}
func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Value, template_name string, node interface{}) (out string) {
@ -380,7 +384,6 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
fmt.Println(n.String())
fmt.Println(n.Ident)
}
out, _ = c.compile_if_varsub(n.String(), varholder, template_name, holdreflect)
return out
case *parse.NilNode:

View File

@ -4,7 +4,7 @@
</div>
<div class="rowblock">
{{range .ItemList}}<div class="rowitem passive" style="{{ if .Avatar }}background-image: url({{ .Avatar }});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}{{ if .Sticky }}background-color: #FFFFCC;{{else if .Is_Closed}}background-color: #eaeaea;{{end}}">
<a href="/topic/{{.ID}}">{{.Title}}</a> {{if .Is_Closed}}<span class="username topic_status_e topic_status_closed" style="float: right;">closed</span>
<a href="/topic/{{.ID}}">{{.Title}}</a> {{if .Is_Closed}}<span class="username topic_status_e topic_status_closed" style="float: right;">shut</span>
{{else}}<span class="username hide_on_micro topic_status_e topic_status_open" style="float: right;">open</span>{{end}}
<span class="username hide_on_micro" style="border-right: 0;float: right;">Status</span>
</div>

View File

@ -4,9 +4,7 @@
<div class="rowitem passive"><a href="/panel/groups/">Groups</a></div>
{{if .CurrentUser.Perms.ManageForums}}<div class="rowitem passive"><a href="/panel/forums/">Forums</a></div>{{end}}
{{if .CurrentUser.Perms.EditSettings}}<div class="rowitem passive"><a href="/panel/settings/">Settings</a></div>{{end}}
{{if .CurrentUser.Perms.ManageThemes}}<div class="rowitem passive"><a href="/panel/themes/">Themes</a></div>{{end}}
{{if .CurrentUser.Perms.ManagePlugins}}<div class="rowitem passive"><a href="/panel/plugins/">Plugins</a></div>{{end}}
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a href="/forum/-1">Reported Content</a></div>
</div>

View File

@ -0,0 +1,20 @@
{{template "header.html" . }}
{{template "panel-menu.html" . }}
<style type="text/css">.rowitem::after {content:"";display:block;clear:both;}</style>
<div class="colblock_right">
<div class="rowitem"><a>Themes</a></div>
</div>
<div class="colblock_right">
{{range .ItemList}}
<div class="rowitem editable_parent" style="font-weight: normal;text-transform: none;">
<span style="float: left;">
<a href="/panel/themes/{{.Name}}" class="editable_block" style="font-size: 20px;">{{.FriendlyName}}</a><br />
<small style="margin-left: 2px;">Author: {{.Creator}}</small>
</span>
<span style="float: right;">
{{if .Active}}<span>Default</span>{{else}}<a href="/panel/themes/default/{{.Name}}?session={{$.CurrentUser.Session}}" class="username">Make Default</a>{{end}}
</span>
</div>
{{end}}
</div>
{{template "footer.html" . }}

View File

@ -13,7 +13,7 @@
<input class='show_on_edit topic_name_input' name="topic_name" value='{{.Topic.Title}}' type="text" />
<select name="topic_status" class='show_on_edit topic_status_input' style='float: right;'>
<option>open</option>
<option>closed</option>
<option>shut</option>
</select>
<button name="topic-button" class="formbutton show_on_edit submit_edit">Update</button>
{{end}}

View File

@ -1,7 +1,7 @@
{{template "header.html" . }}
<div class="rowblock">
<form action='/topic/edit/submit/{{.Topic.ID}}' method="post">
<div class="rowitem"{{ if .Topic.Sticky }} style="background-color: #FFFFEA;"{{else if .Topic.Is_Closed}} style="background-color: #eaeaea;"{{end}}>
<div class="rowitem" style="{{ if .Topic.Sticky }}background-color: #FFFFEA;background: linear-gradient(to bottom, hsl(60, 70%, 96%), hsl(60, 70%, 89%)), url('/static/fabric-base-simple-alpha.png');{{else if .Topic.Is_Closed}}background-color: #eaeaea;background: linear-gradient(to bottom, #eaeaea, hsl(0,0%,79%));{{else}}background: linear-gradient(to bottom, white, hsl(0, 0%, 93%));{{end}}">
<a class='topic_name hide_on_edit'>{{.Topic.Title}}</a>
<span class='username hide_on_micro topic_status_e topic_status_{{.Topic.Status}} hide_on_edit' style="font-weight:normal;float: right;">{{.Topic.Status}}</span>
<span class="username hide_on_micro" style="border-right: 0;font-weight: normal;float: right;">Status</span>
@ -13,7 +13,7 @@
<input class='show_on_edit topic_name_input' name="topic_name" value='{{.Topic.Title}}' type="text" />
<select name="topic_status" class='show_on_edit topic_status_input' style='float: right;'>
<option>open</option>
<option>closed</option>
<option>shut</option>
</select>
<button name="topic-button" class="formbutton show_on_edit submit_edit">Update</button>
{{end}}
@ -24,7 +24,7 @@
<style type="text/css">.rowitem:last-child .content_container { margin-bottom: 5px !important; }</style>
<div class="rowblock post_container" style="border-top: none;">
<div class="rowitem passive deletable_block editable_parent post_item" style="background-color: #eaeaea;padding-top: 4px;padding-left: 5px;clear: both;border-bottom: none;padding-right: 4px;padding-bottom: 2px;">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;position: sticky;top: 4px;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="avatar_item" style="background-image: url({{.Topic.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;background-repeat: no-repeat, repeat-y;background-size: 128px;width: 128px;height: 100%;min-height: 128px;border-style: solid;border-color: #eaeaea;border-width: 1px;">&nbsp;</div>
<div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">{{.Topic.CreatedByName}}</div>
</div>
@ -35,7 +35,7 @@
</div>
{{range .ItemList}}
<div class="rowitem passive deletable_block editable_parent post_item" style="background-color: #eaeaea;padding-top: 4px;padding-left: 5px;clear: both;border-bottom: none;padding-right: 4px;padding-bottom: 2px;">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;position: sticky;top: 4px;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="avatar_item" style="background-image: url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;background-repeat: no-repeat, repeat-y;background-size: 128px;width: 128px;height: 100%;min-height: 128px;border-style: solid;border-color: #eaeaea;border-width: 1px;">&nbsp;</div>
<div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">{{.CreatedByName}}</div>
</div>
@ -50,7 +50,7 @@
</div>
{{end}}</div>
{{if .CurrentUser.Perms.CreateReply}}
<div class="rowblock">
<div class="rowblock" style="border-top: none;">
<form action="/reply/create/" method="post">
<input name="tid" value='{{.Topic.ID}}' type="hidden" />
<div class="formrow">

View File

@ -4,7 +4,7 @@
</div>
<div class="rowblock">
{{range .ItemList}}<div class="rowitem passive" style="{{if .Avatar}}background-image: url({{.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}{{if .Sticky}}background-color: #FFFFCC;{{else if .Is_Closed}}background-color: #eaeaea;{{end}}">
<a href="/topic/{{.ID}}">{{.Title}}</a> {{if .Is_Closed}}<span class="username topic_status_e topic_status_closed" style="float: right;">closed</span>
<a href="/topic/{{.ID}}">{{.Title}}</a> {{if .Is_Closed}}<span class="username topic_status_e topic_status_closed" style="float: right;">shut</span>
{{else}}<span class="username hide_on_micro topic_status_e topic_status_open" style="float: right;">open</span>{{end}}
<span class="username hide_on_micro" style="border-right: 0;float: right;">Status</span>
</div>

319
themes.go Normal file
View File

@ -0,0 +1,319 @@
/* Copyright Azareal 2016 - 2017 */
package main
import "fmt"
import "log"
import "io"
import "os"
import "strings"
import "mime"
import "io/ioutil"
import "path/filepath"
import "encoding/json"
import "net/http"
var defaultTheme string
var themes map[string]Theme = make(map[string]Theme)
//var overriden_templates map[string]interface{} = make(map[string]interface{})
var overriden_templates map[string]bool = make(map[string]bool)
type Theme struct
{
Name string
FriendlyName string
Version string
Creator string
Settings map[string]ThemeSetting
Templates []TemplateMapping
// This variable should only be set and unset by the system, not the theme meta file
Active bool
}
type ThemeSetting struct
{
FriendlyName string
Options []string
}
type TemplateMapping struct
{
Name string
Source string
//When string
}
func init_themes() {
themeFiles, err := ioutil.ReadDir("./themes")
if err != nil {
log.Fatal(err)
}
for _, themeFile := range themeFiles {
if !themeFile.IsDir() {
continue
}
themeName := themeFile.Name()
log.Print("Adding theme '" + themeName + "'")
themeFile, err := ioutil.ReadFile("./themes/" + themeName + "/theme.json")
if err != nil {
log.Fatal(err)
}
var theme Theme
json.Unmarshal(themeFile, &theme)
theme.Active = false // Set this to false, just in case someone explicitly overrode this value in the JSON file
themes[theme.Name] = theme
}
}
func add_theme_static_files(themeName string) {
err := filepath.Walk("./themes/" + themeName + "/public", func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if f.IsDir() {
return nil
}
path = strings.Replace(path,"\\","/",-1)
log.Print("Attempting to add static file '" + path + "' for default theme '" + themeName + "'")
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
path = strings.TrimPrefix(path,"themes/" + themeName + "/public")
log.Print("Added the '" + path + "' static file for default theme " + themeName + ".")
static_files["/static" + path] = SFile{data,0,int64(len(data)),mime.TypeByExtension(filepath.Ext("/themes/" + themeName + "/public" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)}
return nil
})
if err != nil {
panic(err)
}
}
func map_theme_templates(theme Theme) {
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")
}
// go generate is one possibility, but it would simply add another step of compilation
dest_tmpl_ptr, ok := tmpl_ptr_map[themeTmpl.Name]
if !ok {
log.Fatal("The destination template doesn't exist!")
}
source_tmpl_ptr, ok := tmpl_ptr_map[themeTmpl.Source]
if !ok {
log.Fatal("The source template doesn't exist!")
}
switch d_tmpl_ptr := dest_tmpl_ptr.(type) {
case *func(TopicPage,io.Writer):
switch s_tmpl_ptr := source_tmpl_ptr.(type) {
case *func(TopicPage,io.Writer):
//overriden_templates[themeTmpl.Name] = d_tmpl_ptr
overriden_templates[themeTmpl.Name] = true
log.Print("Topic Handle")
fmt.Println(template_topic_handle)
log.Print("Before")
fmt.Println(d_tmpl_ptr)
fmt.Println(*d_tmpl_ptr)
log.Print("Source")
fmt.Println(s_tmpl_ptr)
fmt.Println(*s_tmpl_ptr)
*d_tmpl_ptr = *s_tmpl_ptr
log.Print("After")
fmt.Println(d_tmpl_ptr)
fmt.Println(*d_tmpl_ptr)
log.Print("Source")
fmt.Println(s_tmpl_ptr)
fmt.Println(*s_tmpl_ptr)
default:
log.Fatal("The source and destination templates are incompatible")
}
case *func(TopicsPage,io.Writer):
switch s_tmpl_ptr := source_tmpl_ptr.(type) {
case *func(TopicsPage,io.Writer):
//overriden_templates[themeTmpl.Name] = d_tmpl_ptr
overriden_templates[themeTmpl.Name] = true
*d_tmpl_ptr = *s_tmpl_ptr
default:
log.Fatal("The source and destination templates are incompatible")
}
case *func(ForumPage,io.Writer):
switch s_tmpl_ptr := source_tmpl_ptr.(type) {
case *func(ForumPage,io.Writer):
//overriden_templates[themeTmpl.Name] = d_tmpl_ptr
overriden_templates[themeTmpl.Name] = true
*d_tmpl_ptr = *s_tmpl_ptr
default:
log.Fatal("The source and destination templates are incompatible")
}
case *func(ForumsPage,io.Writer):
switch s_tmpl_ptr := source_tmpl_ptr.(type) {
case *func(ForumsPage,io.Writer):
//overriden_templates[themeTmpl.Name] = d_tmpl_ptr
overriden_templates[themeTmpl.Name] = true
*d_tmpl_ptr = *s_tmpl_ptr
default:
log.Fatal("The source and destination templates are incompatible")
}
case *func(ProfilePage,io.Writer):
switch s_tmpl_ptr := source_tmpl_ptr.(type) {
case *func(ProfilePage,io.Writer):
//overriden_templates[themeTmpl.Name] = d_tmpl_ptr
overriden_templates[themeTmpl.Name] = true
*d_tmpl_ptr = *s_tmpl_ptr
default:
log.Fatal("The source and destination templates are incompatible")
}
case *func(Page,io.Writer):
switch s_tmpl_ptr := source_tmpl_ptr.(type) {
case *func(Page,io.Writer):
//overriden_templates[themeTmpl.Name] = d_tmpl_ptr
overriden_templates[themeTmpl.Name] = true
*d_tmpl_ptr = *s_tmpl_ptr
default:
log.Fatal("The source and destination templates are incompatible")
}
default:
log.Fatal("Unknown destination template type!")
}
}
}
}
func reset_template_overrides() {
log.Print("Resetting the template overrides")
for name, _ := range overriden_templates {
log.Print("Resetting '" + name + "' template override")
origin_pointer, ok := tmpl_ptr_map["o_" + name]
if !ok {
//log.Fatal("The origin template doesn't exist!")
log.Print("The origin template doesn't exist!")
return
}
dest_tmpl_ptr, ok := tmpl_ptr_map[name]
if !ok {
//log.Fatal("The destination template doesn't exist!")
log.Print("The destination template doesn't exist!")
return
}
switch o_ptr := origin_pointer.(type) {
case func(TopicPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(TopicPage,io.Writer):
log.Print("Topic Handle")
fmt.Println(template_topic_handle)
log.Print("Before")
fmt.Println(d_ptr)
fmt.Println(*d_ptr)
log.Print("Origin")
fmt.Println(o_ptr)
*d_ptr = o_ptr
log.Print("After")
fmt.Println(d_ptr)
fmt.Println(*d_ptr)
log.Print("Origin")
fmt.Println(o_ptr)
default:
log.Fatal("The origin and destination templates are incompatible")
}
case *func(TopicsPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(TopicsPage,io.Writer):
*d_ptr = o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
case *func(ForumPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(ForumPage,io.Writer):
*d_ptr = o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
case *func(ForumsPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(ForumsPage,io.Writer):
*d_ptr = o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
case *func(ProfilePage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(ProfilePage,io.Writer):
*d_ptr = o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
default:
log.Fatal("Unknown destination template type!")
}
log.Print("The template override was reset")
}
overriden_templates = make(map[string]bool)
log.Print("All of the template overrides have been reset")
/*for name, origin_pointer := range overriden_templates {
dest_tmpl_ptr, ok := tmpl_ptr_map[name]
if !ok {
log.Fatal("The destination template doesn't exist!")
}
switch o_ptr := origin_pointer.(type) {
case *func(TopicPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(TopicPage,io.Writer):
*d_ptr = *o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
case *func(TopicsPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(TopicsPage,io.Writer):
*d_ptr = *o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
case *func(ForumPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(ForumPage,io.Writer):
*d_ptr = *o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
case *func(ForumsPage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(ForumsPage,io.Writer):
*d_ptr = *o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
case *func(ProfilePage,io.Writer):
switch d_ptr := dest_tmpl_ptr.(type) {
case *func(ProfilePage,io.Writer):
*d_ptr = *o_ptr
default:
log.Fatal("The origin and destination templates are incompatible")
}
default:
log.Fatal("Unknown destination template type!")
}
delete(overriden_templates, name)
}*/
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

12
themes/cosmo/theme.json Normal file
View File

@ -0,0 +1,12 @@
{
"Name": "cosmo",
"FriendlyName": "AtomBB Cosmo",
"Version": "Coming Soon",
"Creator": "Azareal",
"Templates": [
{
"Name": "topic",
"Source": "topic_alt"
}
]
}

View File

@ -0,0 +1,12 @@
{
"Name": "tempra-conflux",
"FriendlyName": "Tempra Conflux",
"Version": "0.0.1",
"Creator": "Azareal",
"Templates": [
{
"Name": "topic",
"Source": "topic_alt"
}
]
}

View File

@ -0,0 +1,409 @@
* {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
body
{
font-family: arial;
}
@font-face {
font-family: 'EmojiFont';
src: url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff2') format('woff2'),
url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff') format('woff'), local("arial");
}
@supports (-ms-ime-align:auto) {
.user_content
{
font-family: EmojiFont, arial;
}
}
@-moz-document url-prefix() {
.user_content
{
font-family: EmojiFont, arial;
}
}
/*.move_left{float: left;position: relative;left: 50%;}
.move_right{float: left;position: relative;left: -50%;}*/
ul
{
padding-left: 0px;
padding-right: 0px;
height: 28px;
list-style-type: none;
border: 1px solid #ccc;
}
li
{
height: 26px;
padding-left: 10px;
padding-top: 5px;
padding-bottom: 5px;
font-weight: bold;
text-transform: uppercase;
}
li:hover { background: rgb(250,250,250); }
li a
{
text-decoration: none;
color: #515151;
}
/*li a:hover { color: #7a7a7a; }*/
.menu_left
{
float: left;
border-right: 1px solid #ccc;
padding-right: 10px;
}
.menu_right
{
float: right;
border-left: 1px solid #ccc;
padding-right: 10px;
}
.container
{
width: 90%;
padding: 0px;
margin-left: auto;
margin-right: auto;
}
.rowblock
{
border: 1px solid #ccc;
width: 100%;
padding: 0px;
padding-top: 0px;
}
.rowblock:empty
{
display: none;
}
.colblock_left
{
border: 1px solid #ccc;
padding: 0px;
padding-top: 0px;
width: 30%;
float: left;
margin-right: 8px;
}
.colblock_right
{
border: 1px solid #ccc;
padding: 0px;
padding-top: 0px;
width: 65%;
overflow: hidden;
word-wrap: break-word;
}
.colblock_left:empty
{
display: none;
}
.colblock_right:empty
{
display: none;
}
.rowitem
{
width: 100%;
padding-left: 8px;
padding-right: 8px;
padding-top: 17px;
padding-bottom: 12px;
font-weight: bold;
text-transform: uppercase;
}
.rowitem.passive
{
font-weight: normal;
text-transform: none;
}
.rowitem:not(:last-child)
{
border-bottom: 1px dotted #ccc;
}
.rowitem a
{
text-decoration: none;
color: black;
}
.rowitem a:hover
{
color: silver;
}
.col_left
{
width: 30%;
float: left;
}
.col_right
{
width: 69%;
overflow: hidden;
}
.colitem
{
padding-left: 8px;
padding-right: 8px;
padding-top: 17px;
padding-bottom: 12px;
font-weight: bold;
text-transform: uppercase;
}
.colitem.passive
{
font-weight: normal;
text-transform: none;
}
.colitem a
{
text-decoration: none;
color: black;
}
.colitem a:hover
{
color: silver;
}
.formrow
{
/*height: 40px;*/
width: 100%;
}
/*Clearfix*/
.formrow:before,
.formrow:after {
content: " ";
display: table;
}
.formrow:after {
clear: both;
}
.formrow:not(:last-child)
{
border-bottom: 1px dotted #ccc;
}
.formitem
{
float: left;
padding-left: 8px;
padding-right: 8px;
padding-top: 13px;
padding-bottom: 8px;
font-weight: bold;
}
.formitem:first-child
{
font-weight: bold;
}
.formitem:not(:last-child)
{
border-right: 1px dotted #ccc;
}
.formitem.invisible_border
{
border: none;
}
/* Mostly for textareas */
.formitem:only-child
{
width: 97%;
}
.formitem textarea
{
width: 100%;
height: 100px;
}
.formitem:has-child()
{
margin: 0 auto;
float: none;
}
button
{
background: white;
border: 1px solid #8e8e8e;
}
/* Topics */
.topic_status
{
text-transform: none;
margin-left: 8px;
padding-left: 2px;
padding-right: 2px;
padding-top: 2px;
padding-bottom: 2px;
background-color: #E8E8E8; /* 232,232,232. All three RGB colours being the same seems to create a shade of gray */
color: #505050; /* 80,80,80 */
border-radius: 2px;
}
.topic_status:empty
{
display: none;
}
.username
{
text-transform: none;
margin-left: 0px;
padding-left: 4px;
padding-right: 4px;
padding-top: 2px;
padding-bottom: 2px;
color: #505050; /* 80,80,80 */
background-color: #FFFFFF;
border-style: dotted;
border-color: #505050; /* 232,232,232. All three RGB colours being the same seems to create a shade of gray */
border-width: 1px;
font-size: 15px;
}
button.username
{
position: relative;
top: -0.25px;
}
.tag-mini
{
text-transform: none;
margin-left: 0px;
padding-left: 3px;
padding-right: 3px;
padding-top: 1.5px;
padding-bottom: 0px;
color: #505050; /* 80,80,80 */
background-color: #FFFFFF;
border-style: dotted;
border-color: #505050; /* 232,232,232. All three RGB colours being the same seems to create a shade of gray */
border-width: 1px;
font-size: 10px;
}
.show_on_edit
{
display: none;
}
.alert
{
display: block;
padding: 5px;
margin-bottom: 10px;
border: 1px solid #ccc;
}
.alert_success
{
display: block;
padding: 5px;
border: 1px solid A2FC00;
margin-bottom: 10px;
background-color: DAF7A6;
}
.alert_error
{
display: block;
padding: 5px;
border: 1px solid #FF004B;
margin-bottom: 8px;
background-color: #FEB7CC;
}
@media (max-width: 880px) {
li
{
height: 25px;
font-size: 15px;
padding-left: 7px;
}
ul { height: 26px; margin-top: 8px; }
.menu_left { padding-right: 7px; }
.menu_right { padding-right: 7px; }
body { padding-left: 4px; padding-right: 4px; margin: 0px !important; width: 100% !important; height: 100% !important; overflow-x: hidden; }
.container { width: auto; }
}
@media (max-width: 810px) {
li
{
font-weight: normal;
text-transform: none;
}
.rowitem
{
text-transform: none;
}
}
@media (max-width: 620px) {
li
{
padding-left: 5px;
padding-top: 2px;
padding-bottom: 2px;
height: 23px;
}
ul { height: 24px; }
.menu_left { padding-right: 5px; }
.menu_right { padding-right: 5px; }
.menu_create_topic { display: none;}
.hide_on_mobile { display: none; }
}
@media (max-width: 470px) {
.menu_overview { display: none; }
.menu_profile { display: none; }
.hide_on_micro { display: none; }
.post_container {
overflow: visible !important;
}
.post_item {
background-position: 0px 2px !important;
background-size: 64px 64px !important;
padding-left: 2px !important;
min-height: 96px;
position: relative !important;
}
.post_item > .user_content {
margin-left: 75px !important;
width: 100% !important;
}
.post_item > .mod_button {
float: right !important;
margin-left: 2px !important;
position: relative;
top: -14px;
}
.post_item > .real_username {
position: absolute;
top: 70px;
float: left;
margin-top: 0px;
padding-top: 3px !important;
margin-right: 2px;
width: 60px;
font-size: 15px;
}
.container { width: 100% !important; }
}

View File

@ -0,0 +1,6 @@
{
"Name": "tempra-simple",
"FriendlyName": "Tempra Simple",
"Version": "0.0.1",
"Creator": "Azareal"
}