Skip to content
Open
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
2 changes: 1 addition & 1 deletion .env-example
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ GEOCODER_UNITS=km # Units for geocoder results (e.g., km or m
# DECIDIM_CLEANER_DELETE_DELETED_AUTHORIZATIONS_DATA=

#= Decidim AI
# DECIDIM_AI_ENABLED="true" Default: true
# DECIDIM_AI_ENABLED="false" Default: false
# DECIDIM_AI_USER_SPAM_ANALYZER_ENABLED="true" Default: true
# DECIDIM_AI_GENERIC_SPAM_ANALYZER_ENABLED="true" Default: true
# DECIDIM_AI_ENDPOINT="https://<ENDPOINT_URL>"
Expand Down
5 changes: 1 addition & 4 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

Expand Down Expand Up @@ -34,4 +31,4 @@ If applicable, add the error stacktrace to help explain your problem.
- Decidim installation: [e.g. Metadecidim]

**Additional context**
Add any other context about the problem here. For instance, add Metadecidim link.
Add any other context about the problem here. For instance, add Metadecidim link.
10 changes: 0 additions & 10 deletions .github/ISSUE_TEMPLATE/tracking.md

This file was deleted.

2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ GIT

GIT
remote: https://github.com/OpenSourcePolitics/decidim-module-ptp.git
revision: d8ed874484fea98870b02e88b2479f2caf5818bf
revision: ce8a886f7186d971e0cf44a5195395d5542ab59d
branch: bump/0.29-budgets_booth
specs:
decidim-budgets_booth (0.29.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class SpamDigestGeneratorJob < ApplicationJob
}.freeze

def perform(frequency)
# Stop if Decidim-AI is disabled
return unless decidim_ai_enabled?

# Skip validation if frequency is nil (called by Decidim core specs)
return if frequency.nil? && Rails.env.test?
raise ArgumentError, "Invalid frequency: #{frequency}" unless frequency && FREQUENCIES.has_key?(frequency.to_sym)
Expand All @@ -37,6 +40,10 @@ def perform(frequency)

private

def decidim_ai_enabled?
Rails.application.secrets.dig(:decidim, :ai, :enabled) == true
end

# Counts the spam reports for the given organization and frequency (daily/weekly)
def count_spam(organization, frequency)
since = frequency == :weekly ? 1.week.ago : 1.day.ago
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- replace "erb[loud]:contains('filter_text_for(leaf.label')" --><%= filter_text_for(CGI.unescapeHTML(leaf.label), id: "dropdown-title-#{data_checkboxes_tree_id}") %>
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);
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<% link_classes = "login__omniauth-button button--#{normalize_provider_name(provider)}" %>
<% display_name = current_organization.enabled_omniauth_providers.dig(provider.to_sym, :display_name) %>
<% provider_name = display_name.present? ? display_name : normalize_provider_name(provider).titleize %>
<% provider_name = I18n.t("decidim.devise.shared.links.log_in_with_provider") if custom_publik_translation?(provider) %>
<%= link_to decidim.send("user_#{provider}_omniauth_authorize_path"), class: link_classes, method: :post, title: t("devise.shared.links.log_in_with_provider", provider: provider_name) do %>
<%= oauth_icon provider %>
<span>
<%= provider_name %>
</span>
<% end %>
<% end %>
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
11 changes: 11 additions & 0 deletions config/initializers/decidim_ai.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@
Decidim::Ai::SpamDetection.user_spam_analyzer_job = "Decidim::Ai::SpamDetection::ThirdParty::UserSpamAnalyzerJob"
Decidim::Ai::SpamDetection.generic_spam_analyzer_job = "Decidim::Ai::SpamDetection::ThirdParty::GenericSpamAnalyzerJob"
else
analyzers = [
{
name: :bayes,
strategy: Decidim::Ai::SpamDetection::Strategy::Bayes,
options: {
adapter: "memory"
}
}
]
Decidim::Ai::SpamDetection.resource_analyzers = analyzers
Decidim::Ai::SpamDetection.user_analyzers = analyzers
Rails.logger.warn "[decidim-ai] Initializer - AI module is not installed or spam detection is disabled. AI features will be disabled."
Rails.logger.warn "[decidim-ai] Initializer - Spam detection enabled: #{Rails.application.secrets.dig(:decidim, :ai, :spam_detection, :enabled).presence || false}"
end
4 changes: 2 additions & 2 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ en:
frequency_label:
daily: today
weekly: this week
subject: AI spam detection summary - %{count} spam items detected (%{frequency_label})
subject: Reports summary - %{count} spam (%{frequency_label})
summary: |-
AI anti-spam summary for %{organization}: %{count} items were automatically flagged %{frequency_label} by the Decidim AI system.<br><br>
Reports summary for %{organization}: %{count} items were flagged %{frequency_label}.<br><br>
<a href="%{moderations_url}" target="_blank">View detected spams</a>.
authorization_handlers:
data_authorization_handler:
Expand Down
2 changes: 1 addition & 1 deletion config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fr:
weekly: cette semaine
subject: Résumé anti-spam, %{count} spams détectés (%{frequency_label})
summary: |-
Résumé IA anti-spam pour %{organization} : %{count} contenus ont été automatiquement signalés %{frequency_label} par le système Decidim AI.<br><br>
Résumé anti-spam pour %{organization} : %{count} contenus ont été signalés %{frequency_label}.<br><br>
<a href="%{moderations_url}" target="_blank">Voir les spams détectés</a>.
authorization_handlers:
data_authorization_handler:
Expand Down
2 changes: 1 addition & 1 deletion config/secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ decidim_default: &decidim_default
repetition_times: <%= Decidim::Env.new("DECIDIM_ADMIN_PASSWORD_REPETITION_TIMES", 5).to_i %>
strong: <%= Decidim::Env.new("DECIDIM_ADMIN_PASSWORD_STRONG", true).to_boolean_string %>
ai:
enabled: <%= Decidim::Env.new("DECIDIM_AI_ENABLED", true).to_boolean_string %>
enabled: <%= Decidim::Env.new("DECIDIM_AI_ENABLED", false).to_boolean_string %>
user_spam_analyzer_enabled: <%= Decidim::Env.new("DECIDIM_AI_USER_SPAM_ANALYZER_ENABLED", true).to_boolean_string %>
generic_spam_analyzer_enabled: <%= Decidim::Env.new("DECIDIM_AI_GENERIC_SPAM_ANALYZER_ENABLED", true).to_boolean_string %>
endpoint: <%= Decidim::Env.new("DECIDIM_AI_ENDPOINT").to_s %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ def create_proposal
proposal.longitude = form.longitude if form.longitude.present?
proposal.add_coauthor(@current_user, user_group:)
proposal.save!
# from decidim_awesome
# Update the proposal with the private body, to
# avoid tracebility on private fields.
proposal.update_private_body!(form.private_body) if form.private_body.present?
@attached_to = proposal
proposal
end
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)
8 changes: 5 additions & 3 deletions lib/extends/helpers/decidim/omniauth_helper_extends.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ def oauth_icon_with_hover(provider)
def normalize_provider_name(provider)
return "x" if provider == :twitter
# customize the name of the omniauth btn login with publik
if provider == :publik && Decidim::TermCustomizer::Translation.where(key: "decidim.devise.shared.links.log_in_with_provider").present?
return I18n.t("decidim.devise.shared.links.log_in_with_provider")
end
return I18n.t("decidim.devise.shared.links.log_in_with_provider") if custom_publik_translation?(provider)

provider.to_s.split("_").first
end

def custom_publik_translation?(provider)
provider == :publik && Decidim::TermCustomizer::Translation.where(key: "decidim.devise.shared.links.log_in_with_provider").present?
end
end

Decidim::OmniauthHelper.module_eval do
Expand Down
Loading
Loading