How is the 123 site made?


coding guild

What’s Mini Squid?

Mini Squid is the guild I run on Hypixel. It doesn’t bring anything to table for its players, except there are players online during all timezones and everyone must submit a drawing to join in.

You can visit the site here: https://minikloon.com/guild

Header

Static site generator

Much like this blog, the guild’s site is made using a static site generator. Those are fancy words, let’s break it down:

So what’s the difference with other websites? Usually, a website is generated every time you request it. This allows for dynamic (the inverse of static) content to be displayed. For example, you could display today’s date, or the temperature outside, or Hypixel’s current player count.

In comparison with the current time on a clock, this website doesn’t change much.

Custom generator

This blog is created using Hugo. It’s a really good generator for creating blogs. but the guild website isn’t a blog, it’s mostly some sort of gallery. In this case, instead of looking for existing solutions, I just went out and created my own!

I kind of knew what I wanted the site to look like (a header pic, some text and a gallery).

But more importantly for the generator code, I knew the workflow I was looking for:

  1. Set up the website’s HTML/CSS once.
  2. When someone submits a drawing, add it to some folder.
  3. Run the generator once in a while to get a bunch of files.
  4. Upload all those files using FTP.

I described a generator as a tool which turns something into something else. In this case, we are turning a base html file + a bunch of drawings into some “cool” html site.

A base website template

HTML defines the structure of a site. Like in this blog, the titles are big, there’s a picture at the top and some lists. CSS defines the style/looks of the site. Again for this blog, everything is black and white.

My first step was to get a gallery site going in pure HTML/CSS.

Here is an example HTML file:

<body>
    <div>
        <h1>Mini Squid</h1>
        <img src="header.jpg" id="header">
        <div id="content">
            <p>To join the guild, Tweet <a href="https://twitter.com/Minikloon">@Minikloon</a> with your best drawing of a rainbow.</p>
			<p>Please include your Minecraft username and leave your current guild.</p>
        </div>
        <h2>Hall of Fame</h2>
        <div class="gallery">
            <!-- INSERT PICTURES HERE WITH COOL HTML -->
        </div>
    </div>
</body>

Oops! This is basically the final site. I just needed to fill in the “gallery” part.

I figured I didn’t have time to sharpen my CSS knowledge enough to be able to center images, even less so to create a grid. So I repeatedly googledhtml gallery example”, “html gallery template”, “html image grid”, “html image grid how” until I got a good example of a simple grid of images.

I found this tutorial. (Associated CodePen)

I didn’t really care for Flexbox. CSS Grid layout didn’t even exist. I just copy/pasted whatever worked for me.

Generating thumbnails

Thumbnails

Here’s the first code part of the generator.

I wanted to automatically turn the drawings into thumbnails for the gallery. One thing out of the way: I built the generator program in Java, because I’m most used to that language.

I googled “java thumbnail” and found a sweet project for my use case: Thumbnailator. It had very high abstraction level and took care of the problem in one snippet, explained on the Github’s readme.

Thumbnails.of(new File("path/to/directory").listFiles())
    .size(640, 480)
    .outputFormat("jpg")
    .toFiles(Rename.PREFIX_DOT_THUMBNAIL);

Templating engine

With my viciously copy/pasted website design + the thumbnails, half the work was done.

What was left was filling the gallery div with copies of this:

<a href="drawings\boston_slime.png" class="gallery-a">
    <figure>
        <img src="thumbnails\boston_slime.jpg" class="gallery-img">
    </figure>
</a>

One of those ^, for each thumbnail.

I could’ve just taken the whole site as a string, search for “INSERT PICTURES HERE WITH COOL HTML” and replace it with the html. But I didn’t really want to write HTML in my code, I didn’t want to deal with tabs and most importantly I wanted it to be cool.

“Template engine” might aswell be defined as “advanced copy/pasting”. If you’ve ever used ASP(.net), you’ve used a templating engine. PHP is also basically a big template engine with SQL bindings. For this use case I wanted to embed a template engine in my program.

So this time I googled “java template engine”. I found another cool project: Jtwig. It has a nice website so it must be good.

Twig is a templating language initially developed for the Symfony PHP framework. The main thing which hooked me were those sweet {{ handlerbars }}, which I had seen somewhere in some hipster scripts.

More importantly, Jtwig has great documentation, which made it easy to integrate in my program.

It’s basically three lines of code to turn a .twig into a not-.twig, in this case html:

JtwigTemplate template = JtwigTemplate.fileTemplate("templates/example.twig");
JtwigModel model = JtwigModel.newModel().with("var", "World");

template.render(model, System.out);

Putting it together

There are three things to understand about the guild site generator:

  1. There’s a .twig file with HTML and {{ template thingies }}
  2. It turns drawings into thumbnails
  3. There are conventions (aka hardcoded) locations where to put this .twig, the drawings and where to find the output.

Here’s the full template file: index.twig

The relevant part:

<h2>Hall of Fame</h2>
<div class="gallery">
    {%- for image in images %}
    <a href="{{ image.url }}" class="gallery-a">
        <figure>
            <img src="{{ image.thumbnailUrl }}" class="gallery-img">
        </figure>
    </a>
    {%- endfor %}
</div>

Full website generator’s code, with thumbnails & templating + some glue between the two:

fun main(args: Array<String>) {
    val outputDir = File("out")
    val thumbnailsDir = outputDir.resolve("thumbnails")

    val srcDrawings = File("src/drawings")
    Thumbnails.of(*srcDrawings.listFiles())
            .forceSize(150, 150)
            .outputFormat("jpg")
            .toFiles(thumbnailsDir, object: Rename() {
                override fun apply(filename: String, param: ThumbnailParameter): String {
                    return filename.replace(".png", "")
                }
            })

    val outDrawings = File(outputDir, "drawings")
    outDrawings.mkdir()
    srcDrawings.listFiles().forEach { inFile ->
        val outFile = File(outDrawings, inFile.name)
        Files.copy(inFile, outFile)
    }

    val template = JtwigTemplate.fileTemplate(File("src/index.twig"))
    val model = JtwigModel.newModel()
            .with("images", thumbnailsDir.listFiles().map {
                file -> ImageLink(
                    url = File(outDrawings, file.name.replace(".jpg", ".png")).relativeTo(outputDir).path,
                    thumbnailUrl = file.relativeTo(outputDir).path
                )
            })
    template.render(model, FileOutputStream("out/index.html"))

    println("blurp!")
}

data class ImageLink(val url: String, val thumbnailUrl: String)

Remember when I said I wanted to write it in Java? I lied. I like the Java ecosystem, with the JVM, Maven and IntelliJ, but this script is written in Kotlin.

Of course it might be difficult to follow the code without a programming background. The two key things to note about the templating are:

Generator usage

There’s a “guild_site” folder on my Desktop.

Guild site folder

“src” contains the input for the program (pictures and the twig template): src folder

When someone sends me a drawing, I put it in the “drawings” folder. sometimes I rotate it.

“out” contains all of the website, ready to upload: out folder

Then when I feel like updating the site:

  1. I click on File -> Recent Projects -> guild_site in IntelliJ (cause it’s usually already open)
  2. Press Ctrl+W to Run the program (custom shortcut from VS habit)
  3. Go in Filezilla (also usually open), open a new tab, connect to minikloon.com, transfer all the files

Filezilla example

Of course from these steps I could further automate it. I could run a program on startup to automatically update the site by watching the folder.

I could also create a web page for people to submit their drawings and I would just have to click yes/no to invite them to the guild automatically and tick the “add to hall of fame” checkbox to upload their drawing.

But these programs are for another day. :-)