summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/commit.rb8
-rw-r--r--app/models/concerns/notifiable.rb15
-rw-r--r--app/models/group.rb1
-rw-r--r--app/models/member.rb12
-rw-r--r--app/models/members/group_member.rb1
-rw-r--r--app/models/members/project_member.rb1
-rw-r--r--app/models/merge_request.rb6
-rw-r--r--app/models/notification.rb77
-rw-r--r--app/models/notification_setting.rb28
-rw-r--r--app/models/project.rb11
-rw-r--r--app/models/project_services/bamboo_service.rb20
-rw-r--r--app/models/project_services/teamcity_service.rb33
-rw-r--r--app/models/repository.rb10
-rw-r--r--app/models/user.rb18
14 files changed, 106 insertions, 135 deletions
diff --git a/app/models/commit.rb b/app/models/commit.rb
index d09876a07d9..11ecfcace14 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -150,13 +150,11 @@ class Commit
end
def hook_attrs(with_changed_files: false)
- path_with_namespace = project.path_with_namespace
-
data = {
id: id,
message: safe_message,
timestamp: committed_date.xmlschema,
- url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{id}",
+ url: commit_url,
author: {
name: author_name,
email: author_email
@@ -170,6 +168,10 @@ class Commit
data
end
+ def commit_url
+ project.present? ? "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{id}" : ""
+ end
+
# Discover issues should be closed when this commit is pushed to a project's
# default branch.
def closes_issues(current_user = self.committer)
diff --git a/app/models/concerns/notifiable.rb b/app/models/concerns/notifiable.rb
deleted file mode 100644
index d7dcd97911d..00000000000
--- a/app/models/concerns/notifiable.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# == Notifiable concern
-#
-# Contains notification functionality
-#
-module Notifiable
- extend ActiveSupport::Concern
-
- included do
- validates :notification_level, inclusion: { in: Notification.project_notification_levels }, presence: true
- end
-
- def notification
- @notification ||= Notification.new(self)
- end
-end
diff --git a/app/models/group.rb b/app/models/group.rb
index b332601c59b..9a04ac70d35 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -27,6 +27,7 @@ class Group < Namespace
has_many :users, through: :group_members
has_many :project_group_links, dependent: :destroy
has_many :shared_projects, through: :project_group_links, source: :project
+ has_many :notification_settings, dependent: :destroy, as: :source
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validate :visibility_level_allowed_by_projects
diff --git a/app/models/member.rb b/app/models/member.rb
index ca08007b7eb..60efafef211 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -19,7 +19,6 @@
class Member < ActiveRecord::Base
include Sortable
- include Notifiable
include Gitlab::Access
attr_accessor :raw_invite_token
@@ -56,12 +55,15 @@ class Member < ActiveRecord::Base
before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
after_create :send_invite, if: :invite?
+ after_create :create_notification_setting, unless: :invite?
after_create :post_create_hook, unless: :invite?
after_update :post_update_hook, unless: :invite?
after_destroy :post_destroy_hook, unless: :invite?
delegate :name, :username, :email, to: :user, prefix: true
+ default_value_for :notification_level, NotificationSetting.levels[:global]
+
class << self
def find_by_invite_token(invite_token)
invite_token = Devise.token_generator.digest(self, :invite_token, invite_token)
@@ -160,6 +162,14 @@ class Member < ActiveRecord::Base
send_invite
end
+ def create_notification_setting
+ user.notification_settings.find_or_create_for(source)
+ end
+
+ def notification_setting
+ @notification_setting ||= user.notification_settings_for(source)
+ end
+
private
def send_invite
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 65d2ea00570..9fb474a1a93 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -24,7 +24,6 @@ class GroupMember < Member
# Make sure group member points only to group as it source
default_value_for :source_type, SOURCE_TYPE
- default_value_for :notification_level, Notification::N_GLOBAL
validates_format_of :source_type, with: /\ANamespace\z/
default_scope { where(source_type: SOURCE_TYPE) }
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 560d1690e14..07ddb02ae9d 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -27,7 +27,6 @@ class ProjectMember < Member
# Make sure project member points only to project as it source
default_value_for :source_type, SOURCE_TYPE
- default_value_for :notification_level, Notification::N_GLOBAL
validates_format_of :source_type, with: /\AProject\z/
default_scope { where(source_type: SOURCE_TYPE) }
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index bf185cb5dd8..e410febdfff 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -128,7 +128,7 @@ class MergeRequest < ActiveRecord::Base
validates :target_project, presence: true
validates :target_branch, presence: true
validates :merge_user, presence: true, if: :merge_when_build_succeeds?
- validate :validate_branches
+ validate :validate_branches, unless: :allow_broken
validate :validate_fork
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
@@ -218,7 +218,7 @@ class MergeRequest < ActiveRecord::Base
end
if opened? || reopened?
- similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.id).opened
+ similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.try(:id)).opened
similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
if similar_mrs.any?
errors.add :validate_branches,
@@ -345,7 +345,7 @@ class MergeRequest < ActiveRecord::Base
def hook_attrs
attrs = {
- source: source_project.hook_attrs,
+ source: source_project.try(:hook_attrs),
target: target_project.hook_attrs,
last_commit: nil,
work_in_progress: work_in_progress?
diff --git a/app/models/notification.rb b/app/models/notification.rb
deleted file mode 100644
index 171b8df45c2..00000000000
--- a/app/models/notification.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-class Notification
- #
- # Notification levels
- #
- N_DISABLED = 0
- N_PARTICIPATING = 1
- N_WATCH = 2
- N_GLOBAL = 3
- N_MENTION = 4
-
- attr_accessor :target
-
- class << self
- def notification_levels
- [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH]
- end
-
- def options_with_labels
- {
- disabled: N_DISABLED,
- participating: N_PARTICIPATING,
- watch: N_WATCH,
- mention: N_MENTION,
- global: N_GLOBAL
- }
- end
-
- def project_notification_levels
- [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH, N_GLOBAL]
- end
- end
-
- def initialize(target)
- @target = target
- end
-
- def disabled?
- target.notification_level == N_DISABLED
- end
-
- def participating?
- target.notification_level == N_PARTICIPATING
- end
-
- def watch?
- target.notification_level == N_WATCH
- end
-
- def global?
- target.notification_level == N_GLOBAL
- end
-
- def mention?
- target.notification_level == N_MENTION
- end
-
- def level
- target.notification_level
- end
-
- def to_s
- case level
- when N_DISABLED
- 'Disabled'
- when N_PARTICIPATING
- 'Participating'
- when N_WATCH
- 'Watching'
- when N_MENTION
- 'On mention'
- when N_GLOBAL
- 'Global'
- else
- # do nothing
- end
- end
-end
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
new file mode 100644
index 00000000000..5001738f411
--- /dev/null
+++ b/app/models/notification_setting.rb
@@ -0,0 +1,28 @@
+class NotificationSetting < ActiveRecord::Base
+ enum level: { disabled: 0, participating: 1, watch: 2, global: 3, mention: 4 }
+
+ default_value_for :level, NotificationSetting.levels[:global]
+
+ belongs_to :user
+ belongs_to :source, polymorphic: true
+
+ validates :user, presence: true
+ validates :source, presence: true
+ validates :level, presence: true
+ validates :user_id, uniqueness: { scope: [:source_type, :source_id],
+ message: "already exists in source",
+ allow_nil: true }
+
+ scope :for_groups, -> { where(source_type: 'Namespace') }
+ scope :for_projects, -> { where(source_type: 'Project') }
+
+ def self.find_or_create_for(source)
+ setting = find_or_initialize_by(source: source)
+
+ unless setting.persisted?
+ setting.save
+ end
+
+ setting
+ end
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 3e1f04b4158..fadc8bb2c9e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -154,6 +154,7 @@ class Project < ActiveRecord::Base
has_many :project_group_links, dependent: :destroy
has_many :invited_groups, through: :project_group_links, source: :group
has_many :todos, dependent: :destroy
+ has_many :notification_settings, dependent: :destroy, as: :source
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
@@ -388,9 +389,15 @@ class Project < ActiveRecord::Base
def add_import_job
if forked?
- RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path)
+ job_id = RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path)
else
- RepositoryImportWorker.perform_async(self.id)
+ job_id = RepositoryImportWorker.perform_async(self.id)
+ end
+
+ if job_id
+ Rails.logger.info "Import job started for #{path_with_namespace} with job ID #{job_id}"
+ else
+ Rails.logger.error "Import job failed to start for #{path_with_namespace}"
end
end
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index 9e7f642180e..060062aaf7a 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -82,17 +82,17 @@ class BambooService < CiService
end
def build_info(sha)
- url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}")
+ url = URI.join(bamboo_url, "/rest/api/latest/result?label=#{sha}").to_s
if username.blank? && password.blank?
- @response = HTTParty.get(parsed_url.to_s, verify: false)
+ @response = HTTParty.get(url, verify: false)
else
- get_url = "#{url}&os_authType=basic"
+ url << '&os_authType=basic'
auth = {
- username: username,
- password: password,
+ username: username,
+ password: password
}
- @response = HTTParty.get(get_url, verify: false, basic_auth: auth)
+ @response = HTTParty.get(url, verify: false, basic_auth: auth)
end
end
@@ -101,11 +101,11 @@ class BambooService < CiService
if @response.code != 200 || @response['results']['results']['size'] == '0'
# If actual build link can't be determined, send user to build summary page.
- "#{bamboo_url}/browse/#{build_key}"
+ URI.join(bamboo_url, "/browse/#{build_key}").to_s
else
# If actual build link is available, go to build result page.
result_key = @response['results']['results']['result']['planResultKey']['key']
- "#{bamboo_url}/browse/#{result_key}"
+ URI.join(bamboo_url, "/browse/#{result_key}").to_s
end
end
@@ -134,7 +134,7 @@ class BambooService < CiService
return unless supported_events.include?(data[:object_kind])
# Bamboo requires a GET and does not take any data.
- self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}",
- verify: false)
+ url = URI.join(bamboo_url, "/updateAndBuild.action?buildKey=#{build_key}").to_s
+ self.class.get(url, verify: false)
end
end
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index b8e9416131a..8dceee5e2c5 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -85,13 +85,15 @@ class TeamcityService < CiService
end
def build_info(sha)
- url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\
- "branch:unspecified:any,number:#{sha}")
+ url = URI.join(
+ teamcity_url,
+ "/httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}"
+ ).to_s
auth = {
username: username,
- password: password,
+ password: password
}
- @response = HTTParty.get("#{url}", verify: false, basic_auth: auth)
+ @response = HTTParty.get(url, verify: false, basic_auth: auth)
end
def build_page(sha, ref)
@@ -100,12 +102,14 @@ class TeamcityService < CiService
if @response.code != 200
# If actual build link can't be determined,
# send user to build summary page.
- "#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}"
+ URI.join(teamcity_url, "/viewLog.html?buildTypeId=#{build_type}").to_s
else
# If actual build link is available, go to build result page.
built_id = @response['build']['id']
- "#{teamcity_url}/viewLog.html?buildId=#{built_id}"\
- "&buildTypeId=#{build_type}"
+ URI.join(
+ teamcity_url,
+ "/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}"
+ ).to_s
end
end
@@ -140,12 +144,13 @@ class TeamcityService < CiService
branch = Gitlab::Git.ref_name(data[:ref])
- self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
- body: "<build branchName=\"#{branch}\">"\
- "<buildType id=\"#{build_type}\"/>"\
- '</build>',
- headers: { 'Content-type' => 'application/xml' },
- basic_auth: auth
- )
+ self.class.post(
+ URI.join(teamcity_url, '/httpAuth/app/rest/buildQueue').to_s,
+ body: "<build branchName=\"#{branch}\">"\
+ "<buildType id=\"#{build_type}\"/>"\
+ '</build>',
+ headers: { 'Content-type' => 'application/xml' },
+ basic_auth: auth
+ )
end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index a8e826c9cbf..0b2289cfa39 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -253,6 +253,8 @@ class Repository
# This ensures this particular cache is flushed after the first commit to a
# new repository.
expire_emptiness_caches if empty?
+ expire_branch_count_cache
+ expire_tag_count_cache
end
def expire_branch_cache(branch_name = nil)
@@ -331,6 +333,8 @@ class Repository
# Runs code after a repository has been created.
def after_create
expire_exists_cache
+ expire_root_ref_cache
+ expire_emptiness_caches
end
# Runs code just before a repository is deleted.
@@ -894,9 +898,9 @@ class Repository
end
def main_language
- unless empty?
- Linguist::Repository.new(rugged, rugged.head.target_id).language
- end
+ return if empty? || rugged.head_unborn?
+
+ Linguist::Repository.new(rugged, rugged.head.target_id).language
end
def avatar
diff --git a/app/models/user.rb b/app/models/user.rb
index 2b0bee2099f..031315debd7 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -143,6 +143,7 @@ class User < ActiveRecord::Base
has_many :spam_logs, dependent: :destroy
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :todos, dependent: :destroy
+ has_many :notification_settings, dependent: :destroy
#
# Validations
@@ -157,7 +158,7 @@ class User < ActiveRecord::Base
presence: true,
uniqueness: { case_sensitive: false }
- validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
+ validates :notification_level, presence: true
validate :namespace_uniq, if: ->(user) { user.username_changed? }
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validate :unique_email, if: ->(user) { user.email_changed? }
@@ -190,6 +191,13 @@ class User < ActiveRecord::Base
# Note: When adding an option, it MUST go on the end of the array.
enum project_view: [:readme, :activity, :files]
+ # Notification level
+ # Note: When adding an option, it MUST go on the end of the array.
+ #
+ # TODO: Add '_prefix: :notification' to enum when update to Rails 5. https://github.com/rails/rails/pull/19813
+ # Because user.notification_disabled? is much better than user.disabled?
+ enum notification_level: [:disabled, :participating, :watch, :global, :mention]
+
alias_attribute :private_token, :authentication_token
delegate :path, to: :namespace, allow_nil: true, prefix: true
@@ -349,10 +357,6 @@ class User < ActiveRecord::Base
"#{self.class.reference_prefix}#{username}"
end
- def notification
- @notification ||= Notification.new(self)
- end
-
def generate_password
if self.force_random_password
self.password = self.password_confirmation = Devise.friendly_token.first(8)
@@ -827,6 +831,10 @@ class User < ActiveRecord::Base
end
end
+ def notification_settings_for(source)
+ notification_settings.find_or_initialize_by(source: source)
+ end
+
private
def projects_union