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