Skip to content

Commit

Permalink
Merge branch 'develop' into feat/attachment_add_event
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentinchampenois authored Nov 14, 2024
2 parents ce01f90 + 48579bf commit 5b4222b
Show file tree
Hide file tree
Showing 26 changed files with 1,339 additions and 15 deletions.
2 changes: 2 additions & 0 deletions .rubocop_rails.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ Rails/SkipsModelValidations:
Enabled: true
Exclude:
- db/migrate/*.rb
- lib/extends/models/decidim/decidim_awesome/proposal_extra_field_extends.rb
- spec/lib/tasks/decidim_app/set_decrypted_private_body_task_spec.rb

Rails/Validation:
Include:
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ gem "decidim-spam_detection", git: "https://github.com/OpenSourcePolitics/decidi
gem "decidim-survey_multiple_answers", git: "https://github.com/OpenSourcePolitics/decidim-module-survey_multiple_answers"
gem "decidim-term_customizer", git: "https://github.com/OpenSourcePolitics/decidim-module-term_customizer.git", branch: "fix/email_with_precompile"

gem "decidim-guest_meeting_registration", git: "https://github.com/alecslupu-pfa/guest-meeting-registration.git", branch: DECIDIM_BRANCH, ref: "532de9e"
gem "decidim-guest_meeting_registration", git: "https://github.com/alecslupu-pfa/guest-meeting-registration.git", branch: DECIDIM_BRANCH
# Omniauth gems
gem "omniauth-france_connect", git: "https://github.com/OpenSourcePolitics/omniauth-france_connect"
gem "omniauth_openid_connect"
Expand Down
3 changes: 1 addition & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ GIT

GIT
remote: https://github.com/alecslupu-pfa/guest-meeting-registration.git
revision: 532de9e87ecb8b2959c0013c6408d4c092abce87
ref: 532de9e
revision: 7b3af0d34d053cc430080e483cd6d1e48dcc0f32
branch: release/0.27-stable
specs:
decidim-guest_meeting_registration (0.27.7)
Expand Down
39 changes: 39 additions & 0 deletions app/jobs/active_storage_clear_orphans_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

class ActiveStorageClearOrphansJob < ApplicationJob
include ActionView::Helpers::NumberHelper
queue_as :default

def perform(**args)
limit = args[:limit] || 10_000
Rails.logger.info "Looking for orphan blobs in S3... (limit: #{limit})"
objects = ActiveStorage::Blob.service.bucket.objects
Rails.logger.info "Total files: #{objects.size}"

current_iteration = 0
sum = 0
orphans_count = 0
objects.each do |obj|
break if current_iteration >= limit

current_iteration += 1
next if ActiveStorage::Blob.exists?(key: obj.key)

sum += delete_object(obj)
orphans_count += 1
end

Rails.logger.info "Size: #{number_to_human_size(sum)} in #{orphans_count} files"
Rails.logger.info "Configuration limit is #{limit} files"
Rails.logger.info "Terminated task... "
end

private

def delete_object(obj)
Rails.logger.info "Removing orphan: #{obj.key}"
size = obj.size
obj.delete
size
end
end
18 changes: 18 additions & 0 deletions app/jobs/private_body_decrypt_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

class PrivateBodyDecryptJob < ApplicationJob
queue_as :default

def perform
extra_fields = Decidim::DecidimAwesome::ProposalExtraField.where(decrypted_private_body: nil).where.not(private_body: nil)
return unless extra_fields.any?

Rails.logger.info "Extra fields to update: #{extra_fields.size}"
count = 0
extra_fields.find_each do |extra_field|
extra_field.update(decrypted_private_body: extra_field.private_body.to_s)
count += 1 if extra_field.decrypted_private_body_previous_change.present?
end
Rails.logger.info "Extra fields updated: #{count}"
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require("formBuilder/dist/form-builder.min.js")
import "src/decidim/decidim_awesome/forms/rich_text_plugin"

window.CustomFieldsBuilders = window.CustomFieldsBuilders || [];

$(() => {
$(".awesome-edit-config .proposal_custom_fields_editor").each((_idx, el) => {
const key = $(el).closest(".proposal_custom_fields_container").data("key");
const configVar = $(el).closest(".proposal_custom_fields_container").data("var");
// DOCS: https://formbuilder.online/docs
window.CustomFieldsBuilders.push({
el: el,
key: key,
var: configVar,
config: {
i18n: {
locale: "fr-FR",
location: "https://decidim.storage.opensourcepolitics.eu/osp-cdn/form_builder/1.1.0"
},
formData: $(`input[name="config[${configVar}][${key}]"]`).val(),
disableFields: ["button", "file"],
disabledActionButtons: ["save", "data", "clear"],
disabledAttrs: [
"access",
"inline",
"className"
],
controlOrder: [
"text",
"textarea",
"number",
"date",
"checkbox-group",
"radio-group",
"select",
"autocomplete",
"header",
"paragraph"
],
disabledSubtypes: {
// default color as it generate hashtags in decidim (TODO: fix hashtag generator with this)
text: ["color"],
// disable default wysiwyg editors as they present problems
textarea: ["tinymce", "quill"]
}
},
instance: null
});
});

$(document).on("formBuilder.create", (_event, idx, list) => {
if (!list[idx]) {
return;
}

$(list[idx].el).formBuilder(list[idx].config).promise.then(function(res) {
list[idx].instance = res;
// Attach to DOM
list[idx].el.FormBuilder = res;
// remove spinner
$(list[idx].el).find(".loading-spinner").remove();
// for external use
$(document).trigger("formBuilder.created", [list[idx]]);
if (idx < list.length) {
$(document).trigger("formBuilder.create", [idx + 1, list]);
}
});
});

if (window.CustomFieldsBuilders.length) {
$(document).trigger("formBuilder.create", [0, window.CustomFieldsBuilders]);
}

$("form.awesome-edit-config").on("submit", () => {
window.CustomFieldsBuilders.forEach((builder) => {
$(`input[name="config[${builder.var}][${builder.key}]"]`).val(builder.instance.actions.getData("json"));
});
});
});
214 changes: 214 additions & 0 deletions app/packs/src/decidim/decidim_awesome/editors/editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/* eslint-disable require-jsdoc, func-style */

/*
* Since version 0.25 we follow a different strategy and opt to destroy and override completely the original editor
* That's because editors are instantiated directly instead of creating a global function to instantiate them
*/

import lineBreakButtonHandler from "src/decidim/editor/linebreak_module"
import InscrybMDE from "inscrybmde"
import Europa from "europa"
import "inline-attachment/src/inline-attachment";
import "inline-attachment/src/codemirror-4.inline-attachment";
import "inline-attachment/src/jquery.inline-attachment";
import hljs from "highlight.js";
import "highlight.js/styles/github.css";
import "src/decidim/editor/clipboard_override"
import "src/decidim/vendor/image-resize.min"
import "src/decidim/vendor/image-upload.min"
import { marked } from "marked";

const DecidimAwesome = window.DecidimAwesome || {};
const quillFormats = ["bold", "italic", "link", "underline", "header", "list", "video", "image", "alt", "break", "width", "style", "code", "blockquote", "indent"];

// A tricky way to destroy the quill editor
export function destroyQuillEditor(container) {
if (container) {
const content = $(container).find(".ql-editor").html();
$(container).html(content);
$(container).siblings(".ql-toolbar").remove();
$(container).find("*[class*='ql-']").removeClass((index, className) => (className.match(/(^|\s)ql-\S+/g) || []).join(" "));
$(container).removeClass((index, className) => (className.match(/(^|\s)ql-\S+/g) || []).join(" "));
if ($(container).next().is("p.help-text")) {
$(container).next().remove();
}
}
else {
console.error(`editor [${container}] not exists`);
}
}

export function createQuillEditor(container) {
const toolbar = $(container).data("toolbar");
const disabled = $(container).data("disabled");
const allowedEmptyContentSelector = "iframe,img";

let quillToolbar = [
["bold", "italic", "underline", "linebreak"],
[{ list: "ordered" }, { list: "bullet" }],
["link", "clean"],
["code", "blockquote"],
[{ "indent": "-1"}, { "indent": "+1" }]
];

let addImage = false;

if (toolbar === "full") {
quillToolbar = [
[{ header: [2, 3, 4, 5, 6, false] }],
...quillToolbar
];
if (DecidimAwesome.allow_images_in_full_editor) {
quillToolbar.push(["video", "image"]);
addImage = true;
} else {
quillToolbar.push(["video"]);
}
} else if (toolbar === "basic") {
if (DecidimAwesome.allow_images_in_small_editor) {
quillToolbar.push(["video", "image"]);
addImage = true;
} else {
quillToolbar.push(["video"]);
}
} else if (DecidimAwesome.allow_images_in_small_editor) {
quillToolbar.push(["image"]);
addImage = true;
}

let modules = {
linebreak: {},
toolbar: {
container: quillToolbar,
handlers: {
"linebreak": lineBreakButtonHandler
}
}
};

const $input = $(container).siblings('input[type="hidden"]');
container.innerHTML = $input.val() || "";
const token = $('meta[name="csrf-token"]').attr("content");
if (addImage) {
modules.imageResize = {
modules: ["Resize", "DisplaySize"]
}
modules.imageUpload = {
url: DecidimAwesome.editor_uploader_path,
method: "POST",
name: "image",
withCredentials: false,
headers: { "X-CSRF-Token": token },
callbackOK: (serverResponse, next) => {
$("div.ql-toolbar").last().removeClass("editor-loading")
next(serverResponse.url);
},
callbackKO: (serverError) => {
$("div.ql-toolbar").last().removeClass("editor-loading")
let msg = serverError && serverError.body;
try {
msg = JSON.parse(msg).message;
} catch (evt) { console.error("Parsing error", evt); }
console.error(`Image upload error: ${msg}`);
let $p = $(`<p class="text-alert help-text">${msg}</p>`);
$(container).after($p)
setTimeout(() => {
$p.fadeOut(1000, () => {
$p.destroy();
});
}, 3000);
},
checkBeforeSend: (file, next) => {
$("div.ql-toolbar").last().addClass("editor-loading")
next(file);
}
}
}
const quill = new Quill(container, {
modules: modules,
formats: quillFormats,
theme: "snow"
});

if (disabled) {
quill.disable();
}

quill.on("text-change", () => {
const text = quill.getText();

// Triggers CustomEvent with the cursor position
// It is required in input_mentions.js
let event = new CustomEvent("quill-position", {
detail: quill.getSelection()
});
container.dispatchEvent(event);

if ((text === "\n" || text === "\n\n") && quill.root.querySelectorAll(allowedEmptyContentSelector).length === 0
&& !$input.val().match(/img/)) {
$input.val("");
} else {
const emptyParagraph = "<p><br></p>";
const cleanHTML = quill.root.innerHTML.replace(
new RegExp(`^${emptyParagraph}|${emptyParagraph}$`, "g"),
""
);
$input.val(cleanHTML);
}
});
// After editor is ready, linebreak_module deletes two extraneous new lines
quill.emitter.emit("editor-ready");

if (addImage) {
const text = $(container).data("dragAndDropHelpText") || DecidimAwesome.texts.drag_and_drop_image;
$(container).after(`<p class="help-text" style="margin-top:-1.5rem;">${text}</p>`);
}

// After editor is ready, linebreak_module deletes two extraneous new lines
quill.emitter.emit("editor-ready");

return quill;
}

export function createMarkdownEditor(container) {
const text = DecidimAwesome.texts.drag_and_drop_image;
const token = $('meta[name="csrf-token"]').attr("content");
const $input = $(container).siblings('input[type="hidden"]');
const $faker = $('<textarea name="faker-inscrybmde"/>');
const $form = $(container).closest("form");
const europa = new Europa();
$faker.val(europa.convert($input.val()));
$faker.insertBefore($(container));
$(container).hide();
const inscrybmde = new InscrybMDE({
element: $faker[0],
spellChecker: false,
renderingConfig: {
codeSyntaxHighlighting: true,
hljs: hljs
}
});
$faker[0].InscrybMDE = inscrybmde;

// Allow image upload
if (DecidimAwesome.allow_images_in_markdown_editor) {
$(inscrybmde.gui.statusbar).prepend(`<span class="help-text" style="float:left;margin:0;text-align:left;">${text}</span>`);
window.inlineAttachment.editors.codemirror4.attach(inscrybmde.codemirror, {
uploadUrl: DecidimAwesome.editor_uploader_path,
uploadFieldName: "image",
jsonFieldName: "url",
extraHeaders: { "X-CSRF-Token": token }
});
}

// Allow linebreaks
marked.setOptions({
breaks: true
});

// convert to html on submit
$form.on("submit", () => {
// e.preventDefault();
$input.val(marked(inscrybmde.value()));
});
}
Loading

0 comments on commit 5b4222b

Please sign in to comment.