diff options
Diffstat (limited to 'app/models/project.rb')
-rw-r--r-- | app/models/project.rb | 100 |
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 ('>') 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) |