Skip to content

Commit

Permalink
Merge pull request #15400 from opf/task/54459-investigate-queries-and…
Browse files Browse the repository at this point in the history
…-filters-to-display-the-project-list-in-projects-attribute-settings-view

[#54459] Add `AvailableProjectAttributesFilter` to project queries to display the mapped projects list in projects attribute settings view

https://community.openproject.org/work_packages/54459
  • Loading branch information
akabiru committed May 15, 2024
2 parents 711236a + 30067c2 commit 0da80c1
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 21 deletions.
35 changes: 18 additions & 17 deletions app/models/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ class Project < ApplicationRecord
.references(:principal, :roles)
}

has_many :memberships, class_name: 'Member'
has_many :memberships, class_name: "Member"
has_many :member_principals,
-> { not_locked },
class_name: 'Member'
class_name: "Member"
has_many :users, through: :members, source: :principal
has_many :principals, through: :member_principals, source: :principal

Expand All @@ -75,18 +75,18 @@ class Project < ApplicationRecord
has_many :queries, dependent: :destroy
has_many :news, -> { includes(:author) }, dependent: :destroy
has_many :categories, -> { order("#{Category.table_name}.name") }, dependent: :delete_all
has_many :forums, -> { order('position ASC') }, dependent: :destroy
has_many :forums, -> { order("position ASC") }, dependent: :destroy
has_one :repository, dependent: :destroy
has_many :changesets, through: :repository
has_one :wiki, dependent: :destroy
# Custom field for the project's work_packages
has_and_belongs_to_many :work_package_custom_fields,
-> { order("#{CustomField.table_name}.position") },
join_table: :custom_fields_projects,
association_foreign_key: 'custom_field_id'
association_foreign_key: "custom_field_id"
has_many :budgets, dependent: :destroy
has_many :notification_settings, dependent: :destroy
has_many :project_storages, dependent: :destroy, class_name: 'Storages::ProjectStorage'
has_many :project_storages, dependent: :destroy, class_name: "Storages::ProjectStorage"
has_many :storages, through: :project_storages

store_attribute :settings, :deactivate_work_package_attachments, :boolean
Expand All @@ -98,26 +98,26 @@ class Project < ApplicationRecord

acts_as_searchable columns: %W(#{table_name}.name #{table_name}.identifier #{table_name}.description),
date_column: "#{table_name}.created_at",
project_key: 'id',
project_key: "id",
permission: nil

acts_as_journalized

# Necessary for acts_as_searchable which depends on the event_datetime method for sorting
acts_as_event title: Proc.new { |o| "#{Project.model_name.human}: #{o.name}" },
url: Proc.new { |o| { controller: 'overviews/overviews', action: 'show', project_id: o } },
url: Proc.new { |o| { controller: "overviews/overviews", action: "show", project_id: o } },
author: nil,
datetime: :created_at

register_journal_formatted_fields(:active_status, 'active')
register_journal_formatted_fields(:template, 'templated')
register_journal_formatted_fields(:plaintext, 'identifier')
register_journal_formatted_fields(:plaintext, 'name')
register_journal_formatted_fields(:diff, 'status_explanation')
register_journal_formatted_fields(:diff, 'description')
register_journal_formatted_fields(:project_status_code, 'status_code')
register_journal_formatted_fields(:visibility, 'public')
register_journal_formatted_fields(:subproject_named_association, 'parent_id')
register_journal_formatted_fields(:active_status, "active")
register_journal_formatted_fields(:template, "templated")
register_journal_formatted_fields(:plaintext, "identifier")
register_journal_formatted_fields(:plaintext, "name")
register_journal_formatted_fields(:diff, "status_explanation")
register_journal_formatted_fields(:diff, "description")
register_journal_formatted_fields(:project_status_code, "status_code")
register_journal_formatted_fields(:visibility, "public")
register_journal_formatted_fields(:subproject_named_association, "parent_id")
register_journal_formatted_fields(:custom_field, /custom_fields_\d+/)

has_paper_trail
Expand Down Expand Up @@ -158,6 +158,7 @@ class Project < ApplicationRecord
friendly_id :identifier, use: :finders

scopes :allowed_to,
:available_custom_fields,
:visible

scope :has_module, ->(mod) {
Expand Down Expand Up @@ -245,7 +246,7 @@ def close_completed_versions
Version.transaction do
versions.where(status: %w(open locked)).find_each do |version|
if version.completed?
version.update_attribute(:status, 'closed')
version.update_attribute(:status, "closed")
end
end
end
Expand Down
52 changes: 52 additions & 0 deletions app/models/projects/scopes/available_custom_fields.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2023 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 Projects::Scopes
module AvailableCustomFields
extend ActiveSupport::Concern

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

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

private

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

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2023 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::AvailableProjectAttributesFilter < Queries::Projects::Filters::ProjectFilter
def self.key
:available_project_attributes
end

def type
:list
end

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

def available?
User.current.admin?
end

def scope
case operator
when "="
model.with_available_custom_fields(values)
when "!"
model.without_available_custom_fields(values)
else
raise "unsupported operator"
end
end

def human_name
I18n.t(:label_available_project_attributes)
end
end
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1902,6 +1902,7 @@ Project attributes and sections are defined in the <a href=%{admin_settings_url}
label_attribute_expand_text: "The complete text for '%{attribute}'"
label_authentication: "Authentication"
label_available_global_roles: "Available global roles"
label_available_project_attributes: "Available project attributes"
label_available_project_forums: "Available forums"
label_available_project_repositories: "Available repositories"
label_available_project_versions: "Available versions"
Expand Down
9 changes: 5 additions & 4 deletions docs/api/apiv3/paths/projects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ get:
JSON specifying filter conditions.
Accepts the same format as returned by the [queries](https://www.openproject.org/docs/api/endpoints/queries/) endpoint.
Currently supported filters are:
+ active: based on the active property of the project
+ ancestor: filters projects by their ancestor. A project is not considered to be it's own ancestor.
+ available_project_attributes: filters projects based on the activated project project attributes.
+ created_at: based on the time the project was created
+ latest_activity_at: based on the time the last activity was registered on a project.
+ name_and_identifier: based on both the name and the identifier.
Expand All @@ -33,7 +34,7 @@ get:
+ user_action: based on the actions the current user has in the project.
+ id: based on projects' id.
+ visible: based on the visibility for the user (id) provided as the filter value. This filter is useful for admins to identify the projects visible to a user.
There might also be additional filters based on the custom fields that have been configured.
example: '[{ "ancestor": { "operator": "=", "values": ["1"] }" }]'
- name: sortBy
Expand All @@ -44,15 +45,15 @@ get:
description: |-
JSON specifying sort criteria.
Currently supported orders are:
+ id
+ name
+ typeahead (sorting by hierarchy and name)
+ created_at
+ public
+ latest_activity_at
+ required_disk_space
There might also be additional orders based on the custom fields that have been configured.
example: '[["id", "asc"]]'
- name: select
Expand Down
50 changes: 50 additions & 0 deletions spec/models/projects/scopes/available_custom_fields_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2023 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.
# ++

require "spec_helper"

RSpec.describe Projects::Scopes::AvailableCustomFields do
shared_let(:project) { create(:project) }
shared_let(:project_custom_field) { create(:project_custom_field) }

shared_let(:project_custom_field_mapping) do
create(:project_custom_field_project_mapping, project:, project_custom_field:)
end

describe ".with_available_custom_fields" do
it "returns projects with the given custom fields" do
expect(Project.with_available_custom_fields([project_custom_field.id])).to contain_exactly(project)
end
end

describe ".without_available_custom_fields" do
it "returns projects without the given custom fields" do
expect(Project.without_available_custom_fields([project_custom_field.id])).to be_empty
end
end
end

0 comments on commit 0da80c1

Please sign in to comment.