https://github.com/luciuschoi/summernote088
Example Project for Demonstrating How to Use Summernote-Rails Gem.
https://github.com/luciuschoi/summernote088
ruby-on-rails summernote-rails wysiwyg-html-editor
Last synced: 3 months ago
JSON representation
Example Project for Demonstrating How to Use Summernote-Rails Gem.
- Host: GitHub
- URL: https://github.com/luciuschoi/summernote088
- Owner: luciuschoi
- Created: 2017-11-13T14:17:59.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2017-12-28T07:46:49.000Z (over 7 years ago)
- Last Synced: 2025-01-09T03:43:39.280Z (5 months ago)
- Topics: ruby-on-rails, summernote-rails, wysiwyg-html-editor
- Language: Ruby
- Homepage: https://withrails.com/2017/11/13/summernote-rails-update-3
- Size: 92.8 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Summernote-rails Update(3)
http://bit.ly/summernote-rails-for-fileupload-1 on Jun. 6, 2015, in Korean
http://bit.ly/summernote-rails-for-fileupload_2 on Jan. 2, 2016, in Korean
In my blog, the posts on summernote-rails gem are always the most frequent in daily visit statistics.
About 22 months were passed since last post on summernote. Official published version of summernote-rails is 0.8.3, as of Nov. 13, 2017.
Rails version is bumped up to 5.1.4 and ruby 2.4.2. And, maybe, Bootstrap will be updated to the official verion 4 as soon as possible. Right now, Bootstrap version is **4.0.0.beta2.1**.
But, the edge version of summernote-rails in GitHub repository is 0.8.8.
In this post, I'll update how to use summernote-rails gem and, introduce how to upload and delete image files in the summernote editor.
First of all, my local dev environment is as follows:
```sh
$ ruby -v
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin16]
$ rails -v
Rails 5.1.4
```Let's get started.
### 1. Create new project
Open your favorite terminal and create new project called "**summernote088**". Of course, we'll use the default serverless database, sqlite3.
```sh
$ rails new summernote088
```If you want another database, you can add your favorite database with '-d' option. For example, if you would use mysql or postgresql, you could add an option as follows:
```sh
$ rails new summernote088 -d [mysql|postgresql]
$ cd summernote088
```### 2. commit initial state
At this time, you can commit the auto-generated source codes to local repository if you want.
```sh
$ git add .
$ git commit -m "initial commit"
```### 3. create develop branch and checkout
And you can add the ''**develop**" branch to checkout to.
```sh
$ git checkout -b develop
```### 4. install jquery gem
We'll use **Bootstrap**, which is dependent on **jQuery** library and so you should add **jquery-rails** gem. The reason is why this project is implemented using **Rails 5.1+** which is not any more dependent on **jQuery** and does not include by default in Gemfile. (https://github.com/rails/jquery-rails)
```sh
gem 'jquery-rails', '~> 4.3.1'
```> **Note**: From **Rails 5.1**, rails-ujs library substitute everything **jquery_ujs** does. So **jquery_ujs** is not needed any more on using **jquery**. https://gorails.com/forum/do-i-need-rails-ujs-and-jquery_ujs
After bundling Gemfile, you should add the following code line at the top of the app/assets/javascripts/ **application.js**.
```js
//= require jquery3
```> **Note**: **jquery3** means jquery version 3. You can use **jquery2** or just **jquery**.
### 5. install bootstrap gem
As of November, 2017, the latest version of Bootstrap is `4.0.0.beta2.1`. (https://github.com/twbs/bootstrap-rubygem)
Next, add the following code line to Gemfile and bundle install
```sh
gem 'bootstrap', '~> 4.0.0.beta2.1'
```To use Bootstrap fully, you need to rename **application.css** file to **application.scss**. After that, you delete all contents of that file and add the following code line.
```css
@import "bootstrap";
```Also, update **application.js** as follows:
```js
//= require jquery3
//= require popper
//= require bootstrap
```> **Note**: In develop mode, you can replace `//= require bootstrap` with `//= require bootstrap-sprockets` which provides individual Bootstrap components for ease of debugging.
### 5. install simple_form
We'll use 'simple_form' gem.
```ruby
gem 'simple_form'
```After running 'bundle install' in terminal, you should install simple_form with '**--bootstrap**' option.
```sh
$ rails generate simple_form:install --bootstrap
```As of now, Simpe_form v3.5.0, it does not support Bootstrap 4 beta. And so you should fix config/initializers/**simple_form_bootstrap.rb** file as follows:
```ruby
# Reference - https://github.com/printercu/rails_sf_bs4/blob/master/config/initializers/simple_form_bootstrap.rb
# Use this setup block to configure all options available in SimpleForm.
# https://github.com/plataformatec/simple_form/pull/1476
SimpleForm::Inputs::Base.prepend Module.new {
def merge_wrapper_options(options, wrapper_options)
if wrapper_options&.key?(:error_class)
wrapper_options = wrapper_options.dup
error_class = wrapper_options.delete(:error_class)
wrapper_options[:class] = "#{wrapper_options[:class]} #{error_class}" if has_errors?
end
super(options, wrapper_options)
end
}SimpleForm.setup do |config|
config.error_notification_class = 'alert alert-danger'
config.button_class = 'btn btn-primary'
config.boolean_label_class = 'form-check-label'
config.boolean_style = :nested
config.item_wrapper_tag = :div
config.item_wrapper_class = 'form-check'# Helpers
wrapper_options = {class: 'form-group'}
input_options = {error_class: 'is-invalid'}
label_class = 'col-form-label'horizontal_options = wrapper_options.merge(class: 'form-group row')
horizontal_label_class = "col-sm-3 #{label_class}"
horizontal_right_class = 'col-sm-9'
horizontal_right_offset_class = 'offset-sm-3'inline_class = 'mb-2 mr-sm-2 mb-sm-0'
basic_input = ->(b, type = :basic) do
b.use :html5
b.use :placeholder
break if type == :boolean
b.optional :maxlength
b.optional :minlength
unless type == :file
b.optional :pattern
b.optional :min_max
end
b.optional :readonly
enderror_and_hint = ->(b) do
b.use :error, wrap_with: {tag: 'span', class: 'invalid-feedback'}
b.use :hint, wrap_with: {tag: 'small', class: 'form-text text-muted'}
end# Vertical forms
config.wrappers :vertical_form, **wrapper_options do |b|
basic_input.call(b)
b.use :label, class: label_class
b.use :input, **input_options, class: 'form-control'
error_and_hint.call(b)
endconfig.wrappers :vertical_file_input, **wrapper_options do |b|
basic_input.call(b, :file)
b.use :label, class: label_class
b.use :input, **input_options, class: 'form-control-file'
error_and_hint.call(b)
endconfig.wrappers :vertical_boolean, **wrapper_options, class: 'form-check' do |b|
basic_input.call(b, :boolean)
b.use :label_input, class: 'form-check-input'
error_and_hint.call(b)
endconfig.wrappers :vertical_radio_and_checkboxes, **wrapper_options do |b|
basic_input.call(b, :boolean)
b.use :label, class: label_class
b.use :input, **input_options, class: 'form-check-input'
error_and_hint.call(b)
end# Horizontal forms
config.wrappers :horizontal_form, **horizontal_options do |b|
basic_input.call(b)
b.use :label, class: horizontal_label_class
b.wrapper class: horizontal_right_class do |ba|
ba.use :input, **input_options, class: 'form-control'
error_and_hint.call(ba)
end
endconfig.wrappers :horizontal_file_input, **horizontal_options do |b|
basic_input.call(b, :file)
b.use :label, class: horizontal_label_class
b.wrapper class: horizontal_right_class do |ba|
ba.use :input, **input_options, class: 'form-control-file'
error_and_hint.call(ba)
end
endconfig.wrappers :horizontal_boolean, **horizontal_options do |b|
basic_input.call(b, :boolean)
b.wrapper class: "#{horizontal_right_class} #{horizontal_right_offset_class}" do |wr|
wr.wrapper class: 'form-check' do |ba|
ba.use :label_input, class: 'form-check-input'
end
error_and_hint.call(wr)
end
endconfig.wrappers :horizontal_radio_and_checkboxes, **horizontal_options do |b|
basic_input.call(b, :boolean)
b.use :label, class: horizontal_label_class
b.wrapper class: horizontal_right_class do |ba|
ba.use :input, **input_options, class: 'form-check-input'
error_and_hint.call(ba)
end
end# Inline forms
config.wrappers :inline_form, class: inline_class do |b|
basic_input.call(b)
b.use :label, class: 'sr-only'
b.use :input, **input_options, class: 'form-control'
error_and_hint.call(b)
endconfig.wrappers :inline_boolean, class: "form-check #{inline_class}" do |b|
basic_input.call(b, :boolean)
b.use :label_input, class: 'form-check-input'
error_and_hint.call(b)
end# Multiple selects
config.wrappers :multi_select, **wrapper_options do |b|
basic_input.call(b, :boolean)
b.use :label, class: label_class
b.wrapper class: 'multi-select d-flex' do |ba|
ba.use :input, **input_options, class: 'form-control'
end
error_and_hint.call(b)
endconfig.wrappers :horizontal_multi_select, **horizontal_options do |b|
basic_input.call(b, :boolean)
b.use :label, class: horizontal_label_class
b.wrapper class: horizontal_right_class do |wr|
wr.wrapper class: 'multi-select d-flex' do |ba|
ba.use :input, **input_options, class: 'form-control'
end
error_and_hint.call(wr)
end
end# Wrappers for forms and inputs using the Bootstrap toolkit.
# Check the Bootstrap docs (http://getbootstrap.com)
# to learn about the different styles for forms and inputs,
# buttons and other elements.
config.default_wrapper = :vertical_form
config.wrapper_mappings = {
check_boxes: :vertical_radio_and_checkboxes,
radio_buttons: :vertical_radio_and_checkboxes,
file: :vertical_file_input,
boolean: :vertical_boolean,
datetime: :multi_select,
date: :multi_select,
time: :multi_select,
}
end
```> **Tip**: you just had better replace the original file with the above codebase.
Here, there is one point to update. You need to change`btn btn-default` to `btn btn-primary` because `btn-default` class was deprecated in Bootstrap 4 beta 2.
### 7. install summernote-rails gem
And now, it's time to add summernote-rail gem. Current available version of summernote-rails published in rubygems.org is **0.8.3**. But you can find the edge version **0.8.8** in GitHub repository.
```ruby
gem 'summernote-rails', github: 'summernote/summernote-rails'
```After bundling, in app/assets/stylesheets/**application.scss**, you should import **summernote** stylesheet for Bootstrap 4. Additionally, you need to customize the editor styles and so to add new "summernote-custom-theme" stylesheet.
```css
@import "bootstrap";
@import "summernote-bs4";
@import "summernote-custom-theme";
```app/assets/stylesheets/**summernote-custom-theme.scss**,
```css
.note-editor {
.note-btn {
background-color: white;
border-color: #ccc;
}
.help-list-item + label {
display: inline-block;
}
.modal-header {
button.close {
font-size: 1.2em;
}
}
.modal-footer {
display: inline-block;
p:last-child {
margin-bottom: 0 !important;
}
}
}
```In app/assets/javascripts/**application.js**, you should add as follows:
```js
//= require ...
//= require bootstrap
//= require summernote/summernote-bs4
//= require summernote/locales/ko-KR
//= require ...
```Additionally, you need create app/assets/javascripts/**summernote-init.coffee** as follows:
```js
$(document).on 'turbolinks:load', ->
$('[data-provider="summernote"]').each ->
$(this).summernote
lang: 'ko-KR'
```and insert it in **application.js**
```js
//= require ...
//= require bootstrap
//= require summernote/summernote-bs4
//= require summernote/locales/ko-KR
//= require summernote-init
//= require ...
```### 8. scaffolding Post model
Using the scaffold generator of Rails, generate Post resource.
```sh
$ rails g scaffold Post title content:text
```**Post** model has two attributes: title and content. Title is string-typed and content text-typed.
On running the above command, the migration file creating posts table is also created. And so you need to run **rails db:create** before **rails db:migrate** in your terminal. Finally, **posts** table will be created physically on your database.
Link tag style of **scaffolds.scss** is ugly and so you need to customize as the follows:
```css
a {
color: #000;&:visited {
color: rgb(181, 181, 181);
}&:hover {
columns: #000;
background-color: #fff;
}
}
```And add this stylesheet to application.scss as follows:
```css
@import "bootstrap";
@import "summernote-bs4";
@import "summernote-custom-theme";
@import "scaffolds";
@import "posts";
```### 9. Set root path
Now that you created the first resource, you can set up **root path** in config/**routes.rb**,
```ruby
root "posts#index"
```### 10. Modify posts/_form.html.erb
The most important point is that you should add an option (**as: :summernote**) to the following code line.
```erb
<%= f.input :content, as: :summernote %>
```This option will produce the **data-provider="summernote"** attribute in the following html rendered.
```html
```
### 11. install carrierwave gem
In summernote editor, you can insert an image using editor menu icon (picture) or using drag and drop local image files to the editor. For file uploading, we'll use 'carrierwave' gem. Add the following to Gemfile and run "**bundle install**" in your terminal.
```ruby
gem 'carrierwave'
```Now, you need create Upload model to store uploaded image information. This model has just one attribute called "image". And generate the uploader for this **Upload** model
```sh
$ rails g model Upload image
$ rails g uploader Image
```Mount image uploader in **Upload** model class as follows:
```ruby
class Upload < ApplicationRecord
mount_uploader :image, ImageUploader
end
```To create and delete uploaded images, you need the controllers for **Upload** model instances as the follows:
```sh
$ rails g controller uploads create destroy
```Let's write codes in app/controllers/uploads/**uploads_controller.rb** as follows:
```ruby
class UploadsController < ApplicationController
def create
@upload = Upload.new(upload_params)
@upload.saverespond_to do |format|
format.json { render :json => { url: @upload.image.url, upload_id: @upload.id } }
end
enddef destroy
@upload = Upload.find(params[:id])
@remember_id = @upload.id
@upload.destroy
FileUtils.remove_dir("#{Rails.root}/public/uploads/upload/image/#{@remember_id}")
respond_to do |format|
format.json { render :json => { status: :ok } }
end
endprivate
def upload_params
params.require(:upload).permit(:image)
end
end
```Also, we need to add resource routing for uploads controller in config/**routes.rb**,
```ruby
resources 'uploads', only: [:create, :destroy]
```You can code **sendFile** and **deleteFile** function and update app/assets/javascripts/**summernote-init.coffee** as the follows:
```javascript
sendFile = (file, toSummernote) ->
data = new FormData
data.append 'upload[image]', file
$.ajax
data: data
type: 'POST'
url: '/uploads'
cache: false
contentType: false
processData: false
success: (data) ->
img = document.createElement('IMG')
img.src = data.url
console.log data
img.setAttribute('id', "sn-image-#{data.upload_id}")
toSummernote.summernote 'insertNode', imgdeleteFile = (file_id) ->
$.ajax
type: 'DELETE'
url: "/uploads/#{file_id}"
cache: false
contentType: false
processData: false$(document).on 'turbolinks:load', ->
$('[data-provider="summernote"]').each ->
$(this).summernote
lang: 'ko-KR'
height: 400
callbacks:
onImageUpload: (files) ->
sendFile files[0], $(this)
onMediaDelete: (target, editor, editable) ->
upload_id = target[0].id.split('-').slice(-1)[0]
console.log upload_id
if !!upload_id
deleteFile upload_id
target.remove()
```Lastly, you need to add raw() method to @post.content to view html code correctly.
```html
Content:
<%=raw @post.content %>
```That's it.