CornerstoneCMS is a lightweight and extensible cms/cmf powered by node.js and mongodb, so make sure you have both installed.
The fastest way to get started with CornerstoneCMS is to use the yeoman generator. We recommend this approach if you are starting a new project and just need a simple cms.
To begin, make sure you have installed the yo
command line utility:
npm install -g yo
...and the CornerstoneCMS generator (generator-cornerstone
):
npm install -g generator-cornerstone
Now, run yo cornerstone
from your command line to generate the project.
By default, Templates are stored in, and automatically loaded from, the /templates
directory and Collections, from /collections
. The /public
directory is home to any publicly available assets such as images and stylesheets. And the /uploads
directory is the destination for any user uploaded images or other content.
Run your project by calling npm start
from the command line.
You can also install CornerstoneCMS from npm if you're adding it to an existing project or want to have more control over the way things are set up.
var express = require('express');
var cornerstone = require('cornerstone-cms');
var app = express();
cornerstone.connect('mongodb://localhost/mysite');
cornerstone.loadTemplates(__dirname + '/templates'); //load all .html files in templates directory
cornerstone.loadCollections(__dirname + '/collections'); //load all .js files in collections directory
cornerstone.setUploadsDirectory(__dirname + '/uploads');
app.use(static('/public'));
app.use(cornerstone.express());
app.listen(8080);
Templating in CornerstoneCMS is built upon marko. A template is a .marko
file that contains html and custom marko tags. It can be used (an reused) to make pages from within the cms admin. You have full access to the features of marko. Additionally, CornerstoneCMS adds some custom tags and attributes to allow developers to make templates editable and pull in data from collections with minimal effort.
Have an <h2>
that needs to be editable? Simply specify that text
is editable and add a unique id
!
<!--original-->
<h2 class="sub-heading">This is my default heading</h2>
<!--editable-->
<h2 id="heading-1" editable={ text } class="sub-heading">This is my default heading</h2>
Need to edit more than one property?
<a id="some-link" editable={ text, href }></a>
Want to nest editable content?
<a id="feature-link" editable={ href }>
<h3 id="feature-header" editable={ text } />
<img id="feature-img" editable={ src } />
<p id="feature-description" editable={ text } />
</a>
Don't want the id
attribute in the actual output? Use _id
!
<a _id="some-link" editable={ text, href }></a>
By default, editable tags are scoped to the current page which means editing the tag on one page will not change the value of the tag on another page. By adding scope="site"
to an editable tag, the tag will now be scoped to the site. This means whenever the tag's value is changed, it will be changed on every page in the site.
<img id="site-logo" editable={ img } scope="site" />
To pass additional options to an editable type, set an options object on the property:
<img id="feature-image" editable={ img:{ ratio:16/9, maxwidth:1920 } } />
CornerstoneCMS ships with 4 built-in editable tag types:
text
allows you to edit the text content of an element. It also allows basic formatting for the text, depending on the tag that it is specified on.src
andbackground-image
allow the user to specify or upload an imagehref
allow the user to change where a link points
These types can be added to (or replaced) either by creating your own types or installing plugins created by the community.
The <find>
tag provides a way to get data out of collections and onto the page. Specify the collection name and a query will be made with the passed options, the contents of the tag will be looped over in a forEach
fashion for every result of the query:
<ul class="post-list">
<find(post in "Posts") filter={ isPublished:true } limit=4>
<li>${post.title}</li>
</find>
</ul>
filter
: a mongodb query object (e.g.{ isPublished:true }
) or a named filter (e.g."Published"
).sort
: a string or object indicating sort order (e.g{ date:-1 }
)skip
: a number indicating how many records to skip before returning resultslimit
: a number indicating the maximum records to returnselect
: an string or object indicating which fields to return (e.g.{ title:1 }
)
There are certain "global" variables that all templates have access to:
site
: any data associated with the sitepage
: any data associated with the pageqs
: the parsed querystring from the url
To register a template and make it available for creating pages within the cms admin, it must use the <register>
tag to register the template with a unique (and unchanging) id
and a name
to display within the cms admin:
<register("test-1" as "Test Template 1") />
Then, call cornerstone.registerTemplate(require([path to template]))
or register all .marko
files in a directory (that register a name
and id
) using cornerstone.loadTemplates([path to templates directory])
.
Collections in CornerstoneCMS are built upon Mongoose. Collections provide a way to manage and view custom, structured data for a site. Some common uses for collections would include users, blog posts, and contact form submissions. Collection data is available to view and manage from within the cms admin, and it is also available to display from templates using the <find>
tag.
var Collection = require('cornerstone-cms').Collection
var Posts = new Collection('Posts', {
schema: {
title:'Text',
date:{ type:'Date', default:() => new Date() },
content:'FormattedText',
author:{ type:'Reference', ref:'Authors' },
published:'Boolean',
}
})
The schema defines the collection's fields, their types, defaults, constraints, etc.
- Text Types:
- Date Types:
Date
- a date, e.g. 2000-01-01Time
- a time, e.g. 17:00:01.85 GMTDateTime
- a date/time, e.g. 2000-01-01 17:00:01.85 GMT
- Number Types:
Number
Integer
Currency
- Other Types:
Image
Color
File
Boolean
- a simple true/false, yes/no, on/off valueReference
- a reference to an object in another collectionPassword
- uses a masked (type="password") field client-side and bcrypts the value before savingAny
- allows any data in any format. displayed as a JSON textarea.
Named filters provide a tabbed interface within the cms admin to view filtered lists. To define a named filter add the name as a key in the filters
object on the collection definition, and a mongodb query object as the value:
var Posts = new Collection('Posts', {
schema: { /*...*/ },
filters: {
'Published':{ published:true },
'Drafts':{ published:false }
}
})
Named filters may also be used from a <find>
tag instead of specifying the equivalent mongodb query.
To register a collection and make it available within the cms admin and templates, call cornerstone.registerCollection([collection definition])
or register all .js
files in a directory using cornerstone.loadCollections([path to collections directory])
Once a collection is registered with CornerstoneCMS, you can access the underlying Mongoose model via its collection name:
var mongoose = require('mongoose');
var Post = mongoose.model('Posts');
When a collection is created, an api endpoint is also created at /api/${collection_name}
, however this can be configured to another path. By default, a collection endpoint is unavailable when logged out, and full access when logged in. This can be configured with an access function on the collection which will be passed the current user object. The example below would make a collection read-only when logged out, full access when logged in.
var Posts = new Collection('Posts', {
schema: { /*...*/ },
access: (user) => {
if(!user) return { read:true }
return { create:true, read:true, update:true, delete:true }
}
})
dfdgdsgfsd
By default, CornerstoneCMS comes with 4 editable types for templates: text
, src
, background-image
, and href
. View the implementation of each of these types to understand how additional types may be implemented.
cornerstone.registerPage('Page Name', '/page/path');
// register a developer created page that can be accessed from the pages section
// within the cms admin and included in the site wide menu.
cornerstone.loadTemplates(__dirname + '/templates');
cornerstone.registerTemplate('/path/to/template.html', options);
cornerstone.loadCollections(__dirname + '/collections');
cornerstone.registerCollection(require('/path/to/collection'));
cornerstone.loadWidgets(__dirname + '/widgets');
cornerstone.registerWidget('member-list', __dirname + '/widgets/member-list.html');
cornerstone.registerPublicPath('/', __dirname + '/public');
cornerstone.registerUploadPath('/uploads', __dirname + '/uploads');
MIT, see LICENSE.md for details.