Added the BBCode plugin.

Restructured the plugin system.
Multiple plugins can bind to the same hook now (not available for variadic hooks yet!)
The parser is now benchmarked. The bench_round4 is run with both plugin_markdown and plugin_bbcode enabled.
Added a benchmark for the BBCode plugin.
Moved some of the template writing logic into a more generalised function.
URLs are now recognised by the system and linked.
Converted more custom errors into LocalError calls.
Faster and less bandwidth intensive Emojis on Edge.
Fixed a bug with replies not working.
This commit is contained in:
Azareal 2017-01-05 14:41:14 +00:00
parent 33c2f4ccb0
commit e90d96961f
20 changed files with 406 additions and 212 deletions

View File

@ -2,8 +2,8 @@
package main package main
import "log" import "log"
var plugins map[string]Plugin = make(map[string]Plugin) var plugins map[string]*Plugin = make(map[string]*Plugin)
var hooks map[string]func(interface{})interface{} = make(map[string]func(interface{})interface{}) var hooks map[string][]func(interface{})interface{} = make(map[string][]func(interface{})interface{})
var vhooks map[string]func(...interface{})interface{} = make(map[string]func(...interface{})interface{}) var vhooks map[string]func(...interface{})interface{} = make(map[string]func(...interface{})interface{})
type Plugin struct type Plugin struct
@ -19,11 +19,64 @@ type Plugin struct
Init func() Init func()
Activate func()error Activate func()error
Deactivate func() Deactivate func()
Hooks map[string]int
} }
/*func add_hook(name string, handler func(interface{})interface{}) { func NewPlugin(uname string, name string, author string, url string, settings string, tag string, ptype string, init func(), activate func()error, deactivate func()) *Plugin {
hooks[name] = handler return &Plugin{
}*/ UName: uname,
Name: name,
Author: author,
URL: url,
Settings: settings,
Tag: tag,
Type: ptype,
Init: init,
Activate: activate,
Deactivate: deactivate,
/*
The Active field should never be altered by a plugin. It's used internally by the software to determine whether an admin has enabled a plugin or not and whether to run it. This will be overwritten by the user's preference.
*/
Active: false,
Hooks: make(map[string]int),
}
}
func (plugin *Plugin) AddHook(name string, handler interface{}) {
switch h := handler.(type) {
case func(interface{})interface{}:
if len(hooks[name]) == 0 {
var hookSlice []func(interface{})interface{}
hookSlice = append(hookSlice, h)
hooks[name] = hookSlice
} else {
hooks[name] = append(hooks[name], h)
}
plugin.Hooks[name] = len(hooks[name])
case func(...interface{}) interface{}:
vhooks[name] = h
plugin.Hooks[name] = 0
default:
panic("I don't recognise this kind of handler!") // Should this be an error for the plugin instead of a panic()?
}
}
func (plugin *Plugin) RemoveHook(name string, handler interface{}) {
switch handler.(type) {
case func(interface{})interface{}:
key := plugin.Hooks[name]
hook := hooks[name]
hook = append(hook[:key], hook[key + 1:]...)
hooks[name] = hook
case func(...interface{}) interface{}:
delete(vhooks, name)
default:
panic("I don't recognise this kind of handler!") // Should this be an error for the plugin instead of a panic()?
}
delete(plugin.Hooks, name)
}
func init_plugins() { func init_plugins() {
for name, body := range plugins { for name, body := range plugins {
@ -35,27 +88,11 @@ func init_plugins() {
} }
} }
func add_hook(name string, handler interface{}) {
switch h := handler.(type) {
case func(interface{})interface{}:
hooks[name] = h
case func(...interface{}) interface{}:
vhooks[name] = h
default:
panic("I don't recognise this kind of handler!") // Should this be an error for the plugin instead of a panic()?
}
}
func remove_hook(name string/*, plugin string */) {
delete(hooks, name)
}
func run_hook(name string, data interface{}) interface{} { func run_hook(name string, data interface{}) interface{} {
return hooks[name](data) for _, hook := range hooks[name] {
} data = hook(data)
}
func remove_vhook(name string) { return data
delete(vhooks, name)
} }
func run_vhook(name string, data ...interface{}) interface{} { func run_vhook(name string, data ...interface{}) interface{} {

View File

@ -714,6 +714,138 @@ func BenchmarkCustomRouter(b *testing.B) {
}) })
} }
func BenchmarkParser(b *testing.B) {
b.ReportAllocs()
b.Run("empty_post", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = parse_message("")
}
})
b.Run("short_post", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = parse_message("Hey everyone, how's it going?")
}
})
b.Run("one_smily", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = parse_message("Hey everyone, how's it going? :)")
}
})
b.Run("five_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = parse_message("Hey everyone, how's it going? :):):):):)")
}
})
b.Run("ten_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = parse_message("Hey everyone, how's it going? :):):):):):):):):):)")
}
})
b.Run("twenty_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = parse_message("Hey everyone, how's it going? :):):):):):):):):):):):):):):):):):):):)")
}
})
}
func BenchmarkBBCodePluginWithRegexp(b *testing.B) {
b.ReportAllocs()
b.Run("empty_post", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("")
}
})
b.Run("short_post", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("Hey everyone, how's it going?")
}
})
b.Run("one_smily", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("Hey everyone, how's it going? :)")
}
})
b.Run("five_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("Hey everyone, how's it going? :):):):):)")
}
})
b.Run("ten_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("Hey everyone, how's it going? :):):):):):):):):):)")
}
})
b.Run("twenty_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("Hey everyone, how's it going? :):):):):):):):):):):):):):):):):):):):)")
}
})
b.Run("one_bold", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("[b]H[/b]ey everyone, how's it going?")
}
})
b.Run("five_bold", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("[b]H[/b][b]e[/b][b]y[/b] [b]e[/b][b]v[/b]eryone, how's it going?")
}
})
b.Run("ten_bold", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse("[b]H[/b][b]e[/b][b]y[/b] [b]e[/b][b]v[/b][b]e[/b][b]r[/b][b]y[/b][b]o[/b][b]n[/b]e, how's it going?")
}
})
}
func BenchmarkBBCodePluginWithCustomParser(b *testing.B) {
b.ReportAllocs()
b.Run("empty_post", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("")
}
})
b.Run("short_post", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("Hey everyone, how's it going?")
}
})
b.Run("one_smily", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("Hey everyone, how's it going? :)")
}
})
b.Run("five_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("Hey everyone, how's it going? :):):):):)")
}
})
b.Run("ten_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("Hey everyone, how's it going? :):):):):):):):):):)")
}
})
b.Run("twenty_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("Hey everyone, how's it going? :):):):):):):):):):):):):):):):):):):):)")
}
})
b.Run("one_bold", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("[b]H[/b]ey everyone, how's it going?")
}
})
b.Run("five_bold", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("[b]H[/b][b]e[/b][b]y[/b] [b]e[/b][b]v[/b]eryone, how's it going?")
}
})
b.Run("ten_bold", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = bbcode_parse2("[b]H[/b][b]e[/b][b]y[/b] [b]e[/b][b]v[/b][b]e[/b][b]r[/b][b]y[/b][b]o[/b][b]n[/b]e, how's it going?")
}
})
}
/*func TestRoute(t *testing.T) { /*func TestRoute(t *testing.T) {
}*/ }*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
images/bench_round4.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

93
main.go
View File

@ -33,7 +33,6 @@ var forums map[int]Forum = make(map[int]Forum)
var static_files map[string]SFile = make(map[string]SFile) var static_files map[string]SFile = make(map[string]SFile)
var template_topic_handle func(TopicPage,io.Writer) = nil var template_topic_handle func(TopicPage,io.Writer) = nil
var template_topic_origin_handle func(TopicPage,io.Writer) = nil
var template_topic_alt_handle func(TopicPage,io.Writer) = nil var template_topic_alt_handle func(TopicPage,io.Writer) = nil
var template_topics_handle func(TopicsPage,io.Writer) = nil var template_topics_handle func(TopicsPage,io.Writer) = nil
var template_forum_handle func(ForumPage,io.Writer) = nil var template_forum_handle func(ForumPage,io.Writer) = nil
@ -75,7 +74,6 @@ func compile_templates() {
topicList = append(topicList, TopicUser{1,"Topic Title","The topic content.",1,false,false,"",1,"open","Admin","","",0,"","","",""}) topicList = append(topicList, TopicUser{1,"Topic Title","The topic content.",1,false,false,"",1,"open","Admin","","",0,"","","",""})
topics_page := TopicsPage{"Topic List",user,noticeList,topicList,""} topics_page := TopicsPage{"Topic List",user,noticeList,topicList,""}
topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList) topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList)
//topics_tmpl := c.compile_template("topics.html","templates/","Page", pi, varList)
forum_page := ForumPage{"General Forum",user,noticeList,topicList,"There aren't any topics in this forum yet."} forum_page := ForumPage{"General Forum",user,noticeList,topicList,"There aren't any topics in this forum yet."}
forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList) forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList)
@ -90,17 +88,7 @@ func compile_templates() {
} }
func write_template(name string, content string) { func write_template(name string, content string) {
f, err := os.Create("./template_" + name + ".go") write_file("./template_" + name + ".go", content)
if err != nil {
log.Fatal(err)
}
_, err = f.WriteString(content)
if err != nil {
log.Fatal(err)
}
f.Sync()
f.Close()
} }
func main(){ func main(){
@ -137,84 +125,8 @@ func main(){
init_plugins() init_plugins()
// In a directory to stop it clashing with the other paths
/*http.HandleFunc("/static/", route_static)
fs_u := http.FileServer(http.Dir("./uploads"))
http.Handle("/uploads/", http.StripPrefix("/uploads/",fs_u))
http.HandleFunc("/overview/", route_overview)
http.HandleFunc("/topics/create/", route_topic_create)
http.HandleFunc("/topics/", route_topics)
http.HandleFunc("/forums/", route_forums)
http.HandleFunc("/forum/", route_forum)
http.HandleFunc("/topic/create/submit/", route_create_topic)
http.HandleFunc("/topic/", route_topic_id)
http.HandleFunc("/reply/create/", route_create_reply)
//http.HandleFunc("/reply/edit/", route_reply_edit)
//http.HandleFunc("/reply/delete/", route_reply_delete)
http.HandleFunc("/reply/edit/submit/", route_reply_edit_submit)
http.HandleFunc("/reply/delete/submit/", route_reply_delete_submit)
http.HandleFunc("/report/submit/", route_report_submit)
http.HandleFunc("/topic/edit/submit/", route_edit_topic)
http.HandleFunc("/topic/delete/submit/", route_delete_topic)
http.HandleFunc("/topic/stick/submit/", route_stick_topic)
http.HandleFunc("/topic/unstick/submit/", route_unstick_topic)
// Custom Pages
http.HandleFunc("/pages/", route_custom_page)
// Accounts
http.HandleFunc("/accounts/login/", route_login)
http.HandleFunc("/accounts/create/", route_register)
http.HandleFunc("/accounts/logout/", route_logout)
http.HandleFunc("/accounts/login/submit/", route_login_submit)
http.HandleFunc("/accounts/create/submit/", route_register_submit)
//http.HandleFunc("/accounts/list/", route_login) // Redirect /accounts/ and /user/ to here..
//http.HandleFunc("/accounts/create/full/", route_logout)
//http.HandleFunc("/user/edit/", route_logout)
http.HandleFunc("/user/edit/critical/", route_account_own_edit_critical) // Password & Email
http.HandleFunc("/user/edit/critical/submit/", route_account_own_edit_critical_submit)
http.HandleFunc("/user/edit/avatar/", route_account_own_edit_avatar)
http.HandleFunc("/user/edit/avatar/submit/", route_account_own_edit_avatar_submit)
http.HandleFunc("/user/edit/username/", route_account_own_edit_username)
http.HandleFunc("/user/edit/username/submit/", route_account_own_edit_username_submit)
http.HandleFunc("/user/edit/email/token/", route_account_own_edit_email_token_submit)
http.HandleFunc("/user/", route_profile)
http.HandleFunc("/profile/reply/create/", route_profile_reply_create)
http.HandleFunc("/profile/reply/edit/submit/", route_profile_reply_edit_submit)
http.HandleFunc("/profile/reply/delete/submit/", route_profile_reply_delete_submit)
//http.HandleFunc("/user/edit/submit/", route_logout)
http.HandleFunc("/users/ban/", route_ban)
http.HandleFunc("/users/ban/submit/", route_ban_submit)
http.HandleFunc("/users/unban/", route_unban)
http.HandleFunc("/users/activate/", route_activate)
// Admin
http.HandleFunc("/panel/", route_panel)
http.HandleFunc("/panel/forums/", route_panel_forums)
http.HandleFunc("/panel/forums/create/", route_panel_forums_create_submit)
http.HandleFunc("/panel/forums/delete/", route_panel_forums_delete)
http.HandleFunc("/panel/forums/delete/submit/", route_panel_forums_delete_submit)
http.HandleFunc("/panel/forums/edit/submit/", route_panel_forums_edit_submit)
http.HandleFunc("/panel/settings/", route_panel_settings)
http.HandleFunc("/panel/settings/edit/", route_panel_setting)
http.HandleFunc("/panel/settings/edit/submit/", route_panel_setting_edit)
http.HandleFunc("/panel/themes/", route_panel_themes)
http.HandleFunc("/panel/themes/default/", route_panel_themes_default)
http.HandleFunc("/panel/plugins/", route_panel_plugins)
http.HandleFunc("/panel/plugins/activate/", route_panel_plugins_activate)
http.HandleFunc("/panel/plugins/deactivate/", route_panel_plugins_deactivate)
http.HandleFunc("/panel/users/", route_panel_users)
http.HandleFunc("/panel/users/edit/", route_panel_users_edit)
http.HandleFunc("/panel/users/edit/submit/", route_panel_users_edit_submit)
http.HandleFunc("/panel/groups/", route_panel_groups)
http.HandleFunc("/", default_route)*/
router := NewRouter() router := NewRouter()
router.HandleFunc("/static/", route_static) router.HandleFunc("/static/", route_static) // In a directory to stop it clashing with the other paths
fs_u := http.FileServer(http.Dir("./uploads")) fs_u := http.FileServer(http.Dir("./uploads"))
router.Handle("/uploads/", http.StripPrefix("/uploads/",fs_u)) router.Handle("/uploads/", http.StripPrefix("/uploads/",fs_u))
@ -295,7 +207,6 @@ func main(){
if server_port == "" { if server_port == "" {
server_port = "80" server_port = "80"
} }
//http.ListenAndServe(":" + server_port, nil)
http.ListenAndServe(":" + server_port, router) http.ListenAndServe(":" + server_port, router)
} else { } else {
if server_port == "" { if server_port == "" {

View File

@ -1,6 +1,6 @@
package main package main
import "strings" import "strings"
//import "regexp" import "regexp"
type Page struct type Page struct
{ {
@ -71,6 +71,13 @@ type AreYouSure struct
Message string Message string
} }
var urlpattern string = `(?s)([ {1}])((http|https|ftp|mailto)*)(:{??)\/\/([\.a-zA-Z\/]+)([ {1}])`
var url_reg *regexp.Regexp
func init() {
url_reg = regexp.MustCompile(urlpattern)
}
func shortcode_to_unicode(msg string) string { func shortcode_to_unicode(msg string) string {
//re := regexp.MustCompile(":(.):") //re := regexp.MustCompile(":(.):")
msg = strings.Replace(msg,":grinning:","😀",-1) msg = strings.Replace(msg,":grinning:","😀",-1)
@ -126,6 +133,7 @@ func parse_message(msg string) string {
msg = strings.Replace(msg,":)","😀",-1) msg = strings.Replace(msg,":)","😀",-1)
msg = strings.Replace(msg,":D","😃",-1) msg = strings.Replace(msg,":D","😃",-1)
msg = strings.Replace(msg,":P","😛",-1) msg = strings.Replace(msg,":P","😛",-1)
msg = url_reg.ReplaceAllString(msg,"<a href=\"$2$3//$4\" rel=\"nofollow\">$2$3//$4</a>")
msg = strings.Replace(msg,"\n","<br>",-1) msg = strings.Replace(msg,"\n","<br>",-1)
if hooks["parse_assign"] != nil { if hooks["parse_assign"] != nil {
out := run_hook("parse_assign", msg) out := run_hook("parse_assign", msg)

110
plugin_bbcode.go Normal file
View File

@ -0,0 +1,110 @@
package main
//import "log"
//import "fmt"
import "regexp"
var bbcode_bold *regexp.Regexp
var bbcode_italic *regexp.Regexp
var bbcode_underline *regexp.Regexp
var bbcode_url *regexp.Regexp
var bbcode_url_label *regexp.Regexp
func init() {
plugins["bbcode"] = NewPlugin("bbcode","BBCode","Azareal","http://github.com/Azareal","","","",init_bbcode,nil,deactivate_bbcode)
}
func init_bbcode() {
plugins["bbcode"].AddHook("parse_assign", bbcode_parse2)
bbcode_bold = regexp.MustCompile(`(?s)\[b\](.*)\[/b\]`)
bbcode_italic = regexp.MustCompile(`(?s)\[i\](.*)\[/i\]`)
bbcode_underline = regexp.MustCompile(`(?s)\[u\](.*)\[/u\]`)
urlpattern := `(http|https|ftp|mailto*)(:??)\/\/([\.a-zA-Z\/]+)`
bbcode_url = regexp.MustCompile(`\[url\]` + urlpattern + `\[/url\]`)
bbcode_url_label = regexp.MustCompile(`(?s)\[url=` + urlpattern + `\](.*)\[/url\]`)
}
func deactivate_bbcode() {
plugins["bbcode"].RemoveHook("parse_assign", bbcode_parse2)
}
func bbcode_parse(data interface{}) interface{} {
msg := data.(string)
msg = bbcode_bold.ReplaceAllString(msg,"<b>$1</b>")
msg = bbcode_italic.ReplaceAllString(msg,"<i>$1</i>")
msg = bbcode_url.ReplaceAllString(msg,"<a href=\"$1$2//$3\" rel=\"nofollow\">$1$2//$3</i>")
msg = bbcode_url_label.ReplaceAllString(msg,"<a href=\"$1$2//$3\" rel=\"nofollow\">$4</i>")
return msg
}
func bbcode_parse2(data interface{}) interface{} {
msg := data.(string)
msgbytes := []byte(msg)
has_u := false
has_b := false
has_i := false
complex_bbc := false
for i := 0; i < len(msgbytes); i++ {
//log.Print(msgbytes[i])
//fmt.Println(string(msgbytes[i]))
//continue
if msgbytes[i] == '[' {
if msgbytes[i + 2] != ']' {
if msgbytes[i + 1] == '/' {
if msgbytes[i + 3] == ']' {
if msgbytes[i + 2] == 'u' {
msgbytes[i] = '<'
msgbytes[i + 3] = '>'
has_u = false
}
if msgbytes[i + 2] == 'b' {
msgbytes[i] = '<'
msgbytes[i + 3] = '>'
has_b = false
}
if msgbytes[i + 2] == 'i' {
msgbytes[i] = '<'
msgbytes[i + 3] = '>'
has_i = false
}
i += 3
} else {
complex_bbc = true
}
} else {
complex_bbc = true
}
} else {
if msgbytes[i + 1] == 'u' {
msgbytes[i] = '<'
msgbytes[i + 2] = '>'
has_u = true
}
if msgbytes[i + 1] == 'b' {
msgbytes[i] = '<'
msgbytes[i + 2] = '>'
has_b = true
}
if msgbytes[i + 1] == 'i' {
msgbytes[i] = '<'
msgbytes[i + 2] = '>'
has_i = true
}
i += 2
}
}
}
// There's an unclosed tag in there x.x
if has_i || has_u || has_b {
closer := []byte("</u></i></b>")
msgbytes = append(msgbytes, closer...)
}
msg = string(msgbytes)
//fmt.Println(msg)
if complex_bbc {
msg = bbcode_url.ReplaceAllString(msg,"<a href=\"$1$2//$3\" rel=\"nofollow\">$1$2//$3</i>")
msg = bbcode_url_label.ReplaceAllString(msg,"<a href=\"$1$2//$3\" rel=\"nofollow\">$4</i>")
}
return msg
}

View File

@ -2,16 +2,16 @@ package main
import "html/template" import "html/template"
func init() { func init() {
plugins["helloworld"] = Plugin{"helloworld","Hello World","Azareal","http://github.com/Azareal","",false,"","",init_helloworld,nil,deactivate_helloworld} plugins["helloworld"] = NewPlugin("helloworld","Hello World","Azareal","http://github.com/Azareal","","","",init_helloworld,nil,deactivate_helloworld)
} }
// init_helloworld is separate from init() as we don't want the plugin to run if the plugin is disabled // init_helloworld is separate from init() as we don't want the plugin to run if the plugin is disabled
func init_helloworld() { func init_helloworld() {
add_hook("rrow_assign", helloworld_reply) plugins["helloworld"].AddHook("rrow_assign", helloworld_reply)
} }
func deactivate_helloworld() { func deactivate_helloworld() {
remove_hook("rrow_assign") plugins["helloworld"].RemoveHook("rrow_assign", helloworld_reply)
} }
func helloworld_reply(data interface{}) interface{} { func helloworld_reply(data interface{}) interface{} {

View File

@ -1,30 +1,29 @@
package main package main
import "regexp" import "regexp"
var bold_italic *regexp.Regexp var markdown_bold_italic *regexp.Regexp
var bold *regexp.Regexp var markdown_bold *regexp.Regexp
var italic *regexp.Regexp var markdown_italic *regexp.Regexp
func init() { func init() {
plugins["markdown"] = Plugin{"markdown","Markdown","Azareal","http://github.com/Azareal","",false,"","",init_markdown,nil,deactivate_markdown} plugins["markdown"] = NewPlugin("markdown","Markdown","Azareal","http://github.com/Azareal","","","",init_markdown,nil,deactivate_markdown)
} }
func init_markdown() { func init_markdown() {
add_hook("parse_assign", markdown_parse) plugins["markdown"].AddHook("parse_assign", markdown_parse)
bold_italic = regexp.MustCompile(`\*\*\*(.*)\*\*\*`) markdown_bold_italic = regexp.MustCompile(`\*\*\*(.*)\*\*\*`)
bold = regexp.MustCompile(`\*\*(.*)\*\*`) markdown_bold = regexp.MustCompile(`\*\*(.*)\*\*`)
italic = regexp.MustCompile(`\*(.*)\*`) markdown_italic = regexp.MustCompile(`\*(.*)\*`)
} }
func deactivate_markdown() { func deactivate_markdown() {
remove_hook("parse_assign") plugins["markdown"].RemoveHook("parse_assign", markdown_parse)
} }
func markdown_parse(data interface{}) interface{} { func markdown_parse(data interface{}) interface{} {
msg := data.(string) msg := data.(string)
msg = bold_italic.ReplaceAllString(msg,"<i><b>$1</b></i>") msg = markdown_bold_italic.ReplaceAllString(msg,"<i><b>$1</b></i>")
msg = bold.ReplaceAllString(msg,"<b>$1</b>") msg = markdown_bold.ReplaceAllString(msg,"<b>$1</b>")
msg = italic.ReplaceAllString(msg,"<i>$1</i>") msg = markdown_italic.ReplaceAllString(msg,"<i>$1</i>")
return msg return msg
} }

View File

@ -12,8 +12,6 @@ func init() {
The Settings field points to the route for managing the settings for this plugin. Coming soon. The Settings field points to the route for managing the settings for this plugin. Coming soon.
The Active field should always be set to false in the init() function of a plugin. It's used internally by the software to determine whether an admin has enabled a plugin or not and whether to run it. This will be overwritten by the user's preference.
The Tag field is for providing a tiny snippet of information separate from the description. The Tag field is for providing a tiny snippet of information separate from the description.
The Type field is for the type of the plugin. This gets changed to "go" automatically and we would suggest leaving "". The Type field is for the type of the plugin. This gets changed to "go" automatically and we would suggest leaving "".
@ -24,7 +22,7 @@ func init() {
The Deactivate field is for the handler which is called by the software when the admin hits the Deactivate button in the control panel. You should clean-up any resources you have allocated, remove any hooks, close any statements, etc. within this handler. The Deactivate field is for the handler which is called by the software when the admin hits the Deactivate button in the control panel. You should clean-up any resources you have allocated, remove any hooks, close any statements, etc. within this handler.
*/ */
plugins["skeleton"] = Plugin{"skeleton","Skeleton","Azareal","","",false,"","",init_skeleton, activate_skeleton, deactivate_skeleton} plugins["skeleton"] = NewPlugin("skeleton","Skeleton","Azareal","","","","",init_skeleton, activate_skeleton, deactivate_skeleton)
} }
func init_skeleton() {} func init_skeleton() {}

View File

@ -605,20 +605,9 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return return
} }
success := 1
tid, err := strconv.Atoi(r.PostFormValue("tid")) tid, err := strconv.Atoi(r.PostFormValue("tid"))
if err != nil { if err != nil {
log.Print(err) LocalError("Failed to convert the TopicID", w, r, user)
success = 0
errmsg := "Unable to create the reply"
pi := Page{"Error","error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return return
} }
@ -626,15 +615,15 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
log.Print(content) log.Print(content)
_, err = create_reply_stmt.Exec(tid,content,parse_message(content),user.ID) _, err = create_reply_stmt.Exec(tid,content,parse_message(content),user.ID)
if err != nil { if err != nil {
log.Print(err) InternalError(err,w,r,user)
success = 0 return
} }
var topic_name string var topic_name string
err = db.QueryRow("select title from topics where tid = ?", tid).Scan(&topic_name) err = db.QueryRow("select title from topics where tid = ?", tid).Scan(&topic_name)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
log.Print(err) LocalError("Couldn't find the parent topic", w, r, user)
success = 0 return
} else if err != nil { } else if err != nil {
InternalError(err,w,r,user) InternalError(err,w,r,user)
return return
@ -646,18 +635,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return return
} }
if success != 1 { http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
errmsg := "Unable to create the reply"
pi := Page{"Error","error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
} else {
http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
}
} }
func route_profile_reply_create(w http.ResponseWriter, r *http.Request) { func route_profile_reply_create(w http.ResponseWriter, r *http.Request) {

View File

@ -1,7 +1,7 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main package main
import "strconv"
import "io" import "io"
import "strconv"
func init() { func init() {
template_forum_handle = template_forum template_forum_handle = template_forum

View File

@ -130,19 +130,19 @@ w.Write([]byte(`
<div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">` + item.CreatedByName + `</div> <div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">` + item.CreatedByName + `</div>
</div> </div>
<div class="content_container" style="background: white;margin-left: 137px;min-height: 128px;margin-bottom: 0;margin-right: 3px;box-shadow:0 1px 2px rgba(0,0,0,.1);"> <div class="content_container" style="background: white;margin-left: 137px;min-height: 128px;margin-bottom: 0;margin-right: 3px;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="editable_block user_content" style="padding: 5px;margin-top: 3px;margin-bottom: 0;background: white;min-height: 133px;padding-bottom: 0;width: 100%;">` + string(item.ContentHtml) + `</div> <div class="editable_block user_content">` + string(item.ContentHtml) + `</div>
<div class="button_container" style="border-top: solid 1px #eaeaea;border-spacing: 0px;border-collapse: collapse;padding: 0;margin: 0;display: block;"> <div class="button_container">
`)) `))
if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply { if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write([]byte(`<a href="/reply/edit/submit/` + strconv.Itoa(item.ID) + `" style="border-right: solid 1px #eaeaea;color: #505050;font-size: 13px;padding-left: 5px;padding-right: 5px;">Edit</a>`)) w.Write([]byte(`<a href="/reply/edit/submit/` + strconv.Itoa(item.ID) + `" class="action_button">Edit</a>`))
} }
w.Write([]byte(` w.Write([]byte(`
`)) `))
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply { if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write([]byte(`<a href="/reply/delete/submit/` + strconv.Itoa(item.ID) + `" style="border-right: solid 1px #eaeaea;color: #505050;font-size: 13px;padding-left: 0;padding-right: 5px;">Delete</a>`)) w.Write([]byte(`<a href="/reply/delete/submit/` + strconv.Itoa(item.ID) + `" class="action_button">Delete</a>`))
} }
w.Write([]byte(` w.Write([]byte(`
<a href="/report/submit/` + strconv.Itoa(item.ID) + `?session=` + tmpl_topic_alt_vars.CurrentUser.Session + `&type=reply" style="border: none;border-right: solid 1px #eaeaea;padding-right: 6px;color: #505050;font-size: 13px;">Report</a> <a href="/report/submit/` + strconv.Itoa(item.ID) + `?session=` + tmpl_topic_alt_vars.CurrentUser.Session + `&type=reply" class="action_button">Report</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
{{template "header.html" . }} {{template "header.html" . }}
<div class="rowblock"> <div class="rowblock">
<div class="rowitem"><a>{{ .Title }}</a></div> <div class="rowitem"><a>{{.Title}}</a></div>
</div> </div>
<div class="rowblock"> <div class="rowblock">
{{range .ItemList}}<div class="rowitem passive" style="{{ if .Avatar }}background-image: url({{ .Avatar }});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}{{ if .Sticky }}background-color: #FFFFCC;{{else if .Is_Closed}}background-color: #eaeaea;{{end}}"> {{range .ItemList}}<div class="rowitem passive" style="{{ if .Avatar }}background-image: url({{ .Avatar }});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}{{ if .Sticky }}background-color: #FFFFCC;{{else if .Is_Closed}}background-color: #eaeaea;{{end}}">

View File

@ -1,9 +1 @@
<!doctype html> {{template "header.html" . }}{{template "$page_name" . }}{{template "footer.html" . }}
<html lang="en">
<head>
{{template "header.html" . }}
</head>
<body>
</body>
</html>

View File

@ -40,11 +40,11 @@
<div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">{{.CreatedByName}}</div> <div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">{{.CreatedByName}}</div>
</div> </div>
<div class="content_container" style="background: white;margin-left: 137px;min-height: 128px;margin-bottom: 0;margin-right: 3px;box-shadow:0 1px 2px rgba(0,0,0,.1);"> <div class="content_container" style="background: white;margin-left: 137px;min-height: 128px;margin-bottom: 0;margin-right: 3px;box-shadow:0 1px 2px rgba(0,0,0,.1);">
<div class="editable_block user_content" style="padding: 5px;margin-top: 3px;margin-bottom: 0;background: white;min-height: 133px;padding-bottom: 0;width: 100%;">{{.ContentHtml}}</div> <div class="editable_block user_content">{{.ContentHtml}}</div>
<div class="button_container" style="border-top: solid 1px #eaeaea;border-spacing: 0px;border-collapse: collapse;padding: 0;margin: 0;display: block;"> <div class="button_container">
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" style="border-right: solid 1px #eaeaea;color: #505050;font-size: 13px;padding-left: 5px;padding-right: 5px;">Edit</a>{{end}} {{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="action_button">Edit</a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" style="border-right: solid 1px #eaeaea;color: #505050;font-size: 13px;padding-left: 0;padding-right: 5px;">Delete</a>{{end}} {{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="action_button">Delete</a>{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" style="border: none;border-right: solid 1px #eaeaea;padding-right: 6px;color: #505050;font-size: 13px;">Report</a> <a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button">Report</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -9,22 +9,11 @@ body
font-family: arial; font-family: arial;
} }
@font-face { /* Patch for Edge */
font-family: 'EmojiFont';
src: url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff2') format('woff2'),
url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff') format('woff'), local("arial");
}
@supports (-ms-ime-align:auto) { @supports (-ms-ime-align:auto) {
.user_content .user_content
{ {
font-family: EmojiFont, arial; font-family: Segoe UI Emoji, arial;
}
}
@-moz-document url-prefix() {
.user_content
{
font-family: EmojiFont, arial;
} }
} }
@ -330,6 +319,41 @@ button.username
background-color: #FEB7CC; background-color: #FEB7CC;
} }
/* Tempra Conflux */
.user_content {
padding: 5px;
margin-top: 3px;
margin-bottom: 0;
background: white;
min-height: 129px;
padding-bottom: 0;
width: 100%;
}
.button_container {
border-top: solid 1px #eaeaea;
border-spacing: 0px;
border-collapse: collapse;
padding: 0;
margin: 0;
margin-top: 3px;
display: block;
height: 20px;
}
.action_button {
display: block;
float: left;
border-right: solid 1px #eaeaea;
color: #505050;
font-size: 13px;
padding-top: 2px;
padding-bottom: 2px;
padding-left: 5px;
padding-right: 5px;
}
/* Media Queries from Simple. Probably useless in Conflux */
@media (max-width: 880px) { @media (max-width: 880px) {
li li
{ {

View File

@ -9,22 +9,11 @@ body
font-family: arial; font-family: arial;
} }
@font-face { /* Patch for Edge */
font-family: 'EmojiFont';
src: url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff2') format('woff2'),
url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff') format('woff'), local("arial");
}
@supports (-ms-ime-align:auto) { @supports (-ms-ime-align:auto) {
.user_content .user_content
{ {
font-family: EmojiFont, arial; font-family: Segoe UI Emoji, arial;
}
}
@-moz-document url-prefix() {
.user_content
{
font-family: EmojiFont, arial;
} }
} }

View File

@ -154,7 +154,7 @@ func SimpleSessionCheck(w http.ResponseWriter, r *http.Request) (user User, succ
user.Session = cookie.Value user.Session = cookie.Value
// Is this session valid..? // Is this session valid..?
err = get_session_stmt.QueryRow(user.ID,user.Session).Scan(&user.ID, &user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName) err = get_session_stmt.QueryRow(user.ID,user.Session).Scan(&user.ID, &user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
user.ID = 0 user.ID = 0
user.Session = "" user.Session = ""

View File

@ -1,6 +1,8 @@
package main package main
import "log"
import "fmt" import "fmt"
import "time" import "time"
import "os"
import "encoding/base64" import "encoding/base64"
import "crypto/rand" import "crypto/rand"
import "net/smtp" import "net/smtp"
@ -87,3 +89,17 @@ func SendEmail(email string, subject string, msg string) bool {
} }
return true return true
} }
func write_file(name string, content string) {
f, err := os.Create(name)
if err != nil {
log.Fatal(err)
}
_, err = f.WriteString(content)
if err != nil {
log.Fatal(err)
}
f.Sync()
f.Close()
}