summaryrefslogtreecommitdiff
path: root/app/models/project.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/project.rb')
-rw-r--r--app/models/project.rb100
1 files changed, 90 insertions, 10 deletions
diff --git a/app/models/project.rb b/app/models/project.rb
index 845e9e83e78..3aa0db56404 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -65,6 +65,7 @@ class Project < ApplicationRecord
cache_markdown_field :description, pipeline: :description
+ default_value_for :packages_enabled, true
default_value_for :archived, false
default_value_for :resolve_outdated_diff_discussions, false
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
@@ -168,6 +169,7 @@ class Project < ApplicationRecord
has_one :custom_issue_tracker_service
has_one :bugzilla_service
has_one :gitlab_issue_tracker_service, inverse_of: :project
+ has_one :confluence_service
has_one :external_wiki_service
has_one :prometheus_service, inverse_of: :project
has_one :mock_ci_service
@@ -190,6 +192,10 @@ class Project < ApplicationRecord
has_many :forks, through: :forked_to_members, source: :project, inverse_of: :forked_from_project
has_many :fork_network_projects, through: :fork_network, source: :projects
+ # Packages
+ has_many :packages, class_name: 'Packages::Package'
+ has_many :package_files, through: :packages, class_name: 'Packages::PackageFile'
+
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :export_jobs, class_name: 'ProjectExportJob'
@@ -200,6 +206,7 @@ class Project < ApplicationRecord
has_one :grafana_integration, inverse_of: :project
has_one :project_setting, inverse_of: :project, autosave: true
has_one :alerting_setting, inverse_of: :project, class_name: 'Alerting::ProjectAlertingSetting'
+ has_one :service_desk_setting, class_name: 'ServiceDeskSetting'
# Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
@@ -363,6 +370,7 @@ class Project < ApplicationRecord
to: :project_setting, allow_nil: true
delegate :scheduled?, :started?, :in_progress?, :failed?, :finished?,
prefix: :import, to: :import_state, allow_nil: true
+ delegate :squash_always?, :squash_never?, :squash_enabled_by_default?, :squash_readonly?, to: :project_setting
delegate :no_import?, to: :import_state, allow_nil: true
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
@@ -376,7 +384,10 @@ class Project < ApplicationRecord
delegate :default_git_depth, :default_git_depth=, to: :ci_cd_settings, prefix: :ci
delegate :forward_deployment_enabled, :forward_deployment_enabled=, :forward_deployment_enabled?, to: :ci_cd_settings
delegate :actual_limits, :actual_plan_name, to: :namespace, allow_nil: true
- delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?, :allow_merge_on_skipped_pipeline=, to: :project_setting
+ delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?,
+ :allow_merge_on_skipped_pipeline=, :has_confluence?,
+ to: :project_setting
+ delegate :active?, to: :prometheus_service, allow_nil: true, prefix: true
# Validations
validates :creator, presence: true, on: :create
@@ -439,6 +450,7 @@ class Project < ApplicationRecord
# Sometimes queries (e.g. using CTEs) require explicit disambiguation with table name
scope :projects_order_id_desc, -> { reorder(self.arel_table['id'].desc) }
+ scope :with_packages, -> { joins(:packages) }
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
@@ -454,6 +466,7 @@ class Project < ApplicationRecord
scope :with_statistics, -> { includes(:statistics) }
scope :with_namespace, -> { includes(:namespace) }
scope :with_import_state, -> { includes(:import_state) }
+ scope :include_project_feature, -> { includes(:project_feature) }
scope :with_service, ->(service) { joins(service).eager_load(service) }
scope :with_shared_runners, -> { where(shared_runners_enabled: true) }
scope :with_container_registry, -> { where(container_registry_enabled: true) }
@@ -488,6 +501,7 @@ class Project < ApplicationRecord
.where(repository_languages: { programming_language_id: lang_id_query })
end
+ scope :service_desk_enabled, -> { where(service_desk_enabled: true) }
scope :with_builds_enabled, -> { with_feature_enabled(:builds) }
scope :with_issues_enabled, -> { with_feature_enabled(:issues) }
scope :with_issues_available_for_user, ->(current_user) { with_feature_available_for_user(:issues, current_user) }
@@ -513,9 +527,8 @@ class Project < ApplicationRecord
.where(project_pages_metadata: { project_id: nil })
end
- scope :with_api_entity_associations, -> {
- preload(:project_feature, :route, :tags,
- group: :ip_restrictions, namespace: [:route, :owner])
+ scope :with_api_commit_entity_associations, -> {
+ preload(:project_feature, :route, namespace: [:route, :owner])
}
enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 }
@@ -532,6 +545,10 @@ class Project < ApplicationRecord
# Used by Projects::CleanupService to hold a map of rewritten object IDs
mount_uploader :bfg_object_map, AttachmentUploader
+ def self.with_api_entity_associations
+ preload(:project_feature, :route, :tags, :group, namespace: [:route, :owner])
+ end
+
def self.with_web_entity_associations
preload(:project_feature, :route, :creator, :group, namespace: [:route, :owner])
end
@@ -589,6 +606,14 @@ class Project < ApplicationRecord
end
end
+ def self.projects_user_can(projects, user, action)
+ projects = where(id: projects)
+
+ DeclarativePolicy.user_scope do
+ projects.select { |project| Ability.allowed?(user, action, project) }
+ end
+ end
+
# This scope returns projects where user has access to both the project and the feature.
def self.filter_by_feature_visibility(feature, user)
with_feature_available_for_user(feature, user)
@@ -675,10 +700,11 @@ class Project < ApplicationRecord
# '>' or its escaped form ('&gt;') are checked for because '>' is sometimes escaped
# when the reference comes from an external source.
def markdown_reference_pattern
- %r{
- #{reference_pattern}
- (#{reference_postfix}|#{reference_postfix_escaped})
- }x
+ @markdown_reference_pattern ||=
+ %r{
+ #{reference_pattern}
+ (#{reference_postfix}|#{reference_postfix_escaped})
+ }x
end
def trending
@@ -706,6 +732,12 @@ class Project < ApplicationRecord
from_union([with_issues_enabled, with_merge_requests_enabled]).select(:id)
end
+
+ def find_by_service_desk_project_key(key)
+ # project_key is not indexed for now
+ # see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24063#note_282435524 for details
+ joins(:service_desk_setting).find_by('service_desk_settings.project_key' => key)
+ end
end
def initialize(attributes = nil)
@@ -839,6 +871,15 @@ class Project < ApplicationRecord
end
end
+ # Because we use default_value_for we need to be sure
+ # packages_enabled= method does exist even if we rollback migration.
+ # Otherwise many tests from spec/migrations will fail.
+ def packages_enabled=(value)
+ if has_attribute?(:packages_enabled)
+ write_attribute(:packages_enabled, value)
+ end
+ end
+
def cleanup
@repository = nil
end
@@ -1699,7 +1740,7 @@ class Project < ApplicationRecord
url_path = full_path.partition('/').last
# If the project path is the same as host, we serve it as group page
- return url if url == "#{Settings.pages.protocol}://#{url_path}"
+ return url if url == "#{Settings.pages.protocol}://#{url_path}".downcase
"#{url}/#{url_path}"
end
@@ -1795,6 +1836,7 @@ class Project < ApplicationRecord
after_create_default_branch
join_pool_repository
refresh_markdown_cache!
+ write_repository_config
end
def update_project_counter_caches
@@ -1922,6 +1964,7 @@ class Project < ApplicationRecord
.append(key: 'CI_PROJECT_PATH', value: full_path)
.append(key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug)
.append(key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path)
+ .append(key: 'CI_PROJECT_ROOT_NAMESPACE', value: namespace.root_ancestor.path)
.append(key: 'CI_PROJECT_URL', value: web_url)
.append(key: 'CI_PROJECT_VISIBILITY', value: Gitlab::VisibilityLevel.string_level(visibility_level))
.append(key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: repository_languages.map(&:name).join(',').downcase)
@@ -2131,7 +2174,13 @@ class Project < ApplicationRecord
# rubocop: disable CodeReuse/ServiceClass
def forks_count
- Projects::ForksCountService.new(self).count
+ BatchLoader.for(self).batch do |projects, loader|
+ fork_count_per_project = ::Projects::BatchForksCountService.new(projects).refresh_cache_and_retrieve_data
+
+ fork_count_per_project.each do |project, count|
+ loader.call(project, count)
+ end
+ end
end
# rubocop: enable CodeReuse/ServiceClass
@@ -2410,6 +2459,37 @@ class Project < ApplicationRecord
super || build_metrics_setting
end
+ def service_desk_enabled
+ Gitlab::ServiceDesk.enabled?(project: self)
+ end
+
+ alias_method :service_desk_enabled?, :service_desk_enabled
+
+ def service_desk_address
+ return unless service_desk_enabled?
+
+ config = Gitlab.config.incoming_email
+ wildcard = Gitlab::IncomingEmail::WILDCARD_PLACEHOLDER
+
+ config.address&.gsub(wildcard, "#{full_path_slug}-#{id}-issue-")
+ end
+
+ def root_namespace
+ if namespace.has_parent?
+ namespace.root_ancestor
+ else
+ namespace
+ end
+ end
+
+ def package_already_taken?(package_name)
+ namespace.root_ancestor.all_projects
+ .joins(:packages)
+ .where.not(id: id)
+ .merge(Packages::Package.with_name(package_name))
+ .exists?
+ end
+
private
def find_service(services, name)