/atomic_cms

CMS for Atomic Asset components

Primary LanguageCoffeeScript

Atomic CMS

The biggest problem with any content management system is the admin users are given too much or too little power when editing their site. This gem provides a means to manage usage of components created from the gem Atomic Assets. By providing admin users with a way to manage their Atomic Assets, developers and designers are able to ensure the components they create remained styled properly while allowing admins to update their content as needed without developer intervention. Below are instructions for getting started.

Gemfile

After initializing a new Rails application, or adding to an existing application, add the following gems to your Gemfile.

gem 'activeadmin', github: 'activeadmin'
gem 'angularjs-rails'
gem 'atomic_assets'
gem 'atomic_cms', github: 'samcdavid/atomic_cms'
gem 'devise'
gem 'redcarpet'
gem 'slim-rails'

Then perform a bundle install.

Initialization

Active Admin

To initialize Active Admin:

rails generate active_admin:install

Remove the comments migration Active Admin created along with disabling comments on line 122 of the Active Admin initializer.

Now you shall run the migration and seeds with:

bundle exec rake db:setup

To verify, start the server and visit localhost:3000/admin. If you can login as admin@example.com with the password password you have successfully completed this step.

JavaScript

Next, update ./app/assets/javascripts/active_admin.js.coffee to match the following:

#= require active_admin/base
#= require angular
#= require angular-sanitize
#= require atomic_cms

Styles

You should install bourbon, bitters, and neat as well as reset.css in order for the next steps to work correctly. Also create a folder under your stylesheets folder for all of the scss for your components and go ahead and create an empty scss file for your text block component.

Now, update your active_admin.scss file to match the following:

rt "active_admin/mixins";
@import "active_admin/base";
@import "bourbon";
@import "neat";
@import "base/variables";
@import "base/grid-settings";
@import "atomic_cms";

#component_preview {
  //@import "base/base";
  @import "reset";
  @import "base/buttons";
  @import "base/forms";
  @import "base/lists";
  @import "base/tables";
  @import "base/typography";
  @import "components/*";
  @include font-feature-settings("kern", "liga", "pnum");
  -webkit-font-smoothing: antialiased;
  color: $base-font-color;
  font-family: $base-font-family;
  font-size: 16px;
  line-height: $base-line-height;
  // When editing a page through the CMS,
  // images with broken links will not be displayed
  img[src="image"] {
    display: none !important;
  }
}

// Overriding any non-variable SASS must be done after the fact.
// For example, to change the default status-tag color:
//
//   .status_tag { background: #6090DB; }

Atomic CMS

Routes

Update your config/routes.rb to include the following:

mount AtomicCms::Engine => "/atomic_cms"
get "*path", to: "pages#show", controller: "pages", as: :page, format: false
root to: 'pages#show', controller: "pages"

The last two lines need to be at the END of your routes.rb file.

Model

Execute the following to create a model for your static pages:

rails g model pages title:string path:string content:text

After this you should run your migrations.

Update your Page model to match the following:

class Page < ActiveRecord::Base
  include AtomicCms::HasComponents
  validates :title, :path, presence: true, uniqueness: true
  component_attr :content
end

Controller

Create a controller by running the following:

rails g controller pages

Update your PagesController to match the following:

require 'json'

class PagesController < ApplicationController
  def show
    @page = Page.find_by_path(request.path)
    if @page
      render cms_template
    else
      render file: "#{Rails.root}/public/404.html", status: :not_found
      Rails.logger.error "404 - Page #{request.path} cannot be found."
    end
  end

  private

  def cms_template
    File.join("pages", "page")
  end
end

Helper

Create app/helpers/application_helper.rb and update it to match the following:

module ApplicationHelper
  def add_option(option, output = nil)
    return unless option
    return output if output
    option
  end

  def markdown(text)
    return unless text
    Redcarpet::Markdown.new(Redcarpet::Render::HTML).render(text).html_safe
  end

  def markdown_help_url
    "http://nestacms.com/docs/creating-content/markdown-cheat-sheet"
  end
end

Views

Create a view at app/views/pages/page.html.slim that contains the following:

= @page.content_object.render

Components

Create the following component view at app/views/components/text_block.html.slim:

.wrapper.large-margin
  .text-block
    .content-text
      - if add_option(options[:header])
        h2
          = options[:header]
      = markdown(options[:content])

Create the following component class at app/components/text_block_component.rb:

class TextBlockComponent < AtomicAssets::Component
  def edit
    rtn = cms_fields(field_types)
    rtn << h.component(:text_block, field_previews).render
    rtn.html_safe
  end

  protected

  def field_previews
    {
      header: "{{preview.header}}",
      content: markdown_preview('preview.content')
    }
  end

  def field_types
    {
      header: { field_type: "text" },
      content: { field_type: "markdown" }
    }
  end
end

Add the following to app/assets/stylesheets/components/_text_block.scss:

.text-block{
  position: relative;
  .content-text{
    ul{
      margin-left: 10px;
      padding-left: 0px;
      position: relative;
      display: inline-block;
      li{
        list-style: disc;
        margin-left: 20px;
      }
    }
  }
  &:before {
    content: '';
    display: inline-block;
    height: 100%;
    vertical-align: middle;
    margin-right: -0.25em; /* Adjusts for spacing */
  }
}

More Active Admin

In order to add components to a page, create app/views/admin/_edit_buttons.html.slim and make it match the following:

ol.edit-buttons
  li
    = link_to 'Text Block', atomic_cms.edit_component_path('text_block'), class: 'button'

Create an admin page for the Pages model at app/admin/page.rb and make it match the following:

ActiveAdmin.register Page do
  permit_params :title, :path, :content

  index do
    selectable_column
    column :path
    column :title
    column :created_at
    column :updated_at
    actions
  end

  form do |f|
    f.semantic_errors(*f.object.errors.keys)

    # new form
    if !f.object.persisted?
      f.inputs 'Page Details' do
        f.input :title
        f.input :path
      end
      f.actions

    # edit form
    else
      div class: 'buttons' do
        render partial: 'admin/edit_buttons'
        f.actions
      end

      columns do
        column span: 3 do
          panel 'Draft', id: 'draft-panel' do
            render partial: 'components/edit', locals: { f: f }
          end
        end

        column id: 'edit-node-column' do
          div id: 'edit-page' do
            f.inputs 'Page Details' do
              f.input :title
              f.input :path
            end
          end

          div id: 'edit-node' do
            f.inputs 'Edit Element' do
              div id: 'edit-node-fields'
            end

            f.actions do
              li class: 'move' do
                a 'Up', '#', class: 'button', id: 'move-node-up'
              end
              li class: 'move' do
                a 'Down', '#', class: 'button', id: 'move-node-down'
              end
              li class: 'cancel' do
                a 'Done', '#', class: 'button', id: 'done-edit-node'
              end
              li class: 'delete' do
                a 'Delete', '#', class: 'button', id: 'delete-node'
              end
            end
          end
        end
      end
    end
  end

  show do
    div id: 'component_preview' do
      div page.content_render
    end
  end

  controller do
    # permit all params until we can whitelist content_object
    def permitted_params
      params.permit!
    end
  end
end

Config

Update config/application.rb to include:

config.autoload_paths += %W(#{config.root}/lib, #{config.root}/app/components/**/)