diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 6f4c0cf90f4..37c66278308 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -64,17 +64,10 @@ class CustomField < ApplicationRecord validates :custom_options, presence: { message: ->(*) { I18n.t(:"activerecord.errors.models.custom_field.at_least_one_custom_option") } }, if: ->(*) { field_format == "list" } - validates :name, presence: true, length: { maximum: 256 } - - validate :uniqueness_of_name_with_scope - - def uniqueness_of_name_with_scope - taken_names = CustomField.where(type:) - taken_names = taken_names.where.not(id:) if id - taken_names = taken_names.pluck(:name) - - errors.add(:name, :taken) if name.in?(taken_names) - end + validates :name, + presence: true, + length: { maximum: 256 }, + uniqueness: { case_sensitive: false, scope: :type } validate :validate_field_format_inclusion validate :validate_default_value diff --git a/app/models/enumeration.rb b/app/models/enumeration.rb index 1f25fa8f186..a7d652a7b88 100644 --- a/app/models/enumeration.rb +++ b/app/models/enumeration.rb @@ -40,11 +40,10 @@ class Enumeration < ApplicationRecord before_save :ensure_activated, if: -> { self.class.can_have_default_value? && is_default? } before_destroy :check_integrity - validates :name, presence: true validates :name, - uniqueness: { scope: %i(type project_id), - case_sensitive: false } - validates :name, length: { maximum: 256 } + presence: true, + length: { maximum: 256 }, + uniqueness: { scope: %i(type project_id), case_sensitive: false } scope :shared, -> { where(project_id: nil) } scope :active, -> { where(active: true) } diff --git a/db/migrate/20260105090537_add_uniqueness_for_custom_field_names.rb b/db/migrate/20260105090537_add_uniqueness_for_custom_field_names.rb new file mode 100644 index 00000000000..a1ce79481be --- /dev/null +++ b/db/migrate/20260105090537_add_uniqueness_for_custom_field_names.rb @@ -0,0 +1,48 @@ +# 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 AddUniquenessForCustomFieldNames < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + execute <<~SQL.squish + UPDATE custom_fields SET name = custom_fields.name || ' ' || counter.rn + FROM (SELECT id, row_number() OVER (PARTITION BY type, LOWER(name) ORDER BY id) AS rn FROM custom_fields) AS counter + WHERE custom_fields.id = counter.id AND counter.rn > 1; + SQL + + add_index :custom_fields, "type, LOWER(name)", unique: true, algorithm: :concurrently, + name: "index_custom_fields_on_type_and_LOWER_name" + end + + def down + remove_index :custom_fields, name: "index_custom_fields_on_type_and_LOWER_name", algorithm: :concurrently + end +end diff --git a/db/migrate/20260105093056_add_uniqueness_for_enumeration_names.rb b/db/migrate/20260105093056_add_uniqueness_for_enumeration_names.rb new file mode 100644 index 00000000000..4f797ee4e98 --- /dev/null +++ b/db/migrate/20260105093056_add_uniqueness_for_enumeration_names.rb @@ -0,0 +1,48 @@ +# 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 AddUniquenessForEnumerationNames < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + execute <<~SQL.squish + UPDATE enumerations SET name = enumerations.name || ' ' || counter.rn + FROM (SELECT id, row_number() OVER (PARTITION BY type, project_id, LOWER(name) ORDER BY id) AS rn FROM enumerations) AS counter + WHERE enumerations.id = counter.id AND counter.rn > 1; + SQL + + add_index :enumerations, "type, project_id, LOWER(name)", unique: true, algorithm: :concurrently, + name: "index_enumerations_on_type_project_id_and_LOWER_name" + end + + def down + remove_index :enumerations, name: "index_enumerations_on_type_project_id_and_LOWER_name", algorithm: :concurrently + end +end diff --git a/db/migrate/20260105095152_add_uniqueness_for_category_names.rb b/db/migrate/20260105095152_add_uniqueness_for_category_names.rb new file mode 100644 index 00000000000..87bc34f6d1b --- /dev/null +++ b/db/migrate/20260105095152_add_uniqueness_for_category_names.rb @@ -0,0 +1,48 @@ +# 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 AddUniquenessForCategoryNames < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + execute <<~SQL.squish + UPDATE categories SET name = categories.name || ' ' || counter.rn + FROM (SELECT id, row_number() OVER (PARTITION BY project_id, LOWER(name) ORDER BY id) AS rn FROM categories) AS counter + WHERE categories.id = counter.id AND counter.rn > 1; + SQL + + add_index :categories, "project_id, LOWER(name)", unique: true, algorithm: :concurrently, + name: "index_categories_on_project_id_and_LOWER_name" + end + + def down + remove_index :categories, name: "index_categories_on_project_id_and_LOWER_name", algorithm: :concurrently + end +end diff --git a/db/migrate/20260105095917_add_uniqueness_for_ldap_auth_source_names.rb b/db/migrate/20260105095917_add_uniqueness_for_ldap_auth_source_names.rb new file mode 100644 index 00000000000..4057ac92984 --- /dev/null +++ b/db/migrate/20260105095917_add_uniqueness_for_ldap_auth_source_names.rb @@ -0,0 +1,48 @@ +# 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 AddUniquenessForLdapAuthSourceNames < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + execute <<~SQL.squish + UPDATE ldap_auth_sources SET name = ldap_auth_sources.name || ' ' || counter.rn + FROM (SELECT id, row_number() OVER (PARTITION BY LOWER(name) ORDER BY id) AS rn FROM ldap_auth_sources) AS counter + WHERE ldap_auth_sources.id = counter.id AND counter.rn > 1; + SQL + + add_index :ldap_auth_sources, "LOWER(name)", unique: true, algorithm: :concurrently, + name: "index_ldap_auth_sources_on_LOWER_name" + end + + def down + remove_index :ldap_auth_sources, name: "index_ldap_auth_sources_on_LOWER_name", algorithm: :concurrently + end +end diff --git a/db/migrate/20260105100429_add_uniqueness_for_version_names.rb b/db/migrate/20260105100429_add_uniqueness_for_version_names.rb new file mode 100644 index 00000000000..b4b2e0d0e88 --- /dev/null +++ b/db/migrate/20260105100429_add_uniqueness_for_version_names.rb @@ -0,0 +1,48 @@ +# 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 AddUniquenessForVersionNames < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + execute <<~SQL.squish + UPDATE versions SET name = versions.name || ' (' || counter.rn || ')' + FROM (SELECT id, row_number() OVER (PARTITION BY project_id, LOWER(name) ORDER BY id) AS rn FROM versions) AS counter + WHERE versions.id = counter.id AND counter.rn > 1; + SQL + + add_index :versions, "project_id, LOWER(name)", unique: true, algorithm: :concurrently, + name: "index_versions_on_project_id_and_LOWER_name" + end + + def down + remove_index :versions, name: "index_versions_on_project_id_and_LOWER_name", algorithm: :concurrently + end +end diff --git a/modules/costs/app/models/cost_type.rb b/modules/costs/app/models/cost_type.rb index fa4919b9035..06f76be8280 100644 --- a/modules/costs/app/models/cost_type.rb +++ b/modules/costs/app/models/cost_type.rb @@ -31,8 +31,8 @@ class CostType < ApplicationRecord has_many :cost_entries, dependent: :destroy has_many :rates, class_name: "CostRate", dependent: :destroy - validates_presence_of :name, :unit, :unit_plural - validates_uniqueness_of :name + validates :unit, :unit_plural, presence: true + validates :name, presence: true, uniqueness: { case_sensitive: false } after_update :save_rates diff --git a/modules/costs/db/migrate/20260105093729_add_uniqueness_for_cost_type_names.rb b/modules/costs/db/migrate/20260105093729_add_uniqueness_for_cost_type_names.rb new file mode 100644 index 00000000000..ca279b5bd34 --- /dev/null +++ b/modules/costs/db/migrate/20260105093729_add_uniqueness_for_cost_type_names.rb @@ -0,0 +1,47 @@ +# 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 AddUniquenessForCostTypeNames < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + execute <<~SQL.squish + UPDATE cost_types SET name = cost_types.name || ' ' || counter.rn + FROM (SELECT id, row_number() OVER (PARTITION BY LOWER(name) ORDER BY id) AS rn FROM cost_types) AS counter + WHERE cost_types.id = counter.id AND counter.rn > 1; + SQL + + add_index :cost_types, "LOWER(name)", unique: true, algorithm: :concurrently, name: "index_cost_types_on_LOWER_name" + end + + def down + remove_index :cost_types, name: "index_cost_types_on_LOWER_name", algorithm: :concurrently + end +end diff --git a/modules/storages/app/models/storages/storage.rb b/modules/storages/app/models/storages/storage.rb index 072285777f7..4ba9cd3acf6 100644 --- a/modules/storages/app/models/storages/storage.rb +++ b/modules/storages/app/models/storages/storage.rb @@ -54,7 +54,7 @@ module Storages has_many :remote_identities, as: :integration, dependent: :destroy validates :host, uniqueness: { allow_nil: true } - validates :name, uniqueness: true + validates :name, uniqueness: { case_sensitive: false } scope :visible, lambda { |user = User.current| if user.allowed_in_any_project?(:manage_files_in_project) diff --git a/modules/storages/db/migrate/20260105101148_add_uniqueness_for_storage_names.rb b/modules/storages/db/migrate/20260105101148_add_uniqueness_for_storage_names.rb new file mode 100644 index 00000000000..946285768c4 --- /dev/null +++ b/modules/storages/db/migrate/20260105101148_add_uniqueness_for_storage_names.rb @@ -0,0 +1,47 @@ +# 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 AddUniquenessForStorageNames < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + execute <<~SQL.squish + UPDATE storages SET name = storages.name || ' ' || counter.rn + FROM (SELECT id, row_number() OVER (PARTITION BY LOWER(name) ORDER BY id) AS rn FROM storages) AS counter + WHERE storages.id = counter.id AND counter.rn > 1; + SQL + + add_index :storages, "LOWER(name)", unique: true, algorithm: :concurrently, name: "index_storages_on_LOWER_name" + end + + def down + remove_index :storages, name: "index_storages_on_LOWER_name", algorithm: :concurrently + end +end diff --git a/modules/webhooks/app/models/webhooks/webhook.rb b/modules/webhooks/app/models/webhooks/webhook.rb index c8084c301d1..06b019d1c89 100644 --- a/modules/webhooks/app/models/webhooks/webhook.rb +++ b/modules/webhooks/app/models/webhooks/webhook.rb @@ -2,11 +2,8 @@ module Webhooks class Webhook < ApplicationRecord default_scope { order(id: :asc) } - validates_presence_of :name - validates_presence_of :url - - validates_uniqueness_of :name - validates :url, url: true + validates :name, presence: true, uniqueness: { case_sensitive: false } + validates :url, presence: true, url: true has_many :events, foreign_key: :webhooks_webhook_id, class_name: "::Webhooks::Event", dependent: :delete_all has_many :webhook_projects, foreign_key: :webhooks_webhook_id, class_name: "::Webhooks::Project", dependent: :delete_all diff --git a/modules/webhooks/db/migrate/20260105092419_add_uniqueness_for_webhook_names.rb b/modules/webhooks/db/migrate/20260105092419_add_uniqueness_for_webhook_names.rb new file mode 100644 index 00000000000..51b635c5ec2 --- /dev/null +++ b/modules/webhooks/db/migrate/20260105092419_add_uniqueness_for_webhook_names.rb @@ -0,0 +1,48 @@ +# 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 AddUniquenessForWebhookNames < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + execute <<~SQL.squish + UPDATE webhooks_webhooks SET name = webhooks_webhooks.name || ' ' || counter.rn + FROM (SELECT id, row_number() OVER (PARTITION BY LOWER(name) ORDER BY id) AS rn FROM webhooks_webhooks) AS counter + WHERE webhooks_webhooks.id = counter.id AND counter.rn > 1; + SQL + + add_index :webhooks_webhooks, "LOWER(name)", unique: true, algorithm: :concurrently, + name: "index_webhooks_webhooks_on_LOWER_name" + end + + def down + remove_index :webhooks_webhooks, name: "index_webhooks_webhooks_on_LOWER_name", algorithm: :concurrently + end +end