😲How To Upload Images With CKEditor & #RubyonRails — CMS Uploads Tutorial

Tutorial Explaining How To Create Back End WYSIWYG In Ruby on Rails Using The CKEditor Gem…

Frontline Have Built Several CMS Systems With Rails / CKEditor

If you’re building a Ruby on Rails CMS, you’ll need a way to input text or media. This is generally achieved with a “WYSIWYG” (what you see is what you get) editor. These are quite common → TinyMCE, Redactor and CKEditor are the most popular.

The problem with using a WYSIWYG in Rails is tying it into the backend, especially with the image upload functionality. Because Rails handles image uploads differently to text, a separate flow is required.

We currently useckeditor (although Redactor is preferred)↴

CKEditor in one of our production apps

CKEditor has been around since 2003, originally named FCKEditor:

We use it because it is fully supported on Rails 5+ (via the gem).

Not only this, but all the plugins & skins available for CKEditor are
fully compatible with the Rails version:

CKEditor has a HUGE library of Plugins & Skins, All Of Which Can Be Used In The Rails Gem 😃

If you wanted to look up other WYSIWYG gems, they are here:

📂 A Note On Redactor 📂

Redactor is the best WYSIWYG in our opinion.

It’s VERY light and easy to use (TinyMCE/CKEditor are fiddly).

However, as it costs $199 and is NOT widely supported, we currently use CKEditor. We do plan to upgrade, so we’ll update this article when we do:

Regardless of which library you use (they ALL work in the same way), 
the trick lies in knowing how they connect to the **BACKEND**

Whilst the CKEditor gem does this out of the box, there are a number of instances where you’ll need to change it.

— 
This tutorial will explain how it works and how you’re able to implement WYSIWYG editing in a Rails application ↴

💼 How It Works 💼

ALL WYSIWYG editors work in the same way.

They replace a <textarea>...</textarea> HTML element with their own input form. This varies from being an iframe to span / input element.

The difference is how

— 
Again, we won’t get into specifics on choosing a WYSIWYG library.

Our recommendation for **this tutorial** is the “CKEditor” gem. Whilst we prefer Redactor, CKEditor support for Rails 5+ is far better…

CKEditor’s Rails support is far better

The gem is a port of the original CKEditor javascript library into Rails.

The gem’s authors have also added several hooks into the Rails ORM system (ActiveRecord / Mongoid etc) which allows it to handle uploads:

— 
This gives the gem direct connectivity to the backend, allowing us to hook into the likes of Paperclip etc, for image uploads.

❗️ IMPORTANT ❗ ️→ *ALWAYS* Use The Repository

As a note, you should pull from the GitHubrepository rather than using the gem directly. They don’t push new gems very often → there are several key updates which only exist in the repo:

A number of essential updates only exist in the repo (important).

📢 HTML 📢

To get it working, you must first realize that every WYSIWYG editor
is an **HTML** parser

What you see is the editor’s way of parsing the HTML to show the “pretty” stylized version to the user. The simple way of clarifying this is to click on the “source” button in all the leading WYSIWYG editors:

This will show you all the HTML that’s currently being shown by the editor.

This is important because it gives insight into how they work. They’re not magic — they take your inputted text, wrap it in HTML and then submit the HTML to the server.

You’re saving pure HTML to the db:

CKEditor — as with all WYSIWYG editors — parse HTML

Saving HTML directly to the db obviously presents security challenges,
which we’ll ignore for now.

The point is that Rails — like Wordpress, Joomla and other CMS systems — needs to save stylized text to the database. How you do this is dependent on the setup of your models.

— 
This means that if you’re looking to build a CMS, you’ll need a way to change the HTML content in your app. We’ll explain how to do this in a second.

📖 Images 📖

In terms of uploading images, there are several options.

Because you’re updating HTML within the WYSIWYG, it’s the equivalent of having a TextArea input form. Images are COMPLETELY separate.

The way CKEditor handles this — as with other WYSIWYG’s — is to create a separate image upload flow (route / controller / model), which allows you to intersperse your HTML with uploaded image objects:

The image object is just an <img /> HTML tag

The images need to be held in a separate database table, processed by a separate model and handled by a separate upload facility:

You need a separate DB table for your uploads in Rails

The key with CKEditor is appreciating how all of the upload flow works with the other parts of the application. Once you get this, you’ll find implementing WYSIWYG editors quite simple…

📤 Uploads 📤

Although the CKEditor gem comes with inbuilt upload functionality,
it requires several steps to make it work.

— 
For experienced developers, these will be simple, but you need to know what you’re doing. You need…

  • Model
  • DB Table
  • File Upload Plugin (included already)

— 
If you get it working properly, here’s what you should expect to see ↴

CKEditor’s “File Upload” Functionality In Rails

— 
The model is provided (needs changing), the database table is separate (needs migrating) and file upload integration is inbuilt into CKEditor already.

The trick with Rails is getting it all to work together


🚦 Installation & Setup 🚦

To get a CMS working in Rails, there are a number of particulars:

  1. You need a place where you can input / edit HTML content.
    We do this in an admin area.
  2. You need ckeditor‘s javascript added to your asset pipeline.
    There are several custom scripts required.
  3. You need to integrate the correct model / backend options.
    We have several tricks for this.

💎Installing The Gem💎

The first step is installing the gem in your app:

As mentioned, you need to do this through the repo — whilst the gem is fine, it is quite far behind the actual repo. We found several specific pull requests in the repo are required to get it working correctly (especially in Rails 5).

— 
Installing the gem will put all the required assets into your app (via the gem):

The CKEditor Gem is a Rails Engine which adds the appropriate files to your system

After installation (bundle install), you need to include ckeditor/init.js in your asset pipeline. This is done via the javascript file that will be present in the area where CKEditor will appear

Since we use a separate admin section, we put this into app/assets/admin.coffee (which is called by the admin layout):

Doing this will put all the required CKEditor files into your JS pipeline. Think of it in the same way as including jquery — you’re including the files required to get the system working.

It does not include configuration (that’s the next step).


❗️ IMPORTANT ❗ ️→ If Using "asset.prefix", Use A Leading Slash

Most will NOT have this problem.

However, if you are changing the asset path prefix, you’ll need to ensure you include the leading slash, as it will prevent CKEditor from assigning the correct base_path.

The issue is caused by the way it copies non-digest assets in the rake task:

Whoever created the “path” variable should have used File.join or similar

📓 Configuring The Gem 📓

The next step is to configure the gem.

The way to do this is to include a config.js file in the app/javascripts/ckeditor directory:

This is automatically included into CKEditor, so there is no need to integrate manually. This file is used to define the customization of your editor:

app/assets/javascripts/ckeditor/config.js

Whilst it’s NOT necessary to use this, it is required if you wish to use image uploads. We’ll explain how to configure this properly in a second.

You can read about it here:


❗️ IMPORTANT ❗ ️→ If Using Skins, Add Markdown Interpolation

Almost all custom CKEditor skins have .md files, which break sprockets (content_type is not supported)

If you want to download skins/plugins, you will need to fully extract them into your app/assets/javascripts/ckeditor/* folder:

You do NOT need any extra plugins/skins to get CKEditor working.

If you do add extras, you will also need to add the plugins & skins folders to your asset manifest. In Rails 5+, this is done through app/assets/config/manifest.js , although it can be done other files as required:

/app/assets/config/manifest.js
/app/assets/javascripts/admin.js

Again, this is only required if you add extra plugins/skins to your CKEditor installation. You ONLY need to add ONE reference to the above (just showing two ways to do it).

💼 Setting Up Rails 💼

Next, we need to get Rails set up.

— 
This is where things get trickier:

  1. We need a way to store & manage HTML content
  2. We need a way to save uploads
  3. We need to make sure it all works together

— 
The initial step is to set up the HTML editor.

This can be done in several ways, but all require a controller, views and model (to store the content). You must remember, this has NOTHING to do with uploads — it’s totally independent.

The way to get this working is to have a central data-set (HTML) which can be edited as required. This can be done with a single controller, views and routes setup. Whilst we do this somewhat differently to most, here is a general setup:

#config/routes.rb
resources :posts #-> url.com/posts/new
#app/models/post.rb
class Post < ActiveRecord::Base
#id|title|content|created_at|updated_at
validates :title, :content, presence: true
end
#app/controllers/posts_controller.rb
PostsController < ApplicationController
before_action -> { @post = Post.find params[:id] }, only: [:edit, :show, :update, :destroy]
  ###########################
###########################
  # edit & show don't need anything - @post defined before action

###########################
###########################
  def new
@post = Post.new
end

def create
@post = Post.new post_params
@post.save
end
  def update
@post.update post_params
end
  def destroy
@post.destroy
end
  ###########################
###########################
  private
  def post_params
params.require(:post).permit(:title, :content)
end
end
#app/views/posts/index.html.erb
<% @posts.each do |post| %>
<%= link_to edit_post_path(@post) %>
<% end %>
#app/views/posts/edit.html.erb
<%= render "form" %>
#app/views/posts/new.html.erb
<%= render "form" %>
#app/views/posts/_form.html.erb
<%= form_for @post do |form| %>
<%= form.text_field :title %>
<%= form.cktext_area :content %>
<%= form.submit %>
<% end %>

As mentioned, we do it differently…

— 
We have a central model called node which has ref & value values:

This allows us to superclass with meta-programming (to create a
“meta” model called Meta::Post):

We’re Able To Superclass the “Node” model To Create a “Meta::Post” model

The Meta::Post model inherits all of the node model’s behavior, except it gives us the ability to separate the data into Meta::Post objects:

[[ Meta::Post.new ]]

Thus, we have a DRY model which we can use to create an input form to edit/create pages:

You can see how to use ckeditor’s form helpers here.

From this, we’re able to create a set of routes to show a simple input form:

./config/routes.rb

… and we’re able to populate the form with the appropriate @content from our controller:

— 
Ultimately, what you need is a SIMPLE INPUT FORM (<textarea>) which you’re able to bind to CKEditor using the cktext_area form helper. It should be relatively simple for an experienced dev to implement.


❗️ IMPORTANT ❗ ️→ Turn Turbolinks OFF

Due to complications with how Turbolinks loads page content, you need to turn it off for CKEditor. This can be done in one of two ways.

Firstly, the way I have shown above works perfectly. It prevents Turbolinks from tracking the form element itself. The benefit of this is that Turbolinks still runs on the page, allowing you to retain its benefits whilst also enjoying CKEditor.

Secondly, you may wish to follow the advice of CKEditor’s gem maintainer and create a separate Turbolinks initialization script:

The issue with not using Turbolinks is that if your page does not refresh (because Turbolinks intercepted the request), CKEditor will not load.

Using one of the above methods to remove CKEditor from Turbolinks’ flow will force it to reload each time you invoke the page.


Uploads

Secondly, you will need to set up uploads.

This is tricky, so we will need to spend some time explaining the process.

Basically, the CKEditor gem has including a
comprehensive backend to support image & attachment uploads:

The way uploads are handled in Rails comes down to a set of gems
(Paperclip or Carrierwave), which take HTML file uploads and convert
them into ActiveRecord objects.

We met the guys at Thoughtbot (who maintain Paperclip), so we use that
gem, although there are few differences between the most popular…

Meeting Thoughtbot In 2014

The gems ALL work by taking an HTML form submit and creating a
record in the database:

The file is stored on the server/cdn, but a record is stored in the DB.

This means that if you’re going to use one of these gems, they need to be tied to a specific model & db table. Whilst you can “attach” a model onto an existing table / model, you need to add a number of attributes to make it work:

Paperclip requires the above attributes

In our apps, we use an Asset model, which allows us to save all the
uploads in one central location:

We Store All Our Uploads In A Central “Assets” Model

For us, this means we’re able to simply save any upload to the Asset model.

Others will add the attachment functionality on top of another model. Both are okay, but you have to remember that for the multi-model upload, your attachment will only be available as part of the record you uploaded it to.

This means several things.

Firstly the way ORM’s work is to take DB data and change it into workable objects you can manipulate in memory. To upload files on Rails, this means you should only attach “upload” models to data that it shares value with.

Secondly it means that if you wanted to handle uploads, you’ll need to create a separate model/database table to put them into. This is the case with CKEditor

The CKEditor gem comes with a series of models which allow it to work with the likes of Paperclip or Carrierwave — the difference lies in how it handles the upload flow:

To get CKEditor working with uploads, you need to add the following code to your app/assets/javascripts/ckeditor/config.js file:

You NEED to type the above into your app/assets/javascripts/ckeditor/config.js file (otherwise uploads don’t work)

Here is our actual config.coffee file:

Our actual config.js (coffee) file

As you can see above, you need to set the fileBrowser paths, as well as the CSRF token (VERY IMPORTANT).

This should correspond with adding the CKEditor routes to your app’s main directory:

This should be added by running the ckeditor:install generator

Once you have these in place, you’ll need a way to upload the images.

This is handled by default by the CKEditor gem’s

[[ model ]]

IF you wanted to keep your assets in a central model, we use an Asset model with corresponding table. This has a polymorphic association which allows us to associate any model to it:

[[ Asset ]]

🔑 Testing The Install🔑

Finally, we need to see if it works.

Remember, the way CKEditor works is to replace <textarea>...</textarea> elements — meaning that you still need the required CRUD operations to manage the HTML (controller etc).

On top of this, you also need to ensure that CKEditor is correctly compiling. This can be checked by identifying the various elements in the public folder of the application:

[[ compilation ]]

Lastly, you then need to manage the


❗️ IMPORTANT ❗ ️→ Add Basepath For Heroku

If you’re using Heroku and have set the “assets prefix” in Rails, you will need to hardcode CKEditor’s “base path” into the javascript. This is mentioned here:

This is only applicable if you have a different assets prefix to the default. 
Here is our code:

app/assets/javascripts/ckeditor/basepath.coffee.erb

🎳Thanks for reading 🎳

If you have any questions, please ask below.