arnaucube.com/public/blogo.html
public/blogo.htmlRaw
1<!DOCTYPE html>
2<html lang="en">
3
4 <head>
5 <meta name="description" content="Webpage description goes here" />
6 <meta charset="utf-8">
7 <title>Static blog template engine implementation in Go - ArnauCube - Blog</title>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 <meta name="author" content="">
10
11 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
12 <link rel="stylesheet" href="css/style.css">
13
14 <!-- highlightjs -->
15 <!-- <link rel="stylesheet" href="js/highlightjs/atom-one-dark.css"> -->
16 <link rel="stylesheet" href="js/highlightjs/gruvbox-dark.css">
17 <script src="js/highlightjs/highlight.pack.js"></script>
18
19 <!-- katex -->
20 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css" integrity="sha384-Um5gpz1odJg5Z4HAmzPtgZKdTBHZdw8S29IecapCSB31ligYPhHQZMIlWLYQGVoc" crossorigin="anonymous">
21 </head>
22
23 <body>
24
25 <!-- o_gradient_background" -->
26 <nav id="mainNav" class="navbar navbar-default navbar-fixed-top"
27 style="height:50px;font-size:130%;">
28 <div class="container">
29 <a href="/blog" style="color:#000;">Blog index</a>
30 <a href="/" style="color:#000;float:right;">arnaucube.com</a>
31 </div>
32 </nav>
33 <div class="o_gradient_background" style="height:5px;"></div>
34
35
36
37 <div class="container" style="margin-top:80px;max-width:800px;">
38 <h1>Static blog template engine implementation in Go</h1>
39
40<p><em>2017-12-26</em></p>
41
42<p>Some 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.</p>
43
44<p>This is how I made <a href="https://github.com/arnaucube/blogo">blogo</a> the blog static template engine to do this blog.</p>
45
46<h3>Static blog template engine?</h3>
47
48<p>The 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.</p>
49
50<h2>Structure</h2>
51
52<p>What we wan to have in the input is:</p>
53
54<pre><code>/blogo
55 /input
56 /css
57 mycss.css
58 /img
59 post01_image.png
60 index.html
61 postThumbTemplate.html
62 post01.md
63 post01thumb.md
64 post02.md
65 post02thumb.md
66</code></pre>
67
68<h4>blogo.json</h4>
69
70<p>This is the file that have the configuration that will be read by the Go script.</p>
71
72<pre><code class="language-json">{
73 "title": "my blog",
74 "indexTemplate": "index.html",
75 "postThumbTemplate": "postThumbTemplate.html",
76 "posts": [
77 {
78 "thumb": "post01thumb.md",
79 "md": "post01.md"
80 },
81 {
82 "thumb": "post02thumb.md",
83 "md": "post02.md"
84 }
85 ],
86 "copyRaw": [
87 "css",
88 "js"
89 ]
90}
91</code></pre>
92
93<p>The <em>copyRaw</em> element, will be all the directories to copy raw to the output.</p>
94
95<h4>index.html</h4>
96
97<p>This is the file that will be used as the template for the main page and also for the posts pages.</p>
98
99<pre><code class="language-html"><!DOCTYPE html>
100<html>
101<head>
102 <title>[blogo-title]</title>
103</head>
104<body>
105 <div>
106 [blogo-content]
107 </div>
108</body>
109</html>
110</code></pre>
111
112<p>As we can see, we just need to define the html file, and in the title define the <em>[blogo-title]</em>, and in the content place the <em>[blogo-content]</em>.</p>
113
114<h4>postThumbTemplate.html</h4>
115
116<p>This is the file where is placed the html box for each post that will be displayed in the main page.</p>
117
118<pre><code class="language-html"><div class="well">
119 [blogo-index-post-template]
120</div>
121</code></pre>
122
123<h4>post01thumb.md</h4>
124
125<pre><code class="language-markdown"># Post 01 thumb
126This is the description of the Post 01
127</code></pre>
128
129<h4>post01.md</h4>
130
131<pre><code class="language-markdown"># Title of the Post 01
132Hi, this is the content of the post
133
134'''js
135 console.log("hello world");
136'''
137</code></pre>
138
139<h2>Let’s start to code</h2>
140
141<p>As 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.</p>
142
143<h4>readConfig.go</h4>
144
145<p>This is the file that reads the <em>blogo.json</em> file to get the blog configuration.</p>
146
147<pre><code class="language-go">package main
148
149import (
150 "encoding/json"
151 "io/ioutil"
152)
153
154//Post is the struct for each post of the blog
155type Post struct {
156 Thumb string `json:"thumb"`
157 Md string `json:"md"`
158}
159
160//Config gets the config.json file into struct
161type Config struct {
162 Title string `json:"title"`
163 IndexTemplate string `json:"indexTemplate"`
164 PostThumbTemplate string `json:"postThumbTemplate"`
165 Posts []Post `json:"posts"`
166 CopyRaw []string `json:"copyRaw"`
167}
168
169var config Config
170
171func readConfig(path string) {
172 file, err := ioutil.ReadFile(path)
173 check(err)
174 content := string(file)
175 json.Unmarshal([]byte(content), &config)
176}
177</code></pre>
178
179<h4>files.go, the operations with files</h4>
180
181<p>We will need some file operation functions, so we have placed all in this file.</p>
182
183<pre><code class="language-go">package main
184
185import (
186 "io/ioutil"
187 "os/exec"
188 "strings"
189
190 "github.com/fatih/color"
191)
192
193func readFile(path string) string {
194 dat, err := ioutil.ReadFile(path)
195 if err != nil {
196 color.Red(path)
197 }
198 check(err)
199 return string(dat)
200}
201
202func writeFile(path string, newContent string) {
203 err := ioutil.WriteFile(path, []byte(newContent), 0644)
204 check(err)
205 color.Green(path)
206}
207
208func getLines(text string) []string {
209 lines := strings.Split(text, "\n")
210 return lines
211}
212
213func concatStringsWithJumps(lines []string) string {
214 var r string
215 for _, l := range lines {
216 r = r + l + "\n"
217 }
218 return r
219}
220
221func copyRaw(original string, destination string) {
222 color.Green(original + " --> to --> " + destination)
223 _, err := exec.Command("cp", "-rf", original, destination).Output()
224 check(err)
225}
226</code></pre>
227
228<h4>main.go</h4>
229
230<p>To convert the HTML content to Markdown content, we will use <a href="https://github.com/russross/blackfriday">https://github.com/russross/blackfriday</a></p>
231
232<pre><code class="language-go">package main
233
234import (
235 "fmt"
236 "strings"
237
238 blackfriday "gopkg.in/russross/blackfriday.v2"
239)
240
241const directory = "blogo-input"
242
243func main() {
244 readConfig(directory + "/blogo.json")
245 fmt.Println(config)
246
247 // generate index page
248 indexTemplate := readFile(directory + "/" + config.IndexTemplate)
249 indexPostTemplate := readFile(directory + "/" + config.PostThumbTemplate)
250 var blogoIndex string
251 blogoIndex = ""
252 for _, post := range config.Posts {
253 mdpostthumb := readFile(directory + "/" + post.Thumb)
254 htmlpostthumb := string(blackfriday.Run([]byte(mdpostthumb)))
255
256 //put the htmlpostthumb in the blogo-index-post-template
257 m := make(map[string]string)
258 m["[blogo-index-post-template]"] = htmlpostthumb
259 r := putHTMLToTemplate(indexPostTemplate, m)
260 filename := strings.Split(post.Md, ".")[0]
261 r = "<a href='" + filename + ".html'>" + r + "</a>"
262 blogoIndex = blogoIndex + r
263 }
264 //put the blogoIndex in the index.html
265 m := make(map[string]string)
266 m["[blogo-title]"] = config.Title
267 m["[blogo-content]"] = blogoIndex
268 r := putHTMLToTemplate(indexTemplate, m)
269 writeFile("index.html", r)
270
271 // generate posts pages
272 for _, post := range config.Posts {
273 mdcontent := readFile(directory + "/" + post.Md)
274 htmlcontent := string(blackfriday.Run([]byte(mdcontent)))
275
276 m := make(map[string]string)
277 m["[blogo-title]"] = config.Title
278 m["[blogo-content]"] = htmlcontent
279
280 r := putHTMLToTemplate(indexTemplate, m)
281 //fmt.Println(r)
282
283 filename := strings.Split(post.Md, ".")[0]
284 writeFile(filename+".html", r)
285 }
286
287 //copy raw
288 fmt.Println("copying raw:")
289 for _, dir := range config.CopyRaw {
290 copyRaw(directory+"/"+dir, ".")
291 }
292}
293
294func putHTMLToTemplate(template string, m map[string]string) string {
295 lines := getLines(template)
296 var resultL []string
297 for _, line := range lines {
298 inserted := false
299 for k, v := range m {
300 if strings.Contains(line, k) {
301 //in the line, change [tag] with the content
302 lineReplaced := strings.Replace(line, k, v, -1)
303 resultL = append(resultL, lineReplaced)
304 inserted = true
305 }
306 }
307 if inserted == false {
308 resultL = append(resultL, line)
309 }
310 }
311 result := concatStringsWithJumps(resultL)
312 return result
313}
314</code></pre>
315
316<h2>Try it</h2>
317
318<p>To try it, we need to compile the Go code:</p>
319
320<pre><code>> go build
321</code></pre>
322
323<p>And run it:</p>
324
325<pre><code>> ./blogo
326</code></pre>
327
328<p>Or we can just build and run to test:</p>
329
330<pre><code>> go run *.go
331</code></pre>
332
333<p>As the output, we will obtain the html pages with the content:</p>
334
335<ul>
336<li>index.html</li>
337</ul>
338
339<pre><code class="language-html"><!DOCTYPE html>
340<html>
341<head>
342 <title>my blog</title>
343</head>
344<body>
345 <div class="row">
346 <a href='post01.html'>
347 <div class="col-md-3">
348 <h1>Post 01 thumb</h1>
349 <p>This is the description of the Post 01</p>
350 </div>
351 </a>
352 <a href='post02.html'>
353 <div class="col-md-3">
354 <p>Post 02 thumb</p>
355 </div>
356 </a>
357 </div>
358</body>
359</html>
360</code></pre>
361
362<ul>
363<li>post01.html</li>
364</ul>
365
366<pre><code class="language-html"><!DOCTYPE html>
367<html>
368<head>
369 <title>my blog</title>
370</head>
371<body>
372 <div>
373 <h1>Title of the Post 01</h1>
374 <p>Hi, this is the content of the post</p>
375 <pre>
376 <code class="language-js"> console.log(&quot;hello world&quot;);
377 </code>
378 </pre>
379 </div>
380</body>
381</html>
382</code></pre>
383
384<h2>Conclusion</h2>
385
386<p>In 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.</p>
387
388<p>There 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.</p>
389
390<p>The complete project code is able in <a href="https://github.com/arnaucube/blogo">https://github.com/arnaucube/blogo</a></p>
391
392 </div>
393
394 <footer style="text-align:center; margin-top:100px;margin-bottom:50px;">
395 <div class="container">
396 <div class="row">
397 <ul class="list-inline">
398 <li><a href="https://twitter.com/arnaucube"
399 style="color:gray;text-decoration:none;"
400 target="_blank">twitter.com/arnaucube</a>
401 </li>
402 <li><a href="https://github.com/arnaucube"
403 style="color:gray;text-decoration:none;"
404 target="_blank">github.com/arnaucube</a>
405 </li>
406 </ul>
407 </div>
408 <div class="row" style="display:inline-block;">
409 Blog made with <a href="http://github.com/arnaucube/blogo/"
410 target="_blank" style="color: gray;text-decoration:none;">Blogo</a>
411 </div>
412 </div>
413 </footer>
414
415 <script>
416 </script>
417 <script src="js/external-links.js"></script>
418 <script>hljs.initHighlightingOnLoad();</script>
419 <script defer src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.js" integrity="sha384-YNHdsYkH6gMx9y3mRkmcJ2mFUjTd0qNQQvY9VYZgQd7DcN7env35GzlmFaZ23JGp" crossorigin="anonymous"></script>
420 <script defer src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/contrib/auto-render.min.js" integrity="sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl" crossorigin="anonymous"></script>
421 <script>
422 document.addEventListener("DOMContentLoaded", function() {
423 renderMathInElement(document.body, {
424 displayMode: false,
425 // customised options
426 // • auto-render specific keys, e.g.:
427 delimiters: [
428 {left: '$$', right: '$$', display: true},
429 {left: '$', right: '$', display: false},
430 ],
431 // • rendering keys, e.g.:
432 throwOnError : true
433 });
434 });
435</script>
436
437
438 </body>
439</html>
440