From b4e8395bd6bae5e62d3595bd19a5d7d92c36b5b6 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Tue, 14 May 2019 18:32:13 +0200 Subject: [PATCH 1/5] Extract settings forms to partials --- .../admin/settings/_featured_settings_form.html.erb | 6 ++++++ .../admin/settings/_featured_settings_table.html.erb | 8 +------- app/views/admin/settings/_settings_form.html.erb | 8 ++++++++ app/views/admin/settings/_settings_table.html.erb | 9 +-------- 4 files changed, 16 insertions(+), 15 deletions(-) create mode 100644 app/views/admin/settings/_featured_settings_form.html.erb create mode 100644 app/views/admin/settings/_settings_form.html.erb diff --git a/app/views/admin/settings/_featured_settings_form.html.erb b/app/views/admin/settings/_featured_settings_form.html.erb new file mode 100644 index 000000000..b1ec7ca7a --- /dev/null +++ b/app/views/admin/settings/_featured_settings_form.html.erb @@ -0,0 +1,6 @@ +<%= form_for(feature, url: admin_setting_path(feature), html: { id: "edit_#{dom_id(feature)}"}) do |f| %> + <%= f.hidden_field :value, id: dom_id(feature), value: (feature.enabled? ? "" : "active") %> + <%= f.submit(t("admin.settings.index.features.#{feature.enabled? ? "disable" : "enable"}"), + class: "button expanded #{feature.enabled? ? "hollow alert" : "success"}", + data: {confirm: t("admin.actions.confirm")}) %> +<% end %> diff --git a/app/views/admin/settings/_featured_settings_table.html.erb b/app/views/admin/settings/_featured_settings_table.html.erb index 5de1dc9b1..f4ba75823 100644 --- a/app/views/admin/settings/_featured_settings_table.html.erb +++ b/app/views/admin/settings/_featured_settings_table.html.erb @@ -32,13 +32,7 @@ - <%= form_for(feature, url: admin_setting_path(feature), html: { id: "edit_#{dom_id(feature)}"}) do |f| %> - - <%= f.hidden_field :value, id: dom_id(feature), value: (feature.enabled? ? "" : "active") %> - <%= f.submit(t("admin.settings.index.features.#{feature.enabled? ? "disable" : "enable"}"), - class: "button expanded #{feature.enabled? ? "hollow alert" : "success"}", - data: {confirm: t("admin.actions.confirm")}) %> - <% end %> + <%= render "admin/settings/featured_settings_form", feature: feature %> <% end %> diff --git a/app/views/admin/settings/_settings_form.html.erb b/app/views/admin/settings/_settings_form.html.erb new file mode 100644 index 000000000..b6851669b --- /dev/null +++ b/app/views/admin/settings/_settings_form.html.erb @@ -0,0 +1,8 @@ +<%= form_for(setting, url: admin_setting_path(setting), html: { id: "edit_#{dom_id(setting)}"}) do |f| %> +
+ <%= f.text_area :value, label: false, id: dom_id(setting), lines: 1 %> +
+
+ <%= f.submit(t("admin.settings.index.update_setting"), class: "button hollow expanded") %> +
+<% end %> diff --git a/app/views/admin/settings/_settings_table.html.erb b/app/views/admin/settings/_settings_table.html.erb index b6c75b440..76f567afc 100644 --- a/app/views/admin/settings/_settings_table.html.erb +++ b/app/views/admin/settings/_settings_table.html.erb @@ -16,14 +16,7 @@ - <%= form_for(setting, url: admin_setting_path(setting), html: { id: "edit_#{dom_id(setting)}"}) do |f| %> -
- <%= f.text_area :value, label: false, id: dom_id(setting), lines: 1 %> -
-
- <%= f.submit(t("admin.settings.index.update_setting"), class: "button hollow expanded") %> -
- <% end %> + <%= render "admin/settings/settings_form", setting: setting %> <% end %> From e32faf3a3ce21983639a65e73a652ea4db15412b Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Tue, 14 May 2019 18:44:29 +0200 Subject: [PATCH 2/5] Extract setting prefix to a method We may need to access the setting key prefix in the future, so it's better to have it in a method to avoid duplication --- app/models/setting.rb | 5 ++++- spec/models/setting_spec.rb | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/models/setting.rb b/app/models/setting.rb index 6210b6ba5..11623624a 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -3,8 +3,11 @@ class Setting < ApplicationRecord default_scope { order(id: :asc) } + def prefix + key.split(".").first + end + def type - prefix = key.split(".").first if %w[feature process proposals map html homepage].include? prefix prefix else diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index 6fa86b686..971fbe3df 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -17,6 +17,16 @@ describe Setting do expect(described_class.where(key: "official_level_1_name", value: "Stormtrooper")).to exist end + describe "#prefix" do + it "returns the prefix of its key" do + expect(Setting.create(key: "prefix.key_name").prefix).to eq "prefix" + end + + it "returns the whole key for a non prefixed key" do + expect(Setting.create(key: "key_name").prefix).to eq "key_name" + end + end + describe "#type" do it "returns the key prefix for 'process' settings" do process_setting = Setting.create(key: "process.whatever") From 35cd26c11714bc782e2e2c2a41193b5a22ea95c2 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 16 May 2019 16:11:43 +0200 Subject: [PATCH 3/5] Add images and document settings to the DB --- app/models/setting.rb | 10 ++++++++++ config/locales/en/settings.yml | 22 ++++++++++++++++++++++ config/locales/es/settings.yml | 22 ++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/app/models/setting.rb b/app/models/setting.rb index 11623624a..c3bb8bca0 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -84,6 +84,16 @@ class Setting < ApplicationRecord "proposals.email_description": nil, "proposals.poster_short_title": nil, "proposals.poster_description": nil, + # Images and Documents + "uploads.images.title.min_length": 4, + "uploads.images.title.max_length": 80, + "uploads.images.min_width": 0, + "uploads.images.min_height": 475, + "uploads.images.max_size": 1, + "uploads.images.content_types": "image/jpeg", + "uploads.documents.max_amount": 3, + "uploads.documents.max_size": 3, + "uploads.documents.content_types": "application/pdf", # Names for the moderation console, as a hint for moderators # to know better how to assign users with official positions "official_level_1_name": I18n.t("seeds.settings.official_level_1_name"), diff --git a/config/locales/en/settings.yml b/config/locales/en/settings.yml index cb0363f48..dc74ae6fb 100644 --- a/config/locales/en/settings.yml +++ b/config/locales/en/settings.yml @@ -139,3 +139,25 @@ en: per_page_code_head_description: "This code will appear inside the label. Useful for entering custom scripts, analytics..." per_page_code_body: "Code to be included on every page ()" per_page_code_body_description: "This code will appear inside the label. Useful for entering custom scripts, analytics..." + uploads: + images: + min_width: "Image minimum width" + min_width_description: "Minimum width allowed for an uploaded image (in pixels)" + min_height: "Image minimum height" + min_height_description: "Minimum height allowed for an uploaded image (in pixels)" + max_size: "Image maximum size" + max_size_description: "Maximum size allowed for an uploaded image (in Megabytes/MB)" + content_types: "Accepted content types for images" + content_types_description: "Select all the content types allowed for uploaded images" + title: + min_length: "Image title minimum length" + min_length_description: "Title provided by the user when uploading an image (used as alt HTML attribute)" + max_length: "Image title maximum length" + max_length_description: "Title provided by the user when uploading an image (used as alt HTML attribute)" + documents: + max_amount: "Maximum number of documents" + max_amount_description: "Maximum number of documents that can be attached to a proposal, investment..." + max_size: "Document maximum size" + max_size_description: "Maximum size allowed for an uploaded document (in Megabytes/MB)" + content_types: "Accepted content types for documents" + content_types_description: "Select all the content types allowed for uploaded documents" diff --git a/config/locales/es/settings.yml b/config/locales/es/settings.yml index 42b0726d1..cdf7f2452 100644 --- a/config/locales/es/settings.yml +++ b/config/locales/es/settings.yml @@ -139,3 +139,25 @@ es: per_page_code_head_description: "Esté código aparecerá dentro de la etiqueta . Útil para introducir scripts personalizados, analitycs..." per_page_code_body: "Código a incluir en cada página ()" per_page_code_body_description: "Esté código aparecerá dentro de la etiqueta . Útil para introducir scripts personalizados, analitycs..." + uploads: + images: + min_width: "Ancho mínimo de imagen" + min_width_description: "Ancho mínimo permitido al subir una imagen (en pixeles)" + min_height: "Alto mínimo de imagen" + min_height_description: "Alto mínimo permitido al subir una imagen (en pixeles)" + max_size: "Tamaño máximo de imagen" + max_size_description: "Tamaño máximo permitido al subir una imagen (en Megabytes/MB)" + content_types: "Tipos de imagenes permitidos" + content_types_description: "Selecciona todos los tipos permitidos para las imágenes subidas" + title: + min_length: "Longitud mínima del título de la imagen" + min_length_description: "El título es proporcionado por el usuario cuando se sube una imagen (usado como atributo HTML alt)" + max_length: "Longitud máxima del título de la imagen" + max_length_description: "El título es proporcionado por el usuario cuando se sube una imagen (usado como atributo HTML alt)" + documents: + max_amount: "Número máximo de documentos" + max_amount_description: "Número máximo de documentos que se pueden añadir a una propuesta, proyecto de gasto..." + max_size: "Tamaño máximo de documento" + max_size_description: "Tamaño máximo permitido al subir un documento (en Megabytes/MB)" + content_types: "Tipos de documentos permitidos" + content_types_description: "Selecciona todos los tipos permitidos para los documentos subidos" From 220bfb065afddc38b11706625632237c252167c2 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 16 May 2019 16:14:40 +0200 Subject: [PATCH 4/5] Add images and documents settings to admin panel --- app/assets/stylesheets/admin.scss | 9 +++++ app/controllers/admin/settings_controller.rb | 15 ++++++++ app/models/setting.rb | 29 +++++++++++++++- .../_content_types_settings_form.html.erb | 19 +++++++++++ .../admin/settings/_filter_subnav.html.erb | 6 ++++ .../_images_and_documents_tab.html.erb | 3 ++ .../admin/settings/_settings_table.html.erb | 6 +++- app/views/admin/settings/index.html.erb | 4 +++ config/locales/en/admin.yml | 1 + config/locales/es/admin.yml | 1 + config/routes/admin.rb | 1 + spec/features/admin/settings_spec.rb | 34 +++++++++++++++++++ spec/models/setting_spec.rb | 20 +++++++++++ 13 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 app/views/admin/settings/_content_types_settings_form.html.erb create mode 100644 app/views/admin/settings/_images_and_documents_tab.html.erb diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 9c95f400f..e42bb0362 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -414,6 +414,15 @@ code { word-break: break-all; } +.content-type { + white-space: nowrap; + padding-right: $line-height; + + label { + margin-left: 0 !important; + } +} + // 02. Sidebar // ----------- diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb index 34fa22704..f24b6c63e 100644 --- a/app/controllers/admin/settings_controller.rb +++ b/app/controllers/admin/settings_controller.rb @@ -14,6 +14,7 @@ class Admin::SettingsController < Admin::BaseController @participation_processes_settings = all_settings["process"] @map_configuration_settings = all_settings["map"] @proposals_settings = all_settings["proposals"] + @uploads_settings = all_settings["uploads"] end def update @@ -29,10 +30,24 @@ class Admin::SettingsController < Admin::BaseController redirect_to admin_settings_path, notice: t("admin.settings.index.map.flash.update") end + def update_content_types + setting = Setting.find(params[:id]) + group = setting.content_type_group + mime_type_values = content_type_params.keys.map do |content_type| + Setting.mime_types[group][content_type] + end + setting.update value: mime_type_values.join(" ") + redirect_to admin_settings_path, notice: t("admin.settings.flash.updated") + end + private def settings_params params.require(:setting).permit(:value) end + def content_type_params + params.permit(:jpg, :png, :gif, :pdf, :doc, :docx, :xls, :xlsx, :csv, :zip) + end + end diff --git a/app/models/setting.rb b/app/models/setting.rb index c3bb8bca0..12162db4e 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -8,7 +8,7 @@ class Setting < ApplicationRecord end def type - if %w[feature process proposals map html homepage].include? prefix + if %w[feature process proposals map html homepage uploads].include? prefix prefix else "configuration" @@ -19,6 +19,14 @@ class Setting < ApplicationRecord value.present? end + def content_type? + key.split(".").last == "content_types" + end + + def content_type_group + key.split(".").second + end + class << self def [](key) where(key: key).pluck(:value).first.presence @@ -44,6 +52,25 @@ class Setting < ApplicationRecord setting.destroy if setting.present? end + def mime_types + { + "images" => { + "jpg" => "image/jpeg", + "png" => "image/png", + "gif" => "image/gif" + }, + "documents" => { + "pdf" => "application/pdf", + "doc" => "application/msword", + "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "xls" => "application/x-ole-storage", + "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "csv" => "text/plain", + "zip" => "application/zip" + } + } + end + def defaults { "feature.featured_proposals": nil, diff --git a/app/views/admin/settings/_content_types_settings_form.html.erb b/app/views/admin/settings/_content_types_settings_form.html.erb new file mode 100644 index 000000000..67262c155 --- /dev/null +++ b/app/views/admin/settings/_content_types_settings_form.html.erb @@ -0,0 +1,19 @@ +<%= form_tag admin_update_content_types_path, method: :put, id: "edit_#{dom_id(setting)}" do %> + <%= hidden_field_tag "id", setting.id %> + +
+ <% group = setting.content_type_group %> + <% Setting.mime_types[group].each do |content_type, mime_type_value| %> + + <%= check_box_tag content_type, + setting.value.split(" ").include?(mime_type_value), + setting.value.split(" ").include?(mime_type_value) %> + <%= label_tag content_type, content_type.upcase %> + + <% end %> +
+ +
+ <%= submit_tag t("admin.settings.index.update_setting"), class: "button hollow expanded" %> +
+<% end %> diff --git a/app/views/admin/settings/_filter_subnav.html.erb b/app/views/admin/settings/_filter_subnav.html.erb index d4e4a00d8..355e7a1c5 100644 --- a/app/views/admin/settings/_filter_subnav.html.erb +++ b/app/views/admin/settings/_filter_subnav.html.erb @@ -29,6 +29,12 @@ <% end %> +
  • + <%= link_to "#tab-images-and-documents" do %> + <%= t("admin.settings.index.images_and_documents") %> + <% end %> +
  • +
  • <%= link_to "#tab-proposals" do %> <%= t("admin.settings.index.dashboard.title") %> diff --git a/app/views/admin/settings/_images_and_documents_tab.html.erb b/app/views/admin/settings/_images_and_documents_tab.html.erb new file mode 100644 index 000000000..740f55f23 --- /dev/null +++ b/app/views/admin/settings/_images_and_documents_tab.html.erb @@ -0,0 +1,3 @@ +

    <%= t("admin.settings.index.images_and_documents") %>

    + +<%= render "settings_table", settings: @uploads_settings %> diff --git a/app/views/admin/settings/_settings_table.html.erb b/app/views/admin/settings/_settings_table.html.erb index 76f567afc..ce99ffe4e 100644 --- a/app/views/admin/settings/_settings_table.html.erb +++ b/app/views/admin/settings/_settings_table.html.erb @@ -16,7 +16,11 @@ - <%= render "admin/settings/settings_form", setting: setting %> + <% if setting.content_type? %> + <%= render "admin/settings/content_types_settings_form", setting: setting %> + <% else %> + <%= render "admin/settings/settings_form", setting: setting %> + <% end %> <% end %> diff --git a/app/views/admin/settings/index.html.erb b/app/views/admin/settings/index.html.erb index 1de5b66be..a000cffc5 100644 --- a/app/views/admin/settings/index.html.erb +++ b/app/views/admin/settings/index.html.erb @@ -18,6 +18,10 @@ <%= render "map_configuration_tab" %> +
    + <%= render "images_and_documents_tab" %> +
    +
    <%= render "proposals_dashboard" %>
    diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index e3c5c597b..c860315ab 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -1261,6 +1261,7 @@ en: title: Configuration settings update_setting: Update participation_processes: "Participation processes" + images_and_documents: "Images and documents" feature_flags: Features features: enabled: "Feature enabled" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 34fc7510b..f783e0777 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -1260,6 +1260,7 @@ es: title: Configuración global update_setting: Actualizar participation_processes: "Procesos de participación" + images_and_documents: "Imágenes y documentos" feature_flags: Funcionalidades features: enabled: "Funcionalidad activada" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 39ed9b44b..ca162b9ca 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -90,6 +90,7 @@ namespace :admin do resources :settings, only: [:index, :update] put :update_map, to: "settings#update_map" + put :update_content_types, to: "settings#update_content_types" resources :moderators, only: [:index, :create, :destroy] do get :search, on: :collection diff --git a/spec/features/admin/settings_spec.rb b/spec/features/admin/settings_spec.rb index 4800b8c3e..ef72b5d0c 100644 --- a/spec/features/admin/settings_spec.rb +++ b/spec/features/admin/settings_spec.rb @@ -98,6 +98,40 @@ describe "Admin settings" do end + describe "Update content types" do + + scenario "stores the correct mime types" do + setting = Setting.create(key: "upload.images.content_types", value: "image/png") + admin = create(:administrator).user + login_as(admin) + visit admin_settings_path + find("#images-and-documents-tab").click + + within "#edit_setting_#{setting.id}" do + expect(find("#png")).to be_checked + expect(find("#jpg")).not_to be_checked + expect(find("#gif")).not_to be_checked + + check "gif" + + click_button "Update" + end + + expect(page).to have_content "Value updated" + expect(Setting["upload.images.content_types"]).to include "image/png" + expect(Setting["upload.images.content_types"]).to include "image/gif" + + visit admin_settings_path(anchor: "tab-images-and-documents") + + within "#edit_setting_#{setting.id}" do + expect(find("#png")).to be_checked + expect(find("#gif")).to be_checked + expect(find("#jpg")).not_to be_checked + end + end + + end + describe "Skip verification" do scenario "deactivate skip verification", :js do diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index 971fbe3df..df764b7ca 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -80,6 +80,26 @@ describe Setting do end end + describe "#content_type?" do + it "returns true if the last part of the key is content_types" do + expect(Setting.create(key: "key_name.content_types").content_type?).to be true + end + + it "returns false if the last part of the key is not content_types" do + expect(Setting.create(key: "key_name.whatever").content_type?).to be false + end + end + + describe "#content_type_group" do + it "returns the group for content_types settings" do + images = Setting.create(key: "update.images.content_types") + documents = Setting.create(key: "update.documents.content_types") + + expect(images.content_type_group).to eq "images" + expect(documents.content_type_group).to eq "documents" + end + end + describe ".rename_key" do it "renames the setting keeping the original value and deletes the old setting" do Setting["old_key"] = "old_value" From 8e0bbf54f6dfc38b8a0f50ce3b06b0a0be97bce7 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Thu, 16 May 2019 17:42:17 +0200 Subject: [PATCH 5/5] Replace harcoded images and documents settings --- app/helpers/documentables_helper.rb | 8 ++---- app/helpers/imageables_helper.rb | 18 ++++++------ app/models/budget/investment.rb | 3 -- app/models/concerns/documentable.rb | 17 +++++------ app/models/dashboard/action.rb | 8 ------ app/models/image.rb | 38 ++++++++++++++++++------- app/models/legislation/process.rb | 3 -- app/models/legislation/proposal.rb | 3 -- app/models/milestone.rb | 3 -- app/models/poll/question/answer.rb | 3 -- app/models/proposal.rb | 3 -- app/models/setting.rb | 5 ++++ config/i18n-tasks.yml | 2 ++ spec/models/setting_spec.rb | 18 ++++++++++++ spec/shared/models/image_validations.rb | 5 ++-- 15 files changed, 77 insertions(+), 60 deletions(-) diff --git a/app/helpers/documentables_helper.rb b/app/helpers/documentables_helper.rb index ef1a2d6b9..98ce55b59 100644 --- a/app/helpers/documentables_helper.rb +++ b/app/helpers/documentables_helper.rb @@ -17,15 +17,11 @@ module DocumentablesHelper end def accepted_content_types_extensions(documentable_class) - documentable_class.accepted_content_types - .collect{ |content_type| ".#{content_type.split("/").last}" } - .join(",") + Setting.accepted_content_types_for("documents").map { |content_type| ".#{content_type}" }.join(",") end def documentable_humanized_accepted_content_types(documentable_class) - documentable_class.accepted_content_types - .collect{ |content_type| content_type.split("/").last } - .join(", ") + Setting.accepted_content_types_for("documents").join(", ") end def documentables_note(documentable) diff --git a/app/helpers/imageables_helper.rb b/app/helpers/imageables_helper.rb index 1a85362d8..7c8d87298 100644 --- a/app/helpers/imageables_helper.rb +++ b/app/helpers/imageables_helper.rb @@ -9,7 +9,7 @@ module ImageablesHelper end def imageable_max_file_size - bytes_to_megabytes(Image::MAX_IMAGE_SIZE) + bytes_to_megabytes(Setting["uploads.images.max_size"].to_i.megabytes) end def bytes_to_megabytes(bytes) @@ -17,19 +17,21 @@ module ImageablesHelper end def imageable_accepted_content_types - Image::ACCEPTED_CONTENT_TYPE + Setting["uploads.images.content_types"]&.split(" ") || [ "image/jpeg" ] end def imageable_accepted_content_types_extensions - Image::ACCEPTED_CONTENT_TYPE - .collect{ |content_type| ".#{content_type.split("/").last}" } - .join(",") + Setting.accepted_content_types_for("images").map do |content_type| + if content_type == "jpg" + ".jpg,.jpeg" + else + ".#{content_type}" + end + end.join(",") end def imageable_humanized_accepted_content_types - Image::ACCEPTED_CONTENT_TYPE - .collect{ |content_type| content_type.split("/").last } - .join(", ") + Setting.accepted_content_types_for("images").join(", ") end def imageables_note(_imageable) diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 2eaf801c1..55634cf4b 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -13,9 +13,6 @@ class Budget include Imageable include Mappable include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] acts_as_votable acts_as_paranoid column: :hidden_at diff --git a/app/models/concerns/documentable.rb b/app/models/concerns/documentable.rb index 36e91ece7..9f49d9af9 100644 --- a/app/models/concerns/documentable.rb +++ b/app/models/concerns/documentable.rb @@ -7,16 +7,17 @@ module Documentable end module ClassMethods - attr_reader :max_documents_allowed, :max_file_size, :accepted_content_types - - private - - def documentable(options = {}) - @max_documents_allowed = options[:max_documents_allowed] - @max_file_size = options[:max_file_size] - @accepted_content_types = options[:accepted_content_types] + def max_documents_allowed + Setting["uploads.documents.max_amount"].to_i end + def max_file_size + Setting["uploads.documents.max_size"].to_i.megabytes + end + + def accepted_content_types + Setting["uploads.documents.content_types"]&.split(" ") || [ "application/pdf" ] + end end end diff --git a/app/models/dashboard/action.rb b/app/models/dashboard/action.rb index afe61c075..4dea153a7 100644 --- a/app/models/dashboard/action.rb +++ b/app/models/dashboard/action.rb @@ -1,13 +1,5 @@ class Dashboard::Action < ApplicationRecord include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf", - "image/jpeg", - "image/jpg", - "image/png", - "application/zip" ] - include Linkable acts_as_paranoid column: :hidden_at diff --git a/app/models/image.rb b/app/models/image.rb index eefcd3926..6929f5f96 100644 --- a/app/models/image.rb +++ b/app/models/image.rb @@ -2,12 +2,11 @@ class Image < ApplicationRecord include ImagesHelper include ImageablesHelper - TITLE_LENGTH_RANGE = 4..80 - MIN_SIZE = 475 - MAX_IMAGE_SIZE = 1.megabyte - ACCEPTED_CONTENT_TYPE = %w(image/jpeg image/jpg).freeze - - has_attached_file :attachment, styles: { large: "x#{MIN_SIZE}", medium: "300x300#", thumb: "140x245#" }, + has_attached_file :attachment, styles: { + large: "x#{Setting["uploads.images.min_height"]}", + medium: "300x300#", + thumb: "140x245#" + }, url: "/system/:class/:prefix/:style/:hash.:extension", hash_data: ":class/:style", use_timestamp: false, @@ -23,7 +22,8 @@ class Image < ApplicationRecord validate :attachment_presence validate :validate_attachment_content_type, if: -> { attachment.present? } validate :validate_attachment_size, if: -> { attachment.present? } - validates :title, presence: true, length: { in: TITLE_LENGTH_RANGE } + validates :title, presence: true + validate :validate_title_length validates :user_id, presence: true validates :imageable_id, presence: true, if: -> { persisted? } validates :imageable_type, presence: true, if: -> { persisted? } @@ -71,20 +71,38 @@ class Image < ApplicationRecord return true if imageable_class == Widget::Card dimensions = Paperclip::Geometry.from_file(attachment.queued_for_write[:original].path) - errors.add(:attachment, :min_image_width, required_min_width: MIN_SIZE) if dimensions.width < MIN_SIZE - errors.add(:attachment, :min_image_height, required_min_height: MIN_SIZE) if dimensions.height < MIN_SIZE + min_width = Setting["uploads.images.min_width"].to_i + min_height = Setting["uploads.images.min_height"].to_i + errors.add(:attachment, :min_image_width, required_min_width: min_width) if dimensions.width < min_width + errors.add(:attachment, :min_image_height, required_min_height: min_height) if dimensions.height < min_height end end def validate_attachment_size if imageable_class && - attachment_file_size > 1.megabytes + attachment_file_size > Setting["uploads.images.max_size"].to_i.megabytes errors.add(:attachment, I18n.t("images.errors.messages.in_between", min: "0 Bytes", max: "#{imageable_max_file_size} MB")) end end + def validate_title_length + if title.present? + + title_min_length = Setting["uploads.images.title.min_length"].to_i + title_max_length = Setting["uploads.images.title.max_length"].to_i + + if title.size < title_min_length + errors.add(:title, I18n.t("errors.messages.too_short", count: title_min_length)) + end + + if title.size > title_max_length + errors.add(:title, I18n.t("errors.messages.too_long", count: title_max_length)) + end + end + end + def validate_attachment_content_type if imageable_class && !attachment_of_valid_content_type? message = I18n.t("images.errors.messages.wrong_content_type", diff --git a/app/models/legislation/process.rb b/app/models/legislation/process.rb index 3d2c72c27..3f91ea27a 100644 --- a/app/models/legislation/process.rb +++ b/app/models/legislation/process.rb @@ -4,9 +4,6 @@ class Legislation::Process < ApplicationRecord include Milestoneable include Imageable include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] acts_as_paranoid column: :hidden_at acts_as_taggable_on :customs diff --git a/app/models/legislation/proposal.rb b/app/models/legislation/proposal.rb index d6fdf5eb3..879eeb27d 100644 --- a/app/models/legislation/proposal.rb +++ b/app/models/legislation/proposal.rb @@ -14,9 +14,6 @@ class Legislation::Proposal < ApplicationRecord include Imageable include Randomizable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] accepts_nested_attributes_for :documents, allow_destroy: true acts_as_votable diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 69d46149a..52f8ccb29 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -1,9 +1,6 @@ class Milestone < ApplicationRecord include Imageable include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] translates :title, :description, touch: true include Globalizable diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb index cde5e00d1..aab7acb59 100644 --- a/app/models/poll/question/answer.rb +++ b/app/models/poll/question/answer.rb @@ -6,9 +6,6 @@ class Poll::Question::Answer < ApplicationRecord translates :description, touch: true include Globalizable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] accepts_nested_attributes_for :documents, allow_destroy: true belongs_to :question, class_name: "Poll::Question", foreign_key: "question_id" diff --git a/app/models/proposal.rb b/app/models/proposal.rb index deb829058..3b4fbd0e2 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -15,9 +15,6 @@ class Proposal < ApplicationRecord include Mappable include Notifiable include Documentable - documentable max_documents_allowed: 3, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] include EmbedVideosHelper include Relationable include Milestoneable diff --git a/app/models/setting.rb b/app/models/setting.rb index 12162db4e..afc363502 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -52,6 +52,11 @@ class Setting < ApplicationRecord setting.destroy if setting.present? end + def accepted_content_types_for(group) + mime_content_types = Setting["uploads.#{group}.content_types"]&.split(" ") || [] + Setting.mime_types[group].select { |_, content_type| mime_content_types.include?(content_type) }.keys + end + def mime_types { "images" => { diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 240313c32..87670d676 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -115,6 +115,8 @@ ignore_missing: - "activerecord.errors.models.direct_message.*" - "errors.messages.blank" - "errors.messages.taken" + - "errors.messages.too_short" + - "errors.messages.too_long" - "devise.failure.invalid" - "devise.registrations.destroyed" - "devise.password_expired.*" diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index df764b7ca..fde1615cd 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -148,6 +148,24 @@ describe Setting do end end + describe ".accepted_content_types_for" do + it "returns the formats accepted according to the setting value" do + Setting["uploads.images.content_types"] = "image/jpeg image/gif" + Setting["uploads.documents.content_types"] = "application/pdf application/msword" + + expect(Setting.accepted_content_types_for("images")).to eq ["jpg", "gif"] + expect(Setting.accepted_content_types_for("documents")).to eq ["pdf", "doc"] + end + + it "returns empty array if setting does't exist" do + Setting.remove("uploads.images.content_types") + Setting.remove("uploads.documents.content_types") + + expect(Setting.accepted_content_types_for("images")).to be_empty + expect(Setting.accepted_content_types_for("documents")).to be_empty + end + end + describe ".add_new_settings" do context "default settings with strings" do before do diff --git a/spec/shared/models/image_validations.rb b/spec/shared/models/image_validations.rb index 691d73bae..c2b02a636 100644 --- a/spec/shared/models/image_validations.rb +++ b/spec/shared/models/image_validations.rb @@ -41,7 +41,8 @@ shared_examples "image validations" do |imageable_factory| end it "is not valid for attachments larger than imageable max_file_size definition" do - allow(image).to receive(:attachment_file_size).and_return(Image::MAX_IMAGE_SIZE + 1.byte) + larger_size = Setting["uploads.images.max_size"].to_i.megabytes + 1.byte + allow(image).to receive(:attachment_file_size).and_return(larger_size) expect(image).not_to be_valid expect(image.errors[:attachment]).to include "must be in between 0 Bytes and 1 MB" @@ -67,4 +68,4 @@ shared_examples "image validations" do |imageable_factory| expect(image).not_to be_valid end -end \ No newline at end of file +end