The Search and Filter Widget is now partly implemented. Just Search to go in the basic implementation.
Added AJAX Pagination for the Topic List and Forum Page. A new log file pair is now created every-time Gosora starts up. Added proper per-theme template overrides. Added EasyJSON to make JSON serialisation faster. Moved a bit of boilerplate into paginator.html Improved paginator.html with a richer template with first, last and symbols instead of text. Phased out direct access to Templates.ExecuteTemplate across the software. Fixed the Live Topic List so it should work again. Added MicroAvatar to WsJSONUser for topic list JSON requests. An instance of the plugin is now passed to plugin handlers rather than having the plugins manipulate the globals directly. Added the pre_render_panel_forum_edit and pre_render_panel_forum_edit_perms hooks to replace pre_render_panel_edit_forum. Renamed the pre_render_panel_edit_user hook to pre_render_panel_user_edit Reduced the amount of noise from fsnotify. Added RawPrepare() to qgen.Accumulator. Added a temporary phrase whitelist to the phrase endpoint. Moved the location of the zone data assignments in the topic list to reduce the chances of security issues in the future. Changed the signature of routes/panel/renderTemplate() requiring some changes across the panel routes. Removed bits of boilerplate in some of the panel routes with renderTemplate() Added a BenchmarkTopicsGuestJSRouteParallelWithRouter benchmark. Removed a fair bit of boilerplate for each page struct by generating a couple of interface casts for each template file instead. Added the profile_comments_row_alt template. Added the topics_quick_topic template to reuse part of the quick topic logic for both the topic list and forum page. Tweaked the CSS for the Online Users Widget. Tweaked the CSS for Widgets in every theme with a sidebar. Refactored the template initialisers to hopefully reduce the amount of boilerplate and make things easier to maintain and follow. Add genIntTmpl in the template initialiser file to reduce the amount of boilerplate needed for the fallback template bindings. Removed the topics_head phrase. Moved the paginator_ phrases into the paginator. namespace and renamed them accordingly. Added the paginator.first_page phrase. Added the paginator.first_page_aria phrase. Added the paginator.last_page phrase. Added the paginator.last_page_aria phrase. Added the panel_forum_delete_are_you_sure phrase. Fixed a data race in LogWarning()
This commit is contained in:
parent
6a8ad887cc
commit
d9acf27c5b
|
@ -14,6 +14,9 @@ go build -o QueryGen "./cmd/query_gen"
|
|||
echo "Running the query generator"
|
||||
./QueryGen
|
||||
|
||||
echo "Generating the JSON handlers"
|
||||
easyjson -pkg common
|
||||
|
||||
echo "Building Gosora"
|
||||
go generate
|
||||
go build -o Gosora
|
||||
|
|
|
@ -20,6 +20,9 @@ cd ../..
|
|||
echo "Running the query generator"
|
||||
./QueryGen
|
||||
|
||||
echo "Generating the JSON handlers"
|
||||
easyjson -pkg common
|
||||
|
||||
echo "Building Gosora"
|
||||
go generate
|
||||
go build -o Gosora -tags no_ws
|
||||
|
|
|
@ -12,6 +12,9 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Generating the JSON handlers
|
||||
easyjson -pkg common
|
||||
|
||||
echo Building the executable
|
||||
go build -o gosora.exe -tags no_ws
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
|
@ -14,6 +14,9 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Generating the JSON handlers
|
||||
easyjson -pkg common
|
||||
|
||||
echo Building the executable
|
||||
go build -o gosora.exe
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
|
@ -118,10 +118,10 @@ func LogWarning(err error, extra ...string) {
|
|||
} else {
|
||||
errmsg += err.Error()
|
||||
}
|
||||
stack := debug.Stack()
|
||||
log.Print(errmsg+"\n", string(stack))
|
||||
errorBufferMutex.Lock()
|
||||
defer errorBufferMutex.Unlock()
|
||||
stack := debug.Stack() // debug.Stack() can't be executed concurrently, so we'll guard this with a mutex too
|
||||
log.Print(errmsg+"\n", string(stack))
|
||||
errorBuffer = append(errorBuffer, ErrorItem{err, stack})
|
||||
}
|
||||
|
||||
|
@ -304,7 +304,7 @@ func SecurityError(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
if RunPreRenderHook("pre_render_security_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
err := pi.Header.Theme.RunTmpl("error", pi, w)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
|
|
|
@ -237,10 +237,11 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User
|
|||
"pre_render_ban": nil,
|
||||
"pre_render_ip_search": nil,
|
||||
|
||||
"pre_render_panel_dashboard": nil,
|
||||
"pre_render_panel_forums": nil,
|
||||
"pre_render_panel_delete_forum": nil,
|
||||
"pre_render_panel_edit_forum": nil,
|
||||
"pre_render_panel_dashboard": nil,
|
||||
"pre_render_panel_forums": nil,
|
||||
"pre_render_panel_delete_forum": nil,
|
||||
"pre_render_panel_forum_edit": nil,
|
||||
"pre_render_panel_forum_edit_perms": nil,
|
||||
|
||||
"pre_render_panel_analytics_views": nil,
|
||||
"pre_render_panel_analytics_routes": nil,
|
||||
|
@ -258,7 +259,7 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User
|
|||
"pre_render_panel_word_filters_edit": nil,
|
||||
"pre_render_panel_plugins": nil,
|
||||
"pre_render_panel_users": nil,
|
||||
"pre_render_panel_edit_user": nil,
|
||||
"pre_render_panel_user_edit": nil,
|
||||
"pre_render_panel_groups": nil,
|
||||
"pre_render_panel_group_edit": nil,
|
||||
"pre_render_panel_group_edit_perms": nil,
|
||||
|
@ -284,11 +285,11 @@ type Plugin struct {
|
|||
Installable bool
|
||||
Installed bool
|
||||
|
||||
Init func() error
|
||||
Activate func() error
|
||||
Deactivate func() // TODO: We might want to let this return an error?
|
||||
Install func() error
|
||||
Uninstall func() error // TODO: I'm not sure uninstall is implemented
|
||||
Init func(plugin *Plugin) error
|
||||
Activate func(plugin *Plugin) error
|
||||
Deactivate func(plugin *Plugin) // TODO: We might want to let this return an error?
|
||||
Install func(plugin *Plugin) error
|
||||
Uninstall func(plugin *Plugin) error // TODO: I'm not sure uninstall is implemented
|
||||
|
||||
Hooks map[string]int
|
||||
Data interface{} // Usually used for hosting the VMs / reusable elements of non-native plugins
|
||||
|
@ -511,8 +512,8 @@ func InitPlugins() {
|
|||
log.Printf("Added plugin '%s'", name)
|
||||
if body.Active {
|
||||
log.Printf("Initialised plugin '%s'", name)
|
||||
if Plugins[name].Init != nil {
|
||||
err := Plugins[name].Init()
|
||||
if body.Init != nil {
|
||||
err := body.Init(body)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
|
|
@ -40,19 +40,20 @@ type CSSData struct {
|
|||
func (list SFileList) JSTmplInit() error {
|
||||
DebugLog("Initialising the client side templates")
|
||||
var fragMap = make(map[string][][]byte)
|
||||
fragMap["alert"] = tmpl.GetFrag("alert")
|
||||
fragMap["topics_topic"] = tmpl.GetFrag("topics_topic")
|
||||
fragMap["topic_posts"] = tmpl.GetFrag("topic_posts")
|
||||
fragMap["topic_alt_posts"] = tmpl.GetFrag("topic_alt_posts")
|
||||
var parseFrags = func(name string) {
|
||||
fragMap[name] = tmpl.GetFrag(name)
|
||||
}
|
||||
parseFrags("alert")
|
||||
parseFrags("forum")
|
||||
parseFrags("topics_topic")
|
||||
parseFrags("topic_posts")
|
||||
parseFrags("topic_alt_posts")
|
||||
parseFrags("paginator")
|
||||
DebugLog("fragMap: ", fragMap)
|
||||
return filepath.Walk("./tmpl_client", func(path string, f os.FileInfo, err error) error {
|
||||
if f.IsDir() {
|
||||
if f.IsDir() || strings.HasSuffix(path, "template_list.go") || strings.HasSuffix(path, "stub.go") {
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(path, "template_list.go") || strings.HasSuffix(path, "stub.go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
path = strings.Replace(path, "\\", "/", -1)
|
||||
DebugLog("Processing client template " + path)
|
||||
data, err := ioutil.ReadFile(path)
|
||||
|
@ -110,6 +111,10 @@ func (list SFileList) JSTmplInit() error {
|
|||
}
|
||||
return out + "]"
|
||||
}*/
|
||||
data = replace(data, `)
|
||||
if !ok {
|
||||
return errors.New("invalid page struct value")
|
||||
}`, "*/tmpl_"+shortName+"_vars = tmpl_"+shortName+"_i")
|
||||
|
||||
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
|
||||
var each = func(phrase string, handle func(index int)) {
|
||||
|
@ -156,7 +161,7 @@ func (list SFileList) JSTmplInit() error {
|
|||
each("RelativeTime(", func(index int) {
|
||||
braceAt, _ := skipUntilIfExistsOrLine(data, index, 10)
|
||||
if data[braceAt-1] == ' ' {
|
||||
data[braceAt-1] = ')' // Blank it
|
||||
data[braceAt-1] = ' ' // Blank it
|
||||
}
|
||||
})
|
||||
each("if ", func(index int) {
|
||||
|
@ -191,8 +196,10 @@ func (list SFileList) JSTmplInit() error {
|
|||
data = replace(data, shortName+"_tmpl_phrase_id = RegisterTmplPhraseNames([]string{", "[")
|
||||
data = replace(data, "var plist = GetTmplPhrasesBytes("+shortName+"_tmpl_phrase_id)", "let plist = tmplPhrases[\""+tmplName+"\"];")
|
||||
data = replace(data, "var cached_var_", "let cached_var_")
|
||||
data = replace(data, `tmpl_`+shortName+`_vars, ok := tmpl_`+shortName+`_i.`, `/*`)
|
||||
data = replace(data, "[]byte(", "")
|
||||
data = replace(data, "StringToBytes(", "")
|
||||
data = replace(data, "RelativeTime(tmpl_"+shortName+"_vars.", "tmpl_"+shortName+"_vars.Relative")
|
||||
// TODO: Format dates properly on the client side
|
||||
data = replace(data, ".Format(\"2006-01-02 15:04:05\"", "")
|
||||
data = replace(data, ", 10", "")
|
||||
|
@ -252,7 +259,6 @@ func (list SFileList) Init() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = strings.TrimPrefix(path, "public/")
|
||||
var ext = filepath.Ext("/public/" + path)
|
||||
mimetype := mime.TypeByExtension(ext)
|
||||
|
|
|
@ -45,7 +45,7 @@ func (js *OttoPluginLang) AddPlugin(meta PluginMeta) (plugin *Plugin, err error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var pluginInit = func() error {
|
||||
var pluginInit = func(plugin *Plugin) error {
|
||||
retValue, err := js.vm.Run(script)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -849,6 +849,7 @@ func CoerceIntString(data string) (res int, length int) {
|
|||
}
|
||||
|
||||
// TODO: Write tests for this
|
||||
// Make sure we reflect changes to this in the JS port in /public/global.js
|
||||
func Paginate(count int, perPage int, maxPages int) []int {
|
||||
if count < perPage {
|
||||
return []int{1}
|
||||
|
@ -866,6 +867,7 @@ func Paginate(count int, perPage int, maxPages int) []int {
|
|||
}
|
||||
|
||||
// TODO: Write tests for this
|
||||
// Make sure we reflect changes to this in the JS port in /public/global.js
|
||||
func PageOffset(count int, page int, perPage int) (int, int, int) {
|
||||
var offset int
|
||||
lastPage := LastPage(count, perPage)
|
||||
|
@ -886,6 +888,7 @@ func PageOffset(count int, page int, perPage int) (int, int, int) {
|
|||
}
|
||||
|
||||
// TODO: Write tests for this
|
||||
// Make sure we reflect changes to this in the JS port in /public/global.js
|
||||
func LastPage(count int, perPage int) int {
|
||||
return (count / perPage) + 1
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Gosora Phrase System
|
||||
* Copyright Azareal 2017 - 2019
|
||||
* Copyright Azareal 2017 - 2020
|
||||
*
|
||||
*/
|
||||
package phrases
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/Azareal/Gosora/query_gen"
|
||||
)
|
||||
|
||||
//var RepliesSearch Searcher
|
||||
|
||||
type Searcher interface {
|
||||
Query(q string) ([]int, error)
|
||||
}
|
||||
|
||||
type ZoneSearcher interface {
|
||||
QueryZone(q string, zoneID int) ([]int, error)
|
||||
}
|
||||
|
||||
// TODO: Implement this
|
||||
// Note: This is slow compared to something like ElasticSearch and very limited
|
||||
type SQLSearcher struct {
|
||||
queryReplies *sql.Stmt
|
||||
queryTopics *sql.Stmt
|
||||
queryZoneReplies *sql.Stmt
|
||||
queryZoneTopics *sql.Stmt
|
||||
}
|
||||
|
||||
// TODO: Support things other than MySQL
|
||||
func NewSQLSearcher(acc *qgen.Accumulator) (*SQLSearcher, error) {
|
||||
if acc.GetAdapter().GetName() != "mysql" {
|
||||
return nil, errors.New("SQLSearcher only supports MySQL at this time")
|
||||
}
|
||||
return &SQLSearcher{
|
||||
queryReplies: acc.RawPrepare("SELECT `rid` FROM `replies` WHERE MATCH(content) AGAINST (? IN NATURAL LANGUAGE MODE);"),
|
||||
queryTopics: acc.RawPrepare("SELECT `tid` FROM `topics` WHERE MATCH(title,content) AGAINST (? IN NATURAL LANGUAGE MODE);"),
|
||||
queryZoneReplies: acc.RawPrepare("SELECT `rid` FROM `replies` WHERE MATCH(content) AGAINST (? IN NATURAL LANGUAGE MODE) AND `parentID` = ?;"),
|
||||
queryZoneTopics: acc.RawPrepare("SELECT `tid` FROM `topics` WHERE MATCH(title,content) AGAINST (? IN NATURAL LANGUAGE MODE) AND `parentID` = ?;"),
|
||||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
func (searcher *SQLSearcher) Query(q string) ([]int, error) {
|
||||
return nil, nil
|
||||
|
||||
/*
|
||||
rows, err := stmt.Query(q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
*/
|
||||
}
|
||||
|
||||
func (searcher *SQLSearcher) QueryZone(q string, zoneID int) ([]int, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: Implement this
|
||||
type ElasticSearchSearcher struct {
|
||||
}
|
||||
|
||||
func NewElasticSearchSearcher() *ElasticSearchSearcher {
|
||||
return &ElasticSearchSearcher{}
|
||||
}
|
||||
|
||||
func (searcher *ElasticSearchSearcher) Query(q string) ([]int, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (searcher *ElasticSearchSearcher) QueryZone(q string, zoneID int) ([]int, error) {
|
||||
return nil, nil
|
||||
}
|
|
@ -16,7 +16,10 @@ import (
|
|||
)
|
||||
|
||||
var Ctemplates []string // TODO: Use this to filter out top level templates we don't need
|
||||
var Templates = template.New("")
|
||||
var DefaultTemplates = template.New("")
|
||||
var DefaultTemplateFuncMap map[string]interface{}
|
||||
|
||||
//var Templates = template.New("")
|
||||
var PrebuildTmplList []func(User, *Header) CTmpl
|
||||
|
||||
func skipCTmpl(key string) bool {
|
||||
|
@ -37,120 +40,52 @@ type CTmpl struct {
|
|||
Imports []string
|
||||
}
|
||||
|
||||
func genIntTmpl(name string) func(pi interface{}, w io.Writer) error {
|
||||
return func(pi interface{}, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap[name]
|
||||
if !ok {
|
||||
mapping = name
|
||||
}
|
||||
return DefaultTemplates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Refactor the template trees to not need these
|
||||
// TODO: Stop duplicating these bits of code
|
||||
// nolint
|
||||
func interpretedTopicTemplate(pi TopicPage, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["topic"]
|
||||
if !ok {
|
||||
mapping = "topic"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
var Template_topic_handle = genIntTmpl("topic")
|
||||
var Template_topic_guest_handle = Template_topic_handle
|
||||
var Template_topic_member_handle = Template_topic_handle
|
||||
var Template_topic_alt_handle = genIntTmpl("topic")
|
||||
var Template_topic_alt_guest_handle = Template_topic_alt_handle
|
||||
var Template_topic_alt_member_handle = Template_topic_alt_handle
|
||||
|
||||
// nolint
|
||||
var Template_topic_handle = interpretedTopicTemplate
|
||||
var Template_topic_alt_handle = interpretedTopicTemplate
|
||||
var Template_topic_alt_guest_handle = interpretedTopicTemplate
|
||||
var Template_topic_alt_member_handle = interpretedTopicTemplate
|
||||
|
||||
// nolint
|
||||
var Template_topics_handle = func(pi TopicListPage, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["topics"]
|
||||
if !ok {
|
||||
mapping = "topics"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
var Template_topics_handle = genIntTmpl("topics")
|
||||
var Template_topics_guest_handle = Template_topics_handle
|
||||
var Template_topics_member_handle = Template_topics_handle
|
||||
|
||||
// nolint
|
||||
var Template_forum_handle = func(pi ForumPage, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["forum"]
|
||||
if !ok {
|
||||
mapping = "forum"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
var Template_forum_handle = genIntTmpl("forum")
|
||||
var Template_forum_guest_handle = Template_forum_handle
|
||||
var Template_forum_member_handle = Template_forum_handle
|
||||
|
||||
// nolint
|
||||
var Template_forums_handle = func(pi ForumsPage, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["forums"]
|
||||
if !ok {
|
||||
mapping = "forums"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
var Template_forums_handle = genIntTmpl("forums")
|
||||
var Template_forums_guest_handle = Template_forums_handle
|
||||
var Template_forums_member_handle = Template_forums_handle
|
||||
|
||||
// nolint
|
||||
var Template_profile_handle = func(pi ProfilePage, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["profile"]
|
||||
if !ok {
|
||||
mapping = "profile"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
var Template_profile_handle = genIntTmpl("profile")
|
||||
var Template_profile_guest_handle = Template_profile_handle
|
||||
var Template_profile_member_handle = Template_profile_handle
|
||||
|
||||
// nolint
|
||||
var Template_create_topic_handle = func(pi CreateTopicPage, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["create_topic"]
|
||||
if !ok {
|
||||
mapping = "create_topic"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_login_handle = func(pi Page, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["login"]
|
||||
if !ok {
|
||||
mapping = "login"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_register_handle = func(pi Page, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["register"]
|
||||
if !ok {
|
||||
mapping = "register"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_error_handle = func(pi ErrorPage, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["error"]
|
||||
if !ok {
|
||||
mapping = "error"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_ip_search_handle = func(pi IPSearchPage, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["ip_search"]
|
||||
if !ok {
|
||||
mapping = "ip_search"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_account_handle = func(pi Account, w io.Writer) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["account"]
|
||||
if !ok {
|
||||
mapping = "account"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
var Template_create_topic_handle = genIntTmpl("create_topic")
|
||||
var Template_login_handle = genIntTmpl("login")
|
||||
var Template_register_handle = genIntTmpl("register")
|
||||
var Template_error_handle = genIntTmpl("error")
|
||||
var Template_ip_search_handle = genIntTmpl("ip_search")
|
||||
var Template_account_handle = genIntTmpl("account")
|
||||
|
||||
func tmplInitUsers() (User, User, User) {
|
||||
avatar, microAvatar := BuildAvatar(62, "")
|
||||
|
@ -198,8 +133,36 @@ type TmplLoggedin struct {
|
|||
|
||||
type nobreak interface{}
|
||||
|
||||
type TItem struct {
|
||||
Expects string
|
||||
ExpectsInt interface{}
|
||||
LoggedIn bool
|
||||
}
|
||||
|
||||
type TItemHold map[string]TItem
|
||||
|
||||
func (hold TItemHold) Add(name string, expects string, expectsInt interface{}) {
|
||||
hold[name] = TItem{expects, expectsInt, true}
|
||||
}
|
||||
|
||||
func (hold TItemHold) AddStd(name string, expects string, expectsInt interface{}) {
|
||||
hold[name] = TItem{expects, expectsInt, false}
|
||||
}
|
||||
|
||||
// ? - Add template hooks?
|
||||
func CompileTemplates() error {
|
||||
log.Print("Compiling the templates")
|
||||
// TODO: Implement per-theme template overrides here too
|
||||
var overriden = make(map[string]map[string]bool)
|
||||
for _, theme := range Themes {
|
||||
overriden[theme.Name] = make(map[string]bool)
|
||||
log.Printf("theme.OverridenTemplates: %+v\n", theme.OverridenTemplates)
|
||||
for _, override := range theme.OverridenTemplates {
|
||||
overriden[theme.Name][override] = true
|
||||
}
|
||||
}
|
||||
log.Printf("overriden: %+v\n", overriden)
|
||||
|
||||
var config tmpl.CTemplateConfig
|
||||
config.Minify = Config.MinifyTemplates
|
||||
config.Debug = Dev.DebugMode
|
||||
|
@ -212,61 +175,48 @@ func CompileTemplates() error {
|
|||
"github.com/Azareal/Gosora/common": "github.com/Azareal/Gosora/common",
|
||||
})
|
||||
c.SetBuildTags("!no_templategen")
|
||||
c.SetOverrideTrack(overriden)
|
||||
c.SetPerThemeTmpls(make(map[string]bool))
|
||||
|
||||
// Schemas to train the template compiler on what to expect
|
||||
log.Print("Compiling the default templates")
|
||||
var wg sync.WaitGroup
|
||||
err := compileTemplates(&wg, c, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oroots := c.GetOverridenRoots()
|
||||
log.Printf("oroots: %+v\n", oroots)
|
||||
|
||||
log.Print("Compiling the per-theme templates")
|
||||
for theme, tmpls := range oroots {
|
||||
c.SetThemeName(theme)
|
||||
c.SetPerThemeTmpls(tmpls)
|
||||
log.Print("theme: ", theme)
|
||||
log.Printf("perThemeTmpls: %+v\n", tmpls)
|
||||
err = compileTemplates(&wg, c, theme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
writeTemplateList(c, &wg, "./")
|
||||
return nil
|
||||
}
|
||||
|
||||
func compileCommons(c *tmpl.CTemplateSet, header *Header, header2 *Header, out TItemHold) error {
|
||||
// TODO: Add support for interface{}s
|
||||
user, user2, user3 := tmplInitUsers()
|
||||
header, header2, _ := tmplInitHeaders(user, user2, user3)
|
||||
_, user2, user3 := tmplInitUsers()
|
||||
now := time.Now()
|
||||
|
||||
log.Print("Compiling the templates")
|
||||
|
||||
poll := Poll{ID: 1, Type: 0, Options: map[int]string{0: "Nothing", 1: "Something"}, Results: map[int]int{0: 5, 1: 2}, QuickOptions: []PollOption{
|
||||
PollOption{0, "Nothing"},
|
||||
PollOption{1, "Something"},
|
||||
}, VoteCount: 7}
|
||||
avatar, microAvatar := BuildAvatar(62, "")
|
||||
miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}}
|
||||
topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, now, now, 1, 1, 0, "", "127.0.0.1", 1, 0, 1, 0, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false, miniAttach}
|
||||
var replyList []ReplyUser
|
||||
// TODO: Do we want the UID on this to be 0?
|
||||
avatar, microAvatar = BuildAvatar(0, "")
|
||||
replyList = append(replyList, ReplyUser{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", Config.DefaultGroup, now, 0, 0, avatar, microAvatar, "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, 1, "", "", miniAttach})
|
||||
|
||||
var varList = make(map[string]tmpl.VarItem)
|
||||
var compile = func(name string, expects string, expectsInt interface{}) (tmpl string, err error) {
|
||||
return c.Compile(name+".html", "templates/", expects, expectsInt, varList)
|
||||
// Convienience function to save a line here and there
|
||||
var htitle = func(name string) *Header {
|
||||
header.Title = name
|
||||
return header
|
||||
}
|
||||
var compileByLoggedin = func(name string, expects string, expectsInt interface{}) (tmpl TmplLoggedin, err error) {
|
||||
stub, guest, member, err := c.CompileByLoggedin(name+".html", "templates/", expects, expectsInt, varList)
|
||||
return TmplLoggedin{stub, guest, member}, err
|
||||
}
|
||||
|
||||
header.Title = "Topic Name"
|
||||
tpage := TopicPage{header, replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, Paginator{[]int{1}, 1, 1}}
|
||||
tpage.Forum.Link = BuildForumURL(NameToSlug(tpage.Forum.Name), tpage.Forum.ID)
|
||||
topicTmpl, err := compile("topic", "common.TopicPage", tpage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
/*topicAltTmpl, err := compile("topic_alt", "common.TopicPage", tpage)
|
||||
if err != nil {
|
||||
return err
|
||||
/*var htitle2 = func(name string) *Header {
|
||||
header2.Title = name
|
||||
return header2
|
||||
}*/
|
||||
|
||||
topicAltTmpl, err := compileByLoggedin("topic_alt", "common.TopicPage", tpage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
varList = make(map[string]tmpl.VarItem)
|
||||
header.Title = "User 526"
|
||||
ppage := ProfilePage{header, replyList, user, 0, 0} // TODO: Use the score from user to generate the currentScore and nextScore
|
||||
profileTmpl, err := compileByLoggedin("profile", "common.ProfilePage", ppage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Use a dummy forum list to avoid o(n) problems
|
||||
var forumList []Forum
|
||||
forums, err := Forums.GetAll()
|
||||
|
@ -277,95 +227,100 @@ func CompileTemplates() error {
|
|||
forumList = append(forumList, *forum)
|
||||
}
|
||||
|
||||
varList = make(map[string]tmpl.VarItem)
|
||||
header.Title = "Forum List"
|
||||
forumsPage := ForumsPage{header, forumList}
|
||||
forumsTmpl, err := compileByLoggedin("forums", "common.ForumsPage", forumsPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var topicsList []*TopicsRow
|
||||
topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "127.0.0.1", 1, 0, 1, 1, 0, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"})
|
||||
header2.Title = "Topic List"
|
||||
topicListPage := TopicListPage{header, topicsList, forumList, Config.DefaultForum, TopicListSort{"lastupdated", false}, Paginator{[]int{1}, 1, 1}}
|
||||
/*topicListTmpl, err := compile("topics", "common.TopicListPage", topicListPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}*/
|
||||
topicListTmpl, err := compileByLoggedin("topics", "common.TopicListPage", topicListPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
topicListPage := TopicListPage{htitle("Topic List"), topicsList, forumList, Config.DefaultForum, TopicListSort{"lastupdated", false}, Paginator{[]int{1}, 1, 1}}
|
||||
out.Add("topics", "common.TopicListPage", topicListPage)
|
||||
|
||||
forumItem := BlankForum(1, "general-forum.1", "General Forum", "Where the general stuff happens", true, "all", 0, "", 0)
|
||||
header.Title = "General Forum"
|
||||
forumPage := ForumPage{header, topicsList, forumItem, Paginator{[]int{1}, 1, 1}}
|
||||
forumTmpl, err := compileByLoggedin("forum", "common.ForumPage", forumPage)
|
||||
forumPage := ForumPage{htitle("General Forum"), topicsList, forumItem, Paginator{[]int{1}, 1, 1}}
|
||||
out.Add("forum", "common.ForumPage", forumPage)
|
||||
out.Add("forums", "common.ForumsPage", ForumsPage{htitle("Forum List"), forumList})
|
||||
|
||||
poll := Poll{ID: 1, Type: 0, Options: map[int]string{0: "Nothing", 1: "Something"}, Results: map[int]int{0: 5, 1: 2}, QuickOptions: []PollOption{
|
||||
PollOption{0, "Nothing"},
|
||||
PollOption{1, "Something"},
|
||||
}, VoteCount: 7}
|
||||
avatar, microAvatar := BuildAvatar(62, "")
|
||||
miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}}
|
||||
topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, now, now, 1, 1, 0, "", "127.0.0.1", 1, 0, 1, 0, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false, miniAttach}
|
||||
var replyList []ReplyUser
|
||||
replyList = append(replyList, ReplyUser{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", Config.DefaultGroup, now, 0, 0, avatar, microAvatar, "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, 1, "", "", miniAttach})
|
||||
tpage := TopicPage{htitle("Topic Name"), replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, Paginator{[]int{1}, 1, 1}}
|
||||
tpage.Forum.Link = BuildForumURL(NameToSlug(tpage.Forum.Name), tpage.Forum.ID)
|
||||
out.Add("topic", "common.TopicPage", tpage)
|
||||
out.Add("topic_alt", "common.TopicPage", tpage)
|
||||
return nil
|
||||
}
|
||||
|
||||
func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string) error {
|
||||
// Schemas to train the template compiler on what to expect
|
||||
// TODO: Add support for interface{}s
|
||||
user, user2, user3 := tmplInitUsers()
|
||||
header, header2, _ := tmplInitHeaders(user, user2, user3)
|
||||
now := time.Now()
|
||||
|
||||
/*poll := Poll{ID: 1, Type: 0, Options: map[int]string{0: "Nothing", 1: "Something"}, Results: map[int]int{0: 5, 1: 2}, QuickOptions: []PollOption{
|
||||
PollOption{0, "Nothing"},
|
||||
PollOption{1, "Something"},
|
||||
}, VoteCount: 7}*/
|
||||
avatar, microAvatar := BuildAvatar(62, "")
|
||||
miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}}
|
||||
var replyList []ReplyUser
|
||||
//topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, now, now, 1, 1, 0, "", "127.0.0.1", 1, 0, 1, 0, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false, miniAttach}
|
||||
// TODO: Do we want the UID on this to be 0?
|
||||
avatar, microAvatar = BuildAvatar(0, "")
|
||||
replyList = append(replyList, ReplyUser{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", Config.DefaultGroup, now, 0, 0, avatar, microAvatar, "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, 1, "", "", miniAttach})
|
||||
|
||||
// Convienience function to save a line here and there
|
||||
var htitle = func(name string) *Header {
|
||||
header.Title = name
|
||||
return header
|
||||
}
|
||||
tmpls := TItemHold(make(map[string]TItem))
|
||||
err := compileCommons(c, header, header2, tmpls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.Title = "Login Page"
|
||||
loginPage := Page{header, tList, nil}
|
||||
loginTmpl, err := compile("login", "common.Page", loginPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ppage := ProfilePage{htitle("User 526"), replyList, user, 0, 0} // TODO: Use the score from user to generate the currentScore and nextScore
|
||||
tmpls.Add("profile", "common.ProfilePage", ppage)
|
||||
|
||||
header.Title = "Registration Page"
|
||||
registerPage := Page{header, tList, "nananana"}
|
||||
registerTmpl, err := compile("register", "common.Page", registerPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpls.AddStd("login", "common.Page", Page{htitle("Login Page"), tList, nil})
|
||||
tmpls.AddStd("register", "common.Page", Page{htitle("Registration Page"), tList, "nananana"})
|
||||
tmpls.AddStd("error", "common.ErrorPage", ErrorPage{htitle("Error"), "A problem has occurred in the system."})
|
||||
|
||||
header.Title = "Error"
|
||||
errorPage := ErrorPage{header, "A problem has occurred in the system."}
|
||||
errorTmpl, err := compile("error", "common.ErrorPage", errorPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ipUserList = make(map[int]*User)
|
||||
ipUserList[1] = &user2
|
||||
header.Title = "IP Search"
|
||||
ipSearchPage := IPSearchPage{header2, ipUserList, "::1"}
|
||||
ipSearchTmpl, err := compile("ip_search", "common.IPSearchPage", ipSearchPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipSearchPage := IPSearchPage{htitle("IP Search"), map[int]*User{1: &user2}, "::1"}
|
||||
tmpls.AddStd("ip_search", "common.IPSearchPage", ipSearchPage)
|
||||
|
||||
var inter nobreak
|
||||
accountPage := Account{header, "dashboard", "account_own_edit", inter}
|
||||
accountTmpl, err := compile("account", "common.Account", accountPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpls.AddStd("account", "common.Account", accountPage)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var writeTemplate = func(name string, content interface{}) {
|
||||
log.Print("Writing template '" + name + "'")
|
||||
|
||||
var writeTmpl = func(name string, content string) {
|
||||
if content == "" {
|
||||
log.Fatal("No content body for " + name)
|
||||
return //log.Fatal("No content body for " + name)
|
||||
}
|
||||
err := writeFile("./template_"+name+".go", content)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
tname := themeName
|
||||
if tname != "" {
|
||||
tname = "_" + tname
|
||||
}
|
||||
switch content := content.(type) {
|
||||
case string:
|
||||
writeTmpl(name, content)
|
||||
writeTmpl(name+tname, content)
|
||||
case TmplLoggedin:
|
||||
writeTmpl(name, content.Stub)
|
||||
writeTmpl(name+"_guest", content.Guest)
|
||||
writeTmpl(name+"_member", content.Member)
|
||||
writeTmpl(name+tname, content.Stub)
|
||||
writeTmpl(name+tname+"_guest", content.Guest)
|
||||
writeTmpl(name+tname+"_member", content.Member)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
@ -373,12 +328,12 @@ func CompileTemplates() error {
|
|||
|
||||
// Let plugins register their own templates
|
||||
DebugLog("Registering the templates for the plugins")
|
||||
config = c.GetConfig()
|
||||
config := c.GetConfig()
|
||||
config.SkipHandles = true
|
||||
c.SetConfig(config)
|
||||
for _, tmplfunc := range PrebuildTmplList {
|
||||
tmplItem := tmplfunc(user, header)
|
||||
varList = make(map[string]tmpl.VarItem)
|
||||
varList := make(map[string]tmpl.VarItem)
|
||||
compiledTmpl, err := c.Compile(tmplItem.Filename, tmplItem.Path, tmplItem.StructName, tmplItem.Data, varList, tmplItem.Imports...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -387,18 +342,30 @@ func CompileTemplates() error {
|
|||
}
|
||||
|
||||
log.Print("Writing the templates")
|
||||
writeTemplate("topic", topicTmpl)
|
||||
writeTemplate("topic_alt", topicAltTmpl)
|
||||
writeTemplate("profile", profileTmpl)
|
||||
for name, titem := range tmpls {
|
||||
log.Print("Writing " + name)
|
||||
varList := make(map[string]tmpl.VarItem)
|
||||
if titem.LoggedIn {
|
||||
stub, guest, member, err := c.CompileByLoggedin(name+".html", "templates/", titem.Expects, titem.ExpectsInt, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeTemplate(name, TmplLoggedin{stub, guest, member})
|
||||
} else {
|
||||
tmpl, err := c.Compile(name+".html", "templates/", titem.Expects, titem.ExpectsInt, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeTemplate(name, tmpl)
|
||||
}
|
||||
}
|
||||
/*writeTemplate("profile", profileTmpl)
|
||||
writeTemplate("forums", forumsTmpl)
|
||||
writeTemplate("topics", topicListTmpl)
|
||||
writeTemplate("forum", forumTmpl)
|
||||
writeTemplate("login", loginTmpl)
|
||||
writeTemplate("register", registerTmpl)
|
||||
writeTemplate("ip_search", ipSearchTmpl)
|
||||
writeTemplate("account", accountTmpl)
|
||||
writeTemplate("error", errorTmpl)
|
||||
writeTemplateList(c, &wg, "./")
|
||||
writeTemplate("error", errorTmpl)*/
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -470,6 +437,15 @@ func CompileJSTemplates() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
itemsPerPage := 25
|
||||
_, page, lastPage := PageOffset(20, 1, itemsPerPage)
|
||||
pageList := Paginate(20, itemsPerPage, 5)
|
||||
paginatorTmpl, err := c.Compile("paginator.html", "templates/", "common.Paginator", Paginator{pageList, page, lastPage}, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/*widget := &Widget{ID: 0}
|
||||
panelWidgetsWidgetTmpl, err := c.Compile("panel_themes_widgets_widget.html", "templates/", "*common.Widget", widget, varList)
|
||||
if err != nil {
|
||||
|
@ -481,9 +457,8 @@ func CompileJSTemplates() error {
|
|||
var writeTemplate = func(name string, content string) {
|
||||
log.Print("Writing template '" + name + "'")
|
||||
if content == "" {
|
||||
log.Fatal("No content body")
|
||||
return //log.Fatal("No content body")
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
err := writeFile(dirPrefix+"template_"+name+".go", content)
|
||||
|
@ -494,59 +469,74 @@ func CompileJSTemplates() error {
|
|||
}()
|
||||
}
|
||||
writeTemplate("alert", alertTmpl)
|
||||
//writeTemplate("forum", forumTmpl)
|
||||
writeTemplate("topics_topic", topicListItemTmpl)
|
||||
writeTemplate("topic_posts", topicPostsTmpl)
|
||||
writeTemplate("topic_alt_posts", topicAltPostsTmpl)
|
||||
writeTemplate("paginator", paginatorTmpl)
|
||||
//writeTemplate("panel_themes_widgets_widget", panelWidgetsWidgetTmpl)
|
||||
writeTemplateList(c, &wg, dirPrefix)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string) string {
|
||||
pout := "\n// nolint\nfunc init() {\n"
|
||||
var tFragCount = make(map[string]int)
|
||||
var bodyMap = make(map[string]string) //map[body]fragmentPrefix
|
||||
//var tmplMap = make(map[string]map[string]string) // map[tmpl]map[body]fragmentPrefix
|
||||
var tmpCount = 0
|
||||
for _, frag := range c.FragOut {
|
||||
front := frag.TmplName + "_frags[" + strconv.Itoa(frag.Index) + "]"
|
||||
/*bodyMap, tok := tmplMap[frag.TmplName]
|
||||
if !tok {
|
||||
tmplMap[frag.TmplName] = make(map[string]string)
|
||||
bodyMap = tmplMap[frag.TmplName]
|
||||
}*/
|
||||
fp, ok := bodyMap[frag.Body]
|
||||
if !ok {
|
||||
bodyMap[frag.Body] = front
|
||||
var bits string
|
||||
for _, char := range []byte(frag.Body) {
|
||||
if char == '\'' {
|
||||
bits += "'\\" + string(char) + "',"
|
||||
} else {
|
||||
bits += "'" + string(char) + "',"
|
||||
}
|
||||
}
|
||||
tmpStr := strconv.Itoa(tmpCount)
|
||||
pout += "arr_" + tmpStr + " := [...]byte{" + bits + "}\n"
|
||||
pout += front + " = arr_" + tmpStr + "[:]\n"
|
||||
tmpCount++
|
||||
//pout += front + " = []byte(`" + frag.Body + "`)\n"
|
||||
} else {
|
||||
pout += front + " = " + fp + "\n"
|
||||
}
|
||||
|
||||
_, ok = tFragCount[frag.TmplName]
|
||||
if !ok {
|
||||
tFragCount[frag.TmplName] = 0
|
||||
}
|
||||
tFragCount[frag.TmplName]++
|
||||
}
|
||||
|
||||
out := "package " + c.GetConfig().PackageName + "\n\n"
|
||||
var getterstr = "\n// nolint\nGetFrag = func(name string) [][]byte {\nswitch(name) {\n"
|
||||
for templateName, count := range tFragCount {
|
||||
out += "var " + templateName + "_frags = make([][]byte," + strconv.Itoa(count) + ")\n"
|
||||
getterstr += "\tcase \"" + templateName + "\":\n"
|
||||
getterstr += "\treturn " + templateName + "_frags\n"
|
||||
}
|
||||
getterstr += "}\nreturn nil\n}\n"
|
||||
out += pout + "\n" + getterstr + "}\n"
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func writeTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string) {
|
||||
log.Print("Writing template list")
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
out := "package " + c.GetConfig().PackageName + "\n\n"
|
||||
var getterstr = "\n// nolint\nGetFrag = func(name string) [][]byte {\nswitch(name) {\n"
|
||||
for templateName, count := range c.TemplateFragmentCount {
|
||||
out += "var " + templateName + "_frags = make([][]byte," + strconv.Itoa(count) + ")\n"
|
||||
getterstr += "\tcase \"" + templateName + "\":\n"
|
||||
getterstr += "\treturn " + templateName + "_frags\n"
|
||||
}
|
||||
getterstr += "}\nreturn nil\n}\n"
|
||||
out += "\n// nolint\nfunc init() {\n"
|
||||
var bodyMap = make(map[string]string) //map[body]fragmentPrefix
|
||||
//var tmplMap = make(map[string]map[string]string) // map[tmpl]map[body]fragmentPrefix
|
||||
var tmpCount = 0
|
||||
for _, frag := range c.FragOut {
|
||||
front := frag.TmplName + "_frags[" + strconv.Itoa(frag.Index) + "]"
|
||||
/*bodyMap, tok := tmplMap[frag.TmplName]
|
||||
if !tok {
|
||||
tmplMap[frag.TmplName] = make(map[string]string)
|
||||
bodyMap = tmplMap[frag.TmplName]
|
||||
}*/
|
||||
fp, ok := bodyMap[frag.Body]
|
||||
if !ok {
|
||||
bodyMap[frag.Body] = front
|
||||
var bits string
|
||||
for _, char := range []byte(frag.Body) {
|
||||
if char == '\'' {
|
||||
bits += "'\\" + string(char) + "',"
|
||||
} else {
|
||||
bits += "'" + string(char) + "',"
|
||||
}
|
||||
}
|
||||
tmpStr := strconv.Itoa(tmpCount)
|
||||
out += "arr_" + tmpStr + " := [...]byte{" + bits + "}\n"
|
||||
out += front + " = arr_" + tmpStr + "[:]\n"
|
||||
tmpCount++
|
||||
//out += front + " = []byte(`" + frag.Body + "`)\n"
|
||||
} else {
|
||||
out += front + " = " + fp + "\n"
|
||||
}
|
||||
}
|
||||
out += "\n" + getterstr + "}\n"
|
||||
err := writeFile(prefix+"template_list.go", out)
|
||||
err := writeFile(prefix+"template_list.go", getTemplateList(c, wg, prefix))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -579,8 +569,7 @@ func arithDuoToInt64(left interface{}, right interface{}) (leftInt int64, rightI
|
|||
return arithToInt64(left), arithToInt64(right)
|
||||
}
|
||||
|
||||
func InitTemplates() error {
|
||||
DebugLog("Initialising the template system")
|
||||
func initDefaultTmplFuncMap() {
|
||||
// TODO: Add support for floats
|
||||
fmap := make(map[string]interface{})
|
||||
fmap["add"] = func(left interface{}, right interface{}) interface{} {
|
||||
|
@ -671,9 +660,11 @@ func InitTemplates() error {
|
|||
return ""
|
||||
}
|
||||
|
||||
// The interpreted templates...
|
||||
DebugLog("Loading the template files...")
|
||||
Templates.Funcs(fmap)
|
||||
DefaultTemplateFuncMap = fmap
|
||||
}
|
||||
|
||||
func loadTemplates(tmpls *template.Template, themeName string) error {
|
||||
tmpls.Funcs(DefaultTemplateFuncMap)
|
||||
templateFiles, err := filepath.Glob("templates/*.html")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -709,8 +700,39 @@ func InitTemplates() error {
|
|||
}
|
||||
templateFiles[index] = path
|
||||
}
|
||||
template.Must(Templates.ParseFiles(templateFiles...))
|
||||
template.Must(Templates.ParseGlob("pages/*"))
|
||||
|
||||
if themeName != "" {
|
||||
overrideFiles, err := filepath.Glob("./themes/" + themeName + "/overrides/*.html")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, path := range overrideFiles {
|
||||
path = strings.Replace(path, "\\", "/", -1)
|
||||
log.Print("overrideFile: ", path)
|
||||
if skipCTmpl(path) {
|
||||
log.Print("skipping")
|
||||
continue
|
||||
}
|
||||
index, ok := templateFileMap["templates/"+strings.TrimPrefix(path, "themes/"+themeName+"/overrides/")]
|
||||
if !ok {
|
||||
log.Print("not ok: templates/" + strings.TrimPrefix(path, "themes/"+themeName+"/overrides/"))
|
||||
templateFiles = append(templateFiles, path)
|
||||
continue
|
||||
}
|
||||
templateFiles[index] = path
|
||||
}
|
||||
}
|
||||
|
||||
template.Must(tmpls.ParseFiles(templateFiles...))
|
||||
template.Must(tmpls.ParseGlob("pages/*"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitTemplates() error {
|
||||
DebugLog("Initialising the template system")
|
||||
initDefaultTmplFuncMap()
|
||||
|
||||
// The interpreted templates...
|
||||
DebugLog("Loading the template files...")
|
||||
return loadTemplates(DefaultTemplates, "")
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ type CContext struct {
|
|||
RootHolder string
|
||||
VarHolder string
|
||||
HoldReflect reflect.Value
|
||||
RootTemplateName string
|
||||
TemplateName string
|
||||
LoopDepth int
|
||||
OutBuf *[]OutBufferFrame
|
||||
|
|
|
@ -63,6 +63,11 @@ type CTemplateSet struct {
|
|||
config CTemplateConfig
|
||||
baseImportMap map[string]string
|
||||
buildTags string
|
||||
|
||||
overridenTrack map[string]map[string]bool
|
||||
overridenRoots map[string]map[string]bool
|
||||
themeName string
|
||||
perThemeTmpls map[string]bool
|
||||
}
|
||||
|
||||
func NewCTemplateSet() *CTemplateSet {
|
||||
|
@ -70,7 +75,8 @@ func NewCTemplateSet() *CTemplateSet {
|
|||
config: CTemplateConfig{
|
||||
PackageName: "main",
|
||||
},
|
||||
baseImportMap: map[string]string{},
|
||||
baseImportMap: map[string]string{},
|
||||
overridenRoots: map[string]map[string]bool{},
|
||||
funcMap: map[string]interface{}{
|
||||
"and": "&&",
|
||||
"not": "!",
|
||||
|
@ -118,6 +124,22 @@ func (c *CTemplateSet) SetBuildTags(tags string) {
|
|||
c.buildTags = tags
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) SetOverrideTrack(overriden map[string]map[string]bool) {
|
||||
c.overridenTrack = overriden
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) GetOverridenRoots() map[string]map[string]bool {
|
||||
return c.overridenRoots
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) SetThemeName(name string) {
|
||||
c.themeName = name
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) SetPerThemeTmpls(perThemeTmpls map[string]bool) {
|
||||
c.perThemeTmpls = perThemeTmpls
|
||||
}
|
||||
|
||||
type SkipBlock struct {
|
||||
Frags map[int]int
|
||||
LastCount int
|
||||
|
@ -139,10 +161,8 @@ func (c *CTemplateSet) CompileByLoggedin(name string, fileDir string, expects st
|
|||
for index, item := range c.baseImportMap {
|
||||
c.importMap[index] = item
|
||||
}
|
||||
if len(imports) > 0 {
|
||||
for _, importItem := range imports {
|
||||
c.importMap[importItem] = importItem
|
||||
}
|
||||
for _, importItem := range imports {
|
||||
c.importMap[importItem] = importItem
|
||||
}
|
||||
var importList string
|
||||
for _, item := range c.importMap {
|
||||
|
@ -150,10 +170,18 @@ func (c *CTemplateSet) CompileByLoggedin(name string, fileDir string, expects st
|
|||
}
|
||||
|
||||
fname := strings.TrimSuffix(name, filepath.Ext(name))
|
||||
if c.themeName != "" {
|
||||
_, ok := c.perThemeTmpls[fname]
|
||||
if !ok {
|
||||
return "", "", "", nil
|
||||
}
|
||||
fname += "_" + c.themeName
|
||||
}
|
||||
c.importMap["github.com/Azareal/Gosora/common"] = "github.com/Azareal/Gosora/common"
|
||||
|
||||
stub = `package ` + c.config.PackageName + `
|
||||
` + importList + `
|
||||
import "errors"
|
||||
`
|
||||
|
||||
if !c.config.SkipInitBlock {
|
||||
|
@ -171,13 +199,18 @@ func (c *CTemplateSet) CompileByLoggedin(name string, fileDir string, expects st
|
|||
stub += "}\n\n"
|
||||
}
|
||||
|
||||
// TODO: Try to remove this redundant interface cast
|
||||
stub += `
|
||||
// nolint
|
||||
func Template_` + fname + `(tmpl_` + fname + `_vars ` + expects + `, w io.Writer) error {
|
||||
if tmpl_` + fname + `_vars.CurrentUser.Loggedin {
|
||||
return Template_` + fname + `_member(tmpl_` + fname + `_vars, w)
|
||||
func Template_` + fname + `(tmpl_` + fname + `_i interface{}, w io.Writer) error {
|
||||
tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `)
|
||||
if !ok {
|
||||
return errors.New("invalid page struct value")
|
||||
}
|
||||
return Template_` + fname + `_guest(tmpl_` + fname + `_vars, w)
|
||||
if tmpl_` + fname + `_vars.CurrentUser.Loggedin {
|
||||
return Template_` + fname + `_member(tmpl_` + fname + `_i, w)
|
||||
}
|
||||
return Template_` + fname + `_guest(tmpl_` + fname + `_i, w)
|
||||
}`
|
||||
|
||||
c.fileDir = fileDir
|
||||
|
@ -213,15 +246,14 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
|||
return c.compile(name, content, expects, expectsInt, varList, imports...)
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) compile(name string, content, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
||||
func (c *CTemplateSet) compile(name string, content string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
||||
c.importMap = map[string]string{}
|
||||
for index, item := range c.baseImportMap {
|
||||
c.importMap[index] = item
|
||||
}
|
||||
if len(imports) > 0 {
|
||||
for _, importItem := range imports {
|
||||
c.importMap[importItem] = importItem
|
||||
}
|
||||
c.importMap["errors"] = "errors"
|
||||
for _, importItem := range imports {
|
||||
c.importMap[importItem] = importItem
|
||||
}
|
||||
|
||||
c.varList = varList
|
||||
|
@ -238,6 +270,13 @@ func (c *CTemplateSet) compile(name string, content, expects string, expectsInt
|
|||
c.detail(name)
|
||||
|
||||
fname := strings.TrimSuffix(name, filepath.Ext(name))
|
||||
if c.themeName != "" {
|
||||
_, ok := c.perThemeTmpls[fname]
|
||||
if !ok {
|
||||
return "", nil
|
||||
}
|
||||
fname += "_" + c.themeName
|
||||
}
|
||||
if c.guestOnly {
|
||||
fname += "_guest"
|
||||
} else if c.memberOnly {
|
||||
|
@ -246,7 +285,14 @@ func (c *CTemplateSet) compile(name string, content, expects string, expectsInt
|
|||
|
||||
var outBuf []OutBufferFrame
|
||||
var rootHold = "tmpl_" + fname + "_vars"
|
||||
con := CContext{RootHolder: rootHold, VarHolder: rootHold, HoldReflect: reflect.ValueOf(expectsInt), TemplateName: fname, OutBuf: &outBuf}
|
||||
con := CContext{
|
||||
RootHolder: rootHold,
|
||||
VarHolder: rootHold,
|
||||
HoldReflect: reflect.ValueOf(expectsInt),
|
||||
RootTemplateName: fname,
|
||||
TemplateName: fname,
|
||||
OutBuf: &outBuf,
|
||||
}
|
||||
c.templateList = map[string]*parse.Tree{fname: tree}
|
||||
c.detail(c.templateList)
|
||||
c.localVars = make(map[string]map[string]VarItemReflect)
|
||||
|
@ -274,7 +320,6 @@ func (c *CTemplateSet) compile(name string, content, expects string, expectsInt
|
|||
if !ok {
|
||||
c.FragOnce[fname] = true
|
||||
}
|
||||
|
||||
if len(c.langIndexToName) > 0 {
|
||||
c.importMap[langPkg] = langPkg
|
||||
}
|
||||
|
@ -319,7 +364,12 @@ func (c *CTemplateSet) compile(name string, content, expects string, expectsInt
|
|||
fout += "}\n\n"
|
||||
}
|
||||
|
||||
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w io.Writer) error {\n"
|
||||
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n"
|
||||
fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `)
|
||||
if !ok {
|
||||
return errors.New("invalid page struct value")
|
||||
}
|
||||
`
|
||||
if len(c.langIndexToName) > 0 {
|
||||
fout += "var plist = phrases.GetTmplPhrasesBytes(" + fname + "_tmpl_phrase_id)\n"
|
||||
}
|
||||
|
@ -427,7 +477,7 @@ func (c *CTemplateSet) rootIterate(tree *parse.Tree, con CContext) {
|
|||
c.retCall("rootIterate")
|
||||
}
|
||||
|
||||
var inSlice = func(haystack []string, expr string) bool {
|
||||
func inSlice(haystack []string, expr string) bool {
|
||||
for _, needle := range haystack {
|
||||
if needle == expr {
|
||||
return true
|
||||
|
@ -600,11 +650,15 @@ func (c *CTemplateSet) compileRangeNode(con CContext, node *parse.RangeNode) {
|
|||
}
|
||||
c.detail("Range item:", item)
|
||||
if !item.IsValid() {
|
||||
c.critical("expr:", expr)
|
||||
c.critical("con.VarHolder", con.VarHolder)
|
||||
panic("item" + "^\n" + "Invalid map. Maybe, it doesn't have any entries for the template engine to analyse?")
|
||||
}
|
||||
startIf(item, true)
|
||||
case reflect.Slice:
|
||||
if outVal.Len() == 0 {
|
||||
c.critical("expr:", expr)
|
||||
c.critical("con.VarHolder", con.VarHolder)
|
||||
panic("The sample data needs at-least one or more elements for the slices. We're looking into removing this requirement at some point!")
|
||||
}
|
||||
startIf(outVal.Index(0), false)
|
||||
|
@ -1367,6 +1421,7 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V
|
|||
|
||||
func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNode) {
|
||||
c.dumpCall("compileSubTemplate", pcon, node)
|
||||
defer c.retCall("compileSubTemplate")
|
||||
c.detail("Template Node: ", node.Name)
|
||||
|
||||
// TODO: Cascade errors back up the tree to the caller?
|
||||
|
@ -1474,9 +1529,52 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
|
|||
if !ok {
|
||||
c.FragOnce[fname] = true
|
||||
}
|
||||
|
||||
// map[string]map[string]bool
|
||||
c.detail("overridenTrack loop")
|
||||
c.detail("fname:", fname)
|
||||
for themeName, track := range c.overridenTrack {
|
||||
c.detail("themeName:", themeName)
|
||||
c.detailf("track: %+v\n", track)
|
||||
croot, ok := c.overridenRoots[themeName]
|
||||
if !ok {
|
||||
croot = make(map[string]bool)
|
||||
c.overridenRoots[themeName] = croot
|
||||
}
|
||||
c.detailf("croot: %+v\n", croot)
|
||||
for tmplName, _ := range track {
|
||||
cname := tmplName
|
||||
if c.guestOnly {
|
||||
cname += "_guest"
|
||||
} else if c.memberOnly {
|
||||
cname += "_member"
|
||||
}
|
||||
c.detail("cname:", cname)
|
||||
if fname == cname {
|
||||
c.detail("match")
|
||||
croot[strings.TrimSuffix(strings.TrimSuffix(con.RootTemplateName, "_guest"), "_member")] = true
|
||||
} else {
|
||||
c.detail("no match")
|
||||
}
|
||||
}
|
||||
}
|
||||
c.detailf("c.overridenRoots: %+v\n", c.overridenRoots)
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) loadTemplate(fileDir string, name string) (content string, err error) {
|
||||
if c.themeName != "" {
|
||||
c.detail("per-theme override: ", "./themes/"+c.themeName+"/overrides/"+name)
|
||||
res, err := ioutil.ReadFile("./themes/" + c.themeName + "/overrides/" + name)
|
||||
if err == nil {
|
||||
content = string(res)
|
||||
if c.config.Minify {
|
||||
content = minify(content)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
c.detail("override err: ", err)
|
||||
}
|
||||
|
||||
res, err := ioutil.ReadFile(c.fileDir + "overrides/" + name)
|
||||
if err != nil {
|
||||
c.detail("override path: ", c.fileDir+"overrides/"+name)
|
||||
|
|
205
common/theme.go
205
common/theme.go
|
@ -5,6 +5,7 @@ import (
|
|||
"bytes"
|
||||
"database/sql"
|
||||
"errors"
|
||||
htmpl "html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -24,26 +25,28 @@ var ErrNoDefaultTheme = errors.New("The default theme isn't registered in the sy
|
|||
type Theme struct {
|
||||
Path string // Redirect this file to another folder
|
||||
|
||||
Name string
|
||||
FriendlyName string
|
||||
Version string
|
||||
Creator string
|
||||
FullImage string
|
||||
MobileFriendly bool
|
||||
Disabled bool
|
||||
HideFromThemes bool
|
||||
BgAvatars bool // For profiles, at the moment
|
||||
GridLists bool // User Manager
|
||||
ForkOf string
|
||||
Tag string
|
||||
URL string
|
||||
Docks []string // Allowed Values: leftSidebar, rightSidebar, footer
|
||||
Settings map[string]ThemeSetting
|
||||
Templates []TemplateMapping
|
||||
TemplatesMap map[string]string
|
||||
TmplPtr map[string]interface{}
|
||||
Resources []ThemeResource
|
||||
ResourceTemplates *template.Template
|
||||
Name string
|
||||
FriendlyName string
|
||||
Version string
|
||||
Creator string
|
||||
FullImage string
|
||||
MobileFriendly bool
|
||||
Disabled bool
|
||||
HideFromThemes bool
|
||||
BgAvatars bool // For profiles, at the moment
|
||||
GridLists bool // User Manager
|
||||
ForkOf string
|
||||
Tag string
|
||||
URL string
|
||||
Docks []string // Allowed Values: leftSidebar, rightSidebar, footer
|
||||
Settings map[string]ThemeSetting
|
||||
IntTmplHandle *htmpl.Template
|
||||
OverridenTemplates []string
|
||||
Templates []TemplateMapping
|
||||
TemplatesMap map[string]string
|
||||
TmplPtr map[string]interface{}
|
||||
Resources []ThemeResource
|
||||
ResourceTemplates *template.Template
|
||||
|
||||
// Dock intercepters
|
||||
// TODO: Implement this
|
||||
|
@ -180,100 +183,22 @@ func (theme *Theme) MapTemplates() {
|
|||
LogError(errors.New("The source template doesn't exist!"))
|
||||
}
|
||||
|
||||
switch dTmplPtr := destTmplPtr.(type) {
|
||||
case *func(CustomPagePage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(CustomPagePage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(TopicPage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(TopicPage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(TopicListPage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(TopicListPage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(ForumPage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(ForumPage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(ForumsPage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(ForumsPage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(ProfilePage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(ProfilePage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(CreateTopicPage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(CreateTopicPage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(IPSearchPage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(IPSearchPage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(AccountDashPage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(AccountDashPage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(ErrorPage, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(ErrorPage, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case *func(Page, io.Writer) error:
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(Page, io.Writer) error:
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
default:
|
||||
dTmplPtr, ok := destTmplPtr.(*func(interface{}, io.Writer) error)
|
||||
if !ok {
|
||||
log.Print("themeTmpl.Name: ", themeTmpl.Name)
|
||||
log.Print("themeTmpl.Source: ", themeTmpl.Source)
|
||||
LogError(errors.New("Unknown destination template type!"))
|
||||
return
|
||||
}
|
||||
|
||||
sTmplPtr, ok := sourceTmplPtr.(*func(interface{}, io.Writer) error)
|
||||
if !ok {
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
return
|
||||
}
|
||||
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,67 +291,17 @@ func (theme *Theme) RunTmpl(template string, pi interface{}, w io.Writer) error
|
|||
|
||||
var getTmpl = theme.GetTmpl(template)
|
||||
switch tmplO := getTmpl.(type) {
|
||||
case *func(CustomPagePage, io.Writer) error:
|
||||
case *func(interface{}, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(CustomPagePage), w)
|
||||
case *func(TopicPage, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(TopicPage), w)
|
||||
case *func(TopicListPage, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(TopicListPage), w)
|
||||
case *func(ForumPage, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(ForumPage), w)
|
||||
case *func(ForumsPage, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(ForumsPage), w)
|
||||
case *func(ProfilePage, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(ProfilePage), w)
|
||||
case *func(CreateTopicPage, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(CreateTopicPage), w)
|
||||
case *func(IPSearchPage, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(IPSearchPage), w)
|
||||
case *func(Account, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(Account), w)
|
||||
case *func(ErrorPage, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(ErrorPage), w)
|
||||
case *func(Page, io.Writer) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(Page), w)
|
||||
case func(CustomPagePage, io.Writer) error:
|
||||
return tmplO(pi.(CustomPagePage), w)
|
||||
case func(TopicPage, io.Writer) error:
|
||||
return tmplO(pi.(TopicPage), w)
|
||||
case func(TopicListPage, io.Writer) error:
|
||||
return tmplO(pi.(TopicListPage), w)
|
||||
case func(ForumPage, io.Writer) error:
|
||||
return tmplO(pi.(ForumPage), w)
|
||||
case func(ForumsPage, io.Writer) error:
|
||||
return tmplO(pi.(ForumsPage), w)
|
||||
case func(ProfilePage, io.Writer) error:
|
||||
return tmplO(pi.(ProfilePage), w)
|
||||
case func(CreateTopicPage, io.Writer) error:
|
||||
return tmplO(pi.(CreateTopicPage), w)
|
||||
case func(IPSearchPage, io.Writer) error:
|
||||
return tmplO(pi.(IPSearchPage), w)
|
||||
case func(Account, io.Writer) error:
|
||||
return tmplO(pi.(Account), w)
|
||||
case func(ErrorPage, io.Writer) error:
|
||||
return tmplO(pi.(ErrorPage), w)
|
||||
case func(Page, io.Writer) error:
|
||||
return tmplO(pi.(Page), w)
|
||||
return tmpl(pi, w)
|
||||
case func(interface{}, io.Writer) error:
|
||||
return tmplO(pi, w)
|
||||
case nil, string:
|
||||
mapping, ok := theme.TemplatesMap[template]
|
||||
if !ok {
|
||||
mapping = template
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
return theme.IntTmplHandle.ExecuteTemplate(w, mapping+".html", pi)
|
||||
default:
|
||||
log.Print("theme ", theme)
|
||||
log.Print("template ", template)
|
||||
|
|
|
@ -4,11 +4,14 @@ import (
|
|||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
|
@ -136,6 +139,40 @@ func NewThemeList() (themes ThemeList, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
theme.IntTmplHandle = DefaultTemplates
|
||||
overrides, err := ioutil.ReadDir(theme.Path + "/overrides/")
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return themes, err
|
||||
}
|
||||
if len(overrides) > 0 {
|
||||
var overCount = 0
|
||||
for _, override := range overrides {
|
||||
if override.IsDir() {
|
||||
continue
|
||||
}
|
||||
var 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++
|
||||
theme.OverridenTemplates = append(theme.OverridenTemplates, strings.TrimSuffix(override.Name(), ext))
|
||||
log.Print("succeeded")
|
||||
}
|
||||
|
||||
localTmpls := template.New("")
|
||||
err = loadTemplates(localTmpls, theme.Name)
|
||||
if err != nil {
|
||||
return themes, err
|
||||
}
|
||||
theme.IntTmplHandle = localTmpls
|
||||
log.Printf("theme.OverridenTemplates: %+v\n", theme.OverridenTemplates)
|
||||
log.Printf("theme.IntTmplHandle: %+v\n", theme.IntTmplHandle)
|
||||
} else {
|
||||
log.Print("no overrides for " + theme.Name)
|
||||
}
|
||||
|
||||
// TODO: Bind the built template, or an interpreted one for any dock overrides this theme has
|
||||
|
||||
themes[theme.Name] = theme
|
||||
|
@ -218,88 +255,20 @@ func ResetTemplateOverrides() {
|
|||
}
|
||||
|
||||
// Not really a pointer, more of a function handle, an artifact from one of the earlier versions of themes.go
|
||||
switch oPtr := originPointer.(type) {
|
||||
case func(CustomPagePage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(CustomPagePage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(TopicPage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(TopicPage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(TopicListPage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(TopicListPage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(ForumPage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(ForumPage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(ForumsPage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(ForumsPage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(ProfilePage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(ProfilePage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(CreateTopicPage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(CreateTopicPage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(IPSearchPage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(IPSearchPage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(AccountDashPage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(AccountDashPage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(ErrorPage, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(ErrorPage, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
case func(Page, io.Writer) error:
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(Page, io.Writer) error:
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
LogError(errors.New("The source and destination templates are incompatible"))
|
||||
}
|
||||
default:
|
||||
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)
|
||||
|
@ -313,7 +282,7 @@ func CreateThemeTemplate(theme string, name string) {
|
|||
if !ok {
|
||||
mapping = name
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
return DefaultTemplates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Gosora Topic File
|
||||
* Copyright Azareal 2017 - 2019
|
||||
* Copyright Azareal 2017 - 2020
|
||||
*
|
||||
*/
|
||||
package common
|
||||
|
|
|
@ -16,9 +16,9 @@ type TopicListHolder struct {
|
|||
}
|
||||
|
||||
type TopicListInt interface {
|
||||
GetListByCanSee(canSee []int, page int, orderby string) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error)
|
||||
GetListByGroup(group *Group, page int, orderby string) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error)
|
||||
GetList(page int, orderby string) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error)
|
||||
GetListByCanSee(canSee []int, page int, orderby string, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error)
|
||||
GetListByGroup(group *Group, page int, orderby string, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error)
|
||||
GetList(page int, orderby string, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error)
|
||||
}
|
||||
|
||||
type DefaultTopicList struct {
|
||||
|
@ -94,7 +94,7 @@ func (tList *DefaultTopicList) Tick() error {
|
|||
|
||||
var canSeeHolders = make(map[string]*TopicListHolder)
|
||||
for name, canSee := range permTree {
|
||||
topicList, forumList, paginator, err := tList.GetListByCanSee(canSee, 1, "")
|
||||
topicList, forumList, paginator, err := tList.GetListByCanSee(canSee, 1, "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -115,12 +115,12 @@ func (tList *DefaultTopicList) Tick() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (tList *DefaultTopicList) GetListByGroup(group *Group, page int, orderby string) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error) {
|
||||
func (tList *DefaultTopicList) GetListByGroup(group *Group, page int, orderby string, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error) {
|
||||
if page == 0 {
|
||||
page = 1
|
||||
}
|
||||
// TODO: Cache the first three pages not just the first along with all the topics on this beaten track
|
||||
if page == 1 && orderby == "" {
|
||||
if page == 1 && orderby == "" && len(filterIDs) == 0 {
|
||||
var holder *TopicListHolder
|
||||
var ok bool
|
||||
if group.ID%2 == 0 {
|
||||
|
@ -139,10 +139,10 @@ func (tList *DefaultTopicList) GetListByGroup(group *Group, page int, orderby st
|
|||
|
||||
// TODO: Make CanSee a method on *Group with a canSee field? Have a CanSee method on *User to cover the case of superadmins?
|
||||
//log.Printf("deoptimising for %d on page %d\n", group.ID, page)
|
||||
return tList.GetListByCanSee(group.CanSee, page, orderby)
|
||||
return tList.GetListByCanSee(group.CanSee, page, orderby, filterIDs)
|
||||
}
|
||||
|
||||
func (tList *DefaultTopicList) GetListByCanSee(canSee []int, page int, orderby string) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error) {
|
||||
func (tList *DefaultTopicList) GetListByCanSee(canSee []int, page int, orderby string, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error) {
|
||||
// We need a list of the visible forums for Quick Topic
|
||||
// ? - Would it be useful, if we could post in social groups from /topics/?
|
||||
for _, fid := range canSee {
|
||||
|
@ -154,6 +154,26 @@ func (tList *DefaultTopicList) GetListByCanSee(canSee []int, page int, orderby s
|
|||
}
|
||||
}
|
||||
|
||||
var inSlice = func(haystack []int, needle int) bool {
|
||||
for _, item := range haystack {
|
||||
if needle == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var filteredForums []Forum
|
||||
if len(filterIDs) > 0 {
|
||||
for _, forum := range forumList {
|
||||
if inSlice(filterIDs, forum.ID) {
|
||||
filteredForums = append(filteredForums, forum)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filteredForums = forumList
|
||||
}
|
||||
|
||||
// ? - Should we be showing plugin_guilds posts on /topics/?
|
||||
argList, qlist := ForumListToArgQ(forumList)
|
||||
if qlist == "" {
|
||||
|
@ -166,13 +186,33 @@ func (tList *DefaultTopicList) GetListByCanSee(canSee []int, page int, orderby s
|
|||
}
|
||||
|
||||
// TODO: Reduce the number of returns
|
||||
func (tList *DefaultTopicList) GetList(page int, orderby string) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error) {
|
||||
func (tList *DefaultTopicList) GetList(page int, orderby string, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, paginator Paginator, err error) {
|
||||
// TODO: Make CanSee a method on *Group with a canSee field? Have a CanSee method on *User to cover the case of superadmins?
|
||||
canSee, err := Forums.GetAllVisibleIDs()
|
||||
cCanSee, err := Forums.GetAllVisibleIDs()
|
||||
if err != nil {
|
||||
return nil, nil, Paginator{nil, 1, 1}, err
|
||||
}
|
||||
|
||||
var inSlice = func(haystack []int, needle int) bool {
|
||||
for _, item := range haystack {
|
||||
if needle == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var canSee []int
|
||||
if len(filterIDs) > 0 {
|
||||
for _, fid := range cCanSee {
|
||||
if inSlice(filterIDs, fid) {
|
||||
canSee = append(canSee, fid)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canSee = cCanSee
|
||||
}
|
||||
|
||||
// We need a list of the visible forums for Quick Topic
|
||||
// ? - Would it be useful, if we could post in social groups from /topics/?
|
||||
for _, fid := range canSee {
|
||||
|
|
|
@ -62,20 +62,21 @@ func (user *User) WebSockets() *WsJSONUser {
|
|||
groupID = user.TempGroup
|
||||
}
|
||||
// TODO: Do we want to leak the user's permissions? Users will probably be able to see their status from the group tags, but still
|
||||
return &WsJSONUser{user.ID, user.Link, user.Name, groupID, user.IsMod, user.Avatar, user.Level, user.Score, user.Liked}
|
||||
return &WsJSONUser{user.ID, user.Link, user.Name, groupID, user.IsMod, user.Avatar, user.MicroAvatar, user.Level, user.Score, user.Liked}
|
||||
}
|
||||
|
||||
// Use struct tags to avoid having to define this? It really depends on the circumstances, sometimes we want the whole thing, sometimes... not.
|
||||
type WsJSONUser struct {
|
||||
ID int
|
||||
Link string
|
||||
Name string
|
||||
Group int // Be sure to mask with TempGroup
|
||||
IsMod bool
|
||||
Avatar string
|
||||
Level int
|
||||
Score int
|
||||
Liked int
|
||||
ID int
|
||||
Link string
|
||||
Name string
|
||||
Group int // Be sure to mask with TempGroup
|
||||
IsMod bool
|
||||
Avatar string
|
||||
MicroAvatar string
|
||||
Level int
|
||||
Score int
|
||||
Liked int
|
||||
}
|
||||
|
||||
func (user *User) Me() *MeUser {
|
||||
|
|
|
@ -37,8 +37,10 @@ func init() {
|
|||
topicWatchers = make(map[int]map[*WSUser]bool)
|
||||
}
|
||||
|
||||
//easyjson:json
|
||||
type WsTopicList struct {
|
||||
Topics []*WsTopicsRow
|
||||
Topics []*WsTopicsRow
|
||||
LastPage int // Not for WebSockets, but for the JSON endpoint for /topics/ to keep the paginator functional
|
||||
}
|
||||
|
||||
// TODO: How should we handle errors for this?
|
||||
|
|
|
@ -3,16 +3,19 @@ package common
|
|||
import "errors"
|
||||
|
||||
// TODO: Move this into it's own package to make neater and tidier
|
||||
type filterForum struct {
|
||||
*Forum
|
||||
Selected bool
|
||||
}
|
||||
type searchAndFilter struct {
|
||||
*Header
|
||||
Forums []*Forum
|
||||
Forums []filterForum
|
||||
}
|
||||
|
||||
func widgetSearchAndFilter(widget *Widget, hvars interface{}) (out string, err error) {
|
||||
header := hvars.(*Header)
|
||||
user := header.CurrentUser
|
||||
|
||||
var forums []*Forum
|
||||
var forums []filterForum
|
||||
var canSee []int
|
||||
if user.IsSuperAdmin {
|
||||
canSee, err = Forums.GetAllVisibleIDs()
|
||||
|
@ -31,7 +34,7 @@ func widgetSearchAndFilter(widget *Widget, hvars interface{}) (out string, err e
|
|||
for _, fid := range canSee {
|
||||
forum := Forums.DirtyGet(fid)
|
||||
if forum.ParentID == 0 && forum.Name != "" && forum.Active {
|
||||
forums = append(forums, forum)
|
||||
forums = append(forums, filterForum{forum, (header.Zone == "view_forum" || header.Zone == "topics") && header.ZoneID == forum.ID})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ type NameTextPair struct {
|
|||
func preparseWidget(widget *Widget, wdata string) (err error) {
|
||||
prebuildWidget := func(name string, data interface{}) (string, error) {
|
||||
var b bytes.Buffer
|
||||
err := Templates.ExecuteTemplate(&b, name+".html", data)
|
||||
err := DefaultTemplates.ExecuteTemplate(&b, name+".html", data)
|
||||
return string(b.Bytes()), err
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,13 @@ func (hub *WsHubImpl) Start() {
|
|||
|
||||
// This Tick is separate from the admin one, as we want to process that in parallel with this due to the blocking calls to gopsutil
|
||||
func (hub *WsHubImpl) Tick() error {
|
||||
return wsTopicListTick(hub)
|
||||
}
|
||||
|
||||
func wsTopicListTick(hub *WsHubImpl) error {
|
||||
// Don't waste CPU time if nothing has happened
|
||||
// TODO: Get a topic list method which strips stickies?
|
||||
tList, _, _, err := TopicList.GetList(1, "")
|
||||
tList, _, _, err := TopicList.GetList(1, "", nil)
|
||||
if err != nil {
|
||||
hub.lastTick = time.Now()
|
||||
return err // TODO: Do we get ErrNoRows here?
|
||||
|
@ -117,7 +121,7 @@ func (hub *WsHubImpl) Tick() error {
|
|||
|
||||
var canSeeRenders = make(map[string][]byte)
|
||||
for name, canSee := range canSeeMap {
|
||||
topicList, forumList, _, err := TopicList.GetListByCanSee(canSee, 1, "")
|
||||
topicList, forumList, _, err := TopicList.GetListByCanSee(canSee, 1, "", nil)
|
||||
if err != nil {
|
||||
return err // TODO: Do we get ErrNoRows here?
|
||||
}
|
||||
|
@ -146,7 +150,7 @@ func (hub *WsHubImpl) Tick() error {
|
|||
wsTopicList[i] = topicRow.WebSockets()
|
||||
}
|
||||
|
||||
outBytes, err := json.Marshal(&WsTopicList{wsTopicList})
|
||||
outBytes, err := json.Marshal(&WsTopicList{wsTopicList, 0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -126,6 +126,10 @@ go build "./cmd/install"
|
|||
|
||||
install.exe
|
||||
|
||||
go get github.com/mailru/easyjson/...
|
||||
|
||||
easyjson -pkg common
|
||||
|
||||
gosora.exe -build-templates
|
||||
|
||||
gosora.exe
|
||||
|
|
|
@ -116,7 +116,7 @@ func CommonAreaWidgets(header *common.Header) {
|
|||
common.WidgetMenuItem{"Create Guild", "/guild/create/", false},
|
||||
}}
|
||||
|
||||
err := common.Templates.ExecuteTemplate(&b, "widget_menu.html", menu)
|
||||
err := header.Theme.RunTmpl("widget_menu", pi, w)
|
||||
if err != nil {
|
||||
common.LogError(err)
|
||||
return
|
||||
|
@ -232,7 +232,7 @@ func RouteCreateGuild(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
CommonAreaWidgets(header)
|
||||
|
||||
pi := common.Page{header, tList, nil}
|
||||
err := common.Templates.ExecuteTemplate(w, "guilds_create_guild.html", pi)
|
||||
err := header.Theme.RunTmpl("guilds_create_guild", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -384,7 +384,7 @@ func PreRenderViewForum(w http.ResponseWriter, r *http.Request, user *common.Use
|
|||
guildItem := guildData.(*Guild)
|
||||
|
||||
guildpi := Page{pi.Title, pi.Header, pi.ItemList, pi.Forum, guildItem, pi.Page, pi.LastPage}
|
||||
err := common.Templates.ExecuteTemplate(w, "guilds_view_guild.html", guildpi)
|
||||
err := header.Theme.RunTmpl("guilds_view_guild", guildpi, w)
|
||||
if err != nil {
|
||||
common.LogError(err)
|
||||
return false
|
||||
|
|
|
@ -15,13 +15,13 @@ func init() {
|
|||
common.PrebuildTmplList = append(common.PrebuildTmplList, guilds.PrebuildTmplList)
|
||||
}
|
||||
|
||||
func initGuilds() (err error) {
|
||||
common.Plugins["guilds"].AddHook("intercept_build_widgets", guilds.Widgets)
|
||||
common.Plugins["guilds"].AddHook("trow_assign", guilds.TrowAssign)
|
||||
common.Plugins["guilds"].AddHook("topic_create_pre_loop", guilds.TopicCreatePreLoop)
|
||||
common.Plugins["guilds"].AddHook("pre_render_forum", guilds.PreRenderViewForum)
|
||||
common.Plugins["guilds"].AddHook("simple_forum_check_pre_perms", guilds.ForumCheck)
|
||||
common.Plugins["guilds"].AddHook("forum_check_pre_perms", guilds.ForumCheck)
|
||||
func initGuilds(plugin *common.Plugin) (err error) {
|
||||
plugin.AddHook("intercept_build_widgets", guilds.Widgets)
|
||||
plugin.AddHook("trow_assign", guilds.TrowAssign)
|
||||
plugin.AddHook("topic_create_pre_loop", guilds.TopicCreatePreLoop)
|
||||
plugin.AddHook("pre_render_forum", guilds.PreRenderViewForum)
|
||||
plugin.AddHook("simple_forum_check_pre_perms", guilds.ForumCheck)
|
||||
plugin.AddHook("forum_check_pre_perms", guilds.ForumCheck)
|
||||
// TODO: Auto-grant this perm to admins upon installation?
|
||||
common.RegisterPluginPerm("CreateGuild")
|
||||
router.HandleFunc("/guilds/", guilds.RouteGuildList)
|
||||
|
@ -54,13 +54,13 @@ func initGuilds() (err error) {
|
|||
return acc.FirstError()
|
||||
}
|
||||
|
||||
func deactivateGuilds() {
|
||||
common.Plugins["guilds"].RemoveHook("intercept_build_widgets", guilds.Widgets)
|
||||
common.Plugins["guilds"].RemoveHook("trow_assign", guilds.TrowAssign)
|
||||
common.Plugins["guilds"].RemoveHook("topic_create_pre_loop", guilds.TopicCreatePreLoop)
|
||||
common.Plugins["guilds"].RemoveHook("pre_render_forum", guilds.PreRenderViewForum)
|
||||
common.Plugins["guilds"].RemoveHook("simple_forum_check_pre_perms", guilds.ForumCheck)
|
||||
common.Plugins["guilds"].RemoveHook("forum_check_pre_perms", guilds.ForumCheck)
|
||||
func deactivateGuilds(plugin *common.Plugin) {
|
||||
plugin.RemoveHook("intercept_build_widgets", guilds.Widgets)
|
||||
plugin.RemoveHook("trow_assign", guilds.TrowAssign)
|
||||
plugin.RemoveHook("topic_create_pre_loop", guilds.TopicCreatePreLoop)
|
||||
plugin.RemoveHook("pre_render_forum", guilds.PreRenderViewForum)
|
||||
plugin.RemoveHook("simple_forum_check_pre_perms", guilds.ForumCheck)
|
||||
plugin.RemoveHook("forum_check_pre_perms", guilds.ForumCheck)
|
||||
common.DeregisterPluginPerm("CreateGuild")
|
||||
_ = router.RemoveFunc("/guilds/")
|
||||
_ = router.RemoveFunc("/guild/")
|
||||
|
@ -76,7 +76,7 @@ func deactivateGuilds() {
|
|||
}
|
||||
|
||||
// TODO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process
|
||||
func installGuilds() error {
|
||||
func installGuilds(plugin *common.Plugin) error {
|
||||
guildTableStmt, err := qgen.Builder.CreateTable("guilds", "utf8mb4", "utf8mb4_general_ci",
|
||||
[]qgen.DBTableColumn{
|
||||
qgen.DBTableColumn{"guildID", "int", 0, false, true, ""},
|
||||
|
@ -125,6 +125,6 @@ func installGuilds() error {
|
|||
}
|
||||
|
||||
// TO-DO; Implement an uninstallation system into Gosora. And a better installation system.
|
||||
func uninstallGuilds() error {
|
||||
func uninstallGuilds(plugin *common.Plugin) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -587,7 +587,7 @@ type GenRouter struct {
|
|||
}
|
||||
|
||||
func NewGenRouter(uploads http.Handler) (*GenRouter, error) {
|
||||
f, err := os.OpenFile("./logs/requests.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||
f, err := os.OpenFile("./logs/reqs-"+strconv.FormatInt(common.StartTime.Unix(),10)+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
@ -66,6 +66,10 @@ func gloinit() (err error) {
|
|||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
err = common.InitTemplates()
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
common.Themes, err = common.NewThemeList()
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
|
@ -342,6 +346,14 @@ func BenchmarkTopicsGuestRouteParallelWithRouter(b *testing.B) {
|
|||
obRoute(b, "/topics/")
|
||||
}
|
||||
|
||||
func BenchmarkTopicsGuestJSRouteParallelWithRouter(b *testing.B) {
|
||||
obRoute(b, "/topics/?js=1")
|
||||
}
|
||||
|
||||
func BenchmarkTopicsGuestEJSRouteParallelWithRouter(b *testing.B) {
|
||||
obRoute(b, "/topics/?ejs=1")
|
||||
}
|
||||
|
||||
func BenchmarkForumsGuestRouteParallelWithRouter(b *testing.B) {
|
||||
obRoute(b, "/forums/")
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -10,6 +10,7 @@ require (
|
|||
github.com/go-sql-driver/mysql v1.4.0
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/lib/pq v1.0.0
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329
|
||||
github.com/oschwald/geoip2-golang v1.2.1
|
||||
github.com/oschwald/maxminddb-golang v1.3.0 // indirect
|
||||
github.com/pkg/errors v0.8.0
|
||||
|
|
2
go.sum
2
go.sum
|
@ -41,6 +41,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/oschwald/geoip2-golang v1.2.1 h1:3iz+jmeJc6fuCyWeKgtXSXu7+zvkxJbHFXkMT5FVebU=
|
||||
|
|
|
@ -537,7 +537,6 @@
|
|||
"status.closed_tooltip":"Status: Closed",
|
||||
"status.pinned_tooltip":"Status: Pinned",
|
||||
|
||||
"topics_head":"All Topics",
|
||||
"topics_locked_tooltip":"You don't have the permissions needed to create a topic",
|
||||
"topics_locked_aria":"You don't have the permissions needed to make a topic anywhere",
|
||||
"topics_list_aria":"A list containing topics from every forum",
|
||||
|
@ -624,12 +623,16 @@
|
|||
|
||||
"topic.your_information":"Your information",
|
||||
|
||||
"paginator_less_than":"<",
|
||||
"paginator_greater_than":">",
|
||||
"paginator_prev_page":"Prev",
|
||||
"paginator_prev_page_aria":"Go to the previous page",
|
||||
"paginator_next_page":"Next",
|
||||
"paginator_next_page_aria":"Go to the next page",
|
||||
"paginator.less_than":"<",
|
||||
"paginator.greater_than":">",
|
||||
"paginator.first_page":"‹‹",
|
||||
"paginator.first_page_aria":"Go to the first page",
|
||||
"paginator.last_page":"››",
|
||||
"paginator.last_page_aria":"Go to the last page",
|
||||
"paginator.prev_page":"‹",
|
||||
"paginator.prev_page_aria":"Go to the previous page",
|
||||
"paginator.next_page":"›",
|
||||
"paginator.next_page_aria":"Go to the next page",
|
||||
|
||||
"profile_login_for_options":"Login for options",
|
||||
"profile_add_friend":"Add Friend",
|
||||
|
@ -754,6 +757,7 @@
|
|||
"panel_forum_edit_button":"Edit",
|
||||
"panel_forum_short_update_button":"Update",
|
||||
"panel_forum_full_edit_button":"Full Edit",
|
||||
"panel_forum_delete_are_you_sure":"Are you sure you want to delete the '%s' forum?",
|
||||
|
||||
"panel_groups_head":"Groups",
|
||||
"panel_groups_rank_prefix":"Rank ",
|
||||
|
|
19
main.go
19
main.go
|
@ -19,6 +19,7 @@ import (
|
|||
"os/signal"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -55,10 +56,6 @@ func afterDBInit() (err error) {
|
|||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
err = common.InitTemplates()
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
err = phrases.InitPhrases(common.Site.Language)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
|
@ -222,10 +219,11 @@ func main() {
|
|||
return
|
||||
}
|
||||
}()*/
|
||||
common.StartTime = time.Now()
|
||||
|
||||
// TODO: Have a file for each run with the time/date the server started as the file name?
|
||||
// TODO: Log panics with recover()
|
||||
f, err := os.OpenFile("./logs/ops.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||
f, err := os.OpenFile("./logs/ops-"+strconv.FormatInt(common.StartTime.Unix(), 10)+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -233,7 +231,6 @@ func main() {
|
|||
log.SetOutput(logWriter)
|
||||
log.Print("Running Gosora v" + common.SoftwareVersion.String())
|
||||
fmt.Println("")
|
||||
common.StartTime = time.Now()
|
||||
|
||||
// TODO: Add a flag for enabling the profiler
|
||||
if false {
|
||||
|
@ -261,6 +258,10 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = common.InitTemplates()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
common.Themes, err = common.NewThemeList()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -332,7 +333,6 @@ func main() {
|
|||
for {
|
||||
select {
|
||||
case event := <-watcher.Events:
|
||||
log.Println("event:", event)
|
||||
// TODO: Handle file deletes (and renames more graciously by removing the old version of it)
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
log.Println("modified file:", event.Name)
|
||||
|
@ -340,12 +340,15 @@ func main() {
|
|||
} else if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
log.Println("new file:", event.Name)
|
||||
err = modifiedFileEvent(event.Name)
|
||||
} else {
|
||||
log.Println("unknown event:", event)
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
common.LogError(err)
|
||||
}
|
||||
case err = <-watcher.Errors:
|
||||
common.LogError(err)
|
||||
common.LogWarning(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
12
misc_test.go
12
misc_test.go
|
@ -861,7 +861,7 @@ func TestPluginManager(t *testing.T) {
|
|||
expectNilErr(t, err)
|
||||
expect(t, hasPlugin, "Plugin bbcode should exist in the database")
|
||||
expect(t, plugin.Init != nil, "Plugin bbcode should have an init function")
|
||||
expectNilErr(t, plugin.Init())
|
||||
expectNilErr(t, plugin.Init(plugin))
|
||||
|
||||
expectNilErr(t, plugin.SetActive(true))
|
||||
expect(t, !plugin.Installable, "Plugin bbcode shouldn't be installable")
|
||||
|
@ -885,7 +885,7 @@ func TestPluginManager(t *testing.T) {
|
|||
expectNilErr(t, err)
|
||||
expect(t, hasPlugin, "Plugin bbcode should still exist in the database")
|
||||
expect(t, plugin.Deactivate != nil, "Plugin bbcode should have an init function")
|
||||
plugin.Deactivate() // Returns nothing
|
||||
plugin.Deactivate(plugin) // Returns nothing
|
||||
|
||||
// Not installable, should not be mutated
|
||||
expect(t, plugin.SetInstalled(true) == common.ErrPluginNotInstallable, "Plugin was set as installed despite not being installable")
|
||||
|
@ -949,12 +949,12 @@ func TestPluginManager(t *testing.T) {
|
|||
expect(t, !hasPlugin, "Plugin markdown shouldn't exist in the database")
|
||||
|
||||
expectNilErr(t, plugin2.AddToDatabase(true, false))
|
||||
expectNilErr(t, plugin2.Init())
|
||||
expectNilErr(t, plugin2.Init(plugin2))
|
||||
expectNilErr(t, plugin.SetActive(true))
|
||||
expectNilErr(t, plugin.Init())
|
||||
plugin2.Deactivate()
|
||||
expectNilErr(t, plugin.Init(plugin))
|
||||
plugin2.Deactivate(plugin2)
|
||||
expectNilErr(t, plugin2.SetActive(false))
|
||||
plugin.Deactivate()
|
||||
plugin.Deactivate(plugin)
|
||||
expectNilErr(t, plugin.SetActive(false))
|
||||
|
||||
// Hook tests
|
||||
|
|
|
@ -16,14 +16,14 @@ func init() {
|
|||
})
|
||||
}
|
||||
|
||||
func initAdventure() error {
|
||||
func initAdventure(plugin *common.Plugin) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Change the signature to return an error?
|
||||
func deactivateAdventure() {
|
||||
func deactivateAdventure(plugin *common.Plugin) {
|
||||
}
|
||||
|
||||
func installAdventure() error {
|
||||
func installAdventure(plugin *common.Plugin) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ func init() {
|
|||
common.Plugins.Add(&common.Plugin{UName: "bbcode", Name: "BBCode", Author: "Azareal", URL: "https://github.com/Azareal", Init: initBbcode, Deactivate: deactivateBbcode})
|
||||
}
|
||||
|
||||
func initBbcode() error {
|
||||
common.Plugins["bbcode"].AddHook("parse_assign", bbcodeFullParse)
|
||||
func initBbcode(plugin *common.Plugin) error {
|
||||
plugin.AddHook("parse_assign", bbcodeFullParse)
|
||||
|
||||
bbcodeInvalidNumber = []byte("<span style='color: red;'>[Invalid Number]</span>")
|
||||
bbcodeNoNegative = []byte("<span style='color: red;'>[No Negative Numbers]</span>")
|
||||
|
@ -49,8 +49,8 @@ func initBbcode() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func deactivateBbcode() {
|
||||
common.Plugins["bbcode"].RemoveHook("parse_assign", bbcodeFullParse)
|
||||
func deactivateBbcode(plugin *common.Plugin) {
|
||||
plugin.RemoveHook("parse_assign", bbcodeFullParse)
|
||||
}
|
||||
|
||||
func bbcodeRegexParse(msg string) string {
|
||||
|
|
|
@ -7,13 +7,13 @@ func init() {
|
|||
}
|
||||
|
||||
// init_heythere is separate from init() as we don't want the plugin to run if the plugin is disabled
|
||||
func initHeythere() error {
|
||||
common.Plugins["heythere"].AddHook("topic_reply_row_assign", heythereReply)
|
||||
func initHeythere(plugin *common.Plugin) error {
|
||||
plugin.AddHook("topic_reply_row_assign", heythereReply)
|
||||
return nil
|
||||
}
|
||||
|
||||
func deactivateHeythere() {
|
||||
common.Plugins["heythere"].RemoveHook("topic_reply_row_assign", heythereReply)
|
||||
func deactivateHeythere(plugin *common.Plugin) {
|
||||
plugin.RemoveHook("topic_reply_row_assign", heythereReply)
|
||||
}
|
||||
|
||||
func heythereReply(data ...interface{}) interface{} {
|
||||
|
|
|
@ -22,8 +22,8 @@ func init() {
|
|||
common.Plugins.Add(&common.Plugin{UName: "markdown", Name: "Markdown", Author: "Azareal", URL: "https://github.com/Azareal", Init: initMarkdown, Deactivate: deactivateMarkdown})
|
||||
}
|
||||
|
||||
func initMarkdown() error {
|
||||
common.Plugins["markdown"].AddHook("parse_assign", markdownParse)
|
||||
func initMarkdown(plugin *common.Plugin) error {
|
||||
plugin.AddHook("parse_assign", markdownParse)
|
||||
|
||||
markdownUnclosedElement = []byte("<span style='color: red;'>[Unclosed Element]</span>")
|
||||
|
||||
|
@ -38,8 +38,8 @@ func initMarkdown() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func deactivateMarkdown() {
|
||||
common.Plugins["markdown"].RemoveHook("parse_assign", markdownParse)
|
||||
func deactivateMarkdown(plugin *common.Plugin) {
|
||||
plugin.RemoveHook("parse_assign", markdownParse)
|
||||
}
|
||||
|
||||
// An adapter for the parser, so that the parser can call itself recursively.
|
||||
|
|
|
@ -31,9 +31,9 @@ func init() {
|
|||
common.Plugins.Add(&common.Plugin{UName: "skeleton", Name: "Skeleton", Author: "Azareal", Init: initSkeleton, Activate: activateSkeleton, Deactivate: deactivateSkeleton})
|
||||
}
|
||||
|
||||
func initSkeleton() error { return nil }
|
||||
func initSkeleton(plugin *common.Plugin) error { return nil }
|
||||
|
||||
// Any errors encountered while trying to activate the plugin are reported back to the admin and the activation is aborted
|
||||
func activateSkeleton() error { return nil }
|
||||
func activateSkeleton(plugin *common.Plugin) error { return nil }
|
||||
|
||||
func deactivateSkeleton() {}
|
||||
func deactivateSkeleton(plugin *common.Plugin) {}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package main
|
||||
|
||||
import "strconv"
|
||||
import "testing"
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/Azareal/Gosora/common"
|
||||
)
|
||||
|
||||
// go test -v
|
||||
|
||||
|
@ -22,7 +26,7 @@ func (tlist *MEPairList) Add(msg string, expects string) {
|
|||
|
||||
func TestBBCodeRender(t *testing.T) {
|
||||
//t.Skip()
|
||||
err := initBbcode()
|
||||
err := initBbcode(common.Plugins["bbcode"])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -211,7 +215,7 @@ func TestBBCodeRender(t *testing.T) {
|
|||
|
||||
func TestMarkdownRender(t *testing.T) {
|
||||
//t.Skip()
|
||||
err := initMarkdown()
|
||||
err := initMarkdown(common.Plugins["markdown"])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ go build -o QueryGen "./cmd/query_gen"
|
|||
echo "Running the query generator"
|
||||
./QueryGen
|
||||
|
||||
echo "Generating the JSON handlers"
|
||||
easyjson -pkg common
|
||||
|
||||
echo "Building Gosora"
|
||||
go build -o Gosora
|
||||
|
||||
|
|
133
public/global.js
133
public/global.js
|
@ -303,6 +303,39 @@ function runWebSockets() {
|
|||
});
|
||||
})();
|
||||
|
||||
// TODO: Use these in .filter_item and pass back an item count from the backend to work with here
|
||||
// Ported from common/parser.go
|
||||
function PageOffset(count, page, perPage) {
|
||||
let offset = 0;
|
||||
let lastPage = LastPage(count, perPage)
|
||||
if(page > 1) {
|
||||
offset = (perPage * page) - perPage
|
||||
} else if (page == -1) {
|
||||
page = lastPage
|
||||
offset = (perPage * page) - perPage
|
||||
} else {
|
||||
page = 1
|
||||
}
|
||||
|
||||
// We don't want the offset to overflow the slices, if everything's in memory
|
||||
if(offset >= (count - 1)) offset = 0;
|
||||
return {Offset:offset, Page:page, LastPage:lastPage}
|
||||
}
|
||||
function LastPage(count, perPage) {
|
||||
return (count / perPage) + 1
|
||||
}
|
||||
function Paginate(count, perPage, maxPages) {
|
||||
if(count < perPage) return [1];
|
||||
let page = 0;
|
||||
let out = [];
|
||||
for(let current = 0; current < count; current += perPage){
|
||||
page++;
|
||||
out.push(page);
|
||||
if(out.length >= maxPages) break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function mainInit(){
|
||||
runInitHook("start_init");
|
||||
|
||||
|
@ -343,9 +376,7 @@ function mainInit(){
|
|||
error: ajaxError,
|
||||
success: function (data, status, xhr) {
|
||||
if("success" in data) {
|
||||
if(data["success"] == "1") {
|
||||
return;
|
||||
}
|
||||
if(data["success"] == "1") return;
|
||||
}
|
||||
// addNotice("Failed to add a like: {err}")
|
||||
likeButton.classList.add("add_like");
|
||||
|
@ -369,6 +400,102 @@ function mainInit(){
|
|||
}
|
||||
});
|
||||
|
||||
function rebuildPaginator(lastPage) {
|
||||
let urlParams = new URLSearchParams(window.location.search);
|
||||
let page = urlParams.get('page');
|
||||
if(page=="") page = 1;
|
||||
let stopAtPage = lastPage;
|
||||
if(stopAtPage>5) stopAtPage = 5;
|
||||
|
||||
let pageList = [];
|
||||
for(let i = 0; i < stopAtPage;i++) pageList.push(i+1);
|
||||
//$(".pageset").html(Template_paginator({PageList: pageList, Page: page, LastPage: lastPage}));
|
||||
let ok = false;
|
||||
$(".pageset").each(function(){
|
||||
this.outerHTML = Template_paginator({PageList: pageList, Page: page, LastPage: lastPage});
|
||||
ok = true;
|
||||
});
|
||||
if(!ok) {
|
||||
$(Template_paginator({PageList: pageList, Page: page, LastPage: lastPage})).insertAfter("#topic_list");
|
||||
}
|
||||
}
|
||||
|
||||
function rebindPaginator() {
|
||||
$(".pageitem a").unbind("click");
|
||||
$(".pageitem a").click(function() {
|
||||
event.preventDefault();
|
||||
// TODO: Take mostviewed into account
|
||||
let url = "//"+window.location.host+window.location.pathname;
|
||||
let urlParams = new URLSearchParams(window.location.search);
|
||||
urlParams.set("page",new URLSearchParams(this.getAttribute("href")).get("page"));
|
||||
let q = "?";
|
||||
for(let item of urlParams.entries()) q += item[0]+"="+item[1]+"&";
|
||||
if(q.length>1) q = q.slice(0,-1);
|
||||
|
||||
// TODO: Try to de-duplicate some of these fetch calls
|
||||
fetch(url+q+"&js=1", {credentials: "same-origin"})
|
||||
.then((resp) => resp.json())
|
||||
.then((data) => {
|
||||
if(!"Topics" in data) throw("no Topics in data");
|
||||
let topics = data["Topics"];
|
||||
|
||||
// TODO: Fix the data race where the function hasn't been loaded yet
|
||||
let out = "";
|
||||
for(let i = 0; i < topics.length;i++) out += Template_topics_topic(topics[i]);
|
||||
$(".topic_list").html(out);
|
||||
|
||||
let obj = {Title: document.title, Url: url+q};
|
||||
history.pushState(obj, obj.Title, obj.Url);
|
||||
rebuildPaginator(data.LastPage);
|
||||
rebindPaginator();
|
||||
}).catch((ex) => {
|
||||
console.log("Unable to get script '"+url+q+"&js=1"+"'");
|
||||
console.log("ex: ", ex);
|
||||
console.trace();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Render a headless topics.html instead of the individual topic rows and a bit of JS glue
|
||||
$(".filter_item").click(function(event) {
|
||||
if(!window.location.pathname.startsWith("/topics/")) return
|
||||
event.preventDefault();
|
||||
let that = this;
|
||||
let fid = this.getAttribute("data-fid");
|
||||
// TODO: Take mostviewed into account
|
||||
let url = "//"+window.location.host+"/topics/?fids="+fid;
|
||||
|
||||
fetch(url+"&js=1", {credentials: "same-origin"})
|
||||
.then((resp) => resp.json())
|
||||
.then((data) => {
|
||||
if(!"Topics" in data) throw("no Topics in data");
|
||||
let topics = data["Topics"];
|
||||
|
||||
// TODO: Fix the data race where the function hasn't been loaded yet
|
||||
let out = "";
|
||||
for(let i = 0; i < topics.length;i++) out += Template_topics_topic(topics[i]);
|
||||
$(".topic_list").html(out);
|
||||
|
||||
let obj = {Title: document.title, Url: url};
|
||||
history.pushState(obj, obj.Title, obj.Url);
|
||||
rebuildPaginator(data.LastPage)
|
||||
rebindPaginator();
|
||||
|
||||
$(".filter_item").each(function(){
|
||||
this.classList.remove("filter_selected");
|
||||
});
|
||||
that.classList.add("filter_selected");
|
||||
$(".topic_list_title h1").text(that.innerText);
|
||||
}).catch((ex) => {
|
||||
console.log("Unable to get script '"+url+"&js=1"+"'");
|
||||
console.log("ex: ", ex);
|
||||
console.trace();
|
||||
});
|
||||
});
|
||||
|
||||
if (document.getElementById("topicsItemList")!==null) rebindPaginator();
|
||||
if (document.getElementById("forumItemList")!==null) rebindPaginator();
|
||||
|
||||
$(".open_edit").click((event) => {
|
||||
event.preventDefault();
|
||||
$('.hide_on_edit').addClass("edit_opened");
|
||||
|
|
|
@ -90,12 +90,23 @@ function loadScript(name, callback) {
|
|||
});
|
||||
}
|
||||
|
||||
/*
|
||||
function loadTmpl(name,callback) {
|
||||
let url = "/static/"+name
|
||||
let worker = new Worker(url);
|
||||
}
|
||||
*/
|
||||
|
||||
function DoNothingButPassBack(item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
function RelativeTime(date) {
|
||||
return date;
|
||||
}
|
||||
|
||||
function initPhrases() {
|
||||
fetchPhrases("status,topic_list,alerts")
|
||||
fetchPhrases("status,topic_list,alerts,paginator")
|
||||
}
|
||||
|
||||
function fetchPhrases(plist) {
|
||||
|
@ -131,6 +142,19 @@ function fetchPhrases(plist) {
|
|||
|
||||
(() => {
|
||||
runInitHook("pre_iife");
|
||||
let toLoad = 2;
|
||||
// TODO: Shunt this into loggedIn if there aren't any search and filter widgets?
|
||||
loadScript("template_topics_topic.js", () => {
|
||||
console.log("Loaded template_topics_topic.js");
|
||||
toLoad--;
|
||||
if(toLoad===0) initPhrases();
|
||||
});
|
||||
loadScript("template_paginator.js", () => {
|
||||
console.log("Loaded template_paginator.js");
|
||||
toLoad--;
|
||||
if(toLoad===0) initPhrases();
|
||||
});
|
||||
|
||||
let loggedIn = document.head.querySelector("[property='x-loggedin']").content;
|
||||
if(loggedIn=="true") {
|
||||
fetch("/api/me/")
|
||||
|
@ -141,13 +165,6 @@ function fetchPhrases(plist) {
|
|||
me = data;
|
||||
runInitHook("pre_init");
|
||||
});
|
||||
|
||||
let toLoad = 1;
|
||||
loadScript("template_topics_topic.js", () => {
|
||||
console.log("Loaded template_topics_topic.js");
|
||||
toLoad--;
|
||||
if(toLoad===0) initPhrases();
|
||||
});
|
||||
} else {
|
||||
me = {User:{ID:0,Session:""},Site:{"MaxRequestSize":0}};
|
||||
runInitHook("pre_init");
|
||||
|
|
|
@ -63,6 +63,10 @@ func (build *Accumulator) prepare(res string, err error) *sql.Stmt {
|
|||
return stmt
|
||||
}
|
||||
|
||||
func (build *Accumulator) RawPrepare(res string) *sql.Stmt {
|
||||
return build.prepare(res, nil)
|
||||
}
|
||||
|
||||
func (build *Accumulator) query(query string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
err = build.FirstError()
|
||||
if err != nil {
|
||||
|
|
|
@ -379,7 +379,7 @@ type GenRouter struct {
|
|||
}
|
||||
|
||||
func NewGenRouter(uploads http.Handler) (*GenRouter, error) {
|
||||
f, err := os.OpenFile("./logs/requests.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||
f, err := os.OpenFile("./logs/reqs-"+strconv.FormatInt(common.StartTime.Unix(),10)+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
37
routes.go
37
routes.go
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Gosora Route Handlers
|
||||
* Copyright Azareal 2016 - 2019
|
||||
* Copyright Azareal 2016 - 2020
|
||||
*
|
||||
*/
|
||||
package main
|
||||
|
@ -138,6 +138,13 @@ var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(common.Day))
|
|||
|
||||
// TODO: Be careful with exposing the panel phrases here, maybe move them into a different namespace? We also need to educate the admin that phrases aren't necessarily secret
|
||||
// TODO: Move to the routes package
|
||||
var phraseWhitelist = []string{
|
||||
"topic",
|
||||
"status",
|
||||
"alerts",
|
||||
"paginator",
|
||||
}
|
||||
|
||||
func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
// TODO: Don't make this too JSON dependent so that we can swap in newer more efficient formats
|
||||
h := w.Header()
|
||||
|
@ -148,7 +155,6 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
if err != nil {
|
||||
return common.PreErrorJS("Bad Form", w, r)
|
||||
}
|
||||
|
||||
query := r.FormValue("query")
|
||||
if query == "" {
|
||||
return common.PreErrorJS("No query provided", w, r)
|
||||
|
@ -183,12 +189,20 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
|
||||
var plist map[string]string
|
||||
// A little optimisation to avoid copying entries from one map to the other, if we don't have to mutate it
|
||||
// TODO: Reduce the amount of duplication here
|
||||
if len(positives) > 1 {
|
||||
plist = make(map[string]string)
|
||||
for _, positive := range positives {
|
||||
// ! Constrain it to topic and status phrases for now
|
||||
if !strings.HasPrefix(positive, "topic") && !strings.HasPrefix(positive, "status") && !strings.HasPrefix(positive, "alerts") {
|
||||
return common.PreErrorJS("Not implemented!", w, r)
|
||||
// ! Constrain it to a subset of phrases for now
|
||||
var ok = false
|
||||
for _, item := range phraseWhitelist {
|
||||
if strings.HasPrefix(positive, item) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return common.PreErrorJS("Outside of phrase prefix whitelist", w, r)
|
||||
}
|
||||
pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positive)
|
||||
if !ok {
|
||||
|
@ -199,9 +213,16 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// ! Constrain it to topic and status phrases for now
|
||||
if !strings.HasPrefix(positives[0], "topic") && !strings.HasPrefix(positives[0], "status") && !strings.HasPrefix(positives[0], "alerts") {
|
||||
return common.PreErrorJS("Not implemented!", w, r)
|
||||
// ! Constrain it to a subset of phrases for now
|
||||
var ok = false
|
||||
for _, item := range phraseWhitelist {
|
||||
if strings.HasPrefix(positives[0], item) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return common.PreErrorJS("Outside of phrase prefix whitelist", w, r)
|
||||
}
|
||||
pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positives[0])
|
||||
if !ok {
|
||||
|
|
|
@ -2,6 +2,7 @@ package routes
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
|
@ -27,6 +28,7 @@ func init() {
|
|||
})
|
||||
}
|
||||
|
||||
// TODO: Retire this in favour of an alias for /topics/?
|
||||
func ViewForum(w http.ResponseWriter, r *http.Request, user common.User, header *common.Header, sfid string) common.RouteError {
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
_, fid, err := ParseSEOURL(sfid)
|
||||
|
@ -41,7 +43,6 @@ func ViewForum(w http.ResponseWriter, r *http.Request, user common.User, header
|
|||
if !user.Perms.ViewTopic {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
header.Zone = "view_forum"
|
||||
header.Path = "/forums/"
|
||||
|
||||
// TODO: Fix this double-check
|
||||
|
@ -108,6 +109,18 @@ func ViewForum(w http.ResponseWriter, r *http.Request, user common.User, header
|
|||
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||
}
|
||||
header.Zone = "view_forum"
|
||||
header.ZoneID = forum.ID
|
||||
|
||||
// TODO: Reduce the amount of boilerplate here
|
||||
if r.FormValue("js") == "1" {
|
||||
outBytes, err := json.Marshal(wsTopicList(topicList, lastPage))
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
w.Write(outBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
pageList := common.Paginate(forum.TopicCount, common.Config.ItemsPerPage, 5)
|
||||
pi := common.ForumPage{header, topicList, forum, common.Paginator{pageList, page, lastPage}}
|
||||
|
|
|
@ -67,7 +67,7 @@ func CustomPage(w http.ResponseWriter, r *http.Request, user common.User, header
|
|||
}
|
||||
|
||||
// ! Is this safe?
|
||||
if common.Templates.Lookup("page_"+name+".html") == nil {
|
||||
if common.DefaultTemplates.Lookup("page_"+name+".html") == nil {
|
||||
return common.NotFound(w, r, header)
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ func CustomPage(w http.ResponseWriter, r *http.Request, user common.User, header
|
|||
if common.RunPreRenderHook("pre_render_tmpl_page", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.Templates.ExecuteTemplate(w, "page_"+name+".html", pi)
|
||||
err = header.Theme.RunTmpl("page_"+name, pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ func AnalyticsViews(w http.ResponseWriter, r *http.Request, user common.User) co
|
|||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_views", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_views", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.User, route string) common.RouteError {
|
||||
|
@ -191,7 +191,7 @@ func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.Use
|
|||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
pi := common.PanelAnalyticsRoutePage{basePage, common.SanitiseSingleLine(route), graph, viewItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_route_views", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_route_views", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.User, agent string) common.RouteError {
|
||||
|
@ -234,7 +234,7 @@ func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.Use
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentPage{basePage, agent, friendlyAgent, graph, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_agent_views", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_agent_views", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsForumViews(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||
|
@ -278,7 +278,7 @@ func AnalyticsForumViews(w http.ResponseWriter, r *http.Request, user common.Use
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentPage{basePage, sfid, forum.Name, graph, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_forum_views", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_forum_views", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.User, system string) common.RouteError {
|
||||
|
@ -319,7 +319,7 @@ func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.Us
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentPage{basePage, system, friendlySystem, graph, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_system_views", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_system_views", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.User, lang string) common.RouteError {
|
||||
|
@ -359,7 +359,7 @@ func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentPage{basePage, lang, friendlyLang, graph, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_lang_views", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_lang_views", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common.User, domain string) common.RouteError {
|
||||
|
@ -393,7 +393,7 @@ func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common.
|
|||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
pi := common.PanelAnalyticsAgentPage{basePage, common.SanitiseSingleLine(domain), "", graph, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_referrer_views", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_referrer_views", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsTopics(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -428,7 +428,7 @@ func AnalyticsTopics(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_topics", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_topics", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -463,7 +463,7 @@ func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user common.User) co
|
|||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_posts", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_posts", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func analyticsRowsToNameMap(rows *sql.Rows) (map[string]int, error) {
|
||||
|
@ -526,7 +526,7 @@ func AnalyticsForums(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentsPage{basePage, forumItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_forums", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_forums", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -559,7 +559,7 @@ func AnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsRoutesPage{basePage, routeItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_routes", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_routes", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsAgents(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -597,7 +597,7 @@ func AnalyticsAgents(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentsPage{basePage, agentItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_agents", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_agents", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsSystems(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -635,7 +635,7 @@ func AnalyticsSystems(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentsPage{basePage, systemItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_systems", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_systems", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsLanguages(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -674,7 +674,7 @@ func AnalyticsLanguages(w http.ResponseWriter, r *http.Request, user common.User
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentsPage{basePage, langItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_langs", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_langs", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -707,5 +707,5 @@ func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User
|
|||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentsPage{basePage, refItems, timeRange.Range}
|
||||
return renderTemplate("panel_analytics_referrers", w, r, user, &pi)
|
||||
return renderTemplate("panel_analytics_referrers", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
|
|
@ -51,5 +51,5 @@ func Backups(w http.ResponseWriter, r *http.Request, user common.User, backupURL
|
|||
}
|
||||
|
||||
pi := common.PanelBackupPage{basePage, backupList}
|
||||
return renderTemplate("panel_backups", w, r, user, &pi)
|
||||
return renderTemplate("panel_backups", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
|
|
@ -21,12 +21,12 @@ func successRedirect(dest string, w http.ResponseWriter, r *http.Request, isJs b
|
|||
return nil
|
||||
}
|
||||
|
||||
func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, user common.User, pi interface{}) common.RouteError {
|
||||
if common.RunPreRenderHook("pre_render_"+tmplName, w, r, &user, pi) {
|
||||
func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, header *common.Header, pi interface{}) common.RouteError {
|
||||
if common.RunPreRenderHook("pre_render_"+tmplName, w, r, &header.CurrentUser, pi) {
|
||||
return nil
|
||||
}
|
||||
// TODO: Prepend this with panel_?
|
||||
err := common.Templates.ExecuteTemplate(w, tmplName+".html", pi)
|
||||
err := header.Theme.RunTmpl(tmplName, pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
|
|
@ -209,5 +209,5 @@ func Dashboard(w http.ResponseWriter, r *http.Request, user common.User) common.
|
|||
}
|
||||
|
||||
pi := common.PanelDashboardPage{basePage, gridElements}
|
||||
return renderTemplate("panel_dashboard", w, r, user, &pi)
|
||||
return renderTemplate("panel_dashboard", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
|
|
@ -42,5 +42,5 @@ func Debug(w http.ResponseWriter, r *http.Request, user common.User) common.Rout
|
|||
runtime.ReadMemStats(&memStats)
|
||||
|
||||
pi := common.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, memStats}
|
||||
return renderTemplate("panel_debug", w, r, user, &pi)
|
||||
return renderTemplate("panel_debug", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func Forums(w http.ResponseWriter, r *http.Request, user common.User) common.Rou
|
|||
}
|
||||
|
||||
pi := common.PanelPage{basePage, forumList, nil}
|
||||
return renderTemplate("panel_forums", w, r, user, &pi)
|
||||
return renderTemplate("panel_forums", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func ForumsCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -96,19 +96,14 @@ func ForumsDelete(w http.ResponseWriter, r *http.Request, user common.User, sfid
|
|||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Make this a phrase
|
||||
confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
||||
confirmMsg := phrases.GetTmplPhrasef("panel_forum_delete_are_you_sure", forum.Name)
|
||||
yousure := common.AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirmMsg}
|
||||
|
||||
pi := common.PanelPage{basePage, tList, yousure}
|
||||
if common.RunPreRenderHook("pre_render_panel_delete_forum", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.Templates.ExecuteTemplate(w, "are_you_sure.html", pi)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
return renderTemplate("panel_are_you_sure", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func ForumsDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||
|
@ -184,15 +179,7 @@ func ForumsEdit(w http.ResponseWriter, r *http.Request, user common.User, sfid s
|
|||
}
|
||||
|
||||
pi := common.PanelEditForumPage{basePage, forum.ID, forum.Name, forum.Desc, forum.Active, forum.Preset, gplist}
|
||||
if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.Templates.ExecuteTemplate(w, "panel_forum_edit.html", pi)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
return renderTemplate("panel_forum_edit", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func ForumsEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||
|
@ -350,15 +337,7 @@ func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, user common.
|
|||
}
|
||||
|
||||
pi := common.PanelEditForumGroupPage{basePage, forum.ID, gid, forum.Name, forum.Desc, forum.Active, forum.Preset, formattedPermList}
|
||||
if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.Templates.ExecuteTemplate(w, "panel_forum_edit_perms.html", pi)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
return renderTemplate("panel_forum_edit_perms", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func ForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Request, user common.User, paramList string) common.RouteError {
|
||||
|
|
|
@ -59,7 +59,7 @@ func Groups(w http.ResponseWriter, r *http.Request, user common.User) common.Rou
|
|||
|
||||
pageList := common.Paginate(basePage.Stats.Groups, perPage, 5)
|
||||
pi := common.PanelGroupPage{basePage, groupList, common.Paginator{pageList, page, lastPage}}
|
||||
return renderTemplate("panel_groups", w, r, user, &pi)
|
||||
return renderTemplate("panel_groups", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func GroupsEdit(w http.ResponseWriter, r *http.Request, user common.User, sgid string) common.RouteError {
|
||||
|
@ -107,7 +107,7 @@ func GroupsEdit(w http.ResponseWriter, r *http.Request, user common.User, sgid s
|
|||
disableRank := !user.Perms.EditGroupGlobalPerms || (group.ID == 6)
|
||||
|
||||
pi := common.PanelEditGroupPage{basePage, group.ID, group.Name, group.Tag, rank, disableRank}
|
||||
return renderTemplate("panel_group_edit", w, r, user, pi)
|
||||
return renderTemplate("panel_group_edit", w, r, basePage.Header, pi)
|
||||
}
|
||||
|
||||
func GroupsEditPerms(w http.ResponseWriter, r *http.Request, user common.User, sgid string) common.RouteError {
|
||||
|
@ -186,7 +186,7 @@ func GroupsEditPerms(w http.ResponseWriter, r *http.Request, user common.User, s
|
|||
addGlobalPerm("UploadFiles", group.Perms.UploadFiles)
|
||||
|
||||
pi := common.PanelEditGroupPermsPage{basePage, group.ID, group.Name, localPerms, globalPerms}
|
||||
return renderTemplate("panel_group_edit_perms", w, r, user, pi)
|
||||
return renderTemplate("panel_group_edit_perms", w, r, basePage.Header, pi)
|
||||
}
|
||||
|
||||
func GroupsEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, sgid string) common.RouteError {
|
||||
|
|
|
@ -33,7 +33,7 @@ func LogsRegs(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
|||
|
||||
pageList := common.Paginate(logCount, perPage, 5)
|
||||
pi := common.PanelRegLogsPage{basePage, llist, common.Paginator{pageList, page, lastPage}}
|
||||
return renderTemplate("panel_reglogs", w, r, user, &pi)
|
||||
return renderTemplate("panel_reglogs", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
// TODO: Log errors when something really screwy is going on?
|
||||
|
@ -125,7 +125,7 @@ func LogsMod(w http.ResponseWriter, r *http.Request, user common.User) common.Ro
|
|||
|
||||
pageList := common.Paginate(logCount, perPage, 5)
|
||||
pi := common.PanelLogsPage{basePage, llist, common.Paginator{pageList, page, lastPage}}
|
||||
return renderTemplate("panel_modlogs", w, r, user, &pi)
|
||||
return renderTemplate("panel_modlogs", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func LogsAdmin(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -152,5 +152,5 @@ func LogsAdmin(w http.ResponseWriter, r *http.Request, user common.User) common.
|
|||
|
||||
pageList := common.Paginate(logCount, perPage, 5)
|
||||
pi := common.PanelLogsPage{basePage, llist, common.Paginator{pageList, page, lastPage}}
|
||||
return renderTemplate("panel_adminlogs", w, r, user, &pi)
|
||||
return renderTemplate("panel_adminlogs", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ func Pages(w http.ResponseWriter, r *http.Request, user common.User) common.Rout
|
|||
|
||||
pageList := common.Paginate(pageCount, perPage, 5)
|
||||
pi := common.PanelCustomPagesPage{basePage, cPages, common.Paginator{pageList, page, lastPage}}
|
||||
return renderTemplate("panel_pages", w, r, user, &pi)
|
||||
return renderTemplate("panel_pages", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -90,7 +90,7 @@ func PagesEdit(w http.ResponseWriter, r *http.Request, user common.User, spid st
|
|||
}
|
||||
|
||||
pi := common.PanelCustomPageEditPage{basePage, page}
|
||||
return renderTemplate("panel_pages_edit", w, r, user, &pi)
|
||||
return renderTemplate("panel_pages_edit", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, spid string) common.RouteError {
|
||||
|
|
|
@ -23,7 +23,7 @@ func Plugins(w http.ResponseWriter, r *http.Request, user common.User) common.Ro
|
|||
}
|
||||
|
||||
pi := common.PanelPage{basePage, pluginList, nil}
|
||||
return renderTemplate("panel_plugins", w, r, user, &pi)
|
||||
return renderTemplate("panel_plugins", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
// TODO: Abstract more of the plugin activation / installation / deactivation logic, so we can test all that more reliably and easily
|
||||
|
@ -51,7 +51,7 @@ func PluginsActivate(w http.ResponseWriter, r *http.Request, user common.User, u
|
|||
}
|
||||
|
||||
if plugin.Activate != nil {
|
||||
err = plugin.Activate()
|
||||
err = plugin.Activate(plugin)
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func PluginsActivate(w http.ResponseWriter, r *http.Request, user common.User, u
|
|||
}
|
||||
|
||||
log.Printf("Activating plugin '%s'", plugin.Name)
|
||||
err = plugin.Init()
|
||||
err = plugin.Init(plugin)
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ func PluginsDeactivate(w http.ResponseWriter, r *http.Request, user common.User,
|
|||
return common.InternalError(err, w, r)
|
||||
}
|
||||
if plugin.Deactivate != nil {
|
||||
plugin.Deactivate()
|
||||
plugin.Deactivate(plugin)
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/panel/plugins/", http.StatusSeeOther)
|
||||
|
@ -143,14 +143,14 @@ func PluginsInstall(w http.ResponseWriter, r *http.Request, user common.User, un
|
|||
}
|
||||
|
||||
if plugin.Install != nil {
|
||||
err = plugin.Install()
|
||||
err = plugin.Install(plugin)
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
}
|
||||
|
||||
if plugin.Activate != nil {
|
||||
err = plugin.Activate()
|
||||
err = plugin.Activate(plugin)
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ func PluginsInstall(w http.ResponseWriter, r *http.Request, user common.User, un
|
|||
}
|
||||
|
||||
log.Printf("Installing plugin '%s'", plugin.Name)
|
||||
err = plugin.Init()
|
||||
err = plugin.Init(plugin)
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ func Settings(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
|||
}
|
||||
|
||||
pi := common.PanelPage{basePage, tList, settingList}
|
||||
return renderTemplate("panel_settings", w, r, user, &pi)
|
||||
return renderTemplate("panel_settings", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func SettingEdit(w http.ResponseWriter, r *http.Request, user common.User, sname string) common.RouteError {
|
||||
|
@ -90,7 +90,7 @@ func SettingEdit(w http.ResponseWriter, r *http.Request, user common.User, sname
|
|||
|
||||
pSetting := &common.PanelSetting{setting, phrases.GetSettingPhrase(setting.Name)}
|
||||
pi := common.PanelSettingPage{basePage, itemList, pSetting}
|
||||
return renderTemplate("panel_setting", w, r, user, &pi)
|
||||
return renderTemplate("panel_setting", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func SettingEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, sname string) common.RouteError {
|
||||
|
|
|
@ -34,7 +34,7 @@ func Themes(w http.ResponseWriter, r *http.Request, user common.User) common.Rou
|
|||
}
|
||||
|
||||
pi := common.PanelThemesPage{basePage, pThemeList, vThemeList}
|
||||
return renderTemplate("panel_themes", w, r, user, &pi)
|
||||
return renderTemplate("panel_themes", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func ThemesSetDefault(w http.ResponseWriter, r *http.Request, user common.User, uname string) common.RouteError {
|
||||
|
@ -86,7 +86,7 @@ func ThemesMenus(w http.ResponseWriter, r *http.Request, user common.User) commo
|
|||
}
|
||||
|
||||
pi := common.PanelMenuListPage{basePage, menuList}
|
||||
return renderTemplate("panel_themes_menus", w, r, user, &pi)
|
||||
return renderTemplate("panel_themes_menus", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func ThemesMenusEdit(w http.ResponseWriter, r *http.Request, user common.User, smid string) common.RouteError {
|
||||
|
@ -133,7 +133,7 @@ func ThemesMenusEdit(w http.ResponseWriter, r *http.Request, user common.User, s
|
|||
}
|
||||
|
||||
pi := common.PanelMenuPage{basePage, mid, menuList}
|
||||
return renderTemplate("panel_themes_menus_items", w, r, user, &pi)
|
||||
return renderTemplate("panel_themes_menus_items", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func ThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, user common.User, sitemID string) common.RouteError {
|
||||
|
@ -159,7 +159,7 @@ func ThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, user common.User
|
|||
}
|
||||
|
||||
pi := common.PanelMenuItemPage{basePage, menuItem}
|
||||
return renderTemplate("panel_themes_menus_item_edit", w, r, user, &pi)
|
||||
return renderTemplate("panel_themes_menus_item_edit", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func themesMenuItemSetters(r *http.Request, menuItem common.MenuItem) common.MenuItem {
|
||||
|
@ -361,7 +361,7 @@ func ThemesWidgets(w http.ResponseWriter, r *http.Request, user common.User) com
|
|||
}
|
||||
|
||||
pi := common.PanelWidgetListPage{basePage, docks, common.WidgetEdit{&common.Widget{ID: 0, Type: "simple"}, make(map[string]string)}}
|
||||
return renderTemplate("panel_themes_widgets", w, r, user, &pi)
|
||||
return renderTemplate("panel_themes_widgets", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func widgetsParseInputs(r *http.Request, widget *common.Widget) (*common.WidgetEdit, error) {
|
||||
|
|
|
@ -25,7 +25,7 @@ func Users(w http.ResponseWriter, r *http.Request, user common.User) common.Rout
|
|||
|
||||
pageList := common.Paginate(basePage.Stats.Users, perPage, 5)
|
||||
pi := common.PanelUserPage{basePage, users, common.Paginator{pageList, page, lastPage}}
|
||||
return renderTemplate("panel_users", w, r, user, &pi)
|
||||
return renderTemplate("panel_users", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func UsersEdit(w http.ResponseWriter, r *http.Request, user common.User, suid string) common.RouteError {
|
||||
|
@ -75,14 +75,7 @@ func UsersEdit(w http.ResponseWriter, r *http.Request, user common.User, suid st
|
|||
}
|
||||
|
||||
pi := common.PanelPage{basePage, groupList, targetUser}
|
||||
if common.RunPreRenderHook("pre_render_panel_edit_user", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.Templates.ExecuteTemplate(w, "panel_user_edit.html", pi)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
return renderTemplate("panel_user_edit", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func UsersEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, suid string) common.RouteError {
|
||||
|
|
|
@ -25,7 +25,7 @@ func WordFilters(w http.ResponseWriter, r *http.Request, user common.User) commo
|
|||
}
|
||||
|
||||
pi := common.PanelPage{basePage, tList, filterList}
|
||||
return renderTemplate("panel_word_filters", w, r, user, &pi)
|
||||
return renderTemplate("panel_word_filters", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func WordFiltersCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
|
@ -67,7 +67,7 @@ func WordFiltersEdit(w http.ResponseWriter, r *http.Request, user common.User, w
|
|||
_ = wfid
|
||||
|
||||
pi := common.PanelPage{basePage, tList, nil}
|
||||
return renderTemplate("panel_word_filters_edit", w, r, user, &pi)
|
||||
return renderTemplate("panel_word_filters_edit", w, r, basePage.Header, &pi)
|
||||
}
|
||||
|
||||
func WordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
||||
|
|
|
@ -4,17 +4,14 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Azareal/Gosora/common"
|
||||
"github.com/Azareal/Gosora/common/phrases"
|
||||
)
|
||||
|
||||
// TODO: Implement search
|
||||
func TopicList(w http.ResponseWriter, r *http.Request, user common.User, header *common.Header) common.RouteError {
|
||||
header.Title = phrases.GetTitlePhrase("topics")
|
||||
header.Zone = "topics"
|
||||
header.Path = "/topics/"
|
||||
header.MetaDesc = header.Settings["meta_desc"].(string)
|
||||
|
||||
group, err := common.Groups.Get(user.Group)
|
||||
if err != nil {
|
||||
log.Printf("Group #%d doesn't exist despite being used by common.User #%d", user.Group, user.ID)
|
||||
|
@ -23,29 +20,70 @@ func TopicList(w http.ResponseWriter, r *http.Request, user common.User, header
|
|||
|
||||
// Get the current page
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
sfids := r.FormValue("fids")
|
||||
var fids []int
|
||||
if sfids != "" {
|
||||
for _, sfid := range strings.Split(sfids, ",") {
|
||||
fid, err := strconv.Atoi(sfid)
|
||||
if err != nil {
|
||||
return common.LocalError("Invalid fid", w, r, user)
|
||||
}
|
||||
fids = append(fids, fid)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Pass a struct back rather than passing back so many variables
|
||||
var topicList []*common.TopicsRow
|
||||
var forumList []common.Forum
|
||||
var paginator common.Paginator
|
||||
if user.IsSuperAdmin {
|
||||
topicList, forumList, paginator, err = common.TopicList.GetList(page, "")
|
||||
topicList, forumList, paginator, err = common.TopicList.GetList(page, "", fids)
|
||||
} else {
|
||||
topicList, forumList, paginator, err = common.TopicList.GetListByGroup(group, page, "")
|
||||
topicList, forumList, paginator, err = common.TopicList.GetListByGroup(group, page, "", fids)
|
||||
}
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// ! Need an inline error not a page level error
|
||||
if len(topicList) == 0 {
|
||||
return common.NotFound(w, r, header)
|
||||
}
|
||||
|
||||
// TODO: Reduce the amount of boilerplate here
|
||||
if r.FormValue("js") == "1" {
|
||||
outBytes, err := wsTopicList(topicList, paginator.LastPage).MarshalJSON()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
w.Write(outBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
header.Title = phrases.GetTitlePhrase("topics")
|
||||
header.Zone = "topics"
|
||||
header.Path = "/topics/"
|
||||
header.MetaDesc = header.Settings["meta_desc"].(string)
|
||||
if len(fids) == 1 {
|
||||
forum, err := common.Forums.Get(fids[0])
|
||||
if err != nil {
|
||||
return common.LocalError("Invalid fid forum", w, r, user)
|
||||
}
|
||||
header.Title = forum.Name
|
||||
header.ZoneID = forum.ID
|
||||
}
|
||||
|
||||
pi := common.TopicListPage{header, topicList, forumList, common.Config.DefaultForum, common.TopicListSort{"lastupdated", false}, paginator}
|
||||
return renderTemplate("topics", w, r, header, pi)
|
||||
}
|
||||
|
||||
func wsTopicList(topicList []*common.TopicsRow, lastPage int) *common.WsTopicList {
|
||||
wsTopicList := make([]*common.WsTopicsRow, len(topicList))
|
||||
for i, topicRow := range topicList {
|
||||
wsTopicList[i] = topicRow.WebSockets()
|
||||
}
|
||||
return &common.WsTopicList{wsTopicList, lastPage}
|
||||
}
|
||||
|
||||
func TopicListMostViewed(w http.ResponseWriter, r *http.Request, user common.User, header *common.Header) common.RouteError {
|
||||
header.Title = phrases.GetTitlePhrase("topics")
|
||||
header.Zone = "topics"
|
||||
|
@ -60,25 +98,54 @@ func TopicListMostViewed(w http.ResponseWriter, r *http.Request, user common.Use
|
|||
|
||||
// Get the current page
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
sfids := r.FormValue("fids")
|
||||
var fids []int
|
||||
if sfids != "" {
|
||||
for _, sfid := range strings.Split(sfids, ",") {
|
||||
fid, err := strconv.Atoi(sfid)
|
||||
if err != nil {
|
||||
return common.LocalError("Invalid fid", w, r, user)
|
||||
}
|
||||
fids = append(fids, fid)
|
||||
}
|
||||
if len(fids) == 1 {
|
||||
forum, err := common.Forums.Get(fids[0])
|
||||
if err != nil {
|
||||
return common.LocalError("Invalid fid forum", w, r, user)
|
||||
}
|
||||
header.Title = forum.Name
|
||||
header.ZoneID = forum.ID
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Pass a struct back rather than passing back so many variables
|
||||
var topicList []*common.TopicsRow
|
||||
var forumList []common.Forum
|
||||
var paginator common.Paginator
|
||||
if user.IsSuperAdmin {
|
||||
topicList, forumList, paginator, err = common.TopicList.GetList(page, "most-viewed")
|
||||
topicList, forumList, paginator, err = common.TopicList.GetList(page, "most-viewed", fids)
|
||||
} else {
|
||||
topicList, forumList, paginator, err = common.TopicList.GetListByGroup(group, page, "most-viewed")
|
||||
topicList, forumList, paginator, err = common.TopicList.GetListByGroup(group, page, "most-viewed", fids)
|
||||
}
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// ! Need an inline error not a page level error
|
||||
if len(topicList) == 0 {
|
||||
return common.NotFound(w, r, header)
|
||||
}
|
||||
|
||||
//MarshalJSON() ([]byte, error)
|
||||
// TODO: Reduce the amount of boilerplate here
|
||||
if r.FormValue("js") == "1" {
|
||||
outBytes, err := wsTopicList(topicList, paginator.LastPage).MarshalJSON()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
w.Write(outBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
pi := common.TopicListPage{header, topicList, forumList, common.Config.DefaultForum, common.TopicListSort{"mostviewed", false}, paginator}
|
||||
return renderTemplate("topics", w, r, header, pi)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ go build -o QueryGen "./cmd/query_gen"
|
|||
echo "Running the query generator"
|
||||
./QueryGen
|
||||
|
||||
echo "Generating the JSON handlers"
|
||||
easyjson -pkg common
|
||||
|
||||
echo "Building Gosora"
|
||||
go build -o Gosora
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ cd ../..
|
|||
echo "Running the query generator"
|
||||
./QueryGen
|
||||
|
||||
echo "Generating the JSON handlers"
|
||||
easyjson -pkg common
|
||||
|
||||
echo "Building Gosora"
|
||||
go build -o Gosora -tags no_ws
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
echo "Generating the dynamic code"
|
||||
go generate
|
||||
echo Generating the JSON handlers
|
||||
easyjson -pkg common
|
||||
echo "Running tests"
|
||||
go build -o mssqlBuild -tags mssql
|
||||
go test -coverprofile c.out
|
||||
|
|
|
@ -40,6 +40,9 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Generating the JSON handlers
|
||||
easyjson -pkg common
|
||||
|
||||
echo Building the executable
|
||||
go build -o gosora.exe -tags no_ws
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
3
run.bat
3
run.bat
|
@ -40,6 +40,9 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Generating the JSON handlers
|
||||
easyjson -pkg common
|
||||
|
||||
echo Building the executable
|
||||
go build -o gosora.exe
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
|
@ -40,6 +40,9 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Generating the JSON handlers
|
||||
easyjson -pkg common
|
||||
|
||||
echo Building the executable
|
||||
go build -o gosora.exe -tags mssql
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
|
@ -40,6 +40,9 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Generating the JSON handlers
|
||||
easyjson -pkg common
|
||||
|
||||
echo Building the executable
|
||||
go test
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
|
@ -40,6 +40,9 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Generating the JSON handlers
|
||||
easyjson -pkg common
|
||||
|
||||
echo Building the executable
|
||||
go test -tags mssql
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
|
@ -15,6 +15,4 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
|
||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator_prev_page_aria"}}" rel="prev" href="{{.Forum.Link}}?page={{subtract .Page 1}}">{{lang "paginator_less_than"}}</a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<div id="nextFloat" class="next_button"><a class="next_link" aria-label="{{lang "paginator_next_page_aria"}}" rel="next" href="{{.Forum.Link}}?page={{add .Page 1}}">{{lang "paginator_greater_than"}}</a></div>{{end}}
|
||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator.prev_page_aria"}}" rel="prev" href="{{.Forum.Link}}?page={{subtract .Page 1}}">{{lang "paginator.less_than"}}</a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<div id="nextFloat" class="next_button"><a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Forum.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a></div>{{end}}
|
||||
<link rel="canonical" href="//{{.Site.URL}}{{.Forum.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}" />
|
||||
|
||||
<main id="forumItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||
|
@ -40,25 +40,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow topic_content_row">
|
||||
<div class="formitem">
|
||||
<textarea form="quick_post_form" id="input_content" name="topic-content" placeholder="{{lang "quick_topic.content_placeholder"}}" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow poll_content_row auto_hide">
|
||||
<div class="formitem">Poll stuff</div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="quick_post_form" name="topic-button" class="formbutton">{{lang "quick_topic.create_topic_button"}}</button>
|
||||
<button form="quick_post_form" class="formbutton" id="add_poll_button">{{lang "quick_topic.add_poll_button"}}</button>
|
||||
{{if .CurrentUser.Perms.UploadFiles}}
|
||||
<input name="upload_files" form="quick_post_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">{{lang "quick_topic.add_file_button"}}</label>
|
||||
<div id="upload_file_dock"></div>{{end}}
|
||||
<button class="formbutton close_form">{{lang "quick_topic.cancel_button"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
{{template "topics_quick_topic.html" . }}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -100,9 +82,6 @@
|
|||
</div>{{else}}<div class="rowitem passive rowmsg">{{lang "forum_no_topics"}}{{if .CurrentUser.Loggedin}}{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">{{lang "forum_start_one"}}</a>{{end}}{{end}}</div>{{end}}
|
||||
</div>
|
||||
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
||||
|
||||
</main>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
{{if gt .LastPage 1}}
|
||||
<div class="pageset">
|
||||
{{if gt .Page 1}}<div class="pageitem"><a href="?page={{subtract .Page 1}}" rel="prev" aria-label="{{lang "paginator_prev_page_aria"}}">{{lang "paginator_prev_page"}}</a></div>
|
||||
{{if gt .Page 1}}<div class="pageitem pagefirst"><a href="?page=1" rel="prev" aria-label="{{lang "paginator.first_page_aria"}}">{{lang "paginator.first_page"}}</a></div>
|
||||
<div class="pageitem pageprev"><a href="?page={{subtract .Page 1}}" rel="prev" aria-label="{{lang "paginator.prev_page_aria"}}">{{lang "paginator.prev_page"}}</a></div>
|
||||
<link rel="prev" href="?page={{subtract .Page 1}}" />{{end}}
|
||||
{{range .PageList}}
|
||||
<div class="pageitem"><a href="?page={{.}}">{{.}}</a></div>
|
||||
{{end}}
|
||||
{{if ne .LastPage .Page}}
|
||||
<link rel="next" href="?page={{add .Page 1}}" />
|
||||
<div class="pageitem"><a href="?page={{add .Page 1}}" rel="next" aria-label="{{lang "paginator_next_page_aria"}}">{{lang "paginator_next_page"}}</a></div>{{end}}
|
||||
<div class="pageitem pagenext"><a href="?page={{add .Page 1}}" rel="next" aria-label="{{lang "paginator.next_page_aria"}}">{{lang "paginator.next_page"}}</a></div>
|
||||
<div class="pageitem pagelast"><a href="?page={{.LastPage}}" rel="next" aria-label="{{lang "paginator.last_page_aria"}}">{{lang "paginator.last_page"}}</a></div>{{end}}
|
||||
</div>
|
||||
{{end}}
|
|
@ -19,9 +19,7 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "areyousure_head"}}</h1></div>
|
||||
</div>
|
||||
<div class="colstack_item">
|
||||
<div class="rowitem passive rowmsg">{{.Something.Message}}<br /><br />
|
||||
<a class="username" href="{{.Something.URL}}?session={{.CurrentUser.Session}}">{{lang "areyousure_continue"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
|
@ -19,9 +19,8 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
||||
|
||||
{{if .CurrentUser.Perms.EditGroup}}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_groups_create_head"}}</h1></div>
|
||||
|
|
|
@ -19,9 +19,7 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -19,9 +19,7 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<option value="simple"{{if eq .Type "simple"}} selected{{end}}>{{lang "panel_themes_widgets_type_simple"}}</option>
|
||||
<option value="wol"{{if eq .Type "wol"}} selected{{end}}>{{lang "panel_themes_widgets_type_wol"}}</option>
|
||||
<option value="wol_context"{{if eq .Type "wol_context"}} selected{{end}}>{{lang "panel_themes_widgets_type_wol_context"}}</option>
|
||||
<!--<option value="search_and_filter"{{if eq .Type "search_and_filter"}} selected{{end}}>{{lang "panel_themes_widgets_type_search_and_filter"}}</option>-->
|
||||
<option value="search_and_filter"{{if eq .Type "search_and_filter"}} selected{{end}}>{{lang "panel_themes_widgets_type_search_and_filter"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,9 +20,7 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -17,28 +17,5 @@
|
|||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{range .ItemList}}
|
||||
<div class="rowitem passive deletable_block editable_parent comment {{.ClassName}}">
|
||||
<div class="topRow">
|
||||
<div class="userbit">
|
||||
<img src="{{.MicroAvatar}}" alt="{{.CreatedByName}}'s Avatar" title="{{.CreatedByName}}'s Avatar" />
|
||||
<span class="nameAndTitle">
|
||||
<a href="{{.UserLink}}" class="real_username username">{{.CreatedByName}}</a>
|
||||
{{if .Tag}}<a class="username hide_on_mobile user_tag" style="float: right;">{{.Tag}}</a>{{end}}
|
||||
</span>
|
||||
</div>
|
||||
<span class="controls">
|
||||
{{if $.CurrentUser.IsMod}}
|
||||
<a href="/profile/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
<a href="/profile/reply/delete/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
|
||||
{{end}}
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="content_column">
|
||||
<span class="editable_block user_content">{{.ContentHtml}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="after_comment"></div>
|
||||
{{end}}
|
||||
{{template "profile_comments_row_alt.html" . }}
|
||||
{{end}}
|
|
@ -0,0 +1,24 @@
|
|||
{{range .ItemList}}
|
||||
<div class="rowitem passive deletable_block editable_parent comment {{.ClassName}}">
|
||||
<div class="topRow">
|
||||
<div class="userbit">
|
||||
<img src="{{.MicroAvatar}}" alt="{{.CreatedByName}}'s Avatar" title="{{.CreatedByName}}'s Avatar" />
|
||||
<span class="nameAndTitle">
|
||||
<a href="{{.UserLink}}" class="real_username username">{{.CreatedByName}}</a>
|
||||
{{if .Tag}}<a class="username hide_on_mobile user_tag" style="float: right;">{{.Tag}}</a>{{end}}
|
||||
</span>
|
||||
</div>
|
||||
<span class="controls">
|
||||
{{if $.CurrentUser.IsMod}}
|
||||
<a href="/profile/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
<a href="/profile/reply/delete/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
|
||||
{{end}}
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="content_column">
|
||||
<span class="editable_block user_content">{{.ContentHtml}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="after_comment"></div>
|
||||
{{end}}
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
<form id="edit_topic_form" action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post"></form>
|
||||
{{if gt .Page 1}}<link rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}" />
|
||||
<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator_prev_page_aria"}}" rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}">{{lang "paginator_less_than"}}</a></div>{{end}}
|
||||
<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator.prev_page_aria"}}" rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}">{{lang "paginator.less_than"}}</a></div>{{end}}
|
||||
|
||||
{{if ne .LastPage .Page}}<link rel="prerender next" href="{{.Topic.Link}}?page={{add .Page 1}}" />
|
||||
<div id="nextFloat" class="next_button">
|
||||
<a class="next_link" aria-label="{{lang "paginator_next_page_aria"}}" rel="next" href="{{.Topic.Link}}?page={{add .Page 1}}">{{lang "paginator_greater_than"}}</a>
|
||||
<a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Topic.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a>
|
||||
</div>{{end}}
|
||||
|
||||
<link rel="canonical" href="//{{.Site.URL}}{{.Topic.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}" />
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{{template "header.html" . }}
|
||||
|
||||
{{if gt .Page 1}}<link rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}" />
|
||||
<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator_prev_page_aria"}}" rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}">{{lang "paginator_less_than"}}</a></div>{{end}}
|
||||
<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator.prev_page_aria"}}" rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}">{{lang "paginator.less_than"}}</a></div>{{end}}
|
||||
|
||||
{{if ne .LastPage .Page}}<link rel="prerender next" href="{{.Topic.Link}}?page={{add .Page 1}}" />
|
||||
<div id="nextFloat" class="next_button"><a class="next_link" aria-label="{{lang "paginator_next_page_aria"}}" rel="next" href="{{.Topic.Link}}?page={{add .Page 1}}">{{lang "paginator_greater_than"}}</a></div>{{end}}
|
||||
<div id="nextFloat" class="next_button"><a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Topic.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a></div>{{end}}
|
||||
|
||||
<link rel="canonical" href="//{{.Site.URL}}{{.Topic.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}" />
|
||||
|
||||
|
@ -111,9 +111,7 @@
|
|||
</article>
|
||||
{{template "topic_alt_posts.html" . }}
|
||||
</div>
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
||||
|
||||
{{if .CurrentUser.Loggedin}}
|
||||
{{if .CurrentUser.Perms.CreateReply}}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<main id="topicsItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||
|
||||
<div class="rowblock rowhead topic_list_title_block{{if .CurrentUser.Loggedin}} has_opt{{end}}">
|
||||
<div class="rowitem topic_list_title"><h1 itemprop="name">{{lang "topics_head"}}</h1></div>
|
||||
<div class="rowitem topic_list_title"><h1 itemprop="name">{{.Title}}</h1></div>
|
||||
{{if .CurrentUser.Loggedin}}
|
||||
<div class="optbox">
|
||||
{{if .ForumList}}
|
||||
|
@ -55,7 +55,6 @@
|
|||
</div>
|
||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="{{lang "quick_topic.aria"}}">
|
||||
<form name="topic_create_form_form" id="quick_post_form" enctype="multipart/form-data" action="/topic/create/submit/?session={{.CurrentUser.Session}}" method="post"></form>
|
||||
<input form="quick_post_form" id="has_poll_input" name="has_poll" value="0" type="hidden" />
|
||||
<img class="little_row_avatar" src="{{.CurrentUser.MicroAvatar}}" height="64" alt="{{lang "quick_topic.avatar_alt"}}" title="{{lang "quick_topic.avatar_tooltip"}}" />
|
||||
<div class="main_form">
|
||||
<div class="topic_meta">
|
||||
|
@ -70,31 +69,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow topic_content_row">
|
||||
<div class="formitem">
|
||||
<textarea form="quick_post_form" id="input_content" name="topic-content" placeholder="{{lang "quick_topic.content_placeholder"}}" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow poll_content_row auto_hide">
|
||||
<div class="formitem">
|
||||
<div class="pollinput" data-pollinput="0">
|
||||
<input type="checkbox" disabled />
|
||||
<label class="pollinputlabel"></label>
|
||||
<input form="quick_post_form" name="pollinputitem[0]" class="pollinputinput" type="text" placeholder="{{lang "quick_topic.add_poll_option"}}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="quick_post_form" class="formbutton">{{lang "quick_topic.create_topic_button"}}</button>
|
||||
<button form="quick_post_form" class="formbutton" id="add_poll_button">{{lang "quick_topic.add_poll_button"}}</button>
|
||||
{{if .CurrentUser.Perms.UploadFiles}}
|
||||
<input name="upload_files" form="quick_post_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">{{lang "quick_topic.add_file_button"}}</label>
|
||||
<div id="upload_file_dock"></div>{{end}}
|
||||
<button class="formbutton close_form">{{lang "quick_topic.cancel_button"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
{{template "topics_quick_topic.html" . }}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -106,9 +81,6 @@
|
|||
{{range .TopicList}}{{template "topics_topic.html" . }}{{else}}<div class="rowitem passive rowmsg">{{lang "topics_no_topics"}}{{if .CurrentUser.Loggedin}}{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/">{{lang "topics_start_one"}}</a>{{end}}{{end}}</div>{{end}}
|
||||
</div>
|
||||
|
||||
{{if gt .LastPage 1}}
|
||||
{{template "paginator.html" . }}
|
||||
{{end}}
|
||||
|
||||
</main>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<input form="quick_post_form" id="has_poll_input" name="has_poll" value="0" type="hidden" />
|
||||
<div class="formrow topic_content_row">
|
||||
<div class="formitem">
|
||||
<textarea form="quick_post_form" id="input_content" name="topic-content" placeholder="{{lang "quick_topic.content_placeholder"}}" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow poll_content_row auto_hide">
|
||||
<div class="formitem">
|
||||
<div class="pollinput" data-pollinput="0">
|
||||
<input type="checkbox" disabled />
|
||||
<label class="pollinputlabel"></label>
|
||||
<input form="quick_post_form" name="pollinputitem[0]" class="pollinputinput" type="text" placeholder="{{lang "quick_topic.add_poll_option"}}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<button form="quick_post_form" class="formbutton">{{lang "quick_topic.create_topic_button"}}</button>
|
||||
<button form="quick_post_form" class="formbutton" id="add_poll_button">{{lang "quick_topic.add_poll_button"}}</button>
|
||||
{{if .CurrentUser.Perms.UploadFiles}}
|
||||
<input name="upload_files" form="quick_post_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">{{lang "quick_topic.add_file_button"}}</label>
|
||||
<div id="upload_file_dock"></div>{{end}}
|
||||
<button class="formbutton close_form">{{lang "quick_topic.cancel_button"}}</button>
|
||||
</div>
|
||||
</div>
|
|
@ -2,6 +2,6 @@
|
|||
<input name="widget_search" placeholder="Search" />
|
||||
</div>
|
||||
<div class="rowblock filter_list widget_filter">
|
||||
{{range .Forums}} <div class="rowitem filter_item" data-fid="{{.ID}}">{{.Name}}</div>
|
||||
{{range .Forums}} <div class="rowitem filter_item{{if .Selected}} filter_selected{{end}}" data-fid="{{.ID}}"><a href="/topics/?fids={{.ID}}" rel="nofollow">{{.Name}}</a></div>
|
||||
{{end}}
|
||||
</div>
|
|
@ -1,6 +1,6 @@
|
|||
<div class="rowblock rowhead">
|
||||
<div class="rowblock rowhead widget_simple">
|
||||
<div class="rowitem"><h1>{{.Name}}</h1></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<div class="rowblock widget_simple">
|
||||
<div class="rowitem">{{.Text}}</div>
|
||||
</div>
|
||||
|
|
|
@ -1415,6 +1415,12 @@ textarea {
|
|||
.pageitem:last-child {
|
||||
border-right: 1px solid var(--element-border-color);
|
||||
}
|
||||
.pagefirst, .pagenext, .pageprev, .pagelast {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.pagefirst a, .pagenext a, .pageprev a, .pagelast a {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* TODO: Make widget_about's CSS less footer centric */
|
||||
.footerBit, .footer .widget {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{{template "profile_comments_row_alt.html" . }}
|
|
@ -156,6 +156,11 @@ li a {
|
|||
.sidebar {
|
||||
width: 320px;
|
||||
}
|
||||
.widget_simple .rowitem {
|
||||
line-height: 18px;
|
||||
padding-top: 14px !important;
|
||||
padding-bottom: 14px !important;
|
||||
}
|
||||
.the_form {
|
||||
border-radius: 3px;
|
||||
background-color: #444444;
|
||||
|
@ -193,6 +198,9 @@ li a {
|
|||
margin-bottom: 5px;
|
||||
padding: 4px;
|
||||
}
|
||||
.filter_item a {
|
||||
color: #BBBBBB;
|
||||
}
|
||||
.colstack_right .colstack_item:not(.colstack_head):not(.rowhead) .rowitem:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
@ -214,6 +222,22 @@ h1, h2, h3, h4, h5 {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* new */
|
||||
.filter_list {
|
||||
margin-top: 5px;
|
||||
background-color: #444444;
|
||||
margin-left: 12px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.filter_item {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
.filter_selected {
|
||||
background-color: #555555 !important;
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
/* new end */
|
||||
|
||||
@keyframes fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
|
@ -586,7 +610,6 @@ button, .formbutton, .panel_right_button:not(.has_inner_button) {
|
|||
display: flex;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.pageitem {
|
||||
font-size: 17px;
|
||||
border-radius: 3px;
|
||||
|
@ -594,6 +617,13 @@ button, .formbutton, .panel_right_button:not(.has_inner_button) {
|
|||
padding: 7px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.pagefirst, .pagenext, .pageprev, .pagelast {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.pagefirst a, .pagenext a, .pageprev a, .pagelast a {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
#prevFloat, #nextFloat {
|
||||
display: none;
|
||||
|
@ -934,13 +964,15 @@ input[type=checkbox]:checked + label .sel {
|
|||
margin-top: 4px;
|
||||
}
|
||||
.rowlist.bgavatars.not_grid .bgsub {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
margin-left: 8px;
|
||||
margin-right: 12px;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
margin-left: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.rowlist.bgavatars.not_grid .rowTitle {
|
||||
font-size: 17px;
|
||||
margin-left: 0px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.ip_search_block {
|
||||
|
|
|
@ -890,6 +890,13 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar .rowhead:not(:first-child) {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.widget_search {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#profile_comments .rowitem {
|
||||
background-repeat: no-repeat, repeat-y;
|
||||
background-size: 128px;
|
||||
|
|
Loading…
Reference in New Issue