diff --git a/CHANGELOG.md b/CHANGELOG.md index b73064f35..eea1b9597 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Features: - Added GraphQL types for custom fields in the API - Adds parsed information about custom fields in the Proposals export - Adds parsed information bout private custom fields when admins exports private data + - Adds a maintenance menu with tools to remove old private data v0.11 ------ diff --git a/Gemfile.lock b/Gemfile.lock index 510d49f94..8e942e19c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - decidim-decidim_awesome (0.11.0) + decidim-decidim_awesome (0.11.1) decidim-admin (>= 0.28.0, < 0.29) decidim-core (>= 0.28.0, < 0.29) deface (>= 1.5) @@ -95,7 +95,7 @@ GEM brakeman (5.4.1) browser (2.7.1) builder (3.3.0) - bullet (7.1.6) + bullet (7.2.0) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) byebug (11.1.3) @@ -141,55 +141,55 @@ GEM date_validator (0.12.0) activemodel (>= 3) activesupport (>= 3) - decidim (0.28.1) - decidim-accountability (= 0.28.1) - decidim-admin (= 0.28.1) - decidim-api (= 0.28.1) - decidim-assemblies (= 0.28.1) - decidim-blogs (= 0.28.1) - decidim-budgets (= 0.28.1) - decidim-comments (= 0.28.1) - decidim-core (= 0.28.1) - decidim-debates (= 0.28.1) - decidim-forms (= 0.28.1) - decidim-generators (= 0.28.1) - decidim-meetings (= 0.28.1) - decidim-pages (= 0.28.1) - decidim-participatory_processes (= 0.28.1) - decidim-proposals (= 0.28.1) - decidim-sortitions (= 0.28.1) - decidim-surveys (= 0.28.1) - decidim-system (= 0.28.1) - decidim-templates (= 0.28.1) - decidim-verifications (= 0.28.1) - decidim-accountability (0.28.1) - decidim-comments (= 0.28.1) - decidim-core (= 0.28.1) - decidim-admin (0.28.1) + decidim (0.28.2) + decidim-accountability (= 0.28.2) + decidim-admin (= 0.28.2) + decidim-api (= 0.28.2) + decidim-assemblies (= 0.28.2) + decidim-blogs (= 0.28.2) + decidim-budgets (= 0.28.2) + decidim-comments (= 0.28.2) + decidim-core (= 0.28.2) + decidim-debates (= 0.28.2) + decidim-forms (= 0.28.2) + decidim-generators (= 0.28.2) + decidim-meetings (= 0.28.2) + decidim-pages (= 0.28.2) + decidim-participatory_processes (= 0.28.2) + decidim-proposals (= 0.28.2) + decidim-sortitions (= 0.28.2) + decidim-surveys (= 0.28.2) + decidim-system (= 0.28.2) + decidim-templates (= 0.28.2) + decidim-verifications (= 0.28.2) + decidim-accountability (0.28.2) + decidim-comments (= 0.28.2) + decidim-core (= 0.28.2) + decidim-admin (0.28.2) active_link_to (~> 1.0) - decidim-core (= 0.28.1) + decidim-core (= 0.28.2) devise (~> 4.7) devise-i18n (~> 1.2) devise_invitable (~> 2.0, >= 2.0.9) - decidim-api (0.28.1) + decidim-api (0.28.2) commonmarker (~> 0.23.0, >= 0.23.9) - decidim-core (= 0.28.1) + decidim-core (= 0.28.2) graphql (~> 2.0.0) graphql-docs (~> 3.0.1) rack-cors (~> 1.0) - decidim-assemblies (0.28.1) - decidim-core (= 0.28.1) - decidim-blogs (0.28.1) - decidim-admin (= 0.28.1) - decidim-comments (= 0.28.1) - decidim-core (= 0.28.1) - decidim-budgets (0.28.1) - decidim-comments (= 0.28.1) - decidim-core (= 0.28.1) - decidim-comments (0.28.1) - decidim-core (= 0.28.1) + decidim-assemblies (0.28.2) + decidim-core (= 0.28.2) + decidim-blogs (0.28.2) + decidim-admin (= 0.28.2) + decidim-comments (= 0.28.2) + decidim-core (= 0.28.2) + decidim-budgets (0.28.2) + decidim-comments (= 0.28.2) + decidim-core (= 0.28.2) + decidim-comments (0.28.2) + decidim-core (= 0.28.2) redcarpet (~> 3.5, >= 3.5.1) - decidim-core (0.28.1) + decidim-core (0.28.2) active_link_to (~> 1.0) acts_as_list (~> 1.0) batch-loader (~> 1.2) @@ -239,14 +239,14 @@ GEM valid_email2 (~> 4.0) web-push (~> 3.0) wisper (~> 2.0) - decidim-debates (0.28.1) - decidim-comments (= 0.28.1) - decidim-core (= 0.28.1) - decidim-dev (0.28.1) + decidim-debates (0.28.2) + decidim-comments (= 0.28.2) + decidim-core (= 0.28.2) + decidim-dev (0.28.2) bullet (~> 7.0) byebug (~> 11.0) capybara (~> 3.39) - decidim (= 0.28.1) + decidim (= 0.28.2) erb_lint (~> 0.4.0) factory_bot_rails (~> 6.2) faker (~> 3.2) @@ -271,44 +271,44 @@ GEM w3c_rspec_validators (~> 0.3.0) webmock (~> 3.18) wisper-rspec (~> 1.0) - decidim-forms (0.28.1) - decidim-core (= 0.28.1) + decidim-forms (0.28.2) + decidim-core (= 0.28.2) wicked_pdf (~> 2.1) wkhtmltopdf-binary (~> 0.12) - decidim-generators (0.28.1) - decidim-core (= 0.28.1) - decidim-meetings (0.28.1) - decidim-core (= 0.28.1) - decidim-forms (= 0.28.1) + decidim-generators (0.28.2) + decidim-core (= 0.28.2) + decidim-meetings (0.28.2) + decidim-core (= 0.28.2) + decidim-forms (= 0.28.2) icalendar (~> 2.5) - decidim-pages (0.28.1) - decidim-core (= 0.28.1) - decidim-participatory_processes (0.28.1) - decidim-core (= 0.28.1) - decidim-proposals (0.28.1) - decidim-comments (= 0.28.1) - decidim-core (= 0.28.1) + decidim-pages (0.28.2) + decidim-core (= 0.28.2) + decidim-participatory_processes (0.28.2) + decidim-core (= 0.28.2) + decidim-proposals (0.28.2) + decidim-comments (= 0.28.2) + decidim-core (= 0.28.2) doc2text (~> 0.4.6) redcarpet (~> 3.5, >= 3.5.1) - decidim-sortitions (0.28.1) - decidim-admin (= 0.28.1) - decidim-comments (= 0.28.1) - decidim-core (= 0.28.1) - decidim-proposals (= 0.28.1) - decidim-surveys (0.28.1) - decidim-core (= 0.28.1) - decidim-forms (= 0.28.1) - decidim-system (0.28.1) + decidim-sortitions (0.28.2) + decidim-admin (= 0.28.2) + decidim-comments (= 0.28.2) + decidim-core (= 0.28.2) + decidim-proposals (= 0.28.2) + decidim-surveys (0.28.2) + decidim-core (= 0.28.2) + decidim-forms (= 0.28.2) + decidim-system (0.28.2) active_link_to (~> 1.0) - decidim-core (= 0.28.1) + decidim-core (= 0.28.2) devise (~> 4.7) devise-i18n (~> 1.2) devise_invitable (~> 2.0, >= 2.0.9) - decidim-templates (0.28.1) - decidim-core (= 0.28.1) - decidim-forms (= 0.28.1) - decidim-verifications (0.28.1) - decidim-core (= 0.28.1) + decidim-templates (0.28.2) + decidim-core (= 0.28.2) + decidim-forms (= 0.28.2) + decidim-verifications (0.28.2) + decidim-core (= 0.28.2) declarative-builder (0.1.0) declarative-option (< 0.2.0) declarative-option (0.1.0) @@ -335,7 +335,7 @@ GEM doc2text (0.4.7) nokogiri (>= 1.13.2, < 1.17.0) rubyzip (~> 2.3.0) - docile (1.4.0) + docile (1.4.1) doorkeeper (5.7.1) railties (>= 5) doorkeeper-i18n (4.0.1) @@ -358,19 +358,14 @@ GEM factory_bot_rails (6.4.3) factory_bot (~> 6.4) railties (>= 5.0.0) - faker (3.4.1) + faker (3.4.2) i18n (>= 1.8.11, < 2) faraday (2.10.0) faraday-net_http (>= 2.0, < 3.2) logger faraday-net_http (3.1.0) net-http - ffi (1.17.0-aarch64-linux-gnu) - ffi (1.17.0-arm-linux-gnu) - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86-linux-gnu) - ffi (1.17.0-x86_64-darwin) - ffi (1.17.0-x86_64-linux-gnu) + ffi (1.17.0) file_validators (3.0.0) activemodel (>= 3.2) mime-types (>= 1.0) @@ -405,7 +400,8 @@ GEM sass (~> 3.4) hashdiff (1.1.0) hashie (5.0.0) - highline (3.0.1) + highline (3.1.0) + reline html-pipeline (2.14.3) activesupport (>= 2) nokogiri (>= 1.4) @@ -422,14 +418,15 @@ GEM rails-i18n rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) - icalendar (2.10.1) + icalendar (2.10.2) ice_cube (~> 0.16) - ice_cube (0.16.4) + ice_cube (0.17.0) image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) invisible_captcha (0.13.0) rails (>= 3.2.0) + io-console (0.7.2) json (2.7.2) jwt (2.8.2) base64 @@ -491,16 +488,6 @@ GEM net-smtp (0.3.4) net-protocol nio4r (2.7.3) - nokogiri (1.16.6-aarch64-linux) - racc (~> 1.4) - nokogiri (1.16.6-arm-linux) - racc (~> 1.4) - nokogiri (1.16.6-arm64-darwin) - racc (~> 1.4) - nokogiri (1.16.6-x86-linux) - racc (~> 1.4) - nokogiri (1.16.6-x86_64-darwin) - racc (~> 1.4) nokogiri (1.16.6-x86_64-linux) racc (~> 1.4) oauth (1.1.0) @@ -547,7 +534,7 @@ GEM parallel (1.25.1) parallel_tests (4.7.1) parallel - parser (3.3.3.0) + parser (3.3.4.0) ast (~> 2.4.1) racc pg (1.4.6) @@ -628,6 +615,8 @@ GEM redcarpet (3.6.0) redis (4.8.1) regexp_parser (2.9.2) + reline (0.5.9) + io-console (~> 0.5) request_store (1.5.1) rack (>= 1.4) responders (3.1.1) @@ -678,25 +667,18 @@ GEM unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.31.3) parser (>= 3.3.1.0) - rubocop-capybara (2.21.0) - rubocop (~> 1.41) - rubocop-factory_bot (2.26.0) + rubocop-capybara (2.18.0) rubocop (~> 1.41) rubocop-faker (1.1.0) faker (>= 2.12.0) rubocop (>= 0.82.0) - rubocop-rails (2.25.1) + rubocop-rails (2.19.1) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rspec (2.31.0) - rubocop (~> 1.40) + rubocop-rspec (2.20.0) + rubocop (~> 1.33) rubocop-capybara (~> 2.17) - rubocop-factory_bot (~> 2.22) - rubocop-rspec_rails (~> 2.28) - rubocop-rspec_rails (2.29.0) - rubocop (~> 1.40) ruby-progressbar (1.13.0) ruby-vips (2.2.1) ffi (~> 1.12) @@ -711,7 +693,7 @@ GEM rb-inotify (~> 0.9, >= 0.9.7) sassc (2.4.0) ffi (~> 1.9) - selenium-webdriver (4.22.0) + selenium-webdriver (4.23.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -789,7 +771,7 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - websocket (1.2.10) + websocket (1.2.11) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -803,20 +785,16 @@ GEM zeitwerk (2.6.16) PLATFORMS - aarch64-linux - arm-linux - arm64-darwin - x86-linux - x86_64-darwin x86_64-linux DEPENDENCIES bootsnap (~> 1.4) brakeman (~> 5.4) byebug (~> 11.0) - decidim (= 0.28.1) + decidim (= 0.28.2) decidim-decidim_awesome! - decidim-dev (= 0.28.1) + decidim-dev (= 0.28.2) + decidim-templates (= 0.28.2) letter_opener_web (~> 2.0) listen (~> 3.1) net-imap (~> 0.2.3) diff --git a/README.md b/README.md index 8ca90dc01..26c1f9a45 100644 --- a/README.md +++ b/README.md @@ -368,6 +368,17 @@ This option is disable by default, must be enabled in the component's configurat ![Limiting amendments](examples/limit_amendments.png) +#### 18. Maintenance tools + +The awesome admin provides with some maintenance tools (more to come in the future); + +##### 18.1 Old private data removal + +These tools are designed to help remove old data as required by laws such as GDPR, particularly in relation to private custom fields. +This menu will show if there's any data older than 6 months (configurable) and will let admins remove it component by component. + +![Private data](examples/private_data.png) + #### To be continued... We're not done! Please check the [issues](/decidim-ice/decidim-module-decidim_awesome/issues) (and participate) to see what's on our mind diff --git a/app/controllers/concerns/decidim/decidim_awesome/admin/maintenance_context.rb b/app/controllers/concerns/decidim/decidim_awesome/admin/maintenance_context.rb new file mode 100644 index 000000000..e2fd2abb2 --- /dev/null +++ b/app/controllers/concerns/decidim/decidim_awesome/admin/maintenance_context.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + module Admin + module MaintenanceContext + extend ActiveSupport::Concern + + included do + layout "decidim/decidim_awesome/admin/maintenance" + helper_method :current_view, :available_views, :present_private_data + + private + + def present_private_data(model) + PrivateDataPresenter.new(model) + end + + def current_view + return params[:id] if available_views.include?(params[:id]) + + available_views.keys.first + end + + def available_views + { + "private_data" => { + title: I18n.t("private_data", scope: "decidim.decidim_awesome.admin.menu.maintenance"), + icon: "spy-line", + path: decidim_admin_decidim_awesome.maintenance_path("private_data") + }, + "checks" => { + title: I18n.t("checks", scope: "decidim.decidim_awesome.admin.menu.maintenance"), + icon: "pulse", + path: decidim_admin_decidim_awesome.checks_maintenance_index_path + } + } + end + end + end + end + end +end diff --git a/app/controllers/decidim/decidim_awesome/admin/application_controller.rb b/app/controllers/decidim/decidim_awesome/admin/application_controller.rb index 63ad2672f..f0c578e54 100644 --- a/app/controllers/decidim/decidim_awesome/admin/application_controller.rb +++ b/app/controllers/decidim/decidim_awesome/admin/application_controller.rb @@ -9,6 +9,8 @@ module Admin # Note that it inherits from `Decidim::Admin::Components::BaseController`, which # override its layout and provide all kinds of useful methods. class ApplicationController < Decidim::Admin::ApplicationController + layout "decidim/decidim_awesome/admin/application" + def permission_class_chain [::Decidim::DecidimAwesome::Admin::Permissions] + super end diff --git a/app/controllers/decidim/decidim_awesome/admin/checks_controller.rb b/app/controllers/decidim/decidim_awesome/admin/checks_controller.rb index cbd4ec32e..47f01a0f4 100644 --- a/app/controllers/decidim/decidim_awesome/admin/checks_controller.rb +++ b/app/controllers/decidim/decidim_awesome/admin/checks_controller.rb @@ -8,17 +8,17 @@ module Admin # System compatibility analyzer class ChecksController < DecidimAwesome::Admin::ApplicationController include NeedsAwesomeConfig + include MaintenanceContext + helper ConfigConstraintsHelpers helper SystemCheckerHelpers - layout "decidim/decidim_awesome/admin/application" - helper_method :head, :admin_head, :head_addons, :admin_addons def migrate_images Decidim::DecidimAwesome::MigrateLegacyImagesJob.perform_later(current_organization.id) flash[:notice] = I18n.t("image_migrations_started", scope: "decidim.decidim_awesome.admin.checks.index") - redirect_to checks_path + redirect_to checks_maintenance_index_path end private @@ -58,6 +58,10 @@ def render_template(partial) rescue ActionView::Template::Error => e flash.now[:alert] = "Partial [#{partial}] has thrown an error: #{e.message}" end + + def current_view + "checks" + end end end end diff --git a/app/controllers/decidim/decidim_awesome/admin/config_controller.rb b/app/controllers/decidim/decidim_awesome/admin/config_controller.rb index 3760dff1a..fe4dfea6f 100644 --- a/app/controllers/decidim/decidim_awesome/admin/config_controller.rb +++ b/app/controllers/decidim/decidim_awesome/admin/config_controller.rb @@ -9,8 +9,6 @@ class ConfigController < DecidimAwesome::Admin::ApplicationController include ConfigConstraintsHelpers helper ConfigConstraintsHelpers - layout "decidim/decidim_awesome/admin/application" - helper_method :constraints_for, :users_for, :config_var before_action do enforce_permission_to :edit_config, configs @@ -19,7 +17,7 @@ class ConfigController < DecidimAwesome::Admin::ApplicationController def show @form = form(ConfigForm).from_params(organization_awesome_config) - redirect_to decidim_admin_decidim_awesome.checks_path unless config_var + redirect_to decidim_admin_decidim_awesome.checks_maintenance_index_path unless config_var end def update diff --git a/app/controllers/decidim/decidim_awesome/admin/custom_redirects_controller.rb b/app/controllers/decidim/decidim_awesome/admin/custom_redirects_controller.rb index bfa23a2d5..f0856897e 100644 --- a/app/controllers/decidim/decidim_awesome/admin/custom_redirects_controller.rb +++ b/app/controllers/decidim/decidim_awesome/admin/custom_redirects_controller.rb @@ -8,8 +8,6 @@ class CustomRedirectsController < DecidimAwesome::Admin::ApplicationController include NeedsAwesomeConfig include ConfigConstraintsHelpers - layout "decidim/decidim_awesome/admin/application" - before_action do enforce_permission_to :edit_config, :menu end diff --git a/app/controllers/decidim/decidim_awesome/admin/maintenance_controller.rb b/app/controllers/decidim/decidim_awesome/admin/maintenance_controller.rb new file mode 100644 index 000000000..b85947cff --- /dev/null +++ b/app/controllers/decidim/decidim_awesome/admin/maintenance_controller.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require "decidim/decidim_awesome/version" + +module Decidim + module DecidimAwesome + module Admin + # System compatibility analyzer + class MaintenanceController < DecidimAwesome::Admin::ApplicationController + include NeedsAwesomeConfig + include MaintenanceContext + include Decidim::Admin::Filterable + include ActionView::Helpers::DateHelper + + helper ConfigConstraintsHelpers + helper_method :collection, :resource, :present, :time_ago + + before_action do + enforce_permission_to :edit_config, :private_data, private_data: + end + + def show + respond_to do |format| + format.json do + render json: private_data_finder.for(params[:resources].to_s.split(",")).map { |resource| present(resource) } + end + format.all do + render :show + end + end + end + + def destroy_private_data + if private_data && private_data.total.to_i.positive? + Decidim::ActionLogger.log("destroy_private_data", current_user, resource, nil, count: private_data.total) + + Lock.new(current_organization).get!(resource) + DestroyPrivateDataJob.set(wait: 1.second).perform_later(resource) + end + redirect_to decidim_admin_decidim_awesome.maintenance_path("private_data"), + notice: I18n.t("destroying_private_data", scope: "decidim.decidim_awesome.admin.maintenance.private_data", title: present_private_data(resource).name) + end + + private + + def resource + @resource ||= Component.find_by(id: params[:resource_id]) + end + + def private_data + @private_data ||= present_private_data(resource) if resource + end + + def collection + filtered_collection + end + + def base_query + private_data_finder.query + end + + def present(resource) + present_private_data(resource) + end + + def private_data_finder + @private_data ||= PrivateDataFinder.new + end + + def time_ago + @time_ago ||= time_ago_in_words(Time.current - Decidim::DecidimAwesome.private_data_expiration_time) + end + end + end + end +end diff --git a/app/controllers/decidim/decidim_awesome/admin/menu_hacks_controller.rb b/app/controllers/decidim/decidim_awesome/admin/menu_hacks_controller.rb index 49504b19e..2437601fc 100644 --- a/app/controllers/decidim/decidim_awesome/admin/menu_hacks_controller.rb +++ b/app/controllers/decidim/decidim_awesome/admin/menu_hacks_controller.rb @@ -8,8 +8,6 @@ class MenuHacksController < DecidimAwesome::Admin::ApplicationController include NeedsAwesomeConfig include ConfigConstraintsHelpers - layout "decidim/decidim_awesome/admin/application" - helper ConfigConstraintsHelpers helper_method :current_items, :visibility_options, :target_options diff --git a/app/jobs/decidim/decidim_awesome/destroy_private_data_job.rb b/app/jobs/decidim/decidim_awesome/destroy_private_data_job.rb new file mode 100644 index 000000000..f63afefdd --- /dev/null +++ b/app/jobs/decidim/decidim_awesome/destroy_private_data_job.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + class DestroyPrivateDataJob < ApplicationJob + queue_as :default + + # Destroys private data associated with the resource + def perform(resource) + extra_fields = Decidim::DecidimAwesome::ProposalExtraField.where( + proposal: Decidim::Proposals::Proposal.where(component: resource) + ).where("private_body_updated_at < ?", DecidimAwesome.private_data_expiration_time.ago) + + extra_fields.find_each do |extra_field| + extra_field.update(private_body: nil) + end + + Lock.new(resource.organization).release!(resource) + end + end + end +end diff --git a/app/models/decidim/decidim_awesome/proposal_extra_field.rb b/app/models/decidim/decidim_awesome/proposal_extra_field.rb index 0d6fc415c..e174d12e7 100644 --- a/app/models/decidim/decidim_awesome/proposal_extra_field.rb +++ b/app/models/decidim/decidim_awesome/proposal_extra_field.rb @@ -14,12 +14,27 @@ class ProposalExtraField < ApplicationRecord encrypt_attribute :private_body, type: :string + after_initialize :store_private_body + before_save :update_private_body_updated_at + # validate not more than one extra field can be associated to a proposal # validates :proposal, uniqueness: true validate :no_more_than_one_extra_field private + def store_private_body + @initial_private_body = private_body + end + + # using private_body_changed? does not sufice as the encrypted value is always updated on saving + def update_private_body_updated_at + if private_body != @initial_private_body + self.private_body_updated_at = Time.current + @initial_private_body = private_body + end + end + def no_more_than_one_extra_field return unless ProposalExtraField.where(proposal:).where.not(id:).exists? diff --git a/app/packs/src/decidim/decidim_awesome/admin/constraint_form_events.js b/app/packs/src/decidim/decidim_awesome/admin/constraint_form_events.js index aaee8db1f..46c1dd423 100644 --- a/app/packs/src/decidim/decidim_awesome/admin/constraint_form_events.js +++ b/app/packs/src/decidim/decidim_awesome/admin/constraint_form_events.js @@ -85,7 +85,7 @@ const initializeDialog = (dialog) => { }; document.addEventListener("DOMContentLoaded", () => { - document.querySelectorAll("[data-dialog]").forEach((dialog) => { + document.querySelectorAll("[data-constraint][data-dialog]").forEach((dialog) => { initializeDialog(dialog); }); }); diff --git a/app/packs/stylesheets/decidim/decidim_awesome/awesome_admin_global.scss b/app/packs/stylesheets/decidim/decidim_awesome/awesome_admin_global.scss index 8f879e0db..e4d1aa9f3 100644 --- a/app/packs/stylesheets/decidim/decidim_awesome/awesome_admin_global.scss +++ b/app/packs/stylesheets/decidim/decidim_awesome/awesome_admin_global.scss @@ -1,3 +1,19 @@ @import "stylesheets/decidim/decidim_awesome/shared/spinner"; @import "stylesheets/decidim/decidim_awesome/admin/intergram_fixes"; @import "stylesheets/decidim/decidim_awesome/forms/custom_fields"; + +.component__show { + &_private_data { + &-grid { + @apply bg-background grid-cols-2 gap-4 my-4 p-2; + + .date_info { + @apply italic text-gray text-sm mt-4; + + a { + @apply not-italic text-secondary; + } + } + } + } +} diff --git a/app/packs/stylesheets/decidim/decidim_awesome/shared/spinner.scss b/app/packs/stylesheets/decidim/decidim_awesome/shared/spinner.scss index e7d9386b6..861e349bc 100644 --- a/app/packs/stylesheets/decidim/decidim_awesome/shared/spinner.scss +++ b/app/packs/stylesheets/decidim/decidim_awesome/shared/spinner.scss @@ -5,4 +5,22 @@ &::before { @apply content-[""] ml-[50%] md:ml-[calc(50%-0.75rem)] block w-6 h-6 rounded-full animate-spin border-4 border-l-background border-y-background border-r-secondary z-20; } + + &.alert { + &::before { + @apply border-r-alert; + } + } + + &.warning { + &::before { + @apply border-r-warning; + } + } + + &.primary { + &::before { + @apply border-r-primary; + } + } } diff --git a/app/permissions/decidim/decidim_awesome/admin/permissions.rb b/app/permissions/decidim/decidim_awesome/admin/permissions.rb index e0d446b61..30c691f8a 100644 --- a/app/permissions/decidim/decidim_awesome/admin/permissions.rb +++ b/app/permissions/decidim/decidim_awesome/admin/permissions.rb @@ -10,6 +10,7 @@ def permissions return permission_action if permission_action.scope != :admin return permission_action unless user return permission_action if user.read_attribute("admin").blank? + return permission_action unless permission_action.action == :edit_config if permission_action.subject == :admin_accountability && DecidimAwesome.admin_accountability.respond_to?(:include?) if global? @@ -17,7 +18,13 @@ def permissions else toggle_allow(DecidimAwesome.admin_accountability.include?(:participatory_space_roles)) end - elsif permission_action.action == :edit_config + elsif permission_action.subject == :private_data && config_enabled?(:proposal_private_custom_fields) + if private_data.present? + allow! if private_data.destroyable? + else + allow! + end + else toggle_allow(config_enabled?(*permission_action.subject)) end @@ -27,7 +34,11 @@ def permissions private def global? - context.fetch(:global) + context.fetch(:global, nil) + end + + def private_data + context.fetch(:private_data, nil) end end end diff --git a/app/presenters/decidim/decidim_awesome/admin_log/component_presenter_override.rb b/app/presenters/decidim/decidim_awesome/admin_log/component_presenter_override.rb new file mode 100644 index 000000000..c6a933ce1 --- /dev/null +++ b/app/presenters/decidim/decidim_awesome/admin_log/component_presenter_override.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + module AdminLog + module ComponentPresenterOverride + extend ActiveSupport::Concern + + included do + alias_method :decidim_original_action_string, :action_string + alias_method :decidim_original_i18n_params, :i18n_params + + def action_string + return "decidim.decidim_awesome.admin_log.component.#{action}" if action == "destroy_private_data" + + decidim_original_action_string + end + + def i18n_params + if action == "destroy_private_data" + decidim_original_i18n_params.merge({ count: action_log.extra["count"] }) + else + decidim_original_i18n_params + end + end + end + end + end + end +end diff --git a/app/presenters/decidim/decidim_awesome/private_data_presenter.rb b/app/presenters/decidim/decidim_awesome/private_data_presenter.rb new file mode 100644 index 000000000..a68d30364 --- /dev/null +++ b/app/presenters/decidim/decidim_awesome/private_data_presenter.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + class PrivateDataPresenter < SimpleDelegator + include Decidim::TranslatableAttributes + include ActionView::Helpers::DateHelper + include ActionView::Helpers::TagHelper + + def name + @name ||= "#{translated_attribute(participatory_space.title)} / #{translated_attribute(super)}" + end + + def path + @path ||= Decidim::EngineRouter.main_proxy(self).root_path + end + + def total + @total ||= Decidim::Proposals::Proposal.joins(:extra_fields) + .where(component: self) + .where.not(extra_fields: { private_body: nil }) + .count.to_s + end + + def last_date + @last_date ||= Decidim::Proposals::Proposal.joins(:extra_fields) + .where(component: self) + .where.not(extra_fields: { private_body: nil }) + .order(private_body_updated_at: :desc) + .first&.extra_fields&.private_body_updated_at + end + + def time_ago + I18n.t("decidim.decidim_awesome.admin.maintenance.private_data.time_ago", time: time_ago_in_words(last_date)) if last_date + end + + def destroyable? + return false unless last_date + + last_date < DecidimAwesome.private_data_expiration_time.ago + end + + def locked? + Decidim::DecidimAwesome::Lock.new(organization).locked?(__getobj__) + end + + def as_json(_options = nil) + { + id:, + name:, + path:, + total:, + last_date:, + time_ago:, + locked: locked?, + done: + } + end + + def done + return content_tag("span", "", class: "loading-spinner primary") if locked? + + return if destroyable? + return if last_date + + I18n.t("decidim.decidim_awesome.admin.maintenance.private_data.done") + end + end + end +end diff --git a/app/queries/decidim/decidim_awesome/private_data_finder.rb b/app/queries/decidim/decidim_awesome/private_data_finder.rb new file mode 100644 index 000000000..d2852f5b1 --- /dev/null +++ b/app/queries/decidim/decidim_awesome/private_data_finder.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + class PrivateDataFinder + def query + Component.where(id: proposals.where.not(extra_fields: { private_body: nil })) + end + + def proposals + Decidim::Proposals::Proposal.select(:decidim_component_id).joins(:extra_fields) + end + + def for(resources) + Component.where(id: proposals).where(id: resources) + end + end + end +end diff --git a/app/views/decidim/decidim_awesome/admin/checks/index.html.erb b/app/views/decidim/decidim_awesome/admin/checks/index.html.erb index 51c432944..979b75801 100644 --- a/app/views/decidim/decidim_awesome/admin/checks/index.html.erb +++ b/app/views/decidim/decidim_awesome/admin/checks/index.html.erb @@ -3,33 +3,29 @@ class="fill-success fill-alert" --> -
+ "><%= t ".decidim_version", version: decidim_version %> + <%= check decidim_version_valid? %> +
- "><%= t ".decidim_version", version: decidim_version %> - <%= check decidim_version_valid? %> -
-<%= t(".images_migrated") %> - <% if images_migrated? %> - <%= check true %>
- <% else %> - <%= check false %> -<%= t(".pending_image_migrations", total: pending_image_migrations).html_safe %>
- <%= link_to t(".start_image_migrations"), migrate_images_path, method: :post, class: "button" %> -<%= t(".description").html_safe %>
<%= t(".help_html", time_ago:) %> +
<%= t(".component") %> | +<%= t(".items_count") %> | +<%= t(".last_date") %> | +<%= t("decidim.decidim_awesome.admin.actions") %> | +
---|---|---|---|
<%= link_to item.name, item.path %> | +<%= item.total %> | +<%= item.time_ago %> | ++ <% if item.locked? %> + "> + <% elsif item.destroyable? %> + <%= link_to destroy_private_data_maintenance_path(:private_data, resource_id: item), method: :delete, class: "button button__primary button__xs tiny", data: { confirm: t(".confirm_delete") } do %> + <%= icon "delete-bin-line" %> + <%= t(".delete") %> + <% end %> + <% else %> + "><%= icon "forbid-line" %> + <% end %> + | +