An IndieWeb-friendly content management system built with Ruby on Rails.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


An IndieWeb-friendly content management system built with Ruby on Rails.

Build Maintainability Coverage

FrancisCMS began life in October 2014 as a Sinatra application (hence the name) before becoming a Rails application before becoming a mountable Rails engine.

At present, FrancisCMS is a white label Rails engine meant to be included in an existing Rails application at a particular mount point (/, for instance). FrancisCMS provides models, controllers, and views for CRUD-ing supported content types. Your host Rails application is responsible for providing a user authentication system, styles with CSS, and behavior with JavaScript. FrancisCMS also exposes a number of useful methods, helpers, and configuration variables available for use in the host Rails application.


  • Supports multiple content types (posts, links, and photos) with drafts, tags, and RSS feeds.
  • Supports syndicating content types to several third-party silos.
  • Receives and verifies webmentions, displaying verified webmentions on content pages.
  • Provides alternative representations of content types as Markdown and jf2-formatted JSON.
  • Inherits styling (CSS) and scripting (JavaScript) from the host Rails application.
  • Delegates authentication to the host Rails application.

Table of Contents


I manage Ruby versions with rbenv. I'd recommend you do the same or use a similar Ruby version manager (chruby or RVM come to mind). Also, if you're using Mac OS X, Homebrew makes installing command line utilities incredibly easy. The following instructions assume you'll use rbenv and Homebrew but you're totally free to install things as you like.

Software Dependencies

General Knowledge

I'll do my best to keep this documentation digestible and will avoid making assumptions about your skill level or depth of knowledge about Ruby, Rails, and any technology used in FrancisCMS. I highly recommend though that you familiarize yourself with Ruby on Rails, the framework upon which FrancisCMS is built. The official Ruby on Rails Guides are a great resource and worth a read.


Include the FrancisCMS engine in your project's Gemfile:

gem 'francis_cms', git: ''

Run bundle install to install FrancisCMS and its dependencies.

Sample Gemfile

ruby '2.2.4'

source '' do
  gem 'rails', '~> 4.2'
  gem 'francis_cms', git: ''


As mentioned above, FrancisCMS is a mountable Rails engine meant for inclusion in a host application. Rails engines are structurally similar to a typical Rails application, but how the host application and engine interact with one another is unique. In this section, we'll build a basic Rails application that mounts FrancisCMS and will detail many of the ways in which it interacts with the host application.

From The Ground Up

  1. mkdir && cd
  2. rbenv local 2.2.4
  3. Create a Gemfile in the root of the project and, in your editor of choice, add the contents from the Sample Gemfile section above.
  4. bundle install
  5. bundle exec rails new . --database=postgresql --skip-gemfile --skip-spring --skip-javascript --skip-turbolinks --skip-test-unit
  6. Edit config/database.yml to match your PostgreSQL configuration and start your local PostgreSQL server.
  7. bundle exec rake db:create && bundle exec rake db:migrate
  8. Create a static pages controller: bundle exec rails generate controller pages homepage --skip-routes --no-helper --no-assets.
  9. In config/routes.rb, add root 'pages#homepage'.
  10. Also in config/routes.rb, add mount FrancisCms::Engine, at: '/', replacing at: '/' with the path at which you wish to mount FrancisCMS.
  11. In app/controllers/application_controller.rb, create a logged_in? method (see Authentication below).
  12. bundle exec rails server -b
  13. Point your favorite browser at http://localhost:3000.

You should now have a templated homepage (source file at app/views/pages/homepage.html.erb) and you should be able to navigate directly to FrancisCMS' content listing pages (assuming FrancisCMS is mounted at /):

  • http://localhost:3000/links
  • http://localhost:3000/photos
  • http://localhost:3000/posts
  • http://localhost:3000/webmentions

See the Routes section below for more on how to create navigational links to these pages in your site's layout.


FrancisCMS has a number of configuration options that you can set in your application by creating an initializer file called config/initializers/francis_cms.rb. This file might look something like:

Rails.application.config.tap do |config|
  config.francis_cms.logged_in_method = :logged_in?
  config.francis_cms.login_path       = '/login' # path may be relative or absolute

  config.francis_cms.site_url         = '' # `site_url` must include protocol and trailing slash
  config.francis_cms.site_title       = 'FrancisCMS Demo Site'
  config.francis_cms.site_description = 'This is the default site description for a new FrancisCMS-powered website.'
  config.francis_cms.site_language    = 'en-US'

  config.francis_cms.user_name        = 'Jane Doe'
  config.francis_cms.user_email       = ''
  config.francis_cms.user_avatar      = '' # path may be relative or absolute

  config.francis_cms.license_title    = 'Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International'
  config.francis_cms.license_url      = ''

The values above are the default configuration options. You will only want to include the options you wish to override in your francis_cms.rb initializer. In your host Rails application, you can access these values with (where foo is replaced with site_url, site_title, etc.).


FrancisCMS does not include an authentication system, preferring instead to delegate authentication to your host Rails application. In keeping with the IndieWeb ethos, my preference is to use a service like IndieAuth, but I didn't want to force that decision on anyone interested in using FrancisCMS. Your application may have different needs so you should choose an authentication system that best suits those needs.

Regardless of which system you choose, you'll need to let FrancisCMS know about it. In the Configuration section above, you'll see settings for logged_in_method and login_path. The former is the method that FrancisCMS will delegate to when it needs to determine a user's authentication state and the latter is the URL to which a user should be redirected when not logged in.

In app/controllers/application_controller.rb, create a logged_in? method and expose it with helper_method:

def logged_in?
  # Your application-specific authentication logic would go here.
  # This method should return a boolean `true` or `false`.
helper_method :logged_in?

The name of this method (and its exposed helper) should match the method name set in your configuration (if you chose to override the default). You can now use conditional logic like if logged_in? in your application's controllers and views. Any actions or views controlled by FrancisCMS will delegate to this method and respond accordingly.

Displaying the Admin Panel

FrancisCMS provides a basic "panel" of admin-related markup that you can include in your pages. It's best to include this in your application's layout file using the following bit of code:

<%= francis_cms_admin_panel if logged_in? %>

This helper method will generate markup similar to:

<div class="admin-panel">
    <menu class="global">
        <li><a href="/posts/new">New post</a></li>
        <li><a href="/links/new">New link</a></li>
        <li><a href="/photos/new">New photo</a></li>


FrancisCMS uses standards Rails-y resource-based routing so generating URLs in views should be familiar. The primary difference, though, is a required francis_cms. prefix when using route helpers. For instance:

<%= link_to 'Posts', francis_cms.posts_path %>

The above ERB will generate the following markup (assuming a FrancisCMS is mounted at /):

<a href="/posts">Posts</a>

For a full list of available routes, run bundle exec rake routes from the root of your application.

RSS Feeds

FrancisCMS makes available RSS feeds for all content types (posts, links, photos, and webmentions). These URLs can be generated using ERB like:

<%= link_to 'Posts RSS feed', francis_cms.posts_path(format: :rss) %>

You can also use Rails' auto_discovery_link_tag helper to generate <link> elements in the <head> of your application's layout:

<%= auto_discovery_link_tag :rss, francis_cms.posts_path(format: :rss), title: %{#{francis_cms_config.site_title}: Posts} %>

Extending Controllers

You may find that your application would benefit by extending FrancisCMS' existing controllers and routing. The reasons for this and the complexity required will vary, of course, but a basic example might look like:

In app/controllers/links_controller.rb:

class LinksController < ApplicationController
  def fetch_json
    render json: { title: 'Sample Link Title', url: '' }

And in config/routes.rb:

post 'links/fetch_json', to: 'links#fetch_json'

The above example would add a route (/links/fetch_json) that responds to POST requests and a controller action that returns a block of JSON. Extending FrancisCMS' functionality in your application can be helpful, but proceed with caution.


FrancisCMS takes advantage of the Rails Internationalization API. Internationalization support is incomplete, but some strings are easily translatable. Default values are set in config/locales/en.yml and may be overridden in your host Rails application by adding the appropriate language files to config/locales. See the official documentation for more on this topic.

Content Types

FrancisCMS currently supports three primary content types: posts, links, and photos. All content types support receiving and displaying webmentions and provide alternative representations as Markdown and JSON.


Posts are textual content of any length and map to the general IndieWeb post construct. FrancisCMS doesn't currently distinguish between articles (akin to blog posts) and notes (akin to tweets). This may change in the future, though.

A post has the following attributes:

  • Title (required)
  • URL slug (e.g. an-example-post-url) (required)
  • Body (Markdown-formatted) (required)
  • Excerpt
  • Tags
  • Save as draft


Links are a type of post consisting primarily of a URL to a third-party website and map to the IndieWeb bookmark construct.

A link has the following attributes:

  • URL (required)
  • Title (required)
  • Body (Markdown-formatted)
  • Tags
  • Save as draft


Photos are a type of post consisting primarily of a photograph or image and map to the IndieWeb photo construct.

A photo has the following attributes:

  • Photo (an image file of type GIF, JPG, or PNG) (required)
  • Body (Markdown-formatted)
  • Tags
  • Save as draft

Photos also have a title virtual attribute (e.g. @photo.title) that will return the first line from the photo's body attribute or Untitled.

Photos are uploaded using CarrierWave and processed with MiniMagick and ImageMagick. Photos are converted to JPG and resized with the longest side being no greater than 500px, 750px, and 1000px.

Syndicating Content

FrancisCMS supports automated and manual syndication of content to third-party silos. In IndieWeb terminology, this is known as POSSE: Publish (on your) Own Site, Syndicate Elsewhere.


  1. Apply for a new Flickr API key. Keep the supplied key and secret handy, you'll need them in subsequent steps.
  2. From the root of your Rails application, run bundle exec rake francis_cms:configure_flickr.
  3. Follow the prompts to obtain the necessary OAuth keys and secrets from Flickr.
  4. Copy and paste your Flickr syndication configuration into your application's config/secrets.yml.


  1. Generate a new Medium integration token.
  2. Add the following to your application's config/secrets.yml, replacing the uppercased strings with your integration token from Medium:
medium_integration_token: YOUR_MEDIUM_INTEGRATION_TOKEN


  1. Visit and click the "Create New App" button.
  2. Enter the required Application Details and agree to the Developer Agreement.
  3. On the Keys and Access Tokens tab, click the "Create my access token" button.
  4. Add the following to your application's config/secrets.yml, replacing the uppercased strings with the appropriate values from Twitter:
twitter_consumer_key: YOUR_TWITTER_CONSUMER_KEY
twitter_consumer_secret: YOUR_TWITTER_CONSUMER_SECRET
twitter_access_token: YOUR_TWITTER_ACCESS_TOKEN
twitter_access_token_secret: YOUR_TWITTER_ACCESS_TOKEN_SECRET

Manual Syndication

For networks that either don't allow for automated syndication (e.g. Instagram) or that aren't currently supported in FrancisCMS, you can manually add syndicated copies to each content type. After publishing a piece of content, and when editing that piece of content, you'll see a form for adding links to syndicated copies.


FrancisCMS supplies views for CRUD-ing all supported content types. The host Rails application is responsible for providing the primary layout (e.g. app/views/layouts/application.html.erb) as well as CSS, JavaScript, and other assets. It's your choice as to how you create and serve those assets. A typical Rails application uses the Asset Pipeline which makes available to you pre-processing technologies like Sass.

FrancisCMS' views are lightweight, accessible, and flexible which means you can easily use a technique like progressive enhancement to build a beautiful and richly interactive website with the provided base markup. The sky's the limit, really!

Markup Conventions

FrancisCMS is built on IndieWeb principles and building blocks. Among those are the inclusion of microformats2 which enable technologies like webmention. As such, you'll see sprinkled throughout the markup class attribute values begining with h-, p-, and u-.

Care has been taken to craft FrancisCMS' markup in a way that is lightweight, accessible, and flexible. Rather than wed FrancisCMS to a particular CSS framework or convention (e.g. BassCSS, SMACSS, or OOCSS), I've instead tried to use descriptive class attribute values and allowed for considered use of the cascade (the "C" in "CSS").

Improving FrancisCMS







If diving into Ruby isn't your thing, but you'd still like to support FrancisCMS, consider making a donation! Any amount—large or small—is greatly appreciated. As a token of my gratitude, I'll add your name to the Acknowledgments below.

Donate via Square Cash Donate via Paypal


Most of this wouldn't have been possible without Tony Pitale's mentorship, encouragement, and patience. Thanks, buddy!

The default user avatar image (app/assets/images/avatar.png) was derived from Brent Jackson's Geomicons Open icon set.

FrancisCMS is written and maintained by Jason Garber.


FrancisCMS is freely available under the MIT License. Use it, learn from it, fork it, improve it, change it, tailor it to your needs.