Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions app/packs/src/decidim/decidim_application.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,90 @@

// Load images
require.context("../../images", true)

// add an error message if loading image with special character in rich-text editor
document.addEventListener("DOMContentLoaded", function(event) {
setTimeout(function (){
const container = document.querySelector('div.proposal_custom_field div.editor-container[type=richtext]') || document.querySelector('div.editor div.editor-container');
const editorToolbarImage = document.querySelector('div.editor-toolbar button[data-editor-type="image"]');

if(container && editorToolbarImage){
// add paragraph before container
const paragraph = document.createElement('p');
paragraph.style.fontSize = "14px";
paragraph.style.textAlign = "justify";
paragraph.style.color = "rgb(62 76 92 / var(--tw-text-opacity, 1))";

const lang = document.querySelector('html').lang
let text;
switch (lang) {
case "fr": text = "Si vous ajoutez une image, le nom du fichier ne doit pas contenir de caractères spéciaux (espace, accent, parenthèse...).";
break;
case "de": text = "Wenn Sie ein Bild hinzufügen, darf der Dateiname keine Sonderzeichen (Leerzeichen, Akzente, Klammern...) enthalten.";
break;
case "nl": text = "Als je een afbeelding toevoegt, mag de bestandsnaam geen speciale tekens bevatten (spaties, accenten, haakjes...).";
break;
default: text = "If you upload an image, the name of the file must not contain special characters (space, accent, parenthesis...).";
}
paragraph.textContent = text;
container.before(paragraph);

// add guidance to modal
const modalText = document.querySelector('div.upload-modal .upload-modal__text ul');
const li = document.createElement('li');
let liText;
switch (lang) {
case "fr": liText = "Pas de caractères spéciaux dans le nom de l'image.";
break;
case "de": liText = "Keine Sonderzeichen im Bildnamen.";
break;
case "nl": liText = "Geen speciale tekens in de afbeeldingsnaam.";
break;
default: liText = "No special characters in image name.";
}
li.textContent = liText;
modalText.appendChild(li);

function showEditorError(message) {
// Remove previous existing error if exists
const existingError = document.querySelector("div.custom-editor-upload-error");
if (existingError) {
existingError.remove();
}

const errorDiv = document.createElement("div");
errorDiv.classList.add("custom-editor-upload-error", "form-error", "is-visible");
errorDiv.textContent = message;

let div = document.querySelector('div.proposal_custom_field div.editor-container[type=richtext]') || document.querySelector('div.editor div.editor-container');
// Insert error after container
div.after(errorDiv);
}

const originalFetch = window.fetch;

window.fetch = async function (...args) {
const response = await originalFetch.apply(this, args);
// target only calls to editor_images endpoint
const url = typeof args[0] === "string" ? args[0] : args[0]?.url;
if (url && url.includes("editor_images")) {
// if EditorForm is invalid, there is a 422
if (response.status === 422) {
// Clone response to read it without consuming it
const cloned = response.clone();
cloned.json().then((data) => {
showEditorError(data.message);
});
} else if (response.ok) {
// Remove previous existing error if upload is a success
const existingError = document.querySelector(".custom-editor-upload-error");
if (existingError) {
existingError.remove();
}
}
}
return response;
};
}
}, 500);
});
1 change: 1 addition & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Application < Rails::Application
require "extends/forms/decidim/comments/comment_form_extends"
require "extends/forms/decidim/system/base_organization_form_extends"
require "extends/forms/decidim/omniauth_registration_form_extends"
require "extends/forms/decidim/editor_image_form_extends"
# controllers
require "extends/controllers/decidim/admin/scopes_controller_extends"
require "extends/controllers/decidim/scopes_controller_extends"
Expand Down
21 changes: 21 additions & 0 deletions lib/extends/forms/decidim/editor_image_form_extends.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require "active_support/concern"

module EditorImageFormExtends
extend ActiveSupport::Concern

included do
validate :no_special_character_in_file_name

# do not allow special characters like accents, spaces..except dash in image filename
# added to avoid broken images in proposals custom fields rich text editor
def no_special_character_in_file_name
if /\W/=~ file.original_filename.split(".").first
errors.add :file, :invalid
end
end
end
end

Decidim::EditorImageForm.include(EditorImageFormExtends)
1 change: 1 addition & 0 deletions spec/factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
require "decidim/surveys/test/factories"
require "decidim/budgets/test/factories"
require "decidim/initiatives/test/factories"
require "decidim/decidim_awesome/test/factories"
117 changes: 117 additions & 0 deletions spec/forms/editor_image_form_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# frozen_string_literal: true

require "spec_helper"

module Decidim
describe EditorImageForm do
subject { described_class.from_params(attributes).with_context(context) }

let(:attributes) do
{
"editor_image" => {
organization:,
author_id: user_id,
file:
}
}
end
let(:context) do
{
current_organization: organization,
current_user: user
}
end
let(:user) { create(:user, :admin, :confirmed) }
let(:organization) { user.organization }
let(:user_id) { user.id }
let(:file) { Decidim::Dev.test_file("city.jpeg", "image/jpeg") }

context "with correct data" do
it "is valid" do
expect(subject).to be_valid
end
end

context "with an empty user_id" do
let(:user_id) { nil }

it "is invalid" do
expect(subject).not_to be_valid
end
end

context "with an empty organization" do
let(:organization) { nil }

it "is invalid" do
expect(subject).not_to be_valid
end
end

context "when images are not the expected type" do
let(:file) { Decidim::Dev.test_file("Exampledocument.pdf", "application/pdf") }

it { is_expected.not_to be_valid }
end

context "when the original filename of the image has an underscore" do
let(:file) do
Rack::Test::UploadedFile.new(
Rails.root.join("app/packs/images/FranceConnect-Bouton/01-Principal/png/franceconnect-btn-principal.png"),
"image/png",
original_filename: "test_image.jpg"
)
end

it { is_expected.to be_valid }
end

context "when the original filename of the image has a dash" do
let(:file) do
Rack::Test::UploadedFile.new(
Rails.root.join("app/packs/images/FranceConnect-Bouton/01-Principal/png/franceconnect-btn-principal.png"),
"image/png",
original_filename: "test-image.jpg"
)
end

it { is_expected.not_to be_valid }
end

context "when the original filename of the image has an accent" do
let(:file) do
Rack::Test::UploadedFile.new(
Rails.root.join("app/packs/images/FranceConnect-Bouton/01-Principal/png/franceconnect-btn-principal.png"),
"image/png",
original_filename: "imàge.jpg"
)
end

it { is_expected.not_to be_valid }
end

context "when the original filename of the image has a space" do
let(:file) do
Rack::Test::UploadedFile.new(
Rails.root.join("app/packs/images/FranceConnect-Bouton/01-Principal/png/franceconnect-btn-principal.png"),
"image/png",
original_filename: "test image.jpg"
)
end

it { is_expected.not_to be_valid }
end

context "when the original filename of the image has a parenthesis" do
let(:file) do
Rack::Test::UploadedFile.new(
Rails.root.join("app/packs/images/FranceConnect-Bouton/01-Principal/png/franceconnect-btn-principal.png"),
"image/png",
original_filename: "test(image.jpg"
)
end

it { is_expected.not_to be_valid }
end
end
end
118 changes: 118 additions & 0 deletions spec/system/new_proposal_and_editor_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# frozen_string_literal: true

require "spec_helper"

describe "Proposals" do
include_context "with a component"
let(:manifest_name) { "proposals" }
let!(:user) { create(:user, :confirmed, organization:) }
let(:images_editor) { false }
let!(:allow_images_in_editors) { create(:awesome_config, organization:, var: :allow_images_in_editors, value: images_editor) }
let!(:component) do
create(:proposal_component,
:with_creation_enabled,
manifest:,
participatory_space: participatory_process,
settings: { new_proposal_body_template: body_template })
end
let(:body_template) do
{ "en" => "<p>This test has <strong>many</strong> characters </p>" }
end

context "when creating a new proposal" do
before do
login_as user, scope: :user
visit_component
end

context "when rich text editor is enabled for participants but images in rich text are not allowed" do
before do
organization.update(rich_text_editor_in_public_views: true)
click_on "New proposal"
end

it_behaves_like "having a rich text editor", "new_proposal", "basic"

it "has helper character counter" do
within "form.new_proposal" do
within ".editor .input-character-counter__text" do
expect(page).to have_content("At least 15 characters", count: 1)
end
end
end

it "displays the text with rich text in the input body" do
within "form.new_proposal" do
within ".editor-input" do
expect(find("p").text).to eq("This test has many characters")
expect(find("strong").text).to eq("many")
end
end
end

it "does not display a text above the editor to avoid special characters in image name" do
within "div.editor" do
expect(page).to have_no_content("If you upload an image, the name of the file must not contain special characters (space, accent, parenthesis...).")
end
end

context "and images are allowed in rich text" do
let(:images_editor) { true }
let(:editor_selector) { ".editor-input" }
let(:image) { Decidim::Dev.test_file("city.jpeg", "image/jpeg") }

it "displays a text above the editor to avoid special characters in image name" do
within "div.editor" do
expect(page).to have_content("If you upload an image, the name of the file must not contain special characters (space, accent, parenthesis...).")
end
end

it "displays a text in the upload modal to avoid special characters in image name" do
find('.editor-toolbar-control[data-editor-type="image"]').click

within "div.upload-modal__text" do
expect(page).to have_content("No special characters in image name.")
end
end
end
end

describe "validating the form" do
before do
click_on "New proposal"
end

context "when focus shifts to body" do
it "displays error when title is empty" do
fill_in :proposal_title, with: " "
find_by_id("proposal_body").click

expect(page).to have_css(".form-error.is-visible", text: "There is an error in this field.")
end

it "displays error when title is invalid" do
fill_in :proposal_title, with: "invalid-title"
find_by_id("proposal_body").click

expect(page).to have_css(".form-error.is-visible", text: "There is an error in this field")
end
end

context "when focus remains on title" do
it "does not display error when title is empty" do
fill_in :proposal_title, with: " "
find_by_id("proposal_title").click

expect(page).to have_no_css(".form-error.is-visible", text: "There is an error in this field.")
end

it "does not display error when title is invalid" do
fill_in :proposal_title, with: "invalid-title"
find_by_id("proposal_title").click

expect(page).to have_no_css(".form-error.is-visible", text: "There is an error in this field")
end
end
end
end
end
Loading