diff --git a/.gitignore b/.gitignore index 7761dba2a..0413aaa36 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ public/sitemap.xml public/system/ +/public/ckeditor_assets/ diff --git a/app/assets/javascripts/ckeditor/config.js b/app/assets/javascripts/ckeditor/config.js index 8d6edd348..1ec099fc0 100644 --- a/app/assets/javascripts/ckeditor/config.js +++ b/app/assets/javascripts/ckeditor/config.js @@ -5,33 +5,16 @@ For licensing, see LICENSE.html or http://ckeditor.com/license CKEDITOR.editorConfig = function( config ) { - // Define changes to default configuration here. For example: - // config.language = 'fr'; - // config.uiColor = '#AADC6E'; - - /* Filebrowser routes */ - // The location of an external file browser, that should be launched when "Browse Server" button is pressed. config.filebrowserBrowseUrl = "/ckeditor/attachment_files"; - - // The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Flash dialog. config.filebrowserFlashBrowseUrl = "/ckeditor/attachment_files"; - - // The location of a script that handles file uploads in the Flash dialog. config.filebrowserFlashUploadUrl = "/ckeditor/attachment_files"; - - // The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Link tab of Image dialog. config.filebrowserImageBrowseLinkUrl = "/ckeditor/pictures"; - - // The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Image dialog. config.filebrowserImageBrowseUrl = "/ckeditor/pictures"; - - // The location of a script that handles file uploads in the Image dialog. config.filebrowserImageUploadUrl = "/ckeditor/pictures"; - - // The location of a script that handles file uploads. config.filebrowserUploadUrl = "/ckeditor/attachment_files"; config.allowedContent = true; + config.format_tags = "p;h2;h3"; // Rails CSRF token config.filebrowserParams = function(){ @@ -109,7 +92,14 @@ CKEDITOR.editorConfig = function( config ) config.toolbar_mini = [ { name: 'paragraph', groups: [ 'list' ], items: [ 'NumberedList', 'BulletedList' ] }, + { name: 'links', items: [ 'Link', 'Unlink' ] }, + { name: 'styles', items: [ 'Format' ] }, { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ], items: [ 'Bold', 'Italic', 'Underline', 'Strike' ] } ]; + + config.toolbar_admin = config.toolbar_mini.concat([ + { name: 'insert', items: [ 'Image' ] } + ]); + config.toolbar = "mini"; }; diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index 40e951326..a761f7abc 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -79,6 +79,9 @@ module Abilities can :manage, SiteCustomization::Image can :manage, SiteCustomization::ContentBlock + can :access, :ckeditor + can :manage, Ckeditor::Picture + can [:manage], ::Legislation::Process can [:manage], ::Legislation::DraftVersion can [:manage], ::Legislation::Question diff --git a/app/models/ckeditor/asset.rb b/app/models/ckeditor/asset.rb new file mode 100644 index 000000000..cf636ed19 --- /dev/null +++ b/app/models/ckeditor/asset.rb @@ -0,0 +1,4 @@ +class Ckeditor::Asset < ActiveRecord::Base + include Ckeditor::Orm::ActiveRecord::AssetBase + include Ckeditor::Backend::Paperclip +end diff --git a/app/models/ckeditor/picture.rb b/app/models/ckeditor/picture.rb new file mode 100644 index 000000000..445c2bbd9 --- /dev/null +++ b/app/models/ckeditor/picture.rb @@ -0,0 +1,14 @@ +class Ckeditor::Picture < Ckeditor::Asset + has_attached_file :data, + url: '/ckeditor_assets/pictures/:id/:style_:basename.:extension', + path: ':rails_root/public/ckeditor_assets/pictures/:id/:style_:basename.:extension', + styles: { content: '800>', thumb: '118x100#' } + + validates_attachment_presence :data + validates_attachment_size :data, less_than: 2.megabytes + validates_attachment_content_type :data, content_type: /\Aimage/ + + def url_content + url(:content) + end +end diff --git a/app/views/admin/site_customization/pages/_form.html.erb b/app/views/admin/site_customization/pages/_form.html.erb index 88c2288b1..6d0416b6e 100644 --- a/app/views/admin/site_customization/pages/_form.html.erb +++ b/app/views/admin/site_customization/pages/_form.html.erb @@ -50,7 +50,8 @@
<%= f.label :content %> - <%= f.cktext_area :content, label: false, cols: 80, rows: 10, ckeditor: { language: I18n.locale } %> + <%= f.cktext_area :content, label: false, cols: 80, rows: 10, + ckeditor: { language: I18n.locale, toolbar: "admin" } %>
diff --git a/app/views/pages/custom_page.html.erb b/app/views/pages/custom_page.html.erb index bdd85ab30..61e167549 100644 --- a/app/views/pages/custom_page.html.erb +++ b/app/views/pages/custom_page.html.erb @@ -8,7 +8,7 @@

<%= @custom_page.subtitle%>

<% end %> - <%= text_with_links @custom_page.content %> + <%= safe_html_with_links AdminWYSIWYGSanitizer.new.sanitize(@custom_page.content) %>
<% if @custom_page.print_content_flag %> diff --git a/config/initializers/ckeditor.rb b/config/initializers/ckeditor.rb index 829c3fa36..1e173cca8 100644 --- a/config/initializers/ckeditor.rb +++ b/config/initializers/ckeditor.rb @@ -1,4 +1,12 @@ Ckeditor.setup do |config| + # ==> ORM configuration + # Load and configure the ORM. Supports :active_record (default), :mongo_mapper and + # :mongoid (bson_ext recommended) by default. Other ORMs may be + # available as additional gems. + require 'ckeditor/orm/active_record' + + config.authorize_with :cancan + config.assets_languages = Rails.application.config.i18n.available_locales.map{|l| l.to_s.downcase} config.assets_plugins = %w[copyformatting tableselection scayt wsc] end diff --git a/config/routes.rb b/config/routes.rb index f3739c922..6f65956f3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,7 @@ Rails.application.routes.draw do + mount Ckeditor::Engine => '/ckeditor' + if Rails.env.development? || Rails.env.staging? get '/sandbox' => 'sandbox#index' get '/sandbox/*template' => 'sandbox#show' diff --git a/db/migrate/20180813141443_create_ckeditor_assets.rb b/db/migrate/20180813141443_create_ckeditor_assets.rb new file mode 100644 index 000000000..f7b5b85f4 --- /dev/null +++ b/db/migrate/20180813141443_create_ckeditor_assets.rb @@ -0,0 +1,22 @@ +class CreateCkeditorAssets < ActiveRecord::Migration + def self.up + create_table :ckeditor_assets do |t| + t.string :data_file_name, null: false + t.string :data_content_type + t.integer :data_file_size + t.string :data_fingerprint + t.string :type, limit: 30 + + t.integer :width + t.integer :height + + t.timestamps null: false + end + + add_index :ckeditor_assets, :type + end + + def self.down + drop_table :ckeditor_assets + end +end diff --git a/db/schema.rb b/db/schema.rb index 9c568058e..867737358 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180727140800) do +ActiveRecord::Schema.define(version: 20180813141443) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -294,6 +294,20 @@ ActiveRecord::Schema.define(version: 20180727140800) do t.datetime "updated_at", null: false end + create_table "ckeditor_assets", force: :cascade do |t| + t.string "data_file_name", null: false + t.string "data_content_type" + t.integer "data_file_size" + t.string "data_fingerprint" + t.string "type", limit: 30 + t.integer "width" + t.integer "height" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "ckeditor_assets", ["type"], name: "index_ckeditor_assets_on_type", using: :btree + create_table "comments", force: :cascade do |t| t.integer "commentable_id" t.string "commentable_type" diff --git a/lib/admin_wysiwyg_sanitizer.rb b/lib/admin_wysiwyg_sanitizer.rb new file mode 100644 index 000000000..6e219f1e8 --- /dev/null +++ b/lib/admin_wysiwyg_sanitizer.rb @@ -0,0 +1,9 @@ +class AdminWYSIWYGSanitizer < WYSIWYGSanitizer + def allowed_tags + super + %w[img] + end + + def allowed_attributes + super + %w[alt src style] + end +end diff --git a/lib/wysiwyg_sanitizer.rb b/lib/wysiwyg_sanitizer.rb index 681c7b5fd..ee056383c 100644 --- a/lib/wysiwyg_sanitizer.rb +++ b/lib/wysiwyg_sanitizer.rb @@ -1,10 +1,14 @@ class WYSIWYGSanitizer + def allowed_tags + %w[p ul ol li strong em u s a h2 h3] + end - ALLOWED_TAGS = %w(p ul ol li strong em u s) - ALLOWED_ATTRIBUTES = [] + def allowed_attributes + %w[href] + end def sanitize(html) - ActionController::Base.helpers.sanitize(html, tags: ALLOWED_TAGS, attributes: ALLOWED_ATTRIBUTES) + ActionController::Base.helpers.sanitize(html, tags: allowed_tags, attributes: allowed_attributes) end end \ No newline at end of file diff --git a/spec/lib/admin_wysiwyg_sanitizer_spec.rb b/spec/lib/admin_wysiwyg_sanitizer_spec.rb new file mode 100644 index 000000000..4593b8699 --- /dev/null +++ b/spec/lib/admin_wysiwyg_sanitizer_spec.rb @@ -0,0 +1,12 @@ +require 'rails_helper' + +describe AdminWYSIWYGSanitizer do + let(:sanitizer) { AdminWYSIWYGSanitizer.new } + + describe '#sanitize' do + it 'allows images' do + html = 'DangerousSmile image' + expect(sanitizer.sanitize(html)).to eq(html) + end + end +end diff --git a/spec/lib/wysiwyg_sanitizer_spec.rb b/spec/lib/wysiwyg_sanitizer_spec.rb index 17236aa03..e86c351ef 100644 --- a/spec/lib/wysiwyg_sanitizer_spec.rb +++ b/spec/lib/wysiwyg_sanitizer_spec.rb @@ -15,10 +15,25 @@ describe WYSIWYGSanitizer do expect(subject.sanitize(html)).to eq(html) end + it 'allows links' do + html = '

Home

' + expect(subject.sanitize(html)).to eq(html) + end + + it 'allows headings' do + html = '

Objectives

Fix flaky specs

Explain why the test is flaky

' + expect(subject.sanitize(html)).to eq(html) + end + it 'filters out dangerous tags' do html = '

This is

' expect(subject.sanitize(html)).to eq('

This is alert("dangerous");

') end + + it 'filters images' do + html = 'DangerousSmile image' + expect(subject.sanitize(html)).to eq('Dangerous image') + end end end diff --git a/spec/models/budget/phase_spec.rb b/spec/models/budget/phase_spec.rb index 1fe39c20f..34b99e26f 100644 --- a/spec/models/budget/phase_spec.rb +++ b/spec/models/budget/phase_spec.rb @@ -223,10 +223,10 @@ describe Budget::Phase do end describe "#sanitize_description" do - it "removes html entities from the description" do + it "removes not allowed html entities from the description" do expect{ - first_phase.update_attributes(description: "a

javascript") - }.to change{ first_phase.description }.to('a javascript') + first_phase.update_attributes(description: '

a

') + }.to change{ first_phase.description }.to('

a

javascript') end end end