├── .gitignore ├── .travis.yml ├── generators └── model.coffee ├── init.js ├── license.md ├── package.json ├── readme.md ├── root ├── .gitignore ├── .gitkeep ├── Makefile ├── app.coffee ├── assets │ ├── css │ │ ├── _settings.styl │ │ └── master.styl │ ├── img │ │ └── .keep │ └── js │ │ └── main.coffee ├── contentful.coffee ├── package.json ├── readme.md ├── ship.conf └── views │ ├── index.jade │ └── layout.jade └── test ├── config └── .gitkeep ├── fixtures └── locals.json ├── mocha.opts ├── support └── helper.js └── test.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | env: 5 | global: 6 | - secure: CoNID6YihIiX0Zt4d6bK5cqvQPJnkR9UWN4PAw54pRxdxMWBNVPs6xfXznijZ5EaRLF7lqWYO0pv4+eV25eJzlLH0u8qWDyShX3N3spw7mBuKU3X79/2VUAzRZQ2WEEw6HCW4kkh+LF5kpEmysCDwBcpnD3VmWHZnXgO3cu8Kzs= 7 | - secure: gAKjS+7E820CCJoeIxid/BA7IjizWDwFQzInnLFi23YVflMkC09mWNb28IE9QYZu3Uw1Qgnk0XHsPDRWACvDipmVgGoEF2bdKWO8dmugKKq4yCsfkgZCSEnr2jiMtkujTEO9zZen/Lpu8tw1/S6+ATxo/HHQ0F7wrZBPQV2C0hk= 8 | - secure: SgUe/nRZUND7IaQ2CytbtKUhWG2lixfyGXpCYWzv5BfIfLVZO1sQBVdgcGmha+7IPKx/ETag0uYTtpZzIlFAOgx/cqXFYwI2HN1cUbm7YVJUAbHzTt1r0zDrwLZCitZ75aPUmbAGqb69xkpIgW1zjci2b3wlnohbmxMD6Ah2FqU= 9 | - secure: b5ZejNag8XRhL+VSZ8aCDZjxuL73MFnIAGUCoRPLwQHtnZPSYICqyshUCT7BZM60r9V9DVyAQYjlGzq29DCEOcUfifNLmF6j0+INk+Qrg1VKTgwpNg0DDKmsEpDlCci0iJmOcXS9lcZVfgzp33ite2FzCrnB1tPsMxrfQ104AWE= 10 | -------------------------------------------------------------------------------- /generators/model.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | _ = require 'lodash' 3 | s = require 'underscore.string' 4 | contentful = require 'contentful-management' 5 | 6 | log = console.log.bind(console) 7 | 8 | valid_field_types = [ 9 | "Symbol" 10 | "Text" 11 | "Integer" 12 | "Number" 13 | "Date" 14 | "Boolean" 15 | "Link" 16 | "Array" 17 | "Object" 18 | "Asset" 19 | ] 20 | 21 | validate_field_type = (type) -> 22 | if not _.contains(valid_field_types, type) 23 | throw new Error("#{type} is not a valid Contentful field type") 24 | 25 | parse_fields = (fields) -> 26 | _.reduce fields, (res, field) -> 27 | split = field.split(':') 28 | name = split[0] 29 | type = s.capitalize(split[1]) || 'Text' 30 | validate_field_type(type) 31 | field_type = {name: name, type: type, id: s.underscored(name)} 32 | if type == 'Asset' 33 | field_type.linkType = type 34 | field_type.type = 'Link' 35 | res.push(field_type) 36 | return res 37 | , [] 38 | 39 | module.exports = (utils, name) -> 40 | fields = parse_fields(Array.prototype.slice.call(arguments, 2)) 41 | cwd = process.cwd() 42 | config = require(path.join(cwd, 'contentful')) 43 | client = contentful.createClient(accessToken: config.management_token) 44 | 45 | client.getSpace(config.space_id) 46 | .then (space) -> 47 | space.createContentType 48 | name: name 49 | fields: fields 50 | .then(log.ok, log.fail) 51 | -------------------------------------------------------------------------------- /init.js: -------------------------------------------------------------------------------- 1 | exports.configure = [ 2 | { 3 | name: 'name', 4 | message: 'What is the name of your project?' 5 | }, { 6 | name: 'description', 7 | message: 'Describe your project' 8 | }, { 9 | name: 'delivery_token', 10 | message: 'What is your Contentful Delivery API token?' 11 | }, { 12 | name: 'management_token', 13 | message: 'What is your Contentful Managment API token?' 14 | }, { 15 | name: 'space_id', 16 | message: 'What is your Contentful Space ID?' 17 | }, { 18 | name: 'netlify_token', 19 | message: 'What is your Netlify access token?' 20 | } 21 | ] 22 | 23 | exports.after = function () { 24 | console.log('Awesome! Your static CMS roots project has been set up and configured. \nTry it out by creating a content type on Contentful, configuring the content type in `app.coffee`, \nand then accessing it in your templates using the `contentful` local variable. To learn more, check out the roots-contentful readme: https://github.com/carrot/roots-contentful') 25 | } 26 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | License (MIT) 2 | ------------- 3 | 4 | Copyright (c) 2014 joshrowley 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sprout-static-cms", 3 | "version": "0.0.1", 4 | "description": "A sprout template for building a static CMS with Roots, Contentful, and Netlify.", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": { 10 | "contentful-management": "0.1.x", 11 | "lodash": "3.6.x", 12 | "underscore.string": "3.0.x" 13 | }, 14 | "devDependencies": { 15 | "chai": "2.2.x", 16 | "coffee-script": "1.9.x", 17 | "dotenv": "1.1.x", 18 | "mocha": "2.2.x", 19 | "rimraf": "2.3.x", 20 | "sprout": "0.2.x", 21 | "when": "3.7.x" 22 | }, 23 | "scripts": { 24 | "test": "mocha" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/carrot/sprout-static-cms.git" 29 | }, 30 | "keywords": [ 31 | "sprout", 32 | "roots", 33 | "template", 34 | "templates", 35 | "cms", 36 | "static", 37 | "netlify", 38 | "contentful" 39 | ], 40 | "author": "Josh Rowley", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/carrot/sprout-static-cms/issues" 44 | }, 45 | "homepage": "https://github.com/carrot/sprout-static-cms" 46 | } 47 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # sprout-static-cms 2 | 3 | A [sprout](http://github.com/carrot/sprout) template for creating a static CMS in Roots using Contentful and Netlify. Please go through the setup steps before running the template. 4 | 5 | ### Installation 6 | 7 | - `npm install sprout-cli -g` 8 | - `sprout add static-cms https://github.com/carrot/sprout-static-cms.git` 9 | 10 | ### Setup 11 | 12 | You should sign up for a Contentful and Netlify account before using this template as you will need an access token for both to complete the template configuration. 13 | 14 | You will need the following set up before you run this: 15 | 16 | #### A Contentful account and "space" 17 | 18 | 1. Create an account on their [website](https://contentful.com/). 19 | 2. Click on the top right menu and create a new space for your project (or edit the default one you start with). 20 | 3. Switch to the space and click on the gear on the top menu to go to Space Settings 21 | 4. Note your Contentful Space ID, you'll need this for the template 22 | 23 | #### A Contentful API token 24 | 25 | 1. Go to your project's space 26 | 2. Click on 'API' on the top menu bar 27 | 3. Click on 'API Keys' under the 'Content Delivery API' section on the left hand bar 28 | 4. Add a new API by clicking 'Add API key' and fill out the form 29 | 5. Note the Production API Access Token, you'll need this for the template. 30 | 31 | #### A Netlify account and API token 32 | 33 | 1. Create an account on their [website](https://www.netlify.com/) 34 | 2. Log in and click on your account dropdown at the top right and select `OAuth apps` 35 | 3. Under Personal Access Tokens, create a new token. 36 | 4. Note your Netlify API token, you'll need this for the template. 37 | 38 | #### Create a project 39 | 40 | `sprout init static-cms my-static-site` 41 | 42 | ### Generators 43 | 44 | Once your static CMS project is set up, this template will also give you useful generators. These generators rely on `contentful.coffee` in order to provide the credentials needed to interact with Contentful, **do not move this file if you need to use a generator**. 45 | 46 | #### Model Generator 47 | 48 | Sprout-static-cms comes equipped with a Contentful Content Model generator. Pass in the Content Model's name followed by a list of fields to generate following a `name:type` format, for example: 49 | 50 | ```bash 51 | sprout run static-cms model Post title:text description:text slug:symbol date:date rank:integer price:number private:boolean 52 | ``` 53 | 54 | If you don't specify a field type, sprout will default to a text field. 55 | 56 | 57 | #### Tests 58 | 59 | In order to run the tests, you'll need to add a couple environment variables. This project is setup to use [dotenv](https://github.com/motdotla/dotenv), so all you should need to do is add a `.env` file with the following variables: 60 | 61 | ``` 62 | DELIVERY_TOKEN=XXXXXXXXXXXX 63 | MANAGEMENT_TOKEN=XXXXXXXXXXXX 64 | SPACE_ID=XXXXXXXXXXXX 65 | ``` 66 | 67 | You should use a test Contentful account (i.e. not in use in production) to populate these values. 68 | 69 | ### Options 70 | 71 | - **name** (name of template) 72 | - **description** (a short description of the template) 73 | - **space_id** (the ID for the Contentful space associated with this project) 74 | - **delivery_token** (your Contentful space's Delivery API token) 75 | - **management_token** (your Contentful account's Management API token) 76 | - **netlify_token** (your Netlify account's personal access token) 77 | -------------------------------------------------------------------------------- /root/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | public -------------------------------------------------------------------------------- /root/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carrot/sprout-static-cms/20085143d8935ba5be22923680f25bb4ffd0d728/root/.gitkeep -------------------------------------------------------------------------------- /root/Makefile: -------------------------------------------------------------------------------- 1 | deploy: 2 | roots clean 3 | roots compile 4 | ship public -to netlify 5 | -------------------------------------------------------------------------------- /root/app.coffee: -------------------------------------------------------------------------------- 1 | axis = require 'axis' 2 | rupture = require 'rupture' 3 | autoprefixer = require 'autoprefixer-stylus' 4 | contentful = require 'roots-contentful' 5 | config = require './contentful' 6 | marked = require 'marked' 7 | 8 | module.exports = 9 | ignores: [ 10 | 'readme.md', '**/layout.*', '**/_*', '.gitignore', 'contentful.coffee', 11 | 'Makefile', 'ship*' 12 | ] 13 | 14 | stylus: 15 | use: [axis(), rupture(), autoprefixer()] 16 | 17 | locals: 18 | marked: marked 19 | 20 | extensions: [contentful(config)] 21 | -------------------------------------------------------------------------------- /root/assets/css/_settings.styl: -------------------------------------------------------------------------------- 1 | // Axis Settings (http://roots.cx/axis) 2 | 3 | // font stack (add your own!) 4 | helvetica-neue = "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif 5 | helvetica = "Helvetica Neue", Helvetica, Arial, sans-serif 6 | georgia = Georgia, Cambria, "Times New Roman", Times, serif 7 | lucidia-grande = "Lucida Grande", Tahoma, Verdana, Arial, sans-serif 8 | monospace = unquote("'Bitstream Vera Sans Mono', Consolas, Courier, monospace") 9 | verdana = Verdana, Geneva, sans-serif 10 | 11 | // default font stack 12 | font-stack = helvetica-neue 13 | font-size = 16 14 | font-color = #555 15 | 16 | // colors (via http://clrs.cc - slightly modded) 17 | navy = #001F3F 18 | blue = #0074D9 19 | aqua = #7FDBFF 20 | teal = #39CCCC 21 | olive = #3D9970 22 | green = #2ECC40 23 | lime = #01FF70 24 | yellow = #FFDC00 25 | orange = #FF851B 26 | red = #D13F19 27 | maroon = #85144B 28 | fuchsia = #F012BE 29 | purple = #B10DC9 30 | white = #FFFFFF 31 | silver = #DDDDDD 32 | gray = #AAAAAA 33 | black = #222222 34 | 35 | // default color 36 | default-color = blue 37 | 38 | // text highlight color 39 | highlight-color = blue 40 | 41 | // ligatures 42 | ligatures = false 43 | 44 | // vertical rhythm 45 | base-line-height = 24px 46 | default-rhythm-border-style = solid 47 | relative-font-sizing = true 48 | round-to-nearest-half-line = false 49 | min-line-padding = 2px 50 | 51 | // custom image base path for roots mixins 52 | img-path = '' 53 | 54 | // support for old IE 55 | support-for-ie = false 56 | 57 | // progressive IE (http://css3pie.com/) 58 | pie-enabled = false 59 | pie-path = '/pie.htc' 60 | -------------------------------------------------------------------------------- /root/assets/css/master.styl: -------------------------------------------------------------------------------- 1 | @import '_settings' 2 | 3 | normalize-css() 4 | base() 5 | 6 | body 7 | padding: 50px 8 | -------------------------------------------------------------------------------- /root/assets/img/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carrot/sprout-static-cms/20085143d8935ba5be22923680f25bb4ffd0d728/root/assets/img/.keep -------------------------------------------------------------------------------- /root/assets/js/main.coffee: -------------------------------------------------------------------------------- 1 | require.config 2 | paths: 3 | jquery: '//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min' 4 | 5 | require ['jquery'], ($) -> 6 | console.log 'jquery loaded' 7 | -------------------------------------------------------------------------------- /root/contentful.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | access_token: '<%= delivery_token %>' 3 | management_token: '<%= management_token %>' 4 | space_id: '<%= space_id %>' 5 | content_types: {} # remove these object braces once the config below is filled out 6 | # posts: # data will be made available through this key on the `contentful` object in your templates 7 | # id: 'xxxx' # ID of your content type 8 | # filters: {} # passes filters to the call to contentful's API, see contentful's docs for more info 9 | # template: 'path/to/template' # if present a single page view will be created for each entry in the content type 10 | # path: (entry) -> # override function for generating single page file path, passed in the entry object 11 | -------------------------------------------------------------------------------- /root/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= S.slugify(name) %>", 3 | "description": "<%= description %>", 4 | "dependencies": { 5 | "jade": "1.9.x", 6 | "marked": "0.3.x", 7 | "stylus": "0.50.x", 8 | "coffee-script": "1.9.x", 9 | "autoprefixer-stylus": "0.5.x", 10 | "axis": "0.3.x", 11 | "marked": "0.3.x", 12 | "rupture": "0.6.x", 13 | "roots": "3.0.x", 14 | "roots-contentful": "0.0.7", 15 | "ship": "0.2.x" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /root/readme.md: -------------------------------------------------------------------------------- 1 | <%= S.humanize(name) %> 2 | 3 | <%= description %> 4 | 5 | Setup 6 | ----- 7 | 8 | - make sure [node.js](http://nodejs.org) and [roots](http://roots.cx) are installed 9 | - clone this repo down and `cd` into the folder 10 | - run `npm install` 11 | - run `roots watch` 12 | - ??? 13 | - get money 14 | 15 | 16 | Deployment 17 | ---------- 18 | 19 | This site is hosted at Netlify. 20 | 21 | Run `make deploy` to deploy the site. This command is a shortcut for compiling the site with roots and deploying using [ship](https://github.com/carrot/ship). See the [Makefile](Makefile) for more info. 22 | -------------------------------------------------------------------------------- /root/ship.conf: -------------------------------------------------------------------------------- 1 | netlify: 2 | name: '<%= S.slugify(name) %>' 3 | access_token: '<%= netlify_token %>' 4 | -------------------------------------------------------------------------------- /root/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h3 Welcome to roots! 5 | :markdown 6 | Find tutorials and documentation at http://roots.cx : ) 7 | 8 | // Once you've added a content type and posts into Contentful 9 | // go update your app.coffee config and then test out iterating 10 | // over your content type here! 11 | 12 | //- ul 13 | //- - for post in contentful.posts 14 | //- li 15 | //- h4= post.title 16 | //- != marked(post.body) 17 | -------------------------------------------------------------------------------- /root/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | meta(charset='utf8') 5 | meta(http-equiv='X-UA-Compatible', content='IE=edge, chrome=1') 6 | meta(name='description', content='description of your site') 7 | meta(name='author', content= "author of the site") 8 | title= path 9 | link(rel='stylesheet', href='css/master.css') 10 | body 11 | block content 12 | 13 | script(data-main="js/main", src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.9/require.min.js") 14 | -------------------------------------------------------------------------------- /test/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carrot/sprout-static-cms/20085143d8935ba5be22923680f25bb4ffd0d728/test/config/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/locals.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "static-cms", 3 | "description": "A static CMS.", 4 | "netlify_token": "test_netlify_token" 5 | } 6 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --compilers coffee:coffee-script/register 3 | --require test/support/helper 4 | --timeout 10000 5 | -------------------------------------------------------------------------------- /test/support/helper.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | path = require('path'), 3 | fs = require('fs'), 4 | Sprout = require('sprout'); 5 | 6 | if (fs.existsSync(path.join(process.cwd(), '.env'))) { 7 | require('dotenv').load() 8 | } 9 | 10 | var sprout = new Sprout(path.join(__dirname, '../config')); 11 | 12 | var _path = path.join(__dirname, '../fixtures'); 13 | var locals = require(path.join(_path, 'locals.json')) 14 | var ENV = process.env 15 | 16 | locals.delivery_token = ENV['DELIVERY_TOKEN'] 17 | locals.management_token = ENV['MANAGEMENT_TOKEN'] 18 | locals.space_id = ENV['SPACE_ID'] 19 | 20 | global.chai = chai; 21 | global.sprout = sprout; 22 | global.expect = chai.expect; 23 | global._path = _path 24 | global.locals = locals 25 | -------------------------------------------------------------------------------- /test/test.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | rimraf = require 'rimraf' 3 | fs = require 'fs' 4 | nodefn = require 'when/node' 5 | 6 | test_template_path = path.resolve(_path, '../..') 7 | test_path = path.join(__dirname, 'tmp') 8 | 9 | tpl = 'sprout-static-cms' 10 | 11 | opts = 12 | locals: locals 13 | verbose: true 14 | 15 | before (done) -> 16 | sprout.add(tpl, test_template_path, {verbose: true}) 17 | .then -> rimraf.sync(test_path) 18 | .then -> sprout.init(tpl, test_path, opts) 19 | .done(-> done()) 20 | 21 | after -> 22 | sprout.remove(tpl).then -> rimraf.sync(test_path) 23 | 24 | describe 'sprout-static-cms', -> 25 | it 'should load the correct values into contentful.coffee', (done) -> 26 | nodefn.call(fs.readFile, path.join(test_path, 'contentful.coffee'), 'utf8') 27 | .then (res) -> 28 | expect(res).to.have.string "access_token: '#{locals.delivery_token}'" 29 | expect(res).to.have.string "management_token: '#{locals.management_token}'" 30 | expect(res).to.have.string "space_id: '#{locals.space_id}'" 31 | .then(-> done()).catch(done) 32 | 33 | it 'should load the correct values into package.json', -> 34 | pkg = require(path.join(test_path, 'package.json')) 35 | expect(pkg.name).to.equal(locals.name) 36 | expect(pkg.description).to.equal(locals.description) 37 | 38 | it 'should load the correct values in the ship file', (done) -> 39 | nodefn.call(fs.readFile, path.join(test_path, 'ship.conf'), 'utf8') 40 | .then (res) -> 41 | expect(res).to.have.string "name: '#{locals.name}'" 42 | expect(res).to.have.string "access_token: '#{locals.netlify_token}'" 43 | .then(-> done()).catch(done) 44 | --------------------------------------------------------------------------------