Skip to content

Commit

Permalink
Merge branch 'fix/meetings_form_embed_type_visibility' into pwa-staging
Browse files Browse the repository at this point in the history
* fix/meetings_form_embed_type_visibility:
  Fix tests by adding missing doubled attributes
  Include value in validation conditional
  Allow participants to set iframe access level of meetings
  Fix embed type visibility in participants form
  Remove blank option in meetings embed type select
  Fix avatar thumbnail in participants' profile (decidim#8577)
  Fix HTML injection in comments and meeting's description (decidim#8511)
  Add search, filters and sorting to admin panel budget projects (decidim#8592)
  Add cache key separator to cache_hash (decidim#8559)
  Move social login buttons to the top of the login modal (decidim#8574)
  Fix the meeting copy functionality (decidim#8430)
  Temporarily ignore CSS validation issue in CI (decidim#8597)
  Fix security instructions (decidim#8587)
  • Loading branch information
entantoencuanto committed Dec 10, 2021
2 parents f4f602b + 7d730c7 commit 4d14e17
Show file tree
Hide file tree
Showing 57 changed files with 971 additions and 316 deletions.
2 changes: 1 addition & 1 deletion SECURITY.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ To download our key:

[source,bash]
----
gpg --keyserver pgp.key-server.io --recv 84B935C4
gpg --keyserver pgp.mit.edu --recv 84B935C4
----
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ def td_resource_scope_for(current_scope)
content_tag(:td, scope_name)
end

# Public: This helper shows th with the sort link element.
def th_scope_sort_link
return unless resource_with_scopes_enabled?

content_tag(:th) do
sort_link(query, :scope_name, t("decidim.admin.resources.index.headers.scope"))
end
end

private

def resource_with_scopes_enabled?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

require "active_support/concern"

module Decidim
module Budgets
module Admin
module Filterable
extend ActiveSupport::Concern

included do
include Decidim::Admin::Filterable

helper Decidim::Budgets::Admin::FilterableHelper

private

def base_query
collection
end

def search_field_predicate
:id_string_or_title_cont
end

def filters
[
:scope_id_eq,
:category_id_eq,
:selected_at_null
]
end

def filters_with_values
{
scope_id_eq: scope_ids_hash(scopes.top_level),
category_id_eq: category_ids_hash(categories.first_class),
selected_at_null: [true, false]
}
end

# Can't user `super` here, because it does not belong to a superclass
# but to a concern.
def dynamically_translated_filters
[:scope_id_eq, :category_id_eq]
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ module Admin
class ProjectsController < Admin::ApplicationController
include Decidim::ApplicationHelper
include Decidim::Proposals::Admin::Picker if Decidim::Budgets.enable_proposal_linking
include Decidim::Budgets::Admin::Filterable

helper_method :projects, :finished_orders, :pending_orders, :present

def collection
@collection ||= budget.projects.page(params[:page]).per(15)
end

def new
enforce_permission_to :create, :project
@form = form(ProjectForm).from_params(
Expand Down Expand Up @@ -72,7 +77,7 @@ def destroy
private

def projects
@projects ||= budget.projects.page(params[:page]).per(15)
@projects ||= filtered_collection
end

def orders
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

module Decidim
module Budgets
module Admin
module FilterableHelper
end
end
end
end
26 changes: 26 additions & 0 deletions decidim-budgets/app/models/decidim/budgets/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,32 @@ def selected?
def attachment_context
:admin
end

ransacker :id_string do
Arel.sql(%{cast("decidim_budgets_projects"."id" as text)})
end

# Allow ransacker to search for a key in a hstore column (`title`.`en`)
ransacker :title do |parent|
Arel::Nodes::InfixOperation.new("->>", parent.table[:title], Arel::Nodes.build_quoted(I18n.locale.to_s))
end

ransacker :selected do
Arel.sql(%{("decidim_budgets_projects"."selected_at")::text})
end

ransacker :confirmed_orders_count do
query = <<-SQL.squish
(
SELECT COUNT(decidim_budgets_line_items.decidim_order_id)
FROM decidim_budgets_line_items
LEFT JOIN decidim_budgets_orders ON decidim_budgets_orders.id = decidim_budgets_line_items.decidim_order_id
WHERE decidim_budgets_orders.checked_out_at IS NOT NULL
AND decidim_budgets_projects.id = decidim_budgets_line_items.decidim_project_id
)
SQL
Arel.sql(query)
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,36 @@
</h2>
</div>

<%= admin_filter_selector(:projects) %>
<div class="card-section">
<div class="table-scroll">
<table class="table-list">
<thead>
<tr>
<th><%= t("models.project.fields.title", scope: "decidim.budgets") %></th>
<th><%= t("index.confirmed_orders_count") %></th>
<th><%= t(".selected") %></th>
<%= th_resource_scope_label %>
<th><%= sort_link(query, :id, t("models.project.fields.id", scope: "decidim.budgets"), default_order: :desc) %>
<th><%= sort_link(query, :title, t("models.project.fields.title", scope: "decidim.budgets")) %></th>
<th><%= sort_link(query, :category_name, t("models.project.fields.category", scope: "decidim.budgets") ) %></th>
<%= th_scope_sort_link %>
<th><%= sort_link(query, :confirmed_orders_count, t("index.confirmed_orders_count")) %></th>
<th><%= sort_link(query, :selected, t(".selected")) %></th>
<th class="actions"><%= t("actions.title", scope: "decidim.budgets") %></th>
</tr>
</thead>
<tbody>
<% projects.each do |project| %>
<tr data-id="<%= project.id %>">
<td>
<%= project.id %><br>
</td>
<td>
<%= translated_attribute(project.title) %><br>
</td>
<td>
<% if project.category %>
<%= translated_attribute project.category.name %>
<% end %>
</td>
<%= td_resource_scope_for(project.scope) %>
<td>
<%= project.confirmed_orders_count %>
</td>
Expand All @@ -43,7 +55,6 @@
<%= content_tag :span, "×", class: "text-muted" %>
<% end %>
</td>
<%= td_resource_scope_for(project.scope) %>
<td class="table-list__actions">
<%= icon_link_to "eye", resource_locator([budget, project]).path, t("actions.preview", scope: "decidim.budgets"), target: :blank, class: "action-icon--preview" %>
Expand Down
14 changes: 14 additions & 0 deletions decidim-budgets/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ en:
one: Project
other: Projects
decidim:
admin:
filters:
projects:
category_id_eq:
label: Category
scope_id_eq:
label: Scope
selected_at_null:
label: Selected
values:
'false': Selected for implementation
'true': Not selected for implementation
budgets:
actions:
attachment_collections: Folders
Expand Down Expand Up @@ -135,6 +147,8 @@ en:
total_budget: Total budget
project:
fields:
category: Category
id: ID
title: Title
order_summary_mailer:
order_summary:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# frozen_string_literal: true

require "spec_helper"

describe "Admin filters, searches, and paginates projects", type: :system do
include_context "when managing a component as an admin"
include_context "with filterable context"

let(:manifest_name) { "budgets" }
let(:resource_controller) { Decidim::Budgets::Admin::ProjectsController }
let!(:budget) { create(:budget, component: current_component) }

before do
visit_component_admin
find("a[title='Manage projects']").click
end

context "when filtering by scope" do
let!(:scope1) { create(:scope, organization: component.organization, name: { "en" => "Scope1" }) }
let!(:scope2) { create(:scope, organization: component.organization, name: { "en" => "Scope2" }) }
let!(:project_with_scope1) { create(:project, budget: budget, scope: scope1) }
let!(:project_with_scope2) { create(:project, budget: budget, scope: scope2) }
let(:project_with_scope1_title) { translated(project_with_scope1.title) }
let(:project_with_scope2_title) { translated(project_with_scope2.title) }

before { visit current_path }

it_behaves_like "a filtered collection", options: "Scope", filter: "Scope1" do
let(:in_filter) { project_with_scope1_title }
let(:not_in_filter) { project_with_scope2_title }
end

it_behaves_like "a filtered collection", options: "Scope", filter: "Scope2" do
let(:in_filter) { project_with_scope2_title }
let(:not_in_filter) { project_with_scope1_title }
end
end

context "when filtering by category" do
let!(:category1) { create(:category, participatory_space: participatory_space, name: { "en" => "Category1" }) }
let!(:category2) { create(:category, participatory_space: participatory_space, name: { "en" => "Category2" }) }
let!(:project_with_category1) { create(:project, budget: budget, category: category1) }
let!(:project_with_category2) { create(:project, budget: budget, category: category2) }
let(:project_with_category1_title) { translated(project_with_category1.title) }
let(:project_with_category2_title) { translated(project_with_category2.title) }

before { visit current_path }

it_behaves_like "a filtered collection", options: "Category", filter: "Category1" do
let(:in_filter) { project_with_category1_title }
let(:not_in_filter) { project_with_category2_title }
end

it_behaves_like "a filtered collection", options: "Category", filter: "Category2" do
let(:in_filter) { project_with_category2_title }
let(:not_in_filter) { project_with_category1_title }
end
end

context "when filtering by selected" do
let!(:project_with_status1) { create(:project, budget: budget, selected_at: Time.current) }
let!(:project_with_status2) { create(:project, budget: budget, selected_at: nil) }
let(:project_with_status1_title) { translated(project_with_status1.title) }
let(:project_with_status2_title) { translated(project_with_status2.title) }

before { visit current_path }

it_behaves_like "a filtered collection", options: "Selected", filter: "Selected for implementation" do
let(:in_filter) { project_with_status1_title }
let(:not_in_filter) { project_with_status2_title }
end

it_behaves_like "a filtered collection", options: "Selected", filter: "Not selected for implementation" do
let(:in_filter) { project_with_status2_title }
let(:not_in_filter) { project_with_status1_title }
end
end

context "when searching by ID or title" do
let!(:project1) { create(:project, budget: budget) }
let!(:project2) { create(:project, budget: budget) }
let!(:project1_title) { translated(project1.title) }
let!(:project2_title) { translated(project2.title) }

before { visit current_path }

it "can be searched by ID" do
search_by_text(project1.id)

expect(page).to have_content(project1_title)
end

it "can be searched by title" do
search_by_text(project2_title)

expect(page).to have_content(project2_title)
end
end

context "when listing projects" do
before { visit current_path }

it_behaves_like "paginating a collection" do
let!(:collection) { create_list(:project, 50, budget: budget) }
end
end
end
Loading

0 comments on commit 4d14e17

Please sign in to comment.