diff --git a/app/models/project.rb b/app/models/project.rb index 1f22f408ada2..25ff937becad 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -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 @@ -75,7 +75,7 @@ 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 @@ -83,10 +83,10 @@ class Project < ApplicationRecord 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 @@ -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 @@ -156,6 +156,7 @@ class Project < ApplicationRecord friendly_id :identifier, use: :finders scopes :allowed_to, + :available_custom_fields, :visible scope :has_module, ->(mod) { @@ -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 diff --git a/app/models/projects/scopes/available_custom_fields.rb b/app/models/projects/scopes/available_custom_fields.rb new file mode 100644 index 000000000000..5ce046be42b1 --- /dev/null +++ b/app/models/projects/scopes/available_custom_fields.rb @@ -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 diff --git a/app/models/queries/projects.rb b/app/models/queries/projects.rb index 0872e520d81b..8b0bf9296871 100644 --- a/app/models/queries/projects.rb +++ b/app/models/queries/projects.rb @@ -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 diff --git a/app/models/queries/projects/filters/available_project_attributes_filter.rb b/app/models/queries/projects/filters/available_project_attributes_filter.rb new file mode 100644 index 000000000000..a503d7bc75f6 --- /dev/null +++ b/app/models/queries/projects/filters/available_project_attributes_filter.rb @@ -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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 63bb76e92017..df58ad40e437 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1913,6 +1913,7 @@ Project attributes and sections are defined in the