summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rwxr-xr-xREADME.md376
1 files changed, 376 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..0f625a9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,376 @@
+makesite.py
+===========
+Take full control of your static website/blog generation by writing your
+own simple, lightweight, and magic-free static site generator in
+Python. That's right! Reinvent the wheel, fellas!
+
+[![View Source][SOURCE-BADGE]](makesite.py)
+[![View Demo][DEMO-BADGE]](https://tmug.github.io/makesite-demo)
+[![MIT License][LICENSE-BADGE]](LICENSE.md)
+
+[SOURCE-BADGE]: https://img.shields.io/badge/view-source-brightgreen.svg
+[DEMO-BADGE]: https://img.shields.io/badge/view-demo-brightgreen.svg
+[LICENSE-BADGE]: https://img.shields.io/badge/license-MIT-blue.svg
+
+
+Contents
+--------
+* [Introduction](#introduction)
+* [But Why?](#but-why)
+* [Get Started](#get-started)
+* [The Code](#the-code)
+* [Layout](#layout)
+* [Content](#content)
+* [Credits](#credits)
+* [License](#license)
+* [Support](#support)
+
+
+Introduction
+------------
+This repository contains the source code of an example website
+containing two static blogs and a few static pages. The website can be
+generated by running [makesite.py](makesite.py). The output looks like
+[this](https://tmug.github.io/makesite-demo). That's it!
+
+So go ahead, fork this repository, replace the [content](content) with
+your own, and generate your static website. It's that simple!
+
+You are [free](LICENSE.md) to copy, use, and modify this project for
+your blog or website, so go ahead and fork this repository and make it
+your own project. Change the [layout](layout) if you wish to, improve
+the [stylesheet](static/css/style.css) to suit your taste, enhance
+[makesite.py](makesite.py) if you need to, and develop your website/blog
+just the way you want it.
+
+
+But Why?
+--------
+For fun and profit! Okay, maybe not for profit, but hopefully for fun.
+
+Have you used a popular static site generator like Jekyll to generate
+your blog? I have too. It is simple and great. But then did you yearn
+to use something even simpler to generate your blog? Do you like Python?
+Perhaps the thought of writing your own static site generator crossed
+your mind but you thought it would be too much work? If you answered
+"yes" to these questions, then this project is for you.
+
+With this project, you are in full control. There is no hidden magic!
+Everything is laid out in [makesite.py](makesite.py) as plain and simple
+Python code. It is just 125 lines of code (excluding blank lines
+and comments). It gets you off the ground pretty quickly. You can have a
+decent website/blog generated within a few minutes and then you can
+begin tinkering with the [source code](makesite.py), the
+[layout](layout), and the [css](static/css/style.css) to customize the
+look and feel of your website to your satisfaction.
+
+
+Get Started
+-----------
+This section provides some quick steps to get you off the ground as
+quickly as possible.
+
+ 1. For a quick demo on your local system, just enter this command:
+
+ make serve
+
+ If you don't have `make` but have Python 3.x, enter this command:
+
+ python3 makesite.py
+ cd _site
+ python3 -m http.server
+
+ Note: In some environments, you may need to use `python` instead of
+ `python3` to invoke Python 3.x.
+
+ If you only have Python 2.7, enter this command:
+
+ python makesite.py
+ cd _site
+ python -m SimpleHTTPServer
+
+ Then visit http://localhost:8000/. It should look like
+ [this](https://tmug.github.io/makesite-demo).
+
+ Note: You can run [makesite.py](makesite.py) with Python 2.7 or
+ Python 3.x.
+
+ 2. You may see a few `Cannot render Markdown` warning messages in the
+ output of the previous command. This is due to the fact that an
+ example [blog](content/blog) in this project has a few posts written
+ in Markdown. To render them correctly, install the `commonmark`
+ package with this command:
+
+ pip install commonmark
+
+ Then try the previous step again.
+
+ 3. For an Internet-facing website, you would be hosting the static
+ website/blog on a hosting service and/or with a web server such as
+ Apache HTTP Server, Nginx, etc. You probably only need to generate
+ the static files and know where the static files are and move them
+ to your hosting location.
+
+ If you have the `make` command, enter this command to generate your
+ website:
+
+ make site
+
+ If you don't have `make` but have `python3`, enter this command:
+
+ python3 makesite.py
+
+ Note: In some environments, you may need to use `python` instead of
+ `python3` to invoke Python 3.x.
+
+ If you only have `python`, enter this command:
+
+ python makesite.py
+
+ The `_site` directory contains the entire generated website. The
+ content of this directory may be copied to your website hosting
+ location.
+
+
+The Code
+--------
+Now that you know how to generate the static website that comes with
+this project, it is time to see what [makesite.py](makesite.py) does.
+You probably don't really need to read the entire section. The source
+code is pretty self-explanatory but just in case, you need a detailed
+overview of what it does, here are the details:
+
+ 1. The `main()` function is the starting point of website generation.
+ It calls the other functions necessary to get the website generation
+ done.
+
+ 2. First it creates a fresh new `_site` directory from scratch. All
+ files in the [static directory](static) are copied to this
+ directory. Later the static website is generated and written to this
+ directory.
+
+ 3. Then it creates a `params` dictionary with some default parameters.
+ This dictionary is passed around to other functions. These other
+ functions would pick values from this dictionary to populate
+ placeholders in the layout template files.
+
+ Let us take the `subtitle` parameter for example. It is set
+ to our example website's fictitious brand name: "Lorem Ipsum". We
+ want each page to include this brand name as a suffix in the title.
+ For example, the [about page](https://tmug.github.io/makesite-demo/about/)
+ has "About - Lorem Ipsum" in its title. Now take a look at the
+ [page layout template](layout/page.html) that is used as the layout
+ for all pages in the static website. This layout file uses the
+ `{{ subtitle }}` syntax to denote that it is a placeholder that
+ should be populated while rendering the template.
+
+ Another interesting thing to note is that a content file can
+ override these parameters by defining its own parameters in the
+ content header. For example, take a look at the content file for
+ the [home page](content/_index.html). In its content header, i.e.,
+ the HTML comments at the top with key-value pairs, it defines a new
+ parameter named `title` and overrides the `subtitle` parameter.
+
+ We will discuss the syntax for placeholders and content headers
+ later. It is quite simple.
+
+ 4. It then loads all the layout templates. There are 6 of them in this
+ project.
+
+ - [layout/page.html](layout/page.html): It contains the base
+ template that applies to all pages. It begins with
+ `<!DOCTYPE html>` and `<html>`, and ends with `</html>`. The
+ `{{ content }}` placeholder in this template is replaced with
+ the actual content of the page. For example, for the about page,
+ the `{{ content }}` placeholder is replaced with the the entire
+ content from [content/about.html](content/about.html). This is
+ done with the `make_pages()` calls further down in the code.
+
+ - [layout/post.html](layout/post.html): It contains the template
+ for the blog posts. Note that it does not begin with `<!DOCTYPE
+ html>` and does not contain the `<html>` and `</html>` tags.
+ This is not a complete standalone template. This template
+ defines only a small portion of the blog post pages that are
+ specific to blog posts. It contains the HTML code and the
+ placeholders to display the title, publication date, and author
+ of blog posts.
+
+ This template must be combined with the
+ [page layout template](layout/page.html) to create the final
+ standalone template. To do so, we replace the `{{ content }}`
+ placeholder in the [page layout template](layout/page.html) with
+ the HTML code in the [post layout template](layout/post.html) to
+ get a final standalone template. This is done with the
+ `render()` calls further down in the code.
+
+ The resulting standalone template still has a `{{ content }}`
+ placeholder from the [post layout template](layout/post.html)
+ template. This `{{ content }}` placeholder is then replaced
+ with the actual content from the [blog posts](content/blog).
+
+ - [layout/list.html](layout/list.html): It contains the template
+ for the blog listing page, the page that lists all the posts in
+ a blog in reverse chronological order. This template does not do
+ much except provide a title at the top and an RSS link at the
+ bottom. The `{{ content }}` placeholder is populated with the
+ list of blog posts in reverse chronological order.
+
+ Just like the [post layout template](layout/post.html) , this
+ template must be combined with the
+ [page layout template](layout/page.html) to arrive at the final
+ standalone template.
+
+ - [layout/item.html](layout/item.html): It contains the template
+ for each blog post item in the blog listing page. The
+ `make_list()` function renders each blog post item with this
+ template and inserts them into the
+ [list layout template](layout/list.html) to create the blog
+ listing page.
+
+ - [layout/feed.xml](layout/feed.xml): It contains the XML template
+ for RSS feeds. The `{{ content }}` placeholder is populated with
+ the list of feed items.
+
+ - [layout/item.xml](layout/item.xml): It contains the XML template for
+ each blog post item to be included in the RSS feed. The
+ `make_list()` function renders each blog post item with this
+ template and inserts them into the
+ [layout/feed.xml](layout/feed.xml) template to create the
+ complete RSS feed.
+
+ 5. After loading all the layout templates, it makes a `render()` call
+ to combine the [post layout template](layout/post.html) with the
+ [page layout template](layout/page.html) to form the final
+ standalone post template.
+
+ Similarly, it combines the [list layout template](layout/list.html)
+ template with the [page layout template](layout/page.html) to form
+ the final list template.
+
+ 6. Then it makes two `make_pages()` calls to render the home page and a
+ couple of other site pages: the [contact page](content/contact.html)
+ and the [about page](content/about.html).
+
+ 7. Then it makes two more `make_pages()` calls to render two blogs: one
+ that is named simply [blog](content/blog) and another that is named
+ [news](content/news).
+
+ Note that the `make_pages()` call accepts three positional
+ arguments:
+
+ - Path to content source files provided as a glob pattern.
+ - Output path template as a string.
+ - Layout template code as a string.
+
+ These three positional arguments are then followed by keyword
+ arguments. These keyword arguments are used as template parameters
+ in the output path template and the layout template to replace the
+ placeholders with their corresponding values.
+
+ As described in point 2 above, a content file can override these
+ parameters in its content header.
+
+ 8. Then it makes two `make_list()` calls to render the blog listing
+ pages for the two blogs. These calls are very similar to the
+ `make_pages()` calls. There are only two things that are different
+ about the `make_list()` calls:
+
+ - There is no point in reading the same blog posts again that were
+ read by `make_pages()`, so instead of passing the path to
+ content source files, we feed a chronologically reverse-sorted
+ index of blog posts returned by `make_pages()` to `make_list()`.
+ - There is an additional argument to pass the
+ [item layout template](layout/item.html) as a string.
+
+ 9. Finally it makes two more `make_list()` calls to generate the RSS
+ feeds for the two blogs. There is nothing different about these
+ calls than the previous ones except that we use the feed XML
+ templates here to generate RSS feeds.
+
+To recap quickly, we create a `_site` directory to write the static site
+generated, define some default parameters, load all the layout
+templates, and then call `make_pages()` to render pages and blog posts
+with these templates, call `make_list()` to render blog listing pages
+and RSS feeds. That's all!
+
+Take a look at how the `make_pages()` and `make_list()` functions are
+implemented. They are very simple with less than 20 lines of code each.
+Once you are comfortable with this code, you can begin modifying it to
+add more blogs or reduce them. For example, you probably don't need a
+news blog, so you may delete the `make_pages()` and `make_list()` calls
+for `'news'` along with its content at [content/news](content/news).
+
+
+Layout
+------
+In this project, the layout template files are located in the [layout
+directory](layout). But they don't necessarily have to be there. You can
+place the layout files wherever you want and update
+[makesite.py](makesite.py) accordingly.
+
+The source code of [makesite.py](makesite.py) that comes with this
+project understands the notion of placeholders in the layout templates.
+The template placeholders have the following syntax:
+
+ {{ <key> }}
+
+Any whitespace before `{{`, around `<key>`, and after `}}` is ignored.
+The `<key>` should be a valid Python identifier. Here is an example of
+template placeholder:
+
+ {{ title }}
+
+This is a very simple template mechanism that is implemented already in
+the [makesite.py](makesite.py). For a simple website or blog, this
+should be sufficient. If you need a more sophisticated template engine
+such as [Jinja2](http://jinja.pocoo.org/) or
+[Cheetah](https://pythonhosted.org/Cheetah/), you need to modify
+[makesite.py](makesite.py) to add support for it.
+
+
+Content
+-------
+In this project, the content files are located in the [content
+directory](content). Most of the content files are written in HTML.
+However, the content files for the blog named [blog](content/blog) are
+written in Markdown.
+
+The notion of headers in the content files is supported by
+[makesite.py](makesite.py). Each content file may begin with one or more
+consecutive HTML comments that contain headers. Each header has the
+following syntax:
+
+ <!-- <key>: <value> -->
+
+Any whitespace before, after, and around the `<!--`, `<key>`, `:`,
+`<value>`, and `-->` tokens are ignored. Here are some example headers:
+
+ <!-- title: About -->
+ <!-- subtitle: Lorem Ipsum -->
+ <!-- author: Admin -->
+
+It looks for the headers at the top of every content file. As soon as
+some non-header text is encountered, the rest of the content from that
+point is not checked for headers.
+
+
+Credits
+-------
+Thanks to [Susam](https://github.com/susam) for writing the
+documentation and the unit tests.
+
+
+License
+-------
+This is free and open source software. You can use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of it,
+under the terms of the [MIT License](LICENSE.md).
+
+This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
+express or implied. See the [MIT License](LICENSE.md) for details.
+
+
+Support
+-------
+To report bugs, suggest improvements, or ask questions, please visit
+<https://github.com/sunainapai/makesite/issues>.