arnaucube
arnaucube/arnaucube.comPublic Self-hosted repository
arnaucube.com/blogo-input/posts/blogo.md
blogo-input/posts/blogo.mdRaw
  1# Static blog template engine implementation in Go
  2*2017-12-26*
  3
  4Some days ago, I decided to start this blog, to put there all the thoughts and ideas that goes through my mind. After some research, I've found some interesting projects, but with a lot of features that I don't need to use. So I decided to write my own minimalistic static blog template engine with Go lang.
  5
  6This is how I made [blogo](https://github.com/arnaucube/blogo) the blog static template engine to do this blog.
  7
  8
  9### Static blog template engine?
 10The main idea is to be able to write the blog posts in Markdown files, and with the template engine, output the HTML files ready to upload in the web hosting server.
 11
 12## Structure
 13What we wan to have in the input is:
 14
 15```
 16/blogo
 17    /input
 18        /css
 19            mycss.css
 20        /img
 21            post01_image.png
 22        index.html
 23        postThumbTemplate.html
 24        post01.md
 25        post01thumb.md
 26        post02.md
 27        post02thumb.md
 28```
 29
 30#### blogo.json
 31This is the file that have the configuration that will be read by the Go script.
 32
 33```json
 34{
 35    "title": "my blog",
 36    "indexTemplate": "index.html",
 37    "postThumbTemplate": "postThumbTemplate.html",
 38    "posts": [
 39        {
 40            "thumb": "post01thumb.md",
 41            "md": "post01.md"
 42        },
 43        {
 44            "thumb": "post02thumb.md",
 45            "md": "post02.md"
 46        }
 47    ],
 48    "copyRaw": [
 49      "css",
 50      "js"
 51    ]
 52}
 53```
 54The *copyRaw* element, will be all the directories to copy raw to the output.
 55
 56#### index.html
 57This is the file that will be used as the template for the main page and also for the posts pages.
 58
 59```html
 60<!DOCTYPE html>
 61<html>
 62<head>
 63    <title>[blogo-title]</title>
 64</head>
 65<body>
 66    <div>
 67        [blogo-content]
 68    </div>
 69</body>
 70</html>
 71```
 72
 73As we can see, we just need to define the html file, and in the title define the *[blogo-title]*, and in the content place the *[blogo-content]*.
 74
 75#### postThumbTemplate.html
 76This is the file where is placed the html box for each post that will be displayed in the main page.
 77
 78```html
 79<div class="well">
 80    [blogo-index-post-template]
 81</div>
 82```
 83
 84#### post01thumb.md
 85
 86```markdown
 87# Post 01 thumb
 88This is the description of the Post 01
 89```
 90
 91#### post01.md
 92```markdown
 93# Title of the Post 01
 94Hi, this is the content of the post
 95
 96'''js
 97    console.log("hello world");
 98'''
 99```
100
101## Let's start to code
102As we have exposed, we want to develop a Go lang script that from some HTML template and the Markdown text files, generates the complete blog with the main page and all the posts.
103
104#### readConfig.go
105This is the file that reads the *blogo.json* file to get the blog configuration.
106
107```go
108package main
109
110import (
111	"encoding/json"
112	"io/ioutil"
113)
114
115//Post is the struct for each post of the blog
116type Post struct {
117	Thumb string `json:"thumb"`
118	Md    string `json:"md"`
119}
120
121//Config gets the config.json file into struct
122type Config struct {
123	Title             string   `json:"title"`
124	IndexTemplate     string   `json:"indexTemplate"`
125	PostThumbTemplate string   `json:"postThumbTemplate"`
126	Posts             []Post   `json:"posts"`
127	CopyRaw           []string `json:"copyRaw"`
128}
129
130var config Config
131
132func readConfig(path string) {
133	file, err := ioutil.ReadFile(path)
134	check(err)
135	content := string(file)
136	json.Unmarshal([]byte(content), &config)
137}
138```
139
140#### files.go, the operations with files
141We will need some file operation functions, so we have placed all in this file.
142
143```go
144package main
145
146import (
147	"io/ioutil"
148	"os/exec"
149	"strings"
150
151	"github.com/fatih/color"
152)
153
154func readFile(path string) string {
155	dat, err := ioutil.ReadFile(path)
156	if err != nil {
157		color.Red(path)
158	}
159	check(err)
160	return string(dat)
161}
162
163func writeFile(path string, newContent string) {
164	err := ioutil.WriteFile(path, []byte(newContent), 0644)
165	check(err)
166	color.Green(path)
167}
168
169func getLines(text string) []string {
170	lines := strings.Split(text, "\n")
171	return lines
172}
173
174func concatStringsWithJumps(lines []string) string {
175	var r string
176	for _, l := range lines {
177		r = r + l + "\n"
178	}
179	return r
180}
181
182func copyRaw(original string, destination string) {
183	color.Green(original + " --> to --> " + destination)
184	_, err := exec.Command("cp", "-rf", original, destination).Output()
185	check(err)
186}
187```
188
189#### main.go
190
191To convert the HTML content to Markdown content, we will use https://github.com/russross/blackfriday
192
193```go
194package main
195
196import (
197	"fmt"
198	"strings"
199
200	blackfriday "gopkg.in/russross/blackfriday.v2"
201)
202
203const directory = "blogo-input"
204
205func main() {
206	readConfig(directory + "/blogo.json")
207	fmt.Println(config)
208
209	// generate index page
210	indexTemplate := readFile(directory + "/" + config.IndexTemplate)
211	indexPostTemplate := readFile(directory + "/" + config.PostThumbTemplate)
212	var blogoIndex string
213	blogoIndex = ""
214	for _, post := range config.Posts {
215		mdpostthumb := readFile(directory + "/" + post.Thumb)
216		htmlpostthumb := string(blackfriday.Run([]byte(mdpostthumb)))
217
218		//put the htmlpostthumb in the blogo-index-post-template
219		m := make(map[string]string)
220		m["[blogo-index-post-template]"] = htmlpostthumb
221		r := putHTMLToTemplate(indexPostTemplate, m)
222		filename := strings.Split(post.Md, ".")[0]
223		r = "<a href='" + filename + ".html'>" + r + "</a>"
224		blogoIndex = blogoIndex + r
225	}
226	//put the blogoIndex in the index.html
227	m := make(map[string]string)
228	m["[blogo-title]"] = config.Title
229	m["[blogo-content]"] = blogoIndex
230	r := putHTMLToTemplate(indexTemplate, m)
231	writeFile("index.html", r)
232
233	// generate posts pages
234	for _, post := range config.Posts {
235		mdcontent := readFile(directory + "/" + post.Md)
236		htmlcontent := string(blackfriday.Run([]byte(mdcontent)))
237
238		m := make(map[string]string)
239		m["[blogo-title]"] = config.Title
240		m["[blogo-content]"] = htmlcontent
241
242		r := putHTMLToTemplate(indexTemplate, m)
243		//fmt.Println(r)
244
245		filename := strings.Split(post.Md, ".")[0]
246		writeFile(filename+".html", r)
247	}
248
249	//copy raw
250	fmt.Println("copying raw:")
251	for _, dir := range config.CopyRaw {
252		copyRaw(directory+"/"+dir, ".")
253	}
254}
255
256func putHTMLToTemplate(template string, m map[string]string) string {
257	lines := getLines(template)
258	var resultL []string
259	for _, line := range lines {
260		inserted := false
261		for k, v := range m {
262			if strings.Contains(line, k) {
263				//in the line, change [tag] with the content
264				lineReplaced := strings.Replace(line, k, v, -1)
265				resultL = append(resultL, lineReplaced)
266				inserted = true
267			}
268		}
269		if inserted == false {
270			resultL = append(resultL, line)
271		}
272	}
273	result := concatStringsWithJumps(resultL)
274	return result
275}
276```
277
278## Try it
279To try it, we need to compile the Go code:
280```
281> go build
282```
283And run it:
284```
285> ./blogo
286```
287
288Or we can just build and run to test:
289```
290> go run *.go
291```
292
293As the output, we will obtain the html pages with the content:
294
295- index.html
296
297```html
298<!DOCTYPE html>
299<html>
300<head>
301    <title>my blog</title>
302</head>
303<body>
304    <div class="row">
305        <a href='post01.html'>
306            <div class="col-md-3">
307                <h1>Post 01 thumb</h1>
308                <p>This is the description of the Post 01</p>
309            </div>
310        </a>
311        <a href='post02.html'>
312            <div class="col-md-3">
313                <p>Post 02 thumb</p>
314            </div>
315        </a>
316    </div>
317</body>
318</html>
319```
320
321- post01.html
322
323```html
324<!DOCTYPE html>
325<html>
326<head>
327    <title>my blog</title>
328</head>
329<body>
330    <div>
331        <h1>Title of the Post 01</h1>
332        <p>Hi, this is the content of the post</p>
333        <pre>
334            <code class="language-js">    console.log(&quot;hello world&quot;);
335            </code>
336        </pre>
337    </div>
338</body>
339</html>
340```
341
342## Conclusion
343In this post, we have seen how to develop a very minimalistic static blog template engine with Go. In fact, is the blog engine that I'm using for this blog.
344
345There are lots of blog template engines nowadays, but maybe we don't need sophisticated engine, and we just need a minimalistic one. In that case, we have seen how to develop one.
346
347The complete project code is able in https://github.com/arnaucube/blogo
348