Skip to content

Commit

Permalink
Feat: custom sort for processes (#596)
Browse files Browse the repository at this point in the history
* feat: add custom sort for processesdepending on new variable

* feat: update locales files

* feat: update seeds

* test: add controller tests for assemblies and processes

* chore: update i18n config for unused keys

* docs: update overrides

* refactor: update env variable after review
  • Loading branch information
Stef-Rousset authored Oct 9, 2024
1 parent 0f95315 commit c89380d
Show file tree
Hide file tree
Showing 12 changed files with 806 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .env-example
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,7 @@ RAILS_LOG_LEVEL=warn

# Automatically save AH metadata to user extended data
# Format : comma separated list of auhtorization handler names
# AUTO_EXPORT_AUTHORIZATIONS_DATA_TO_USER_DATA_ENABLED_FOR="authorization1,authorization2"
# AUTO_EXPORT_AUTHORIZATIONS_DATA_TO_USER_DATA_ENABLED_FOR="authorization1,authorization2"

# Sort participatory processes by date
SORT_PROCESSES_BY_DATE=false
9 changes: 9 additions & 0 deletions OVERLOADS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
* `app/cells/decidim/version_cell.rb`
This override the default `VersionCell` from `decidim-core`, by adding sanitization for `version_number` to prevent XSS attacks.

* `app/controllers/decidim/assemblies/assemblies_controller.rb`
This override the default `AssembliesController` from `decidim-assemblies`, by adding custom sort for assembly_participatory_processes

* `app/helpers/decidim/assemblies/assemblies_helper.rb`
This override the default `AssembliesHelpler` from `decidim-assemblies`, by adding custom html for sorted assembly_participatory_processes

* `app/controllers/decidim/participatory_processes/participatory_processes_controller.rb`
This override the default `ParticipatoryProcessesController` from `decidim-participatory_processes`, by adding custom sort for participatory_processes

## Proposal's draft (Decidim awesome overrides 0.26.7)
* `app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb`

Expand Down
106 changes: 106 additions & 0 deletions app/controllers/decidim/assemblies/assemblies_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# frozen_string_literal: true

module Decidim
module Assemblies
# A controller that holds the logic to show Assemblies in a public layout.
class AssembliesController < Decidim::Assemblies::ApplicationController
include ParticipatorySpaceContext
participatory_space_layout only: :show
include FilterResource

helper_method :parent_assemblies, :promoted_assemblies, :stats, :assembly_participatory_processes, :current_assemblies_settings

def index
enforce_permission_to :list, :assembly

respond_to do |format|
format.html do
raise ActionController::RoutingError, "Not Found" if published_assemblies.none?

render "index"
end

format.js do
raise ActionController::RoutingError, "Not Found" if published_assemblies.none?

render "index"
end

format.json do
render json: published_assemblies.query.includes(:children).where(parent: nil).collect { |assembly|
{
name: assembly.title[I18n.locale.to_s],
children: assembly.children.collect do |child|
{
name: child.title[I18n.locale.to_s],
children: child.children.collect { |child_of_child| { name: child_of_child.title[I18n.locale.to_s] } }
}
end
}
}
end
end
end

def show
enforce_permission_to :read, :assembly, assembly: current_participatory_space
end

private

def search_collection
Assembly.where(organization: current_organization).published.visible_for(current_user)
end

def default_filter_params
{
with_scope: nil,
with_area: nil,
type_id_eq: nil
}
end

def current_participatory_space
return unless params[:slug]

@current_participatory_space ||= OrganizationAssemblies.new(current_organization).query.where(slug: params[:slug]).or(
OrganizationAssemblies.new(current_organization).query.where(id: params[:slug])
).first!
end

def published_assemblies
@published_assemblies ||= OrganizationPublishedAssemblies.new(current_organization, current_user)
end

def promoted_assemblies
@promoted_assemblies ||= published_assemblies | PromotedAssemblies.new
end

def parent_assemblies
search.result.parent_assemblies.order(weight: :asc, promoted: :desc)
end

def stats
@stats ||= AssemblyStatsPresenter.new(assembly: current_participatory_space)
end

def assembly_participatory_processes
if Rails.application.secrets.dig(:decidim, :participatory_processes, :sort_by_date) == false
@assembly_participatory_processes ||= @current_participatory_space.linked_participatory_space_resources(:participatory_processes, "included_participatory_processes")
else
@assembly_participatory_processes = @current_participatory_space.linked_participatory_space_resources(:participatory_processes, "included_participatory_processes")
sorted_by_date = {
active: @assembly_participatory_processes.active_spaces.sort_by(&:end_date),
future: @assembly_participatory_processes.future_spaces.sort_by(&:start_date),
past: @assembly_participatory_processes.past_spaces.sort_by(&:end_date).reverse
}
@assembly_participatory_processes = sorted_by_date
end
end

def current_assemblies_settings
@current_assemblies_settings ||= Decidim::AssembliesSetting.find_or_create_by(decidim_organization_id: current_organization.id)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# frozen_string_literal: true

module Decidim
module ParticipatoryProcesses
# A controller that holds the logic to show ParticipatoryProcesses in a
# public layout.
class ParticipatoryProcessesController < Decidim::ParticipatoryProcesses::ApplicationController
include ParticipatorySpaceContext
participatory_space_layout only: [:show, :all_metrics]
include FilterResource

helper_method :collection,
:promoted_collection,
:participatory_processes,
:stats,
:metrics,
:participatory_process_group,
:default_date_filter,
:related_processes,
:linked_assemblies

def index
raise ActionController::RoutingError, "Not Found" if published_processes.none?

enforce_permission_to :list, :process
enforce_permission_to :list, :process_group
end

def show
enforce_permission_to :read, :process, process: current_participatory_space
end

def all_metrics
if current_participatory_space.show_statistics
enforce_permission_to :read, :process, process: current_participatory_space
else
render status: :not_found
end
end

private

def search_collection
ParticipatoryProcess.where(organization: current_organization).published.visible_for(current_user).includes(:area)
end

def default_filter_params
{
with_scope: nil,
with_area: nil,
with_type: nil,
with_date: default_date_filter
}
end

def organization_participatory_processes
@organization_participatory_processes ||= OrganizationParticipatoryProcesses.new(current_organization).query
end

def current_participatory_space
return unless params["slug"]

@current_participatory_space ||= organization_participatory_processes.where(slug: params["slug"]).or(
organization_participatory_processes.where(id: params["slug"])
).first!
end

def published_processes
@published_processes ||= OrganizationPublishedParticipatoryProcesses.new(current_organization, current_user)
end

def promoted_participatory_processes
@promoted_participatory_processes ||= published_processes | PromotedParticipatoryProcesses.new
end

def promoted_participatory_process_groups
@promoted_participatory_process_groups ||= OrganizationPromotedParticipatoryProcessGroups.new(current_organization)
end

def promoted_collection
@promoted_collection ||= promoted_participatory_processes.query + promoted_participatory_process_groups.query
end

def collection
@collection ||= participatory_processes + participatory_process_groups
end

def filtered_processes
search.result
end

def participatory_processes
@participatory_processes ||= filtered_processes.groupless.includes(attachments: :file_attachment)
return @participatory_processes if Rails.application.secrets.dig(:decidim, :participatory_processes, :sort_by_date) == false

custom_sort(search.with_date)
end

def participatory_process_groups
@participatory_process_groups ||= OrganizationParticipatoryProcessGroups.new(current_organization).query
.where(id: filtered_processes.grouped.group_ids)
end

def stats
@stats ||= ParticipatoryProcessStatsPresenter.new(participatory_process: current_participatory_space)
end

def metrics
@metrics ||= ParticipatoryProcessMetricChartsPresenter.new(participatory_process: current_participatory_space, view_context: view_context)
end

def participatory_process_group
@participatory_process_group ||= current_participatory_space.participatory_process_group
end

def default_date_filter
return "active" if published_processes.any?(&:active?)
return "upcoming" if published_processes.any?(&:upcoming?)
return "past" if published_processes.any?(&:past?)

"all"
end

def related_processes
@related_processes ||=
current_participatory_space
.linked_participatory_space_resources(:participatory_processes, "related_processes")
.published
.all
end

def linked_assemblies
@linked_assemblies ||= current_participatory_space.linked_participatory_space_resources(:assembly, "included_participatory_processes").public_spaces
end

def custom_sort(date)
case date
when "active"
@participatory_processes.sort_by(&:end_date)
when "past"
@participatory_processes.sort_by(&:end_date).reverse
when "upcoming"
@participatory_processes.sort_by(&:start_date)
when "all"
@participatory_processes = sort_all_processes
else
@participatory_processes
end
end

def sort_all_processes
actives = @participatory_processes.select(&:active?).sort_by(&:end_date)
pasts = @participatory_processes.select(&:past?).sort_by(&:end_date).reverse
upcomings = @participatory_processes.select(&:upcoming?).sort_by(&:start_date)
(actives + upcomings + pasts)
end
end
end
end
99 changes: 99 additions & 0 deletions app/helpers/decidim/assemblies/assemblies_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen_string_literal: true

module Decidim
module Assemblies
# Helpers related to the Assemblies layout.
module AssembliesHelper
include Decidim::ResourceHelper
include Decidim::AttachmentsHelper
include Decidim::IconHelper
include Decidim::WidgetUrlsHelper
include Decidim::SanitizeHelper
include Decidim::ResourceReferenceHelper
include Decidim::FiltersHelper
include FilterAssembliesHelper

# Public: Returns the characteristics of an assembly in a readable format like
# "title: close, no public, no transparent and is restricted to the members of the assembly"
def participatory_processes_for_assembly(assembly_participatory_processes)
if Rails.application.secrets.dig(:decidim, :participatory_processes, :sort_by_date) == true
sorted_participatory_processes_for_assembly(assembly_participatory_processes)
else
html = ""
html += %( <div class="section"> ).html_safe
html += %( <h4 class="section-heading">#{t("assemblies.show.related_participatory_processes", scope: "decidim")}</h4> ).html_safe
html += %( <div class="row small-up-1 medium-up-2 card-grid"> ).html_safe
assembly_participatory_processes.each do |assembly_participatory_process|
html += render partial: "decidim/participatory_processes/participatory_process", locals: { participatory_process: assembly_participatory_process }
end
html += %( </div> ).html_safe
html += %( </div> ).html_safe
html.html_safe
end
end

def sorted_participatory_processes_for_assembly(assembly_participatory_processes)
return if assembly_participatory_processes.values.all?(&:empty?)

html = ""
html += %( <div class="section"> ).html_safe
html += %( <h4 class="section-heading">#{t("assemblies.show.related_participatory_processes", scope: "decidim")} <span class="margin-right-1"></span> ).html_safe

assembly_participatory_processes.each do |type, processes|
next if processes.empty?

html += %( <a href="##{type}_assembly_participatory_processes" class="order-by__tab text-small">
#{t("assemblies.show.#{type}_assembly_participatory_processes_mini", scope: "decidim")}
(#{processes.count})</a> ).html_safe
end

html += %( </h4> ).html_safe
html += %( <div class="column"> ).html_safe

assembly_participatory_processes.each do |type, processes|
next if processes.empty?

html += %( <h5 id="#{type}_assembly_participatory_processes" class="section-heading">
#{t("assemblies.show.#{type}_assembly_participatory_processes", scope: "decidim")}</h5> ).html_safe
html += %( <div class="row small-up-1 medium-up-2 card-grid"> ).html_safe
processes.each do |process|
html += render partial: "decidim/participatory_processes/participatory_process", locals: { participatory_process: process }
end
html += %( </div> ).html_safe
end
html += %( </div> ).html_safe
html += %( </div> ).html_safe
html.html_safe
end

def assembly_features(assembly)
html = "".html_safe
html += "<strong>#{translated_attribute(assembly.title)}: </strong>".html_safe
html += t("assemblies.show.private_space", scope: "decidim").to_s.html_safe
html += ", #{t("assemblies.show.is_transparent.#{assembly.is_transparent}", scope: "decidim")}".html_safe if assembly.is_transparent?
html += " #{decidim_sanitize_editor translated_attribute(assembly.special_features)}".html_safe
html.html_safe
end

def social_handler_links(assembly)
html = "".html_safe
if Decidim::Assembly::SOCIAL_HANDLERS.any? { |h| assembly.try("#{h}_handler").present? }
html += "<div class='definition-data__item social_networks'>".html_safe
html += "<span class='definition-data__title'>#{t("assemblies.show.social_networks", scope: "decidim")}</span>".html_safe
Decidim::Assembly::SOCIAL_HANDLERS.each do |handler|
handler_name = "#{handler}_handler"
next if assembly.send(handler_name).blank?

html += link_to handler.capitalize, "https://#{handler}.com/#{assembly.send(handler_name)}",
target: "_blank",
class: "",
title: t("assemblies.show.social_networks_title", scope: "decidim") << " " << handler.capitalize.to_s, rel: "noopener"
end
html += "</div>".html_safe
end

html.html_safe
end
end
end
end
Loading

0 comments on commit c89380d

Please sign in to comment.