diff --git a/flag.go b/flag.go index 2bd83ff..551e0cf 100644 --- a/flag.go +++ b/flag.go @@ -10,7 +10,7 @@ import ( ) func init() { - flag.StringVar(&util.URL, "url", "http://0.0.0.0:$port", "URL at which your wiki can be found. Used to generate feeds") + flag.StringVar(&util.URL, "url", "http://0.0.0.0:$port", "URL at which your wiki can be found. Used to generate feeds and social media previews") flag.StringVar(&util.ServerPort, "port", "1737", "Port to serve the wiki at using HTTP") flag.StringVar(&util.HomePage, "home", "home", "The home page name") flag.StringVar(&util.SiteTitle, "title", "🍄", "How to call your wiki in the navititle") diff --git a/http_readers.go b/http_readers.go index 6f7c58e..497a30f 100644 --- a/http_readers.go +++ b/http_readers.go @@ -36,7 +36,7 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) { textContents, err = history.FileAtRevision(textPath, revHash) ) if err == nil { - contents = markup.ToHtml(hyphaName, textContents) + contents = markup.Doc(hyphaName, textContents).AsHTML() } treeHTML, _, _ := tree.Tree(hyphaName, IterateHyphaNamesWith) page := templates.RevisionHTML( @@ -81,20 +81,27 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) { hyphaName = HyphaNameFromRq(rq, "page") data, hyphaExists = HyphaStorage[hyphaName] contents string + openGraph string ) if hyphaExists { fileContentsT, errT := ioutil.ReadFile(data.textPath) _, errB := os.Stat(data.binaryPath) if errT == nil { - contents = markup.ToHtml(hyphaName, string(fileContentsT)) + md := markup.Doc(hyphaName, string(fileContentsT)) + contents = md.AsHTML() + openGraph = md.OpenGraphHTML() } if !os.IsNotExist(errB) { contents = binaryHtmlBlock(hyphaName, data) + contents } } treeHTML, prevHypha, nextHypha := tree.Tree(hyphaName, IterateHyphaNamesWith) - util.HTTP200Page(w, base(hyphaName, templates.PageHTML(rq, hyphaName, - naviTitle(hyphaName), - contents, - treeHTML, prevHypha, nextHypha))) + util.HTTP200Page(w, + templates.BaseHTML( + hyphaName, + templates.PageHTML(rq, hyphaName, + naviTitle(hyphaName), + contents, + treeHTML, prevHypha, nextHypha), + openGraph)) } diff --git a/hypha.go b/hypha.go index e412c14..5280639 100644 --- a/hypha.go +++ b/hypha.go @@ -33,6 +33,12 @@ func init() { return } markup.HyphaIterate = IterateHyphaNamesWith + markup.HyphaImageForOG = func(hyphaName string) string { + if hd, isOld := GetHyphaData(hyphaName); isOld && hd.binaryPath != "" { + return util.URL + "/binary/" + hyphaName + } + return util.URL + "/favicon.ico" + } } // GetHyphaData finds a hypha addressed by `hyphaName` and returns its `hyphaData`. `hyphaData` is set to a zero value if this hypha does not exist. `isOld` is false if this hypha does not exist. diff --git a/markup/img.go b/markup/img.go index 9539498..99c919b 100644 --- a/markup/img.go +++ b/markup/img.go @@ -4,6 +4,8 @@ import ( "fmt" "regexp" "strings" + + "github.com/bouncepaw/mycorrhiza/util" ) var imgRe = regexp.MustCompile(`^img\s+{`) @@ -184,6 +186,14 @@ func (img *Img) binaryPathFor(path string) string { } } +func (img *Img) ogBinaryPathFor(path string) string { + path = img.binaryPathFor(path) + if strings.HasPrefix(path, "/binary/") { + return util.URL + path + } + return path +} + func (img *Img) pagePathFor(path string) string { path = strings.TrimSpace(path) if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 { diff --git a/markup/lexer.go b/markup/lexer.go index 6ba3b94..b1c3abc 100644 --- a/markup/lexer.go +++ b/markup/lexer.go @@ -9,6 +9,9 @@ import ( // HyphaExists holds function that checks that a hypha is present. var HyphaExists func(string) bool +// +var HyphaImageForOG func(string) string + // HyphaAccess holds function that accesses a hypha by its name. var HyphaAccess func(string) (rawText, binaryHtml string, err error) @@ -29,21 +32,21 @@ type GemLexerState struct { type Line struct { id int - // interface{} may be bad. What I need is a sum of string and Transclusion + // interface{} may be bad. TODO: a proper type contents interface{} } -func lex(name, content string) (ast []Line) { - var state = GemLexerState{name: name} +func (md *MycoDoc) lex() (ast []Line) { + var state = GemLexerState{name: md.hyphaName} - for _, line := range append(strings.Split(content, "\n"), "") { - geminiLineToAST(line, &state, &ast) + for _, line := range append(strings.Split(md.contents, "\n"), "") { + lineToAST(line, &state, &ast) } return ast } // Lex `line` in markup and save it to `ast` using `state`. -func geminiLineToAST(line string, state *GemLexerState, ast *[]Line) { +func lineToAST(line string, state *GemLexerState, ast *[]Line) { addLine := func(text interface{}) { *ast = append(*ast, Line{id: state.id, contents: text}) } diff --git a/markup/mycomarkup.go b/markup/mycomarkup.go index f3fb7a2..656ca28 100644 --- a/markup/mycomarkup.go +++ b/markup/mycomarkup.go @@ -2,8 +2,12 @@ package markup import ( + "fmt" "html" + "regexp" "strings" + + "github.com/bouncepaw/mycorrhiza/util" ) // A Mycomarkup-formatted document @@ -11,24 +15,75 @@ type MycoDoc struct { // data hyphaName string contents string + // indicators + parsedAlready bool + // results + ast []Line + html string + firstImageURL string + description string } // Constructor func Doc(hyphaName, contents string) *MycoDoc { - return &MycoDoc{ + md := &MycoDoc{ hyphaName: hyphaName, contents: contents, } + return md +} + +func (md *MycoDoc) Lex(recursionLevel int) *MycoDoc { + if !md.parsedAlready { + md.ast = md.lex() + } + md.parsedAlready = true + return md } // AsHtml returns an html representation of the document func (md *MycoDoc) AsHTML() string { - return "" + md.html = Parse(md.Lex(0).ast, 0, 0, 0) + return md.html } +// Used to clear opengraph description from html tags. This method is usually bad because of dangers of malformed HTML, but I'm going to use it only for Mycorrhiza-generated HTML, so it's okay. The question mark is required; without it the whole string is eaten away. +var htmlTagRe = regexp.MustCompile(`<.*?>`) + // OpenGraphHTML returns an html representation of og: meta tags. func (md *MycoDoc) OpenGraphHTML() string { - return "" + md.ogFillVars() + return strings.Join([]string{ + ogTag("title", md.hyphaName), + ogTag("type", "article"), + ogTag("image", md.firstImageURL), + ogTag("url", util.URL+"/page/"+md.hyphaName), + ogTag("determiner", ""), + ogTag("description", htmlTagRe.ReplaceAllString(md.description, "")), + }, "\n") +} + +func (md *MycoDoc) ogFillVars() *MycoDoc { + foundDesc := false + md.firstImageURL = HyphaImageForOG(md.hyphaName) + for _, line := range md.ast { + switch v := line.contents.(type) { + case string: + if !foundDesc { + md.description = v + foundDesc = true + } + case Img: + if len(v.entries) > 0 { + md.firstImageURL = v.entries[0].path.String() + } + } + } + return md +} + +func ogTag(property, content string) string { + return fmt.Sprintf(``, property, content) } /* The rest of this file is currently unused. TODO: use it I guess */ diff --git a/markup/parser.go b/markup/parser.go index 23887d1..50ff158 100644 --- a/markup/parser.go +++ b/markup/parser.go @@ -1,35 +1,24 @@ package markup -import () - const maxRecursionLevel = 3 -type GemParserState struct { - recursionLevel int -} - -func Parse(ast []Line, from, to int, state GemParserState) (html string) { - if state.recursionLevel > maxRecursionLevel { +func Parse(ast []Line, from, to int, recursionLevel int) (html string) { + if recursionLevel > maxRecursionLevel { return "Transclusion depth limit" } for _, line := range ast { if line.id >= from && (line.id <= to || to == 0) || line.id == -1 { switch v := line.contents.(type) { case Transclusion: - html += Transclude(v, state) + html += Transclude(v, recursionLevel) case Img: html += v.ToHtml() case string: html += v default: - html += "Unknown" + html += "Unknown element." } } } return html } - -func ToHtml(name, text string) string { - state := GemParserState{} - return Parse(lex(name, text), 0, 0, state) -} diff --git a/markup/xclusion.go b/markup/xclusion.go index b4a4370..a5df6c8 100644 --- a/markup/xclusion.go +++ b/markup/xclusion.go @@ -17,14 +17,14 @@ type Transclusion struct { } // Transclude transcludes `xcl` and returns html representation. -func Transclude(xcl Transclusion, state GemParserState) (html string) { - state.recursionLevel++ +func Transclude(xcl Transclusion, recursionLevel int) (html string) { + recursionLevel++ tmptOk := `
%s
%s
` tmptFailed := `
-

Failed to transclude %s

+

Hypha %s does not exist

` if xcl.from == xclError || xcl.to == xclError || xcl.from > xcl.to { return fmt.Sprintf(tmptFailed, xcl.name, xcl.name) @@ -34,7 +34,8 @@ func Transclude(xcl Transclusion, state GemParserState) (html string) { if err != nil { return fmt.Sprintf(tmptFailed, xcl.name, xcl.name) } - xclText := Parse(lex(xcl.name, rawText), xcl.from, xcl.to, state) + md := Doc(xcl.name, rawText) + xclText := Parse(md.lex(), xcl.from, xcl.to, recursionLevel) return fmt.Sprintf(tmptOk, xcl.name, xcl.name, binaryHtml+xclText) } diff --git a/templates/http_stuff.qtpl b/templates/http_stuff.qtpl index 9bacf51..3a88e2f 100644 --- a/templates/http_stuff.qtpl +++ b/templates/http_stuff.qtpl @@ -1,10 +1,11 @@ -{% func BaseHTML(title, body string) %} +{% func BaseHTML(title, body string, headElements ...string) %} {%s title %} + {% for _, el := range headElements %}{%s= el %}{% endfor %} {%s= body %} diff --git a/templates/http_stuff.qtpl.go b/templates/http_stuff.qtpl.go index 8fa0096..1e2bd99 100644 --- a/templates/http_stuff.qtpl.go +++ b/templates/http_stuff.qtpl.go @@ -18,7 +18,7 @@ var ( ) //line templates/http_stuff.qtpl:1 -func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string) { +func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string, headElements ...string) { //line templates/http_stuff.qtpl:1 qw422016.N().S(` @@ -31,55 +31,64 @@ func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string) { qw422016.E().S(title) //line templates/http_stuff.qtpl:7 qw422016.N().S(` + `) +//line templates/http_stuff.qtpl:8 + for _, el := range headElements { +//line templates/http_stuff.qtpl:8 + qw422016.N().S(el) +//line templates/http_stuff.qtpl:8 + } +//line templates/http_stuff.qtpl:8 + qw422016.N().S(` `) -//line templates/http_stuff.qtpl:10 +//line templates/http_stuff.qtpl:11 qw422016.N().S(body) -//line templates/http_stuff.qtpl:10 +//line templates/http_stuff.qtpl:11 qw422016.N().S(` `) -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 } -//line templates/http_stuff.qtpl:13 -func WriteBaseHTML(qq422016 qtio422016.Writer, title, body string) { -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 +func WriteBaseHTML(qq422016 qtio422016.Writer, title, body string, headElements ...string) { +//line templates/http_stuff.qtpl:14 qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/http_stuff.qtpl:13 - StreamBaseHTML(qw422016, title, body) -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 + StreamBaseHTML(qw422016, title, body, headElements...) +//line templates/http_stuff.qtpl:14 qt422016.ReleaseWriter(qw422016) -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 } -//line templates/http_stuff.qtpl:13 -func BaseHTML(title, body string) string { -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 +func BaseHTML(title, body string, headElements ...string) string { +//line templates/http_stuff.qtpl:14 qb422016 := qt422016.AcquireByteBuffer() -//line templates/http_stuff.qtpl:13 - WriteBaseHTML(qb422016, title, body) -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 + WriteBaseHTML(qb422016, title, body, headElements...) +//line templates/http_stuff.qtpl:14 qs422016 := string(qb422016.B) -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 qt422016.ReleaseByteBuffer(qb422016) -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 return qs422016 -//line templates/http_stuff.qtpl:13 +//line templates/http_stuff.qtpl:14 } -//line templates/http_stuff.qtpl:15 +//line templates/http_stuff.qtpl:16 func StreamHyphaListHTML(qw422016 *qt422016.Writer, tbody string, pageCount int) { -//line templates/http_stuff.qtpl:15 +//line templates/http_stuff.qtpl:16 qw422016.N().S(`

List of hyphae

This wiki has `) -//line templates/http_stuff.qtpl:18 +//line templates/http_stuff.qtpl:19 qw422016.N().D(pageCount) -//line templates/http_stuff.qtpl:18 +//line templates/http_stuff.qtpl:19 qw422016.N().S(` hyphae.

@@ -90,105 +99,105 @@ func StreamHyphaListHTML(qw422016 *qt422016.Writer, tbody string, pageCount int) `) -//line templates/http_stuff.qtpl:27 +//line templates/http_stuff.qtpl:28 qw422016.N().S(tbody) -//line templates/http_stuff.qtpl:27 +//line templates/http_stuff.qtpl:28 qw422016.N().S(`
`) -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 } -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 func WriteHyphaListHTML(qq422016 qtio422016.Writer, tbody string, pageCount int) { -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 StreamHyphaListHTML(qw422016, tbody, pageCount) -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 qt422016.ReleaseWriter(qw422016) -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 } -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 func HyphaListHTML(tbody string, pageCount int) string { -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 qb422016 := qt422016.AcquireByteBuffer() -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 WriteHyphaListHTML(qb422016, tbody, pageCount) -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 qs422016 := string(qb422016.B) -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 qt422016.ReleaseByteBuffer(qb422016) -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 return qs422016 -//line templates/http_stuff.qtpl:31 +//line templates/http_stuff.qtpl:32 } -//line templates/http_stuff.qtpl:33 +//line templates/http_stuff.qtpl:34 func StreamHyphaListRowHTML(qw422016 *qt422016.Writer, hyphaName, binaryMime string, binaryPresent bool) { -//line templates/http_stuff.qtpl:33 +//line templates/http_stuff.qtpl:34 qw422016.N().S(` `) -//line templates/http_stuff.qtpl:35 +//line templates/http_stuff.qtpl:36 qw422016.E().S(hyphaName) -//line templates/http_stuff.qtpl:35 +//line templates/http_stuff.qtpl:36 qw422016.N().S(` `) -//line templates/http_stuff.qtpl:36 +//line templates/http_stuff.qtpl:37 if binaryPresent { -//line templates/http_stuff.qtpl:36 +//line templates/http_stuff.qtpl:37 qw422016.N().S(` `) -//line templates/http_stuff.qtpl:37 +//line templates/http_stuff.qtpl:38 qw422016.E().S(binaryMime) -//line templates/http_stuff.qtpl:37 +//line templates/http_stuff.qtpl:38 qw422016.N().S(` `) -//line templates/http_stuff.qtpl:38 +//line templates/http_stuff.qtpl:39 } else { -//line templates/http_stuff.qtpl:38 +//line templates/http_stuff.qtpl:39 qw422016.N().S(` `) -//line templates/http_stuff.qtpl:40 +//line templates/http_stuff.qtpl:41 } -//line templates/http_stuff.qtpl:40 +//line templates/http_stuff.qtpl:41 qw422016.N().S(` `) -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 } -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 func WriteHyphaListRowHTML(qq422016 qtio422016.Writer, hyphaName, binaryMime string, binaryPresent bool) { -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 StreamHyphaListRowHTML(qw422016, hyphaName, binaryMime, binaryPresent) -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 qt422016.ReleaseWriter(qw422016) -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 } -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 func HyphaListRowHTML(hyphaName, binaryMime string, binaryPresent bool) string { -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 qb422016 := qt422016.AcquireByteBuffer() -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 WriteHyphaListRowHTML(qb422016, hyphaName, binaryMime, binaryPresent) -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 qs422016 := string(qb422016.B) -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 qt422016.ReleaseByteBuffer(qb422016) -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 return qs422016 -//line templates/http_stuff.qtpl:42 +//line templates/http_stuff.qtpl:43 }