summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
authorAlfredo Sumaran <alfredo@gitlab.com>2016-03-22 11:13:27 -0500
committerAlfredo Sumaran <alfredo@gitlab.com>2016-03-22 11:13:27 -0500
commita42b2c388df5154eecefd8eb7d8a68373e794d6e (patch)
tree7cfcbca6f78d66fb05d56586f322067dcb4d9c92 /app/models
parent647f28bd1d92eabe239a7f0f4a65eb100c0cda73 (diff)
parent18c049886e0f9ad2d094f02483aea272d0e029fb (diff)
downloadgitlab-ce-a42b2c388df5154eecefd8eb7d8a68373e794d6e.tar.gz
Merge branch 'master' into issue_7959
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ability.rb63
-rw-r--r--app/models/application_setting.rb1
-rw-r--r--app/models/commit_status.rb2
-rw-r--r--app/models/concerns/internal_id.rb5
-rw-r--r--app/models/concerns/issuable.rb11
-rw-r--r--app/models/concerns/milestoneish.rb20
-rw-r--r--app/models/event.rb6
-rw-r--r--app/models/group.rb37
-rw-r--r--app/models/issue.rb32
-rw-r--r--app/models/merge_request.rb20
-rw-r--r--app/models/milestone.rb4
-rw-r--r--app/models/project.rb56
-rw-r--r--app/models/project_wiki.rb12
-rw-r--r--app/models/repository.rb79
-rw-r--r--app/models/todo.rb32
-rw-r--r--app/models/user.rb2
16 files changed, 279 insertions, 103 deletions
diff --git a/app/models/ability.rb b/app/models/ability.rb
index ccac08b7d3f..fa2345f6faa 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -49,7 +49,6 @@ class Ability
rules = [
:read_project,
:read_wiki,
- :read_issue,
:read_label,
:read_milestone,
:read_project_snippet,
@@ -63,6 +62,9 @@ class Ability
# Allow to read builds by anonymous user if guests are allowed
rules << :read_build if project.public_builds?
+ # Allow to read issues by anonymous user if issue is not confidential
+ rules << :read_issue unless subject.is_a?(Issue) && subject.confidential?
+
rules - project_disabled_features_rules(project)
else
[]
@@ -83,7 +85,7 @@ class Ability
subject.group
end
- if group && group.projects.public_only.any?
+ if group && group.public?
[:read_group]
else
[]
@@ -112,6 +114,13 @@ class Ability
# Push abilities on the users team role
rules.push(*project_team_rules(project.team, user))
+ if project.owner == user ||
+ (project.group && project.group.has_owner?(user)) ||
+ user.admin?
+
+ rules.push(*project_owner_rules)
+ end
+
if project.public? || (project.internal? && !user.external?)
rules.push(*public_project_rules)
@@ -119,14 +128,6 @@ class Ability
rules << :read_build if project.public_builds?
end
- if project.owner == user || user.admin?
- rules.push(*project_admin_rules)
- end
-
- if project.group && project.group.has_owner?(user)
- rules.push(*project_admin_rules)
- end
-
if project.archived?
rules -= project_archived_rules
end
@@ -169,7 +170,8 @@ class Ability
:read_note,
:create_project,
:create_issue,
- :create_note
+ :create_note,
+ :upload_file
]
end
@@ -226,14 +228,16 @@ class Ability
]
end
- def project_admin_rules
- @project_admin_rules ||= project_master_rules + [
+ def project_owner_rules
+ @project_owner_rules ||= project_master_rules + [
:change_namespace,
:change_visibility_level,
:rename_project,
:remove_project,
:archive_project,
- :remove_fork_project
+ :remove_fork_project,
+ :destroy_merge_request,
+ :destroy_issue
]
end
@@ -271,11 +275,9 @@ class Ability
def group_abilities(user, group)
rules = []
- if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
- rules << :read_group
- end
+ rules << :read_group if can_read_group?(user, group)
- # Only group masters and group owners can create new projects in group
+ # Only group masters and group owners can create new projects
if group.has_master?(user) || group.has_owner?(user) || user.admin?
rules += [
:create_projects,
@@ -288,13 +290,23 @@ class Ability
rules += [
:admin_group,
:admin_namespace,
- :admin_group_member
+ :admin_group_member,
+ :change_visibility_level
]
end
rules.flatten
end
+ def can_read_group?(user, group)
+ return true if user.admin?
+ return true if group.public?
+ return true if group.internal? && !user.external?
+ return true if group.users.include?(user)
+
+ GroupProjectsFinder.new(group).execute(user).any?
+ end
+
def namespace_abilities(user, namespace)
rules = []
@@ -321,6 +333,7 @@ class Ability
end
rules += project_abilities(user, subject.project)
+ rules = filter_confidential_issues_abilities(user, subject, rules) if subject.is_a?(Issue)
rules
end
end
@@ -439,5 +452,17 @@ class Ability
:"admin_#{name}"
]
end
+
+ def filter_confidential_issues_abilities(user, issue, rules)
+ return rules if user.admin? || !issue.confidential?
+
+ unless issue.author == user || issue.assignee == user || issue.project.team.member?(user.id)
+ rules.delete(:admin_issue)
+ rules.delete(:read_issue)
+ rules.delete(:update_issue)
+ end
+
+ rules
+ end
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 269056e0e77..c4879598c4e 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -18,6 +18,7 @@
# max_attachment_size :integer default(10), not null
# default_project_visibility :integer
# default_snippet_visibility :integer
+# default_group_visibility :integer
# restricted_signup_domains :text
# user_oauth_applications :boolean default(TRUE)
# after_sign_out_path :string(255)
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 3b1aa0f5c80..3377a85a55a 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -114,7 +114,7 @@ class CommitStatus < ActiveRecord::Base
end
def ignored?
- failed? && allow_failure?
+ allow_failure? && (failed? || canceled?)
end
def duration
diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/internal_id.rb
index 821ed54fb98..51288094ef1 100644
--- a/app/models/concerns/internal_id.rb
+++ b/app/models/concerns/internal_id.rb
@@ -7,7 +7,10 @@ module InternalId
end
def set_iid
- max_iid = project.send(self.class.name.tableize).maximum(:iid)
+ records = project.send(self.class.name.tableize)
+ records = records.with_deleted if self.paranoid?
+ max_iid = records.maximum(:iid)
+
self.iid = max_iid.to_i + 1
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 86ab84615ba..476e1ce7af0 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -58,6 +58,8 @@ module Issuable
attr_mentionable :description, cache: true
participant :author, :assignee, :notes_with_associations
strip_attributes :title
+
+ acts_as_paranoid
end
module ClassMethods
@@ -209,4 +211,13 @@ module Issuable
Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
new_content: description)
end
+
+ ##
+ # Method that checks if issuable can be moved to another project.
+ #
+ # Should be overridden if issuable can be moved.
+ #
+ def can_move?(*)
+ false
+ end
end
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index d67df7c1d9c..5b8e3f654ea 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -1,18 +1,18 @@
module Milestoneish
- def closed_items_count
- issues.closed.size + merge_requests.closed_and_merged.size
+ def closed_items_count(user = nil)
+ issues_visible_to_user(user).closed.size + merge_requests.closed_and_merged.size
end
- def total_items_count
- issues.size + merge_requests.size
+ def total_items_count(user = nil)
+ issues_visible_to_user(user).size + merge_requests.size
end
- def complete?
- total_items_count == closed_items_count
+ def complete?(user = nil)
+ total_items_count(user) == closed_items_count(user)
end
- def percent_complete
- ((closed_items_count * 100) / total_items_count).abs
+ def percent_complete(user = nil)
+ ((closed_items_count(user) * 100) / total_items_count(user)).abs
rescue ZeroDivisionError
0
end
@@ -22,4 +22,8 @@ module Milestoneish
(due_date - Date.today).to_i
end
+
+ def issues_visible_to_user(user = nil)
+ issues.visible_to_user(user)
+ end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 9a0bbf50f8b..a5cfeaf388e 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -73,15 +73,17 @@ class Event < ActiveRecord::Base
end
end
- def proper?
+ def proper?(user = nil)
if push?
true
elsif membership_changed?
true
elsif created_project?
true
+ elsif issue?
+ Ability.abilities.allowed?(user, :read_issue, issue)
else
- ((issue? || merge_request? || note?) && target) || milestone?
+ ((merge_request? || note?) && target) || milestone?
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 9919ca112dc..b332601c59b 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -2,15 +2,16 @@
#
# Table name: namespaces
#
-# id :integer not null, primary key
-# name :string(255) not null
-# path :string(255) not null
-# owner_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255)
-# description :string(255) default(""), not null
-# avatar :string(255)
+# id :integer not null, primary key
+# name :string(255) not null
+# path :string(255) not null
+# owner_id :integer
+# visibility_level :integer default(20), not null
+# created_at :datetime
+# updated_at :datetime
+# type :string(255)
+# description :string(255) default(""), not null
+# avatar :string(255)
#
require 'carrierwave/orm/activerecord'
@@ -18,6 +19,7 @@ require 'file_size_validator'
class Group < Namespace
include Gitlab::ConfigHelper
+ include Gitlab::VisibilityLevel
include Referable
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
@@ -27,6 +29,8 @@ class Group < Namespace
has_many :shared_projects, through: :project_group_links, source: :project
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
+ validate :visibility_level_allowed_by_projects
+
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
mount_uploader :avatar, AvatarUploader
@@ -74,6 +78,21 @@ class Group < Namespace
name
end
+ def visibility_level_field
+ visibility_level
+ end
+
+ def visibility_level_allowed_by_projects
+ allowed_by_projects = self.projects.where('visibility_level > ?', self.visibility_level).none?
+
+ unless allowed_by_projects
+ level_name = Gitlab::VisibilityLevel.level_name(visibility_level).downcase
+ self.errors.add(:visibility_level, "#{level_name} is not allowed since there are projects with higher visibility.")
+ end
+
+ allowed_by_projects
+ end
+
def avatar_url(size = nil)
if avatar.present?
[gitlab_config.url, avatar.url].join
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 2447f860c5a..f32db59ac9f 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -16,6 +16,7 @@
# state :string(255)
# iid :integer
# updated_by_id :integer
+# moved_to_id :integer
#
require 'carrierwave/orm/activerecord'
@@ -31,10 +32,9 @@ class Issue < ActiveRecord::Base
ActsAsTaggableOn.strict_case_match = true
belongs_to :project
- validates :project, presence: true
+ belongs_to :moved_to, class_name: 'Issue'
- scope :of_group,
- ->(group) { where(project_id: group.projects.select(:id).reorder(nil)) }
+ validates :project, presence: true
scope :cared, ->(user) { where(assignee_id: user) }
scope :open_for, ->(user) { opened.assigned_to(user) }
@@ -58,6 +58,13 @@ class Issue < ActiveRecord::Base
attributes
end
+ def self.visible_to_user(user)
+ return where(confidential: false) if user.blank?
+ return all if user.admin?
+
+ where('issues.confidential = false OR (issues.confidential = true AND (issues.author_id = :user_id OR issues.assignee_id = :user_id OR issues.project_id IN(:project_ids)))', user_id: user.id, project_ids: user.authorized_projects.select(:id))
+ end
+
def self.reference_prefix
'#'
end
@@ -98,9 +105,8 @@ class Issue < ActiveRecord::Base
end
def related_branches
- return [] if self.project.empty_repo?
- self.project.repository.branch_names.select do |branch|
- branch =~ /\A#{iid}-(?!\d+-stable)/i
+ project.repository.branch_names.select do |branch|
+ branch.end_with?("-#{iid}")
end
end
@@ -131,8 +137,20 @@ class Issue < ActiveRecord::Base
end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
end
+ def moved?
+ !moved_to.nil?
+ end
+
+ def can_move?(user, to_project = nil)
+ if to_project
+ return false unless user.can?(:admin_issue, to_project)
+ end
+
+ !moved? && user.can?(:admin_issue, self.project)
+ end
+
def to_branch_name
- "#{iid}-#{title.parameterize}"
+ "#{title.parameterize}-#{iid}"
end
def can_be_worked_on?(current_user)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 188325045e2..ef48207f956 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -131,7 +131,6 @@ class MergeRequest < ActiveRecord::Base
validate :validate_branches
validate :validate_fork
- scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id).reorder(nil)) }
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
@@ -277,8 +276,14 @@ class MergeRequest < ActiveRecord::Base
self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
end
+ WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
+
def work_in_progress?
- !!(title =~ /\A\[?WIP(\]|:| )/i)
+ title =~ WIP_REGEX
+ end
+
+ def wipless_title
+ self.title.sub(WIP_REGEX, "")
end
def mergeable?
@@ -516,11 +521,15 @@ class MergeRequest < ActiveRecord::Base
end
def target_sha
- @target_sha ||= target_project.repository.commit(target_branch).sha
+ @target_sha ||= target_project.repository.commit(target_branch).try(:sha)
end
def source_sha
- last_commit.try(:sha)
+ last_commit.try(:sha) || source_tip.try(:sha)
+ end
+
+ def source_tip
+ source_branch && source_project.repository.commit(source_branch)
end
def fetch_ref
@@ -568,8 +577,11 @@ class MergeRequest < ActiveRecord::Base
end
def compute_diverged_commits_count
+ return 0 unless source_sha && target_sha
+
Gitlab::Git::Commit.between(target_project.repository.raw_repository, source_sha, target_sha).size
end
+ private :compute_diverged_commits_count
def diverged_from_target_branch?
diverged_commits_count > 0
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 374590ba0c5..de7183bf6b4 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -121,8 +121,8 @@ class Milestone < ActiveRecord::Base
active? && issues.opened.count.zero?
end
- def is_empty?
- total_items_count.zero?
+ def is_empty?(user = nil)
+ total_items_count(user).zero?
end
def author_id
diff --git a/app/models/project.rb b/app/models/project.rb
index ab4913e99a8..9c8246e8ac0 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -73,7 +73,7 @@ class Project < ActiveRecord::Base
update_column(:last_activity_at, self.created_at)
end
- # update visibility_levet of forks
+ # update visibility_level of forks
after_update :update_forks_visibility_level
def update_forks_visibility_level
return unless visibility_level < visibility_level_was
@@ -197,6 +197,8 @@ class Project < ActiveRecord::Base
validate :avatar_type,
if: ->(project) { project.avatar.present? && project.avatar_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
+ validate :visibility_level_allowed_by_group
+ validate :visibility_level_allowed_as_fork
add_authentication_token_field :runners_token
before_save :ensure_runners_token
@@ -215,8 +217,6 @@ class Project < ActiveRecord::Base
scope :in_group_namespace, -> { joins(:group) }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
- scope :public_only, -> { where(visibility_level: Project::PUBLIC) }
- scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
@@ -246,10 +246,6 @@ class Project < ActiveRecord::Base
end
class << self
- def public_and_internal_levels
- [Project::PUBLIC, Project::INTERNAL]
- end
-
def abandoned
where('projects.last_activity_at < ?', 6.months.ago)
end
@@ -435,6 +431,7 @@ class Project < ActiveRecord::Base
def safe_import_url
result = URI.parse(self.import_url)
result.password = '*****' unless result.password.nil?
+ result.user = '*****' unless result.user.nil? || result.user == "git" #tokens or other data may be saved as user
result.to_s
rescue
self.import_url
@@ -442,10 +439,25 @@ class Project < ActiveRecord::Base
def check_limit
unless creator.can_create_project? or namespace.kind == 'group'
- errors[:limit_reached] << ("Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it")
+ self.errors.add(:limit_reached, "Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it")
end
rescue
- errors[:base] << ("Can't check your ability to create project")
+ self.errors.add(:base, "Can't check your ability to create project")
+ end
+
+ def visibility_level_allowed_by_group
+ return if visibility_level_allowed_by_group?
+
+ level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase
+ group_level_name = Gitlab::VisibilityLevel.level_name(self.group.visibility_level).downcase
+ self.errors.add(:visibility_level, "#{level_name} is not allowed in a #{group_level_name} group.")
+ end
+
+ def visibility_level_allowed_as_fork
+ return if visibility_level_allowed_as_fork?
+
+ level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase
+ self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.")
end
def to_param
@@ -571,10 +583,7 @@ class Project < ActiveRecord::Base
end
def avatar_in_git
- @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png')
- @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg')
- @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif')
- @avatar_file
+ repository.avatar
end
def avatar_url
@@ -879,6 +888,7 @@ class Project < ActiveRecord::Base
# Forked import is handled asynchronously
unless forked?
if gitlab_shell.add_repository(path_with_namespace)
+ repository.after_create
true
else
errors.add(:base, 'Failed to create repository via gitlab-shell')
@@ -963,9 +973,25 @@ class Project < ActiveRecord::Base
issues.opened.count
end
- def visibility_level_allowed?(level)
+ def visibility_level_allowed_as_fork?(level = self.visibility_level)
return true unless forked?
- Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
+
+ # self.forked_from_project will be nil before the project is saved, so
+ # we need to go through the relation
+ original_project = forked_project_link.forked_from_project
+ return true unless original_project
+
+ level <= original_project.visibility_level
+ end
+
+ def visibility_level_allowed_by_group?(level = self.visibility_level)
+ return true unless group
+
+ level <= group.visibility_level
+ end
+
+ def visibility_level_allowed?(level = self.visibility_level)
+ visibility_level_allowed_as_fork?(level) && visibility_level_allowed_by_group?(level)
end
def runners_token
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 59b1b86d1fb..7c1a61bb0bf 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -123,23 +123,27 @@ class ProjectWiki
end
def repository
- Repository.new(path_with_namespace, @project)
+ @repository ||= Repository.new(path_with_namespace, @project)
end
def default_branch
wiki.class.default_ref
end
- private
-
def create_repo!
if init_repo(path_with_namespace)
- Gollum::Wiki.new(path_to_repo)
+ wiki = Gollum::Wiki.new(path_to_repo)
else
raise CouldNotCreateWikiError
end
+
+ repository.after_create
+
+ wiki
end
+ private
+
def init_repo(path_with_namespace)
gitlab_shell.add_repository(path_with_namespace)
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index e555e97689d..13154eb4205 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -3,6 +3,10 @@ require 'securerandom'
class Repository
class CommitError < StandardError; end
+ # Files to use as a project avatar in case no avatar was uploaded via the web
+ # UI.
+ AVATAR_FILES = %w{logo.png logo.jpg logo.gif}
+
include Gitlab::ShellAdapter
attr_accessor :path_with_namespace, :project
@@ -38,12 +42,15 @@ class Repository
end
def exists?
- return false unless raw_repository
+ return @exists unless @exists.nil?
- raw_repository.rugged
- true
- rescue Gitlab::Git::Repository::NoRepository
- false
+ @exists = cache.fetch(:exists?) do
+ begin
+ raw_repository && raw_repository.rugged ? true : false
+ rescue Gitlab::Git::Repository::NoRepository
+ false
+ end
+ end
end
def empty?
@@ -223,12 +230,6 @@ class Repository
send(key)
end
end
-
- branches.each do |branch|
- unless cache.exist?(:"diverging_commit_counts_#{branch.name}")
- send(:diverging_commit_counts, branch)
- end
- end
end
def expire_tags_cache
@@ -241,12 +242,13 @@ class Repository
@branches = nil
end
- def expire_cache(branch_name = nil)
+ def expire_cache(branch_name = nil, revision = nil)
cache_keys.each do |key|
cache.expire(key)
end
expire_branch_cache(branch_name)
+ expire_avatar_cache(branch_name, revision)
# This ensures this particular cache is flushed after the first commit to a
# new repository.
@@ -296,18 +298,6 @@ class Repository
@tag_count = nil
end
- def rebuild_cache
- cache_keys.each do |key|
- cache.expire(key)
- send(key)
- end
-
- branches.each do |branch|
- cache.expire(:"diverging_commit_counts_#{branch.name}")
- diverging_commit_counts(branch)
- end
- end
-
def lookup_cache
@lookup_cache ||= {}
end
@@ -316,12 +306,40 @@ class Repository
cache.expire(:branch_names)
end
+ def expire_avatar_cache(branch_name = nil, revision = nil)
+ # Avatars are pulled from the default branch, thus if somebody pushes to a
+ # different branch there's no need to expire anything.
+ return if branch_name && branch_name != root_ref
+
+ # We don't want to flush the cache if the commit didn't actually make any
+ # changes to any of the possible avatar files.
+ if revision && commit = self.commit(revision)
+ return unless commit.diffs.
+ any? { |diff| AVATAR_FILES.include?(diff.new_path) }
+ end
+
+ cache.expire(:avatar)
+
+ @avatar = nil
+ end
+
+ def expire_exists_cache
+ cache.expire(:exists?)
+ @exists = nil
+ end
+
+ # Runs code after a repository has been created.
+ def after_create
+ expire_exists_cache
+ end
+
# Runs code just before a repository is deleted.
def before_delete
expire_cache if exists?
expire_root_ref_cache
expire_emptiness_caches
+ expire_exists_cache
end
# Runs code just before the HEAD of a repository is changed.
@@ -347,11 +365,12 @@ class Repository
# Runs code after a repository has been forked/imported.
def after_import
expire_emptiness_caches
+ expire_exists_cache
end
# Runs code after a new commit has been pushed.
- def after_push_commit(branch_name)
- expire_cache(branch_name)
+ def after_push_commit(branch_name, revision)
+ expire_cache(branch_name, revision)
end
# Runs code after a new branch has been created.
@@ -857,6 +876,14 @@ class Repository
end
end
+ def avatar
+ @avatar ||= cache.fetch(:avatar) do
+ AVATAR_FILES.find do |file|
+ blob_at_branch('master', file)
+ end
+ end
+ end
+
private
def cache
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 5f91991f781..d85f7bfdf57 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -5,14 +5,15 @@
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
-# target_id :integer not null
+# target_id :integer
# target_type :string not null
# author_id :integer
-# note_id :integer
# action :integer not null
# state :string not null
# created_at :datetime
# updated_at :datetime
+# note_id :integer
+# commit_id :string
#
class Todo < ActiveRecord::Base
@@ -27,7 +28,9 @@ class Todo < ActiveRecord::Base
delegate :name, :email, to: :author, prefix: true, allow_nil: true
- validates :action, :project, :target, :user, presence: true
+ validates :action, :project, :target_type, :user, presence: true
+ validates :target_id, presence: true, unless: :for_commit?
+ validates :commit_id, presence: true, if: :for_commit?
default_scope { reorder(id: :desc) }
@@ -36,7 +39,7 @@ class Todo < ActiveRecord::Base
state_machine :state, initial: :pending do
event :done do
- transition [:pending, :done] => :done
+ transition [:pending] => :done
end
state :pending
@@ -50,4 +53,25 @@ class Todo < ActiveRecord::Base
target.title
end
end
+
+ def for_commit?
+ target_type == "Commit"
+ end
+
+ # override to return commits, which are not active record
+ def target
+ if for_commit?
+ project.commit(commit_id) rescue nil
+ else
+ super
+ end
+ end
+
+ def target_reference
+ if for_commit?
+ target.short_id
+ else
+ target.to_reference
+ end
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index c011af03591..9c315cfe966 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -435,7 +435,7 @@ class User < ActiveRecord::Base
Group.where("namespaces.id IN (#{union.to_sql})")
end
- # Returns the groups a user is authorized to access.
+ # Returns projects user is authorized to access.
def authorized_projects
Project.where("projects.id IN (#{projects_union.to_sql})")
end