diff options
author | Alfredo Sumaran <alfredo@gitlab.com> | 2016-03-22 11:13:27 -0500 |
---|---|---|
committer | Alfredo Sumaran <alfredo@gitlab.com> | 2016-03-22 11:13:27 -0500 |
commit | a42b2c388df5154eecefd8eb7d8a68373e794d6e (patch) | |
tree | 7cfcbca6f78d66fb05d56586f322067dcb4d9c92 /app/models | |
parent | 647f28bd1d92eabe239a7f0f4a65eb100c0cda73 (diff) | |
parent | 18c049886e0f9ad2d094f02483aea272d0e029fb (diff) | |
download | gitlab-ce-a42b2c388df5154eecefd8eb7d8a68373e794d6e.tar.gz |
Merge branch 'master' into issue_7959
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/ability.rb | 63 | ||||
-rw-r--r-- | app/models/application_setting.rb | 1 | ||||
-rw-r--r-- | app/models/commit_status.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/internal_id.rb | 5 | ||||
-rw-r--r-- | app/models/concerns/issuable.rb | 11 | ||||
-rw-r--r-- | app/models/concerns/milestoneish.rb | 20 | ||||
-rw-r--r-- | app/models/event.rb | 6 | ||||
-rw-r--r-- | app/models/group.rb | 37 | ||||
-rw-r--r-- | app/models/issue.rb | 32 | ||||
-rw-r--r-- | app/models/merge_request.rb | 20 | ||||
-rw-r--r-- | app/models/milestone.rb | 4 | ||||
-rw-r--r-- | app/models/project.rb | 56 | ||||
-rw-r--r-- | app/models/project_wiki.rb | 12 | ||||
-rw-r--r-- | app/models/repository.rb | 79 | ||||
-rw-r--r-- | app/models/todo.rb | 32 | ||||
-rw-r--r-- | app/models/user.rb | 2 |
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 |