Project

sinicum

0.01
No commit activity in last 3 years
No release in over 3 years
Provides the necessary functionality to work with Magnolia-managed content in a Rails application.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

Runtime

>= 3.2
 Project Readme

Sinicum

Build Status Code Climate

Use content created in Magnolia CMS in your Ruby/Rails application. Sinicum works as a Ruby Client for the REST API provided by Sinicum Server. Basically it is an "Object-Document-Mapper" mapping the JSON-responses to Ruby objects. It is fully integrated into Rails and lets Controller handle the requests to Magnolia CMS.

Sinicum has been used internally at Dievision for quite some time and has only recently been open sourced. We think it’s a great way to build Rails Applications in concert with a really advanced Content Management System. If you are interested in the concept but have trouble getting started, [please let us know](mailto: sinicum@dievision.de). We are happy to help and interested in your pain points.

We plan to expand the documentation and add more examples until mid-May 2014.

So dive in and get started!

Installation

Add this line to your application's Gemfile:

gem 'sinicum'

And then execute:

$ bundle

Or install it yourself as:

$ gem install sinicum

Installation for Rails

Please note: Sinicum works with Rails >= 4.2. We also only support Ruby versions >= 2.3.

Requirements

Please make sure to have Maven 3.x and PostgreSQL installed on your system. If you are using OS X and Homebrew, run

$ brew install maven
$ brew install postgresql

By default, we will prepare Magnolia CMS with a postgres database. Please create the database you have entered in the installation step following.

Please note that all template files installed in the installation step are using haml. You can install this Gem by adding

gem 'haml-rails'

to your Gemfile.

In order to use the Sinicum Imaging functionality, you will need to install Imagemagick on your machine. If you are using OS X and Homebrew you can do this with

$ brew install imagemagick

Installation

In order to set up Sinicum in a Rails project, you need to add sinicum-runner to your Gemfile:

gem 'sinicum-runner'

Then set up the necessary files by running

$ rails generate sinicum:install

You will be asked a few questions. After this all necessary files for a Magnolia Maven Project will be generated and your Rails project will be configured. The generator will ask you, if you would like to have some sample templates installed. We highly recommend you to answer with yes.

At this point, please remember to create your database, you specifed before. (as a quick reminder: createdb and createuser for psql).

Then start up Magnolia CMS with

$ bundle exec sinicum-runner

and Rails with

$ rails server

You can visit the Magnolia installation at http://localhost:8080. Rails is accessible as usual at http://localhost:3000.

Important Note:

The first thing you should do after logging into Magnolia CMS with the credentials superuser:superuser is to go to Security and edit the superuser in the System Users tab. You need to add the role sinicum-server manually, to be able to access Sinicum Server from rails.

Getting Started

Basic Magnolia CMS knowledge is highly recommended

If you want to use Sinicum with all its features you will need to learn some basics of Magnolia CMS. It is always a good idea to have a look at the Magnolia CMS Documentation.

To just use it as a CMS with Rails we provide you with all information you'll need.

Templating

Magnolia CMS provides three types of templates:

  • Page is the highest level template. It renders a page. Pages are the building blocks of a site hierarchy. They create a tree hierarchy of parent pages and child pages that you can see in Magnolia AdminCentral and visualize to site visitors in a sitemap and in navigation. Each part of a URI is also a page. For example, example.com/products/bicycles would have at least three pages: home page, products section page, and a bicycles page.

  • Area is the next level down. Pages consist of areas which can consist of further areas or components. Areas have two purposes: they structure the page and control what components editors can place inside the area. This makes area the most powerful template. It ensures consistency across the site. Areas also provide repeatability. An area template typically loops through the components inside it, rendering the components one by one. Most areas are rendered with containing div elements in the generated HTML so you can control their layout on the page with CSS.

  • Component is the smallest block of content that editors can edit, delete and move as a single unit. Think of a component as content that belongs together. At its simplest, a component may be a heading and some text that belong together. However, it can contain almost anything: text and a related image, list of links, teased content from another page and so on.

(Taken from Magnolia CMS Documentation)

Nodes

As Magnolia CMS uses the Apache Jackrabbit implementation of the Java Content Repository standard as its internal data format. JCR-Nodes are exposed in Sincium as instances of the class Sinicum::Jcr::Node.

Sinicum::Jcr::Node

This class follows common Ruby/ActiveRecord semantics.

Finders and queries

Finders are included with the NodeQueries module.

find_by_path(workspace, path)
Sinicum::Jcr::Node.find_by_path(:website, '/en/page/subpage')
find_by_uuid(workspace, uuid)
Sinicum::Jcr::Node.find_by_uuid(:website, 'dd793410-89d7-41f3-8cc2-7654c9e8e72b')
query(workspace, language, query, parameters = nil, options = {})
Sinicum::Jcr::Node.query(:website, :xpath, '//en/page/subpage')

Valid options are:

  • limit
  • offset

Valid languages are:

  • xpath
  • jcr-sql2
  • sql

You can get a quick overview at the JCR-Query cheat sheet

Properties

Once a node is retrieved you can access various parameters defined by Magnolia CMS:

  • jcr_path
  • jcr_name
  • jcr_primary_type
  • jcr_workspace

You can use [] to access all custom defined parameters (defined by you in the Magnolia CMS Template definition). So it would be possible to retrieve a press_date with

node = Sinicum::Jcr::Node.find_by_path(:website, '/en/page/press/article01')
node[:press_date]

Since JCR Nodes are stored in a tree structure, you can also access the children and the parent.

node.children
node.parent

Mapping JCR objects to Ruby classes

All JCR nodes in Magnolia are mapped to Sinicum::Jcr::Node objects with the help of TypeTranslators. This is somewhat of an analogy to ActiveRecord::Base. But depending on various factors, Sinicum tries to find a better matching subclass of Node. The rules are quite flexible and can be modified (the default rules can be found in the Sinicum::Jcr::TypeTranslators module) and are probably best explained in an example.

Templates are configured in Magnolia CMS and then mapped to Rails. This is done . (A good example is the ComponentTranslator. It checks the template of a component and tries to initialise a matching class.

Example:

A component article has the template my_project:components/article (generated in the example templates). It is now accessed in a content area and a the ComponentTranslator tries to constantize the templatename. The resulting class would be MyProject::Components::Article.

module MyProject
  module Components
    class Article < Sinicum::Jcr::Node
        
    end
  end
end
Example: Adding new TypeTranslators

It is possible to extend the base functionality by writing an own TypeTranslator. To use the example from before, we create a new file article_type_translator.rb under lib/type_translators:

module MyProject
  module Components
    class ArticleTypeTranslator
      include Sinicum::Jcr::TypeTranslators::TranslatorBase
      
      WORKSPACE = "example"
      NODE_TYPE = "mgnl:example"
      
      
        def self.initialize_node(json)
    	    if workspace(json) == WORKSPACE && jcr_primary_type(json) == NODE_TYPE
              Article.new(:json_response => json)
            end
        end
        
      end
    end
  end
end 

The necessary initializer file type_translators.rb contains:

Sinicum::Jcr::TypeTranslator.use(MyProject::TypeTranslators::FaqTypeTranslator)

Now nodes belonging to the correct workspace and node type are being translated.

Controllers

Sinicum provides convenience functionality to let Rails Controller handle requests to Magnolia CMS and to map Magnolia CMS template definitions to Rails layout files. This is done by including the Sinicum::ControllerBase

class ContentController < ApplicationController
  include Sinicum::ControllerBase
end

Best practice would be to use the generated ContentController as the base Controller for all Controllers, that should have access to Magnolia CMS data. ControllerBase adds a few filters to the controller and overrides the render method so that by default the content from the Magnolia CMS page matching the path of the Rails request is fetched and pushed upon a node stack.

Example:

Let's say we have a basic blog with posts and we want to handle the different posts in Magnolia CMS. They should be located under the path /blog/posts and have the mgnl:template myProject:pages/blog_entry We need two things first.

class BlogController < ContentController
  
  def posts
    @posts = Sinicum::Jcr::Node.query(:website, :sql, 'select * from mgnl:page where jcr:path like '/blog/posts/%'
    render
  end
end

and a matching route

get '/blog/posts', to: 'blog#posts'

The call to render will now try to find a matching view in views/blog (e.g. posts.html.haml) and use the layout in views/layouts/my_project/blog_entry.html.haml.

Redirect Template

Sometimes you want to redirect from a specific page in Magnolia CMS to another page. Normally you would need to set up a route and a redirect action. Since this can lead to errors (e.g. when the page name is changed in magnolia, or the page is moved), Sinicum handles redirects on a template base with the redirect template. It is already generated for you with rails g sinicum:install.

If you select the redirect template in Magnolia CMS for a page, you can set its redirect_link in the page preferences. It will be saved as a uuid-string so you can change the location of the target page or rename it and the redirect will still work.

Imaging

Sinicum makes image handling very easy. You can define several styles for your images you want to render by adding them to config/imaging.yml (a default one will be generated during rails g sinicum:install).

renderer:
  default:
    render_type: resize_crop
    format: jpeg
    x: 307
    y: 202
    hires_factor: 2

The render_type can be

  • resize_crop - Resizes an image to an exact given size and crops anything else
  • resize_max - Resizes an image to a predefined maximum size
  • default - Simple converter that simply serves a copy of the original file

A new feature (available since v0.9.0) introduces the configuration of the Imaging Module via apps. You have to define a few values in the imaging.yml (default values for DAM are already generated) and you can basically use Imaging for the asset handling of all you content apps.

apps:
  videos:
    imaging_prefix: "/videofiles"
    magnolia_prefix: "/videos"
    workspace: "videos"
    node_type: "mgnl:video"
  dam:
    imaging_prefix: "/damfiles"
    magnolia_prefix: "/dam"
    workspace: "dam"
    node_type: "mgnl:asset"

In this example, we have configured two apps that will be able to serve assets via the Imaging Module.

  • imaging_prefix - Is the prefix that the imaging path will have
  • magnolia_prefix - Is the prefix that the original path from magnolia has
  • workspace - The workspace of the content app
  • node_type - The node_type that has been configured for nodes from the content app

This is all you need to know for a quick start. More details will be added soon.

Caching

Sinicum also has some nice caching features, which are located in the GlobalStateCache. Every page which is rendered by a controller that includes Sinicum::ControllerBase gets cached by the GlobalStateCache. Sinicum-Server provides a cache-key, which is changed everytime a page gets published in the Magnolia CMS. This means, that you will always get the cached page, as long as you don't publish anything. On the other hand, this means, that a cache refresh is only one publication away.

For pages, that - besides rendering components from the CMS - also render dynamic content like social media tiles, the global_state_cache_expiration_time controller method has been implemented. With this method, you can control the cache duration from a specific controller.

Let's say you have a homepage, that needs to show some social media contents. Just create a controller for the homepage that inherits from ContentController and implements global_state_cache_expiration_time.

class HomepageController < ContentController

  def global_state_cache_expiration_time
    10.minutes
  end
end
get '/' => 'homepage#index'

That's it - the cache is refreshed every 10 minutes and you can enjoy your up-to-date social media contents.

Multisite

In order to deliver as many websites as you want from one Magnolia/Sinicum installation, we introduced the multisite feature. Basically it's a content app, which lets you define site-definitions, representing one website.

A site-definition consists of 3 attributes:

root_node: The node on the root level of magnolia, that should be the base for the website.
primary_domain: The domain, that this website should be accessible from. (complete with http(s)://)
alias_domains: An array of domains, that will result in a redirect to the primary_domain.

These site-definitions are then matched against the current request path or the domain, depending on which environment you are using and saved into the session.

To enable the domain matching, you will have to add Rails.configuration.x.multisite_production = true to your production.rb environment file.

Also you will have to add the Multisite Content App to your Magnolia Project.

<dependency>
  <groupId>com.dievision</groupId>
  <artifactId>multisite-app</artifactId>
  <version>1.5.3</version>
</dependency>

Now you should be ready to use Multisite. For more details on how the matching process is working, have a look in the actual MultisiteMiddleware.

Helpers

To make it as easy as possible for you to navigate your way around the Magnolia CMS content wrapped in Sinicum::Jcr::Node, Sinicum provides you with some neat helper methods. They are split up in two helper modules: MgnlHelper and MgnlImageHelper. Both are included in MgnlHelper5 which is automatically included in your ´ApplicationHelper`.

MgnlHelper

mgnl_content_data

You can always access the current node pushed by the Controller by calling mgnl_content_data.

mgnl_original_content

You can access the the content object that represents the "base" of the request by calling mgnl_original_content

mgnl_value(key)

This method will access a property of the node that sits on top of the node stack.

mgnl_value :title

mgnl_push(key_or_object, options = {})

This method is best explained by an example:

mgnl_value :title
mgnl_push :contact_link, workspace: 'contacts' do
  "#{mgnl_value :firstName} #{mgnl_value :lastName}"

In the first line of the example, we print out the property title of the node that was pushed on top of the stack by the controller. Then we have a property called contact_link which is a uuid-string. We want to access firstName and lastName of this linked node with the mgnl_value helper method. In order to do this, we need to push the linked node onto the node stack. This done by this function. At the end of the block the node will be removed and the previous node is on top of the node stack again.

mgnl_path(key_or_object, options = {})

This method returns the path for an object. If the object is a Sinicum::Jcr::Node it will return its path. If the object is a uuid-string, it will be resolved to the matching node and then its path will be returned.

link_to 'Redirect', mgnl_path(:redirect_link)

You can pass :workspace as an option, which will tell Sinicum where to look for your node (website is the default workspace).

mgnl_link(key_or_object, options = {}, &block)

mgnl_link does basically the same as mgnl_path except that it renders a link_to for you and that you can pass a normal string as key_or_object.

mgnl_link :link, class: "big-link" do
  'Click me'

mgnl_exists?(key_or_object, options = {})

mgnl_exists? is a good method to check for the existence of a Magnolia CMS object. It returns true or false.

mgnl_meta(options = {})

Displays the <title> tag and the <meta> tags for a page. The attributes must follow the default naming conventions.

  • title
  • meta_title
  • meta_description
  • meta_keywords
  • meta_noindex
  • meta_search_weight
  • meta_search_boost

Returns a String with all necessary <meta> tags and the <title> tag.

mgnl_navigation(base_node_or_path, type, options = {}, &block)

mgnl_navigation iterates over an array with NavigationElement instances. You can pass a base node and then select the type of navigation you want (currently only :children or:parents are supported though). For the options, you can pass on a hash with { depth: 2 }. If you pass on a block, you will get a NavigationElement and the corresponding NavigationStatus.

Let's show you with an example:

mgnl_navigation( '/blog', :children, depth: 1).each do |e,s|
  link_to e.title, e.path

NavigationStatus provides meta-information about the status of a NavigationElement in the iteration.

  • first?
  • last?
  • size
  • count

MgnlImageHelper

mgnl_asset_path(key_or_object = nil, options = {})

Basically a call to mgnl_path with the default workspace :dam.

mgnl_img(key_or_object, options = {})

mgnl_img queries the Magnolia CMS DAM for the given link or uuid and returns it wrapped in ready-to-use <img> tag. It takes several options:

  • :renderer - this is the renderer from the imaging.yml file
  • :width
  • :height
  • :src
  • :alt

An example would be:

mgnl_img :image, renderer: "big_image"

Questions or problems?

If you have any issues with Sinicum which you cannot solve by reading the readme, please add an issue on GitHub or write us an email at sinicum@dievision.de. We will be happy to get you started!

Contributing

Contributions are more than welcome! Feel free to fork Sinicum and submit a new feature-branch as pull request. If you encounter any bugs or non expected behaviour, please open a GitHub issue or write an email.