Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
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

acts_as_favorable
Expand All @@ -96,26 +96,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 @@ -156,6 +156,7 @@ class Project < ApplicationRecord
friendly_id :identifier, use: :finders

scopes :allowed_to,
:available_custom_fields,
:visible

scope :has_module, ->(mod) {
Expand Down Expand Up @@ -243,7 +244,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
akabiru marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -1913,6 +1913,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