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