CirclesCMS (or just Circles) is an easy to use and easy to program, single page html5 content management system and blog engine built around websockets and angularjs.
It accomodates some ideas of various projects like Wheat or Sling.
The following is a short list of high-lights of Circles:
- Node.js - Circles runs on Node.js, as lightweight, scalable platform
- socketstream - a realtime, single page web app framework
- git - for storing persistent content and metadata, and everything is versioned - out of the box (similiar to what Wheat does
- marked - markdown parser and compiler, built for speed
- metamd - for parsing markdown metadata
- stylus - Expressive, robust, feature-rich CSS language
- Resource Resolution - The url represents the resource, which is first resolved. Based on the resource a client side template will be chosen to render the content.
- HTML5 History API - No reloading of the site when url changes.
Some ideas coming soon:
- redis - Advanced and fast key-value store for caching.
Circles is built to make single page websites possible. That means, after the initial load, the user does not need to reload the page or leaves the page when clicking on a link. Modern web pages make this possible with the use of Ajax or even more modern web pages with the use of websockets. CirclesCMS uses the latter approach.
To achieve this behaviour of not reloading a page on a click, the html5 history API is used. Circles listens on changes of the url therefore and makes an rpc call to the socketserver. Based on the url a resource on the server is resolved and based on the resource returned from the server the client resolves a template.
Circles uses this approach to answer 3 Questions arriving in a CMS: What? How? Where?
- What should the system deliver? -> A resource.
- How should the client display this resource? -> Which template to use.
- Where in the DOM should the rendered resource be viewed? -> Choosing the right element.
A resource is basically either a directory or a file in a repository. By default, two types of resources are known: list and item
A directory will always be a resource of type list. A file will by default be a resource of type item, which can be overridden with the use of metatags.
Currently only two types of files are supported:
- Markdown (suffix .md)
- HTML (suffix .html)
HTML parsing is not supported at the moment, which means HTML-files will be delivered as is with the type set to the default type item.
The library metamd supports the usage of metatags in markdown files, which means the resource type can be overriden, by defining the metatag type.
A resource will be tried to resolve in the following order:
- file
- directory
To give you an idea I give some examples:
/a -> no suffix, trying default suffixes
- a.md
- a.html
- a/
- 404
/a.html ->
- a.html
- a.html/
- 404
/a.md ->
- a.md
- a.md/
- 404
/blog/a ->
- blog/a.md
- blog/a.html
- blog/a/
- 404
Templates reside on the client side. The use the angular notation. They will have the suffix .html.
After retrieving the response from the server the client decides based on the same principle which template to use for rendering.
A template will be chosen based on the type and the path.
Some examples:
/a -> found content a.md, no type given, default type item
- a.item.html
- item.html
- no template
/a -> found content a.html, no metadata or type support, type none
- no template
/blog/a -> found content blog/a.md, type article
- blog/a.article.html
- blog/article.html
- article.html
- no template
/blog -> found content blog/, directory, type list
- blog.list.html
- list.html
- no template
/doesnotexist -> no resource found, error returned
- error.html
- no template, errormessage will be output in either the container with the id error or in the container with the id content.
/blog/doesnotexist -> no resource found, error returned
- blog/error.html
- error.html
- no template, errormessage will be output in either the container with the id error or in the container with the id content.
At the moment all resources are rendered in the same container using the angular directive ngView.
Depending on the url following content will be retrieved assuming following content structure:
/
|- about.md
|- contact.md
|- blog/
|- afile.md
|- anotherfile.md
|- directory.md
|- directory/
| |- file1.md
| |- file2.md
|
|- tmp/
URL | Content | Result |
---|---|---|
/blog | Directory | A json list containing the non recursive content of the Directory blog (see json). |
/about | File | The rendered content of the file about.md. |
/blog/afile | File | The rendered content of the file blog/afile.md. |
/doesnotexist | Error | An error message formated as json. |
/blog/directory | File | The rendered content of the file blog/directory.md. |
As you can see, an Item has precedence over a List. Thus I encourage to use unique paths, because in the case of /blog/directory you are not able to retrieve the directory listing. That means you can for example link to the files in /blog/directory manually in the file directory.md.
The json of an item contains all metatags. So a get on /blog, assuming blog is a directory, will return following object:
{
"res": [
{
"title": "A title",
"date": "2013-01-01",
"author": "Christian Sterzl",
//
// other metatags
//
"path": "blog/afile",
"type": "item"
},
{
"title": "Another title",
"date": "2013-02-02",
"author": "Christian Sterzl",
//
// other metatags
//
"path": "blog/anotherfile",
"type": "item"
},
{
"title": "Another title",
"date": "2013-02-02",
"author": "Christian Sterzl",
//
// other metatags
//
"path": "blog/directory",
"type": "item"
},
{
"path": "blog/tmp",
"type": "list"
}
]
}
A GET-request on /blog/directory resolves to blog/directory.md and will return following resource:
{
"title": "Another title",
"date": "2013-02-02",
"author": "Christian Sterzl",
//
// other metatags
//
"path": "blog/directory",
"type": "item",
"body": "<h1>Escaped Body Content</h1>"
}
An error message in json format will look like following:
{
"type": "error",
"code": "404",
"message": "Content Not Found"
}