Create a gist now

Instantly share code, notes, and snippets.

@maxivak /readme.md
Last active Dec 13, 2016

Install TinyMCE editor 4 with Elfinder file manager in Rails 4 app

TinyMCE with file manager

This post shows how to use TinyMCE 4.1 WYSIWYG text editor in a Rails 4 application for editing content in a textarea field.

TinyMCE is a powerful online WYSIWYG editor with many plugins.

If you want to insert an image by selecting it from images stored on server or upload an image to server, then you need to have a file manager with tinyMCE. TinyMCE comes with a file manager which is not free.

elFinder is an open-source file manager which can be easily integrated with tinyMCE 4.1. elFinder needs a backend (connector) on Rails for reading/storing files from/on server.

The phallstrom/el_finder gem (https://github.com/phallstrom/el_finder) will be used as a backend.

tinyMCE

We have two options for installing tinyMCE in Rails 4 application:

  • download it from http://tinymce.com
  • save files in public/js inside your Rails application.

or use tinymce-rails gem (https://github.com/spohlenz/tinymce-rails) which integrates TinyMCE editor with the Rails asset pipeline.

Gemfile:

gem 'tinymce-rails', '4.1.6'

elFinder file manager

install elFinder

https://github.com/Studio-42/elFinder

Read the instructions to install elFinder: https://github.com/Studio-42/elFinder/wiki. Instructions to integrate elFinder with TinyMCE 4.x - https://github.com/Studio-42/elFinder/wiki/Integration-with-TinyMCE-4.x.

To add elFinder to our Rails project:

  • Download archive from github
  • unzip the archive and copy files to 'public/elfinder'

ImageMagick

http://www.imagemagick.org/

Download and install ImageMagick for your platform.

Rails connector for elFinder

https://github.com/phallstrom/el_finder gem is used to provide server side functionality for elFinder.

Gemfile:

gem 'el_finder', '1.1.12'

create folder where files will be stored, say, 'public/files'.

routes for elfinder

# routes.rb

  get '/elfinder_manager', to: 'elfinder#index'
  match 'elfinder' => 'elfinder#elfinder', via: [:get, :post]

create controller which will process files for the file manager.

app/controllers/elfinder_controller.rb:

require 'el_finder/action'

class ElfinderController < ApplicationController
  skip_before_filter :verify_authenticity_token, :only => ['elfinder']

  def index
     render :layout => false
  end
  
  def elfinder
    rootpath = File.join(Rails.public_path, 'uploads')
    rooturl = '/uploads'

    h, r = ElFinder::Connector.new(
      :root => rootpath,
      :url => rooturl,
      :perms => {
         /^(Welcome|README)$/ => {:read => true, :write => false, :rm => false},
         '.' => {:read => true, :write => true, :rm => true}, # '.' is the proper way to specify the home/root directory.
         #/^test$/ => {:read => true, :write => true, :rm => false},
         #'logo.png' => {:read => true},
         #/\.png$/ => {:read => false} # This will cause 'logo.png' to be unreadable.
                                      # Permissions err on the safe side. Once false, always false.
      },
      :extractors => {
         'application/zip' => ['unzip', '-qq', '-o'], # Each argument will be shellescaped (also true for archivers)
         'application/x-gzip' => ['tar', '-xzf'],
      },
      :archivers => {
         'application/zip' => ['.zip', 'zip', '-qr9'], # Note first argument is archive extension
         'application/x-gzip' => ['.tgz', 'tar', '-czf'],
      },
      :thumbs => true
    ).run(params)

    headers.merge!(h)

    if r.empty?
      (render :nothing => true) and return
    end

    render :json => r, :layout => false
  end
end

change settings in ElFinder::Connector.new appropriately.

create a view for elfinder file manager.

app/views/elfinder/index.html.erb:

<!DOCTYPE html>
<html>
<head>
  <title>File Manager</title>

<link rel="stylesheet" type="text/css" media="screen" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>


  <script type="text/javascript" src="/elfinder/js/elfinder.min.js"></script>
  <script src="/elfinder/js/proxy/elFinderSupportVer1.js" type="text/javascript" charset="utf-8"></script>
  <script type="text/javascript" src="/elfinder/js/i18n/elfinder.de.js"></script><!-- optional -->

  <link rel="stylesheet" type="text/css" media="screen" href="/elfinder/css/elfinder.min.css">
  <link rel="stylesheet" type="text/css" media="screen" href="/elfinder/css/theme.css">
</head>
<body>


<div id="elfinder"></div>

<script type="text/javascript">
  var FileBrowserDialogue = {
    init: function() {
      // Here goes your code for setting your custom things onLoad.
    },
    mySubmit: function (URL) {
      // pass selected file path to TinyMCE
      parent.tinymce.activeEditor.windowManager.getParams().setUrl(URL);

      // close popup window
      parent.tinymce.activeEditor.windowManager.close();
    }
  }

  $().ready(function() {
    var elf = $('#elfinder').elfinder({
      // set your elFinder options here
      url: '/elfinder',  // connector URL
      transport : new elFinderSupportVer1(),

      getFileCallback: function(file) { // editor callback
        // file.url - commandsOptions.getfile.onlyURL = false (default)
        // file     - commandsOptions.getfile.onlyURL = true
        FileBrowserDialogue.mySubmit(file); // pass selected file path to TinyMCE
      }
    }).elfinder('instance');
  });
</script>

</body>
</html>

Here is some explanation of the code above:

include jQuery and jQuery UI

  <link rel="stylesheet" type="text/css" media="screen" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/smoothness/jquery-ui.css">
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>

include elFinder js script:

  <script type="text/javascript" src="/elfinder/js/elfinder.min.js"></script>

include proxy js script so our connector works with elFinder

  <script src="/elfinder/js/proxy/elFinderSupportVer1.js" type="text/javascript" charset="utf-8"></script>

(OPTIONAL) include necessary locale for elFinder, for example,

    <script type="text/javascript" src="/elfinder/js/i18n/elfinder.de.js"></script>

include elFinder css files

  <link rel="stylesheet" type="text/css" media="screen" href="/elfinder/css/elfinder.min.css">
  <link rel="stylesheet" type="text/css" media="screen" href="/elfinder/css/theme.css">

integrate elFinder file manager with TinyMCE

<script type="text/javascript" charset="utf-8">
	$().ready(function() {
		var elf = $('#elfinder').elfinder({
      url: '/elfinder',
      transport : new elFinderSupportVer1(),
			lang: 'en'             // language (OPTIONAL)
		}).elfinder('instance');
	});
</script>

Textarea with TinyMCE editor

create a view in your Rails app where you want to have online editor.

In this example it is 'app/views/pages/edit.html.haml'.

<script type="text/javascript" src="/js/tinymce/tinymce.min.js"></script>
<script type="text/javascript" src="/js/tinymce/jquery.tinymce.min.js"></script>

:javascript
  tinymce.init({
      selector: "textarea",
      plugins: [
          "advlist autolink lists link image charmap print preview anchor",
          "searchreplace visualblocks code fullscreen",
          "insertdatetime media table contextmenu paste"
      ],
      toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
      file_browser_callback : elFinderBrowser
  });

  function elFinderBrowser (field_name, url, type, win) {
    tinymce.activeEditor.windowManager.open({
      file: '/elfinder_manager',// use an absolute path!
      title: 'elFinder 2.0',
      width: 900,
      height: 450,
      resizable: 'yes'
    }, {
      setUrl: function (url) {
        win.document.getElementById(field_name).value = url;
      }
    });
    return false;
  }



%form(method="post" action="somepage")
  %textarea(name="content" style="width:100%")
    content here
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment