Skip to content

Commit

Permalink
Merge pull request #16565 from opf/implementation/57515-show-project-…
Browse files Browse the repository at this point in the history
…list-of-all-projects-in-which-a-given-custom-field-is-activated

Implementation/57515 show a list of all projects in which a given custom field is activated
  • Loading branch information
akabiru authored Sep 6, 2024
2 parents 318059a + f240ef7 commit 1d37cad
Show file tree
Hide file tree
Showing 16 changed files with 488 additions and 45 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module Admin
module CustomFields
module CustomFieldProjects
class RowComponent < Projects::RowComponent
include OpTurbo::Streamable

def wrapper_uniq_by
"project-#{project.id}"
end

def more_menu_items
[]
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module Admin
module CustomFields
module CustomFieldProjects
class TableComponent < Projects::TableComponent
include OpTurbo::Streamable

def columns
@columns ||= query.selects.reject { |select| select.is_a?(Queries::Selects::NotExistingSelect) }
end

def sortable?
false
end

# @override optional_pagination_options are passed to the pagination_options
# which are passed to #pagination_links_full in pagination_helper.rb
#
# In Turbo streamable components, we need to be able to specify the url_for(action:) so that links are
# generated in the context of the component index action, instead of any turbo stream actions performing
# partial updates on the page.
#
# params[:url_for_action] is passed to the pagination_options making it's way down to any pagination links
# that are generated via link_to which calls url_for which uses the params[:url_for_action] to specify
# the controller action that link_to should use.
#
def optional_pagination_options
return super unless params[:url_for_action]

super.merge(params: { action: params[:url_for_action] })
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,7 @@
module Settings
module ProjectCustomFields
module ProjectCustomFieldMapping
class RowComponent < Projects::RowComponent
include OpTurbo::Streamable

def wrapper_uniq_by
"project-#{project.id}"
end

class RowComponent < Admin::CustomFields::CustomFieldProjects::RowComponent
def more_menu_items
@more_menu_items ||= [more_menu_detach_project].compact
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,7 @@
module Settings
module ProjectCustomFields
module ProjectCustomFieldMapping
class TableComponent < Projects::TableComponent # rubocop:disable OpenProject/AddPreviewForViewComponent
include OpTurbo::Streamable

def columns
@columns ||= query.selects.reject { |select| select.is_a?(Queries::Selects::NotExistingSelect) }
end

def sortable?
false
end

# @override optional_pagination_options are passed to the pagination_options
# which are passed to #pagination_links_full in pagination_helper.rb
#
# In Turbo streamable components, we need to be able to specify the url_for(action:) so that links are
# generated in the context of the component index action, instead of any turbo stream actions performing
# partial updates on the page.
#
# params[:url_for_action] is passed to the pagination_options making it's way down to any pagination links
# that are generated via link_to which calls url_for which uses the params[:url_for_action] to specify
# the controller action that link_to should use.
#
def optional_pagination_options
return super unless params[:url_for_action]

super.merge(params: { action: params[:url_for_action] })
end
class TableComponent < Admin::CustomFields::CustomFieldProjects::TableComponent
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,21 @@ class Admin::CustomFields::CustomFieldProjectsController < ApplicationController

menu_item :custom_fields

def index; end
def index
@available_custom_fields_projects_query = ProjectQuery.new(
name: "custom-fields-projects-#{@custom_field.id}"
) do |query|
query.where(:available_custom_fields_projects, "=", [@custom_field.id])
query.select(:name)
query.order("lft" => "asc")
end
end

def default_breadcrumb; end

def show_local_breadcrumb
false
end

private

Expand Down
22 changes: 16 additions & 6 deletions app/models/projects/scopes/available_custom_fields.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,30 @@ module AvailableCustomFields

class_methods do
def with_available_custom_fields(custom_field_ids)
subquery = project_custom_fields_project_mapping_subquery(custom_field_ids:)
where(id: subquery)
where(id: project_custom_fields_join_table_subquery(custom_field_ids:))
end

def with_available_project_custom_fields(custom_field_ids)
where(id: project_custom_fields_project_mapping_subquery(custom_field_ids:))
end

def without_available_custom_fields(custom_field_ids)
subquery = project_custom_fields_project_mapping_subquery(custom_field_ids:)
where.not(id: subquery)
where.not(id: project_custom_fields_join_table_subquery(custom_field_ids:))
end

def without_available_project_custom_fields(custom_field_ids)
where.not(id: project_custom_fields_project_mapping_subquery(custom_field_ids:))
end

private

def project_custom_fields_project_mapping_subquery(custom_field_ids:)
ProjectCustomFieldProjectMapping.select(:project_id)
.where(custom_field_id: custom_field_ids)
project_custom_fields_join_table_subquery(custom_field_ids:, join_table: ProjectCustomFieldProjectMapping)
end

def project_custom_fields_join_table_subquery(custom_field_ids:, join_table: CustomFieldsProject)
join_table.select(:project_id)
.where(custom_field_id: custom_field_ids)
end
end
end
Expand Down
1 change: 1 addition & 0 deletions app/models/queries/projects.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module Queries::Projects
::Queries::Register.register(ProjectQuery) do
filter Filters::AncestorFilter
filter Filters::AvailableProjectAttributesFilter
filter Filters::AvailableCustomFieldsProjectsFilter
filter Filters::TypeFilter
filter Filters::ActiveFilter
filter Filters::TemplatedFilter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
#

class Queries::Projects::Filters::AvailableCustomFieldsProjectsFilter < Queries::Projects::Filters::Base
def self.key
:available_custom_fields_projects
end

def type
:list
end

def allowed_values
@allowed_values ||= CustomFieldsProject
.includes(:custom_field)
.distinct
.pluck(:name, :custom_field_id)
end

def available?
User.current.admin?
end

def apply_to(_query_scope)
case operator
when "="
super.with_available_custom_fields(values)
when "!"
super.without_available_custom_fields(values)
else
raise "unsupported operator"
end
end

def where
nil
end

def human_name
I18n.t(:label_available_custom_fields_projects)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ def available?
def apply_to(_query_scope)
case operator
when "="
super.with_available_custom_fields(values)
super.with_available_project_custom_fields(values)
when "!"
super.without_available_custom_fields(values)
super.without_available_project_custom_fields(values)
else
raise "unsupported operator"
end
Expand Down
15 changes: 15 additions & 0 deletions app/views/admin/custom_fields/custom_field_projects/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,18 @@ See COPYRIGHT and LICENSE files for more details.
render(Admin::CustomFields::EditFormHeaderComponent.new(custom_field: @custom_field,
selected: :custom_field_projects))
%>

<%=
if @custom_field.is_for_all?
render Primer::Beta::Blankslate.new(border: true) do |component|
component.with_visual_icon(icon: :checklist)
component.with_heading(tag: :h2).with_content(I18n.t("custom_fields.admin.custom_field_projects.is_for_all_blank_slate.heading"))
component.with_description { I18n.t("custom_fields.admin.custom_field_projects.is_for_all_blank_slate.description") }
end
else
render(Admin::CustomFields::CustomFieldProjects::TableComponent.new(
query: @available_custom_fields_projects_query,
params: params.merge({ custom_field: @custom_field }))
)
end
%>
7 changes: 7 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ en:
description: "Custom actions are one-click shortcuts to a set of pre-defined actions that you can make available on certain work packages based on status, role, type or project."

custom_fields:
admin:
custom_field_projects:
is_for_all_blank_slate:
heading: For all projects
description: This custom field is enabled in all projects since the "For all projects" option is checked. It cannot be deactivated for individual projects.

text_add_new_custom_field: >
To add new custom fields to a project you first need to create them before
you can add them to this project.
Expand Down Expand Up @@ -2064,6 +2070,7 @@ en:
label_attribute_expand_text: "The complete text for '%{attribute}'"
label_authentication: "Authentication"
label_authentication_settings: "Authentication settings"
label_available_custom_fields_projects: "Available custom fields projects"
label_available_global_roles: "Available global roles"
label_available_project_attributes: "Available project attributes"
label_available_project_forums: "Available forums"
Expand Down
34 changes: 34 additions & 0 deletions spec/factories/custom_fields_project_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

FactoryBot.define do
factory :custom_fields_project do
custom_field
project
end
end
Loading

0 comments on commit 1d37cad

Please sign in to comment.