arnaucube
arnaucube/arnaucube.comPublic Self-hosted repository
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&rsquo;ve found some interesting projects, but with a lot of features that I don&rsquo;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    &quot;title&quot;: &quot;my blog&quot;,
101    &quot;indexTemplate&quot;: &quot;index.html&quot;,
102    &quot;postThumbTemplate&quot;: &quot;postThumbTemplate.html&quot;,
103    &quot;posts&quot;: [
104        {
105            &quot;thumb&quot;: &quot;post01thumb.md&quot;,
106            &quot;md&quot;: &quot;post01.md&quot;
107        },
108        {
109            &quot;thumb&quot;: &quot;post02thumb.md&quot;,
110            &quot;md&quot;: &quot;post02.md&quot;
111        }
112    ],
113    &quot;copyRaw&quot;: [
114      &quot;css&quot;,
115      &quot;js&quot;
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">&lt;!DOCTYPE html&gt;
127&lt;html&gt;
128&lt;head&gt;
129    &lt;title&gt;[blogo-title]&lt;/title&gt;
130&lt;/head&gt;
131&lt;body&gt;
132    &lt;div&gt;
133        [blogo-content]
134    &lt;/div&gt;
135&lt;/body&gt;
136&lt;/html&gt;
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">&lt;div class=&quot;well&quot;&gt;
146    [blogo-index-post-template]
147&lt;/div&gt;
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(&quot;hello world&quot;);
163'''
164</code></pre>
165
166<h2>Let&rsquo;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	&quot;encoding/json&quot;
178	&quot;io/ioutil&quot;
179)
180
181//Post is the struct for each post of the blog
182type Post struct {
183	Thumb string `json:&quot;thumb&quot;`
184	Md    string `json:&quot;md&quot;`
185}
186
187//Config gets the config.json file into struct
188type Config struct {
189	Title             string   `json:&quot;title&quot;`
190	IndexTemplate     string   `json:&quot;indexTemplate&quot;`
191	PostThumbTemplate string   `json:&quot;postThumbTemplate&quot;`
192	Posts             []Post   `json:&quot;posts&quot;`
193	CopyRaw           []string `json:&quot;copyRaw&quot;`
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), &amp;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	&quot;io/ioutil&quot;
214	&quot;os/exec&quot;
215	&quot;strings&quot;
216
217	&quot;github.com/fatih/color&quot;
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, &quot;\n&quot;)
237	return lines
238}
239
240func concatStringsWithJumps(lines []string) string {
241	var r string
242	for _, l := range lines {
243		r = r + l + &quot;\n&quot;
244	}
245	return r
246}
247
248func copyRaw(original string, destination string) {
249	color.Green(original + &quot; --&gt; to --&gt; &quot; + destination)
250	_, err := exec.Command(&quot;cp&quot;, &quot;-rf&quot;, 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	&quot;fmt&quot;
263	&quot;strings&quot;
264
265	blackfriday &quot;gopkg.in/russross/blackfriday.v2&quot;
266)
267
268const directory = &quot;blogo-input&quot;
269
270func main() {
271	readConfig(directory + &quot;/blogo.json&quot;)
272	fmt.Println(config)
273
274	// generate index page
275	indexTemplate := readFile(directory + &quot;/&quot; + config.IndexTemplate)
276	indexPostTemplate := readFile(directory + &quot;/&quot; + config.PostThumbTemplate)
277	var blogoIndex string
278	blogoIndex = &quot;&quot;
279	for _, post := range config.Posts {
280		mdpostthumb := readFile(directory + &quot;/&quot; + 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[&quot;[blogo-index-post-template]&quot;] = htmlpostthumb
286		r := putHTMLToTemplate(indexPostTemplate, m)
287		filename := strings.Split(post.Md, &quot;.&quot;)[0]
288		r = &quot;&lt;a href='&quot; + filename + &quot;.html'&gt;&quot; + r + &quot;&lt;/a&gt;&quot;
289		blogoIndex = blogoIndex + r
290	}
291	//put the blogoIndex in the index.html
292	m := make(map[string]string)
293	m[&quot;[blogo-title]&quot;] = config.Title
294	m[&quot;[blogo-content]&quot;] = blogoIndex
295	r := putHTMLToTemplate(indexTemplate, m)
296	writeFile(&quot;index.html&quot;, r)
297
298	// generate posts pages
299	for _, post := range config.Posts {
300		mdcontent := readFile(directory + &quot;/&quot; + post.Md)
301		htmlcontent := string(blackfriday.Run([]byte(mdcontent)))
302
303		m := make(map[string]string)
304		m[&quot;[blogo-title]&quot;] = config.Title
305		m[&quot;[blogo-content]&quot;] = htmlcontent
306
307		r := putHTMLToTemplate(indexTemplate, m)
308		//fmt.Println(r)
309
310		filename := strings.Split(post.Md, &quot;.&quot;)[0]
311		writeFile(filename+&quot;.html&quot;, r)
312	}
313
314	//copy raw
315	fmt.Println(&quot;copying raw:&quot;)
316	for _, dir := range config.CopyRaw {
317		copyRaw(directory+&quot;/&quot;+dir, &quot;.&quot;)
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>&gt; go build
348</code></pre>
349
350<p>And run it:</p>
351
352<pre><code>&gt; ./blogo
353</code></pre>
354
355<p>Or we can just build and run to test:</p>
356
357<pre><code>&gt; 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">&lt;!DOCTYPE html&gt;
367&lt;html&gt;
368&lt;head&gt;
369    &lt;title&gt;my blog&lt;/title&gt;
370&lt;/head&gt;
371&lt;body&gt;
372    &lt;div class=&quot;row&quot;&gt;
373        &lt;a href='post01.html'&gt;
374            &lt;div class=&quot;col-md-3&quot;&gt;
375                &lt;h1&gt;Post 01 thumb&lt;/h1&gt;
376                &lt;p&gt;This is the description of the Post 01&lt;/p&gt;
377            &lt;/div&gt;
378        &lt;/a&gt;
379        &lt;a href='post02.html'&gt;
380            &lt;div class=&quot;col-md-3&quot;&gt;
381                &lt;p&gt;Post 02 thumb&lt;/p&gt;
382            &lt;/div&gt;
383        &lt;/a&gt;
384    &lt;/div&gt;
385&lt;/body&gt;
386&lt;/html&gt;
387</code></pre>
388
389<ul>
390<li>post01.html</li>
391</ul>
392
393<pre><code class="language-html">&lt;!DOCTYPE html&gt;
394&lt;html&gt;
395&lt;head&gt;
396    &lt;title&gt;my blog&lt;/title&gt;
397&lt;/head&gt;
398&lt;body&gt;
399    &lt;div&gt;
400        &lt;h1&gt;Title of the Post 01&lt;/h1&gt;
401        &lt;p&gt;Hi, this is the content of the post&lt;/p&gt;
402        &lt;pre&gt;
403            &lt;code class=&quot;language-js&quot;&gt;    console.log(&amp;quot;hello world&amp;quot;);
404            &lt;/code&gt;
405        &lt;/pre&gt;
406    &lt;/div&gt;
407&lt;/body&gt;
408&lt;/html&gt;
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&rsquo;m using for this blog.</p>
414
415<p>There are lots of blog template engines nowadays, but maybe we don&rsquo;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          ],
463          // • rendering keys, e.g.:
464          throwOnError : true
465        });
466      });
467
468      ///
469      let theme = localStorage.getItem("theme");
470      if ((theme === "light-theme")||(theme==null)) {
471          theme = "light-theme";
472          document.getElementById("themeSwitcher").checked = false;
473      } else if (theme === "dark-theme") {
474          theme = "dark-theme";
475          document.getElementById("themeSwitcher").checked = true;
476      }
477      document.body.className = theme;
478      localStorage.setItem("theme", theme);
479
480      function switchTheme() {
481        theme = localStorage.getItem("theme");
482        if (theme === "light-theme") {
483          theme = "dark-theme";
484          document.getElementById("themeSwitcher").checked = true;
485        } else {
486          theme = "light-theme";
487          document.getElementById("themeSwitcher").checked = false;
488        }
489        document.body.className = theme;
490        localStorage.setItem("theme", theme);
491
492        console.log(theme);
493      }
494    </script>
495    <script>
496    function tagLinks(tagName) {
497      var tags = document.getElementsByTagName(tagName);
498      for (var i=0, hElem; hElem =  tags[i]; i++) {
499        if (hElem.parentNode.className=="row postThumb") {
500          continue;
501        }
502        hElem.id = hElem.innerHTML.toLowerCase().replace(" ", "-");
503        hElem.innerHTML = "<a style='text-decoration:none;color:black;' href='#"+hElem.id+"'>"+hElem.innerHTML+"</a>";
504      }
505    }
506    tagLinks("h2");
507    tagLinks("h3");
508    tagLinks("h4");
509    tagLinks("h5");
510    </script>
511    <script src="js/mermaid.min.js"></script>
512
513
514  </body>
515</html>
516